aim-init
Wire a repo into ai-memory so its sessions auto-capture and the agent recalls
durable knowledge. Works greenfield or brownfield (incl. migrating off the old
qmd/wiki/ stack).
ai-memory replaces the qmd wiki/ + local-index stack: knowledge lives server-side
in the ai-memory instance (recalled via the MCP), not in a per-repo wiki/ folder.
Full usage model in references/usage.md.
Server side (install a new instance, upgrade the engine, providers, consumer keys,
backup/restore) is aim-ops — this skill only wires clients to it.
Route intent
- "how's it looking?", "do I need to migrate?", "doctor" (also PT: "como está?", "preciso migrar?") → run doctor (read-only diagnosis).
- "set up ai-memory here", "turn on capture", "init" (also PT: "configura ai-memory aqui", "liga a captura") → install (greenfield).
- "migrate off qmd", "drop qmd and use ai-memory" (also PT: "migra do qmd", "tira o qmd e põe ai-memory") → migrate (brownfield).
- "update/pair the CLI", "refresh the hooks", "it's on an old version", "upgrade" (also PT: "atualiza/pareia o CLI", "refresca os hooks", "tá numa versão velha") → refresh
(detect the environment + bring the local CLI and hooks to parity with the server — no
marker/snippet/MCP changes).
Before writing anything, confirm the three routing parameters with the user — there can be
multiple ai-memory instances (e.g. a personal memory.example.dev, a separate org
instance), so never hardcode the endpoint:
- MCP endpoint — the ai-memory instance URL (e.g.
https://memory.example.dev/mcp) and
the server name to register it under (e.g. memory-personal, memory-acme). Ask which
instance this repo should talk to.
- Workspace — personal →
default; org repo → its own workspace (e.g. acmecorp).
- Project — defaults to the repo basename; let the user override.
The endpoint chosen here is what aim-query / aim-write will target later (by server name).
CLI prerequisite (no Docker wrapper by default)
aim-init needs the ai-memory CLI to install MCP entries, install hooks, and refresh the
routing snippet. Prefer a native CLI binary over the Docker wrapper:
- If
ai-memory is already on PATH, run ai-memory --version and compare it to the latest
upstream release (gh release view -R akitaonrails/ai-memory --json tagName -q .tagName, or
the releases/latest page). Keep it only if it's at a recent release — a CLI behind it misses
newer subcommands (e.g. auto-improve, pending-writes, curator) and hook fixes, so upgrade
it (see refresh). The server's exact .version lives at GET /admin/status, which needs
the admin token (a user's OIDC/JWT gets 401 there) — so the latest release is the practical
parity target; the connected MCP client also learns the server version from its initialize
handshake.
- If it is missing or stale, check the latest upstream release first:
https://github.com/akitaonrails/ai-memory/releases/latest.
- Linux: download the matching
ai-memory-linux-<arch>.tar.gz asset, verify its .sha256,
and install the binary into ~/.local/bin/ai-memory (or another user-owned PATH dir).
- Windows: download
ai-memory-windows-x86_64.zip, verify its .sha256, and follow the
bundled Windows docs.
- macOS/Darwin: download the matching
ai-memory-macos-<arch>.tar.gz asset
(ai-memory-macos-aarch64.tar.gz on Apple Silicon, -x86_64 on Intel), verify its
.sha256, and install the binary into ~/.local/bin/ai-memory. Fallback only if no asset
for your arch: cargo install --git https://github.com/akitaonrails/ai-memory --tag <latest-tag>.
- Use the Docker wrapper (
bin/ai-memory or the quick-start wrapper from GitHub) only when the
user explicitly wants Docker. Do not suggest it as the default local CLI on macOS or when the
user asks for a non-Docker setup.
After installing, verify:
command -v ai-memory
ai-memory --version
AI_MEMORY_SERVER_URL=https://memory.example.dev AI_MEMORY_AUTH_TOKEN=<token-or-env> ai-memory status --json
What a wired repo has
.ai-memory.toml at the repo root — stable, non-secret routing for captures + recall
to a workspace/project, and (optionally) the Keycloak/OIDC instance this repo onboards onto.
Tracking policy is contextual, not automatic:
- Private/team repo with shared routing: commit it so every developer gets the same
workspace/project and onboarding profile on clone. These values are instance config, NOT
credentials, and normally do not change after setup.
- Public repo, personal fork, or per-operator routing: keep it local via the repo-local
.git/info/exclude.
Do not add it to the global core.excludesfile, and do not run
git rm --cached .ai-memory.toml unless the user explicitly chooses local-only routing.
workspace = "default"
project = "<repo-name>"
# Optional routing/policy (defaults shown; uncomment to change — commented = no-op):
# project_strategy = "repo-root" # collapse worktrees/subdirs to the git root (default: basename)
# drop_subagent_captures = "true" # accept-but-don't-store THIS project's subagent captures (default: off)
# Optional: Keycloak/OIDC onboarding profile the agent reads to wire hooks
# WITHOUT asking (see "Keycloak-gated instance"). Non-secret. The native hook
# reads the routing/policy keys above but NOT this [instance] block, so the
# block is inert at runtime — it exists purely so `aim-init` can self-serve
# the device-flow setup.
[instance]
issuer = "https://kc.example/auth/realms/<realm>"
server_url = "https://memory.example.dev/wiki" # ai-memory URL incl. base path
client_id = "ai-memory-cli" # device-flow client
agents = ["claude-code"] # agents to wire hooks for
Routing snippet in CLAUDE.md and AGENTS.md — the <!-- ai-memory:start -->…end -->
block. It drives proactive recall mid-session. The block's text is owned by the
ai-memory binary, not by this skill — obtain the current canonical block from the
source and write it between the markers; do not hand-maintain a copy here (a copy
drifts from the binary):
- Agent:
memory_install_self_routing (returns the block for your Write/Edit tool).
- CLI:
ai-memory install-instructions (writes CLAUDE.md; --target AGENTS.md).
The canonical block is already generic (no workspace/project/server names, no page
paths — the .ai-memory.toml marker scopes recall and the MCP auto-scopes), so it is safe
to commit even in a public repo. Whatever you do, never expand the committed block into a
"where things live" map that enumerates the knowledge base — that leaks the project's
internal information architecture. Cross-scope / shared-rules wiring belongs in
operator-global config (~/.claude/CLAUDE.md), not the per-repo block.
MCP server entry in .mcp.json (Claude), opencode.json (OpenCode), .codex/config.toml
(Codex) — points at the ai-memory instance (template: templates/mcp-entry.json.tmpl).
Prefer the CLI installer so each agent gets its native global config:
ai-memory install-mcp --client codex --server-url https://memory.example.dev/mcp --name memory-personal --apply
ai-memory install-mcp --client open-code --server-url https://memory.example.dev/mcp --name memory-personal --apply
ai-memory install-mcp --client claude-code --server-url https://memory.example.dev/mcp --name memory-personal --apply
MCP definitions are stable repo config when they contain only server names/URLs and no
credentials. A private/team repo MAY commit .mcp.json so every agent discovers the same
MCP endpoints on clone. Keep personal endpoints or auth-bearing entries (for example
Authorization headers, bearer tokens, or per-user secrets) in the agent's global/user
config, or keep a local-only .mcp.json via .git/info/exclude.
Do not add .mcp.json to the global core.excludesfile, and do not untrack an
existing .mcp.json automatically. Only run git rm --cached .mcp.json after the user
explicitly chooses local-only MCP config.
Auto-capture hooks are global (Claude settings, Codex ~/.codex/hooks.json,
OpenCode plugin config, plus staged hook scripts under the platform data dir:
~/.local/share/ai-memory/hooks/ on Linux,
~/Library/Application Support/ai-memory/hooks/ on macOS, and
%LOCALAPPDATA%\ai-memory\hooks\ on Windows). They are marker-gated: they fire in any
repo that has .ai-memory.toml. A repo needs **no per-repo hook scripts** — just the marker.
Install or refresh them per agent with the capture base URL, not the /mcp URL.
Hook auth — pick the mode that matches the server. This is the #1 cause of silent
capture failure: the wrong mode 401s on every drain, the spool fills, captures are lost.
There are four modes:
- Open hook routes (no auth on
/hook+/handoff) → install with no --auth-token.
- Static bearer — the server accepts a shared/per-user token on
/hook+/handoff
(e.g. AI_MEMORY_AUTH_TOKEN on a single-tenant engine, or a token from
ai-memory user add) → pass it via --auth-token. Embedded in each agent's hook config,
so treat that file as sensitive (chmod 600).
- Consumer key (
amk_, keys-only gateway) — the mcp-auth sidecar runs without an
identity provider and resolves per-consumer keys locally (KEYS_DB). Each agent/CLI gets
its own key (POST /keys with an admin-scoped key; issued by aim-ops keys), scoped
read,write; pass it via --auth-token for hooks and as the MCP Authorization header.
Attribution comes from the key's actor_user. Expect 403 on /admin/* with a
non-admin key — by design, not a server fault. Rotation = issue new + revoke old.
- OIDC / Keycloak gateway — a forwardAuth sidecar (
mcp-auth) that validates a JWT
and answers 401 WWW-Authenticate: Bearer resource_metadata=… (RFC 9728). A static hex
token is rejected (401) here — the gateway only accepts a Keycloak JWT, and the
engine's upstream bearer is proxy-injected, not client-sendable. Use a per-developer
OIDC device token: run ai-memory auth login oidc-device --issuer <oidc-issuer> --client-id <public-device-client> once (browser approval), then install hooks
without --auth-token — the hook's resolve_bearer loads the token from auth.json
and refreshes it headlessly. Full recipe in "Second / custom instance" below.
Quick test of which mode a server is in: curl -sI <server>/hook → 401 with a
WWW-Authenticate: Bearer resource_metadata= header means OIDC/Keycloak (device flow);
a plain 401/200 without that header means static-bearer/open.
GET <server>/keys/whoami → 200 with {identity, can_issue} means the keys-only gateway
is present (consumer keys); 404 means the sidecar has no KEYS_DB.
Add --hooks-dir <ai-memory/hooks> when the binary can't locate its vendored scripts
(e.g. a cargo install build):
# static-bearer server:
TOKEN=<HOOK_AUTH_TOKEN>
ai-memory install-hooks --apply --agent claude-code --server-url https://memory.example.dev --auth-token "$TOKEN"
# OIDC/Keycloak server (after `auth login oidc-device`) OR open server — NO --auth-token:
ai-memory install-hooks --apply --agent claude-code --server-url https://memory.example.dev
ai-memory install-hooks --apply --agent codex --server-url https://memory.example.dev
ai-memory install-hooks --apply --agent open-code --server-url https://memory.example.dev
Modern install-hooks wires native hooks (ai-memory hook --event … calling the binary
directly) instead of shell scripts — the binary emits the correct per-agent stdout contract
(e.g. Claude Code's hookSpecificOutput.additionalContext JSON wrapper for handoff
injection), so there are no hand-patched hook scripts to drift. Do not hand-maintain a
custom _lib.sh overlay for auth (e.g. a Keycloak token-mint prepend) — the supported paths
are --auth-token (static-bearer servers) or auth login oidc-device + no---auth-token
(OIDC/Keycloak servers). The device token is not minted per event: it's stored once in
auth.json and refreshed headlessly at drain time.
On Codex, confirm ~/.codex/hooks.json contains the ai-memory lifecycle hooks and trust the
new hook commands when Codex prompts on the next start. Grok Build CLI (~/.grok/hooks/*.json)
is not yet a supported --agent — integrate manually, and note that its SessionStart ignores
hook stdout (no handoff injection; use the MCP memory_handoff_accept instead). (Legacy qmd
repos have per-repo wiki-reindex hooks; migration removes them.)
Hook config — per-platform paths + delivery model (macOS / Linux / Windows)
install-hooks and auth login oidc-device both default to the platform data dir
(dirs::data_local_dir()), where auth.json, the hook-spool/, and the staged hook scripts
live — so the OIDC device token lands exactly where the hooks look for it. Same flow on all
three OSes; only the paths differ:
| OS |
data dir (auth.json, hook-spool/, hooks/) |
Claude Code settings |
| macOS |
~/Library/Application Support/ai-memory |
~/.claude/settings.json |
| Linux |
~/.local/share/ai-memory |
~/.claude/settings.json |
| Windows |
%LOCALAPPDATA%\ai-memory |
%USERPROFILE%\.claude\settings.json |
Hooks are native (ai-memory hook --event … calls the binary directly — no shell spawn;
Windows uses the WindowsNative config with the same spool + OIDC-token fallback). Codex →
~/.codex/hooks.json; OpenCode → its plugin config. If the binary can't find its vendored
scripts (e.g. a cargo install build), pass --hooks-dir <data_dir>/hooks. auth.json is
sensitive → chmod 600 on POSIX (the CLI writes it that way).
Delivery is batched at session boundaries, not per event. Per-event hooks
(user-prompt-submit, pre/post-tool-use, pre-compact, stop) only enqueue to
<data_dir>/hook-spool (instant, fire-and-forget). The spool is drained on
session-start (clears prior-session backlog) and session-end (the main delivery
point). So "captures don't show up immediately" is normal — they flush at the next boundary.
resolve_bearer at drain time: explicit --auth-token wins (static); else the OIDC token
from auth.json (refreshed if near expiry); else anonymous. The spool drops entries after 8
failed attempts or 7 days, capped at 10000 files. Symptom of a wrong auth mode:
hook-spool filling toward 10000 with auth_mode: static entries that 401 forever (a static
token against a Keycloak gateway) → switch to device flow and clear the dead backlog.
Token lifetime (device flow): access token auto-refreshes; the refresh token lasts as long as
the issuer's offline-session idle window — after long inactivity, re-run auth login oidc-device. Verify state any time with ai-memory auth status.
Keycloak-gated instance — the agent drives onboarding (mac / Linux / Windows)
The developer should not hand-run setup scripts. When a dev needs hook
capture on a Keycloak/OIDC instance, you (the agent) drive it: gather the
inputs, check/create the device client, run the per-machine setup, and verify.
The dev only answers a couple of questions and approves a browser prompt once.
The scripts/ are your canonical, cross-platform command reference
(*.sh for macOS/Linux, *.ps1 for Windows) — invoke them, or run the
equivalent ai-memory commands directly.
Inputs you need — read them from the repo's .ai-memory.toml [instance] block
first (issuer / server_url / client_id / agents, see "What a wired repo has");
only ask the dev (or fall back to operator-global config) when that block is absent:
ISSUER — realm URL, e.g. https://kc.example/auth/realms/<realm>.
SERVER_URL — ai-memory server URL including any base path, e.g.
https://memory.example.dev or https://memory.example.dev/wiki.
CLIENT_ID — the device-flow client (default ai-memory-cli).
- which
AGENTS the dev uses (claude-code, codex, open-code, …).
When the [instance] block is present (e.g. committed in a private team repo), the agent
uses it and does NOT ask — git clone + aim-init is turnkey for the next developer.
1. Check the device-flow client exists (read-only):
GET <ISSUER>/.well-known/openid-configuration (confirm device_authorization_endpoint),
then POST client_id=<CLIENT_ID>&scope=openid to that endpoint. A device_code
→ ready. invalid_client (no such client) or unauthorized_client … device flow is disabled → the realm needs the client (step 2). The browser/MCP client is
PKCE-enforced and 400s with Missing parameter: code_challenge_method, so it
can't be reused — that's why a dedicated client exists.
2. Create the client IF missing — one-time per realm, needs Keycloak admin.
If you have Keycloak admin access, use scripts/kc-create-device-client.sh
(idempotent; public, device-only, no PKCE). For a containerized Keycloak run it
INSIDE the pod (it runs sh there regardless of your OS), reusing the pod's own
admin env so no secret leaves the cluster:
# macOS/Linux/WSL:
kubectl exec -i <keycloak-pod> -- sh -c \
'CLIENT_ID=ai-memory-cli KC_SERVER=http://localhost:8080/auth KC_REALM=<realm> \
KC_ADMIN_USER="$KEYCLOAK_ADMIN" KC_ADMIN_PASS="$KEYCLOAK_ADMIN_PASSWORD" sh -s' \
< scripts/kc-create-device-client.sh
# Windows PowerShell: Get-Content scripts/kc-create-device-client.sh | kubectl exec -i <pod> -- sh -c '…'
Adjust KC_SERVER to …:8080 vs …:8080/auth per the realm's
KC_HTTP_RELATIVE_PATH (a 404 Not Found on login means flip it). If you lack
admin access, ask the operator to run it once, then resume.
3. Run the per-machine setup yourself — don't make the dev run a script. Per
the dev's OS:
- macOS/Linux:
ISSUER=… SERVER_URL=… CLIENT_ID=… AGENTS="…" sh scripts/dev-setup-hooks.sh
- Windows:
pwsh scripts/dev-setup-hooks.ps1 -Issuer … -ServerUrl … -Agents "…"
- Either way it runs
ai-memory auth login oidc-device --issuer <ISSUER> --client-id <CLIENT_ID> (the dev approves the printed URL in their browser
ONCE) then ai-memory install-hooks --apply --agent <each> --server-url <SERVER_URL> (NO --auth-token).
4. Verify: ai-memory auth status → oidc-device: logged in. The dev needs
the realm role the server checks (mcp:read/mcp:write) — the same one MCP
login already requires.
Record each instance's ISSUER/SERVER_URL/CLIENT_ID in operator-global
config (~/.claude/CLAUDE.md or a private runbook), NOT in this skill — the
scripts stay generic; these endpoints are operator/instance-specific.
Second / custom instance (e.g. a client Keycloak instance)
Some repos talk to two ai-memory instances — a personal one and a client/org one
that authenticates with OAuth/OIDC (Keycloak, RFC 9728) rather than (or in addition to) a
static bearer. Both are read + write; keep them in sync, with the personal instance as the
superset (it accumulates everything any custom instance has).
- Hook capture works against an OAuth/Keycloak instance too. A hook is headless and cannot run
an interactive OAuth PKCE flow per event, so it uses a stored OIDC device-flow token: run
ai-memory auth login oidc-device --issuer <oidc-issuer> --client-id <public-device-client>
once (browser approval), and the hook (resolve_bearer) loads it from auth.json and refreshes
it headlessly. Then install-hooks without --auth-token.
- A forwardAuth-JWT-only gateway rejects static bearers. If the gateway answers
401 WWW-Authenticate: Bearer resource_metadata=… (RFC 9728), a static hex token gets 401 —
the upstream bearer is proxy-injected, not client-sendable — so device flow is the only
headless option there. Only pass --auth-token when the instance genuinely accepts a static
HOOK_AUTH_TOKEN on /hook.
- Device-flow client requirements (issuer side): a public client with the
device-authorization grant enabled and no PKCE enforcement (PKCE has no place in the
device grant — there's no redirect; keep the browser/DCR/MCP client, which does enforce PKCE,
as a separate client so you don't have to weaken it). The user needs the realm role the
server checks (e.g.
mcp:read/mcp:write) — those ride into the JWT via the realm-role mapper,
not via a scope.
- Dual-capture: global hooks → personal instance; project-level hooks
(
.claude/settings.local.json) → the client instance. Each leg uses that instance's auth
mode (static bearer or OIDC device token — they're independent). With both wired, every lifecycle
event captures to both. (Or capture to one and reconcile via sync.)
- Dual-write durable pages: a
memory_write_page in a two-instance project goes to both
MCPs, same (workspace, project).
- Pass explicit
workspace+project on recall/write to the client instance — with per_actor
- the scope-bleed fail-closed fix, an un-scoped call lands on that instance's baked default. The
.ai-memory.toml marker gives the canonical scope.
- Keep in sync (bidirectional, on-demand): personal ⊇ everything; reconcile drift both ways
preserving
(workspace, project).
This operator-specific dual-instance wiring belongs in operator-global config
(~/.claude/CLAUDE.md or a shared memory rule), not the committed per-repo CLAUDE.md
ai-memory block — that block is generic + binary-owned, and in a shared/client repo it would
leak the dual-instance/capture setup and the project's information architecture.
doctor (read-only)
Report, without writing:
- Is the
ai-memory CLI available on PATH? What version? If missing/stale, report the native
install path from the latest upstream release — download the ai-memory-<os>-<arch>.tar.gz
asset (macOS ships ai-memory-macos-aarch64/-x86_64; Linux ai-memory-linux-aarch64/
-x86_64), verify its .sha256; cargo install only if no asset for your arch. Do not
fall back to the Docker wrapper unless the user wants Docker.
- Can the CLI reach the chosen server (
ai-memory status --json with the right
AI_MEMORY_SERVER_URL / auth env or flags)?
- CLI version parity. Compare
ai-memory --version (local) to the latest upstream release
(gh release view -R akitaonrails/ai-memory --json tagName -q .tagName). Flag a CLI behind it —
it lacks newer subcommands (auto-improve, pending-writes, curator) and hook fixes — and
recommend refresh. (The server's exact .version is admin-gated at /admin/status: a user's
OIDC/JWT gets 401, so don't rely on ai-memory status against the server for it; the latest
release is the checkable target, and the MCP initialize handshake also carries it.)
- Is there a
.ai-memory.toml? What workspace/project? Is its tracking policy intentional
(committed for shared stable routing, or ignored local-only)?
- Is the routing snippet present in CLAUDE.md / AGENTS.md?
- Does the snippet teach the search strategy? Flag a stale snippet that lacks the
"broaden when the current project misses" guidance — i.e. no mention of
scopes /
sibling projects, or no warning that memory_query returns snippets (not full page
bodies). Re-installing the current template (below) backfills it. This matters because
memory_query has no global search: an agent that searches only the current project
and stops will miss cross-cutting knowledge that lives in a sibling (infra/ops) project.
- Is an ai-memory MCP entry present? Check the agent's global config FIRST — the MCP is
usually installed there (
~/.claude.json top-level mcpServers, e.g. memory-personal;
~/.codex/config.toml; the OpenCode global config), then the per-repo .mcp.json /
opencode.json. Don't conclude "no recall" from an empty per-repo .mcp.json — a global
entry applies to every repo. If a per-repo .mcp.json IS used, inspect whether it contains
only stable non-secret definitions or auth material, then confirm its tracking policy matches
the repo's convention; tracked .mcp.json is valid for private/team repos with shared MCPs.
- For Codex, are global ai-memory hooks present in
~/.codex/hooks.json (SessionStart,
UserPromptSubmit, PreToolUse, PostToolUse, PreCompact, Stop) and do the staged
scripts exist under the platform ai-memory hooks dir?
- Are the hooks native + authenticated? Flag any agent still calling hand-patched shell
scripts under a custom overlay (e.g.
~/.config/ai-memory/hooks/ sourcing a Keycloak-mint
_lib.sh) instead of the binary's ai-memory hook --event … form — those drift and miss
upstream fixes (e.g. the hookSpecificOutput JSON wrapper + tab-escape fix that handoff
injection depends on). If the server gates /hook//handoff, confirm the hook carries the
right credential for that server's mode (otherwise captures 401 silently — the CLI
spools locally and returns {} regardless): a static-bearer server needs --auth-token;
an OIDC/Keycloak gateway (401 WWW-Authenticate: Bearer resource_metadata=…) needs a
device-flow token (auth login oidc-device) and the hooks installed without --auth-token
— a static token is rejected there. Re-run install-hooks --apply (with or without
--auth-token per mode) to migrate and remove any overlay dependency.
hook-spool at/near the 10000 cap — two DISTINCT causes; read auth_mode / attempts /
last_error on a few entries to tell them apart:
- Wrong auth (fixable):
auth_mode: static against a Keycloak/JWT gateway → attempts climb,
last_error shows 401, entries drop after 8 tries. Fix: device flow (auth login oidc-device)
install-hooks --apply without --auth-token, and clear the dead backlog.
- Drain cadence (NOT an auth bug):
auth_mode: oidc (correct), attempts: 0,
last_error: null — delivery works, but drains only fire at session-start / session-end, so
a marathon session exceeds MAX_SPOOL_FILES (10000) before it ends and the oldest are
evicted. Mitigate by draining at a boundary (start a fresh session, or run the session-start
hook manually to drain) or shorter sessions; the durable fix is mid-session incremental
draining (upstream — track the hook-spool delivery-reliability work). The spool is shared
across all agents on the same data dir, so parallel long sessions accumulate together.
- MCP auth (DCR): if an agent's MCP login shows Keycloak "Client not found", its cached
Dynamic-Client-Registration id was orphaned (realm recreated/migrated). Clear the stale entry
from the agent's MCP-auth cache (e.g. OpenCode's
~/.local/share/opencode/mcp-auth.json) and
re-auth to force a fresh DCR.
- MCP OAuth discovery failure: if a client's
reauth errors Could not discover OAuth endpoints from server response (it doesn't chase the RFC 9728 → 8414 discovery — seen with OMP's native MCP client), bridge the remote as a stdio server via npx -y mcp-remote <url> <port> in that client's own (git-excluded) config, keeping the committed .mcp.json portable type: http. If discovery+authorization then pass but the token POST 404s, the SDK looked up auth-server metadata at the domain root and dropped a path-based issuer's /<base>/realms/<realm> — a native client (Claude Code) connects via issuer-relative OIDC, so it's not a dead endpoint and not fixable by mcp-remote version/flags; fix with a server-side root .well-known/oauth-authorization-server (+ openid-configuration) alias to the realm, else use the native client. Full recipe: references/usage.md §MCP OAuth compatibility.
- Two-instance (e.g. client Keycloak) repo? If the repo registers a second MCP (a client/org
instance), confirm: both are treated as read+write with the personal instance as the
superset; durable writes are dual-written; if auto-capture to the client instance is wanted,
project-level hooks point at it with a static
HOOK_AUTH_TOKEN or a stored OIDC device-flow
token (ai-memory auth login oidc-device … → resolve_bearer refreshes headlessly) — capture is
feasible, not "impossible"; recall/write pass explicit workspace+project (per_actor
fail-closed). Flag any committed CLAUDE.md that hard-codes this dual-instance/capture wiring —
it belongs in operator-global config, not the shared per-repo block.
- Legacy qmd? Flag any of: a
qmd MCP entry, a wiki/ dir with CONVENTIONS.md, per-repo
*/hooks/wiki-reindex.sh, a "Wiki (wiki/)" block in CLAUDE.md/AGENTS.md, .wiki-guardrails.yml.
install (greenfield)
- Confirm workspace + project with the user.
- Ensure the native
ai-memory CLI is installed and functional (see "CLI prerequisite" above).
Install/upgrade from upstream release assets (macOS ships ai-memory-macos-<arch>.tar.gz,
Linux ai-memory-linux-<arch>.tar.gz; verify the .sha256); cargo install/source build
only if no asset for your arch. Avoid the Docker wrapper unless explicitly requested.
- Decide
.ai-memory.toml tracking policy from the repo context and user input. For a
private/team repo with stable shared routing, write it and leave it tracked/committable.
For public repos, personal forks, or per-operator routing, add it to the repo-local
.git/info/exclude. Do not use the global core.excludesfile, and do not untrack an
existing marker unless the user explicitly chooses local-only routing.
- Obtain the canonical routing block from the binary (
memory_install_self_routing, or
ai-memory install-instructions) and write it into CLAUDE.md and AGENTS.md (idempotent
— between the <!-- ai-memory:start -->/<!-- ai-memory:end --> markers; replace if
present). Don't paste a hand-maintained copy.
- Add the ai-memory MCP entry to the agent configs the repo uses. If the entry is stable and
contains no credentials, it may live in a tracked per-repo
.mcp.json; if it is personal or
auth-bearing, install it in the agent's global/user config or keep it local-only via
.git/info/exclude. Do not run git rm --cached .mcp.json unless the user explicitly asks
for local-only MCP config.
For Codex, prefer ai-memory install-mcp --client codex ... --apply so the server lands in
~/.codex/config.toml.
- Ensure global auto-capture hooks are installed for the active agents. For Codex, run
ai-memory install-hooks --agent codex --server-url https://memory.example.dev --apply and verify
~/.codex/hooks.json plus the staged ai-memory/hooks/codex scripts.
- Tell the user auto-capture is now live (global hooks + the new marker); recall is via the
session-start handoff + the routing snippet.
migrate (brownfield: qmd → ai-memory)
Run doctor first; then, with the user's confirmation:
- Marker — write
.ai-memory.toml (workspace/project) and preserve/choose the tracking
policy: commit stable shared routing in private/team repos; keep personal/public routing
local-only via .git/info/exclude.
- MCP — in
.mcp.json / opencode.json / .codex/config.toml, replace the qmd
server entry with the ai-memory entry. Preserve the repo's existing tracking policy; commit
stable non-secret server definitions when the repo intentionally shares MCPs, and keep
auth-bearing or personal endpoints local/global.
- CLAUDE.md / AGENTS.md — replace the "Wiki (
wiki/)" / qmd-MCP block with the routing
snippet. Drop instructions that tell the agent to query qmd or maintain wiki/.
- Remove ALL qmd-era artifacts. The old qmd/wiki setup installs more than hooks —
enumerate every one and remove it:
.wiki-guardrails.yml (guardrails config).
- Hooks —
.claude/hooks/wiki-*.sh (policy-check, reindex, drift-audit, suggest-ingest);
.codex/hooks/wiki-*.sh (policy-check, reindex, drift-audit, consider) + .codex/hooks.json;
.opencode/hooks/wiki-*.sh + .opencode/plugins/wiki-guardrails.js. Remove the matching
hook entries from .claude/settings.json, and [features] codex_hooks = true from
.codex/config.toml (it only existed to enable the codex wiki hooks).
opencode.json permission.skill."wiki-*" (swap to "aim-*" or drop).
- ANTIGRAVITY (if the repo uses it) — the managed instruction block + any
.antigravity*
hooks/config.
- Auto-capture now comes from the global ai-memory hooks — no per-repo hook scripts needed.
- Global QMD checkout/wrapper (operator-level, outside the repo) — the per-project wrapper
(
~/.local/share/skills/qmd/wrappers/<project>-qmd, or the legacy
~/.local/share/essential-skills/qmd/...) and the managed qmd checkout/cache are now
orphaned. Remove the wrapper; remove the shared checkout only if no other repo still uses qmd.
wiki/ content — leave it in place as history by default. If the user wants it in
ai-memory, ingest the markdown into the chosen workspace/project (one page per file,
redact any literal secrets) and then they can remove wiki/. Do not delete wiki/
without explicit confirmation.
- Re-run doctor to confirm: marker present + tracking policy intentional, snippet in
CLAUDE/AGENTS, ai-memory MCP wired with no committed secrets, and no qmd remnants (no
.wiki-guardrails.yml, no wiki-* hooks/plugins, no qmd MCP entry, no codex_hooks/
wiki-* permission leftovers).
refresh (upgrade CLI + re-stage hooks to parity)
For an already-wired environment that drifted from the server — CLI behind, or after the
server upgrades. No marker/snippet/MCP changes; detect what's installed and bring it to parity:
- Detect the current state (read-only): local
ai-memory --version vs the latest upstream
release (gh release view -R akitaonrails/ai-memory --json tagName -q .tagName; the server's
own .version is admin-gated at /admin/status → 401 for a user, so the release is the
checkable target); which agents have ai-memory hooks — ai-memory install-hooks --help lists the
canonical supported agents (don't hardcode a stale subset): claude-code
(~/.claude/settings.json), codex (~/.codex/hooks.json), gemini-cli (~/.gemini/settings.json),
antigravity-cli / agy (~/.gemini/config/hooks.json), grok (~/.grok/hooks/ai-memory.json),
cursor (~/.cursor/hooks.json), open-code (~/.config/opencode/plugins/), omp
(~/.omp/agent/extensions/), openclaw; and ai-memory auth status (is the OIDC device token valid?).
- Upgrade the CLI if behind the latest release (see "CLI prerequisite"): download the
ai-memory-<os>-<arch>.tar.gz asset, verify its .sha256, back up the current binary, and
install over ~/.local/bin/ai-memory. Confirm ai-memory --version is now ≥ the server's.
- Re-stage hooks for every detected agent:
ai-memory install-hooks --apply --agent <agent> --server-url <capture-url> (OIDC/Keycloak server → no --auth-token; static-bearer →
--auth-token). It re-verifies the staged scripts and refreshes the agent config (writes a
backup). Native hooks (ai-memory hook --event …) pick up the new binary automatically; this
refreshes the config + staged fallback scripts and drops any legacy _lib.sh overlay.
- Verify:
ai-memory --version matches the server; ai-memory auth status → logged in; the
agent configs still call ai-memory hook --event natively; <data_dir>/hook-spool is well
below the 10000 cap.
- ≥ 2.0 notes:
install-hooks --apply now stores the bearer in <data_dir>/auth-token
(0600) and renders a resolveToken() helper in generated TypeScript integrations; the
managed Agent Skills changed — refresh them with memory_install_self_routing (or
ai-memory install-instructions). Codex prompts Hooks need review on next start: trust
them. A CLI ≥ 2.0 talks fine to a 1.3x server for hooks (/hook/batch unchanged), but
upgrade the server first (aim-ops upgrade) and then clients, on the same day.
refresh does not touch the marker, routing snippet, or MCP entry (version-agnostic, and the
snippet is binary-owned) — run install / migrate for those. Hooks are global (one data
dir for all agents), so refreshing once updates every wired repo, not just the current one.
Verify
git check-ignore .ai-memory.toml → ignored (not committed).
- Routing snippet present once (between the markers) in CLAUDE.md + AGENTS.md.
- No
qmd MCP entry / wiki-reindex hooks remain (for migrate).
- A capture round-trips: a real agent session in the repo lands a page under the chosen
workspace/project (check via
aim-status / memory_status or /api/v1/projects).
Multi-user mode (optional)
The instructions above cover the single-operator case (one human, one ai-memory instance).
When multiple humans share an instance — or one operator wants strict per-repo
isolation between several parallel Claude Code windows — switch the engine to multi-user
mode and onboard each user explicitly. The ai-memory CLI already ships the workflow.
Pre-req: the engine must have [auth].token_pepper set (auto-generated by
ai-memory init; multi-user admin endpoints 503 otherwise). Confirm with
curl <endpoint>/admin/status or ai-memory status.
Per-user setup (canonical: one person, one identity)
Create the user on the engine (root token required). Token is printed exactly once:
ai-memory user add --username alice --email alice@home
# → {"id": "...", "username": "alice", "token": "<COPY-THIS-NOW>"}
Lifecycle subcommands: user list (no tokens surfaced), user expire/revive,
user rotate-token (issues fresh plaintext once).
Install hooks stamped with that user's bearer — one command per agent CLI the
person uses (claude-code, codex, cursor, gemini-cli, open-code, omp,
openclaw, antigravity-cli):
ai-memory install-hooks --apply \
--agent codex \
--as-user alice \
--auth-token <alice-token>
Use --agent claude-code or --agent open-code for those harnesses.
--apply mutates the agent config in place (idempotent; writes a timestamped backup).
Defaults to the global hook config (~/.claude/settings.json, ~/.codex/hooks.json,
OpenCode plugin config, …).
Pass --config-file ./.claude/settings.json to target project-level config instead
(see "tokens per context" below).
Wire the bearer into the MCP entry so MCP tool calls authenticate as the same
user the hooks do:
{
"mcpServers": {
"memory-personal": {
"type": "http",
"url": "https://memory.example.dev/mcp",
"httpHeaders": { "Authorization": "Bearer <alice-token>" }
}
}
}
ai-memory install-mcp --apply --auth-token <alice-token> writes this entry for you.
Run the engine in per_actor mode (chart: aiMemory.autoScope.mode: per_actor).
The active-project map keys by (user, session_id) with a user-only fallback —
Alice never inherits Bob's last project on a session-less probe, and writes from
…(truncated)
1---2name: aim-init3description: Initialize, migrate, or refresh a repo's ai-memory wiring: the .ai-memory.toml routing marker (workspace/project), the recall/write routing snippet in CLAUDE.md/AGENTS.md, the ai-memory MCP server entry, and the global capture hooks. Includes the qmd→ai-memory migration for repos still on the old wiki/qmd stack, and a refresh flow that detects CLI↔server version skew and re-stages hooks to parity. Use when the user asks to set up ai-memory in a project (greenfield or brownfield), wire the MCP, enable auto-capture, migrate off qmd, diagnose with doctor, or upgrade/refresh an existing setup (local CLI + hooks) to the server's version.4---56# aim-init78Wire a repo into **ai-memory** so its sessions auto-capture and the agent recalls9durable knowledge. Works greenfield or brownfield (incl. migrating off the old10`qmd`/`wiki/` stack).1112> ai-memory replaces the qmd `wiki/` + local-index stack: knowledge lives server-side13> in the ai-memory instance (recalled via the MCP), not in a per-repo `wiki/` folder.14> Full usage model in [references/usage.md](references/usage.md).15> **Server side** (install a new instance, upgrade the engine, providers, consumer keys,16> backup/restore) is [aim-ops](../aim-ops/SKILL.md) — this skill only wires clients to it.1718## Route intent1920- "how's it looking?", "do I need to migrate?", "doctor" (also PT: "como está?", "preciso migrar?") → run **doctor** (read-only diagnosis).21- "set up ai-memory here", "turn on capture", "init" (also PT: "configura ai-memory aqui", "liga a captura") → **install** (greenfield).22- "migrate off qmd", "drop qmd and use ai-memory" (also PT: "migra do qmd", "tira o qmd e põe ai-memory") → **migrate** (brownfield).23- "update/pair the CLI", "refresh the hooks", "it's on an old version", "upgrade" (also PT: "atualiza/pareia o CLI", "refresca os hooks", "tá numa versão velha") → **refresh**24 (detect the environment + bring the local CLI and hooks to parity with the server — no25 marker/snippet/MCP changes).2627Before writing anything, confirm the three routing parameters with the user — there can be28**multiple ai-memory instances** (e.g. a personal `memory.example.dev`, a separate org29instance), so never hardcode the endpoint:30311. **MCP endpoint** — the ai-memory instance URL (e.g. `https://memory.example.dev/mcp`) and32 the server name to register it under (e.g. `memory-personal`, `memory-acme`). Ask which33 instance this repo should talk to.342. **Workspace** — personal → `default`; org repo → its own workspace (e.g. `acmecorp`).353. **Project** — defaults to the repo basename; let the user override.3637The endpoint chosen here is what `aim-query` / `aim-write` will target later (by server name).3839## CLI prerequisite (no Docker wrapper by default)4041`aim-init` needs the `ai-memory` CLI to install MCP entries, install hooks, and refresh the42routing snippet. Prefer a **native CLI binary** over the Docker wrapper:43441. If `ai-memory` is already on `PATH`, run `ai-memory --version` and compare it to the **latest45 upstream release** (`gh release view -R akitaonrails/ai-memory --json tagName -q .tagName`, or46 the releases/latest page). Keep it only if it's at a recent release — a CLI **behind it** misses47 newer subcommands (e.g. `auto-improve`, `pending-writes`, `curator`) and hook fixes, so upgrade48 it (see **refresh**). The **server's** exact `.version` lives at `GET /admin/status`, which needs49 the **admin** token (a user's OIDC/JWT gets `401` there) — so the latest release is the practical50 parity target; the connected MCP client also learns the server version from its `initialize`51 handshake.522. If it is missing or stale, check the latest upstream release first:53 `https://github.com/akitaonrails/ai-memory/releases/latest`.54 - Linux: download the matching `ai-memory-linux-<arch>.tar.gz` asset, verify its `.sha256`,55 and install the binary into `~/.local/bin/ai-memory` (or another user-owned PATH dir).56 - Windows: download `ai-memory-windows-x86_64.zip`, verify its `.sha256`, and follow the57 bundled Windows docs.58 - macOS/Darwin: download the matching `ai-memory-macos-<arch>.tar.gz` asset59 (`ai-memory-macos-aarch64.tar.gz` on Apple Silicon, `-x86_64` on Intel), verify its60 `.sha256`, and install the binary into `~/.local/bin/ai-memory`. Fallback only if no asset61 for your arch: `cargo install --git https://github.com/akitaonrails/ai-memory --tag <latest-tag>`.623. Use the Docker wrapper (`bin/ai-memory` or the quick-start wrapper from GitHub) only when the63 user explicitly wants Docker. Do not suggest it as the default local CLI on macOS or when the64 user asks for a non-Docker setup.6566After installing, verify:6768```bash69command -v ai-memory70ai-memory --version71AI_MEMORY_SERVER_URL=https://memory.example.dev AI_MEMORY_AUTH_TOKEN=<token-or-env> ai-memory status --json72```7374## What a wired repo has75761. **`.ai-memory.toml`** at the repo root — stable, non-secret routing for captures + recall77 to a workspace/project, and (optionally) the Keycloak/OIDC instance this repo onboards onto.78 Tracking policy is contextual, not automatic:79 - **Private/team repo with shared routing:** commit it so every developer gets the same80 workspace/project and onboarding profile on clone. These values are instance config, NOT81 credentials, and normally do not change after setup.82 - **Public repo, personal fork, or per-operator routing:** keep it local via the repo-local83 `.git/info/exclude`.84 Do **not** add it to the global `core.excludesfile`, and do **not** run85 `git rm --cached .ai-memory.toml` unless the user explicitly chooses local-only routing.86 ```toml87 workspace = "default"88 project = "<repo-name>"8990 # Optional routing/policy (defaults shown; uncomment to change — commented = no-op):91 # project_strategy = "repo-root" # collapse worktrees/subdirs to the git root (default: basename)92 # drop_subagent_captures = "true" # accept-but-don't-store THIS project's subagent captures (default: off)9394 # Optional: Keycloak/OIDC onboarding profile the agent reads to wire hooks95 # WITHOUT asking (see "Keycloak-gated instance"). Non-secret. The native hook96 # reads the routing/policy keys above but NOT this [instance] block, so the97 # block is inert at runtime — it exists purely so `aim-init` can self-serve98 # the device-flow setup.99 [instance]100 issuer = "https://kc.example/auth/realms/<realm>"101 server_url = "https://memory.example.dev/wiki" # ai-memory URL incl. base path102 client_id = "ai-memory-cli" # device-flow client103 agents = ["claude-code"] # agents to wire hooks for104 ```1052. **Routing snippet** in `CLAUDE.md` and `AGENTS.md` — the `<!-- ai-memory:start -->…end -->`106 block. It drives proactive recall mid-session. **The block's text is owned by the107 ai-memory binary, not by this skill** — obtain the current canonical block from the108 source and write it between the markers; do **not** hand-maintain a copy here (a copy109 drifts from the binary):110 - **Agent:** `memory_install_self_routing` (returns the block for your Write/Edit tool).111 - **CLI:** `ai-memory install-instructions` (writes `CLAUDE.md`; `--target AGENTS.md`).112113 The canonical block is already **generic** (no workspace/project/server names, no page114 paths — the `.ai-memory.toml` marker scopes recall and the MCP auto-scopes), so it is safe115 to commit even in a public repo. Whatever you do, never expand the committed block into a116 "where things live" map that enumerates the knowledge base — that leaks the project's117 internal information architecture. Cross-scope / shared-rules wiring belongs in118 operator-global config (`~/.claude/CLAUDE.md`), not the per-repo block.1193. **MCP server entry** in `.mcp.json` (Claude), `opencode.json` (OpenCode), `.codex/config.toml`120 (Codex) — points at the ai-memory instance (template: [templates/mcp-entry.json.tmpl](templates/mcp-entry.json.tmpl)).121 Prefer the CLI installer so each agent gets its native global config:122 ```bash123 ai-memory install-mcp --client codex --server-url https://memory.example.dev/mcp --name memory-personal --apply124 ai-memory install-mcp --client open-code --server-url https://memory.example.dev/mcp --name memory-personal --apply125 ai-memory install-mcp --client claude-code --server-url https://memory.example.dev/mcp --name memory-personal --apply126 ```127 MCP definitions are stable repo config when they contain only server names/URLs and no128 credentials. A private/team repo MAY commit `.mcp.json` so every agent discovers the same129 MCP endpoints on clone. Keep personal endpoints or auth-bearing entries (for example130 `Authorization` headers, bearer tokens, or per-user secrets) in the agent's global/user131 config, or keep a local-only `.mcp.json` via `.git/info/exclude`.132 Do **not** add `.mcp.json` to the global `core.excludesfile`, and do **not** untrack an133 existing `.mcp.json` automatically. Only run `git rm --cached .mcp.json` after the user134 explicitly chooses local-only MCP config.1354. **Auto-capture hooks** are **global** (Claude settings, Codex `~/.codex/hooks.json`,136 OpenCode plugin config, plus staged hook scripts under the platform data dir:137 `~/.local/share/ai-memory/hooks/` on Linux,138 `~/Library/Application Support/ai-memory/hooks/` on macOS, and139 `%LOCALAPPDATA%\ai-memory\hooks\` on Windows). They are marker-gated: they fire in any140 repo that has `.ai-memory.toml`. A repo needs **no per-repo hook scripts** — just the marker.141 Install or refresh them per agent with the capture base URL, not the `/mcp` URL.142 **Hook auth — pick the mode that matches the server. This is the #1 cause of silent143 capture failure: the wrong mode 401s on every drain, the spool fills, captures are lost.**144 There are four modes:145 - **Open hook routes** (no auth on `/hook`+`/handoff`) → install with **no** `--auth-token`.146 - **Static bearer** — the server accepts a shared/per-user token on `/hook`+`/handoff`147 (e.g. `AI_MEMORY_AUTH_TOKEN` on a single-tenant engine, or a token from148 `ai-memory user add`) → pass it via `--auth-token`. Embedded in each agent's hook config,149 so treat that file as sensitive (`chmod 600`).150 - **Consumer key (`amk_`, keys-only gateway)** — the `mcp-auth` sidecar runs without an151 identity provider and resolves per-consumer keys locally (`KEYS_DB`). Each agent/CLI gets152 its **own** key (`POST /keys` with an `admin`-scoped key; issued by `aim-ops keys`), scoped153 `read,write`; pass it via `--auth-token` for hooks and as the MCP `Authorization` header.154 Attribution comes from the key's `actor_user`. Expect **403 on `/admin/*`** with a155 non-admin key — by design, not a server fault. Rotation = issue new + revoke old.156 - **OIDC / Keycloak gateway** — a forwardAuth sidecar (`mcp-auth`) that validates a **JWT**157 and answers `401 WWW-Authenticate: Bearer resource_metadata=…` (RFC 9728). A static hex158 token is **rejected (401)** here — the gateway only accepts a Keycloak JWT, and the159 engine's upstream bearer is proxy-injected, not client-sendable. Use a per-developer160 **OIDC device token**: run `ai-memory auth login oidc-device --issuer <oidc-issuer>161 --client-id <public-device-client>` **once** (browser approval), then install hooks162 **without** `--auth-token` — the hook's `resolve_bearer` loads the token from `auth.json`163 and refreshes it headlessly. Full recipe in "Second / custom instance" below.164165 Quick test of which mode a server is in: `curl -sI <server>/hook` → `401` with a166 `WWW-Authenticate: Bearer resource_metadata=` header means OIDC/Keycloak (device flow);167 a plain `401`/`200` without that header means static-bearer/open.168 `GET <server>/keys/whoami` → `200` with `{identity, can_issue}` means the keys-only gateway169 is present (consumer keys); `404` means the sidecar has no `KEYS_DB`.170171 Add `--hooks-dir <ai-memory/hooks>` when the binary can't locate its vendored scripts172 (e.g. a `cargo install` build):173 ```bash174 # static-bearer server:175 TOKEN=<HOOK_AUTH_TOKEN>176 ai-memory install-hooks --apply --agent claude-code --server-url https://memory.example.dev --auth-token "$TOKEN"177 # OIDC/Keycloak server (after `auth login oidc-device`) OR open server — NO --auth-token:178 ai-memory install-hooks --apply --agent claude-code --server-url https://memory.example.dev179 ai-memory install-hooks --apply --agent codex --server-url https://memory.example.dev180 ai-memory install-hooks --apply --agent open-code --server-url https://memory.example.dev181 ```182 Modern `install-hooks` wires **native** hooks (`ai-memory hook --event …` calling the binary183 directly) instead of shell scripts — the binary emits the correct per-agent stdout contract184 (e.g. Claude Code's `hookSpecificOutput.additionalContext` JSON wrapper for handoff185 injection), so there are **no hand-patched hook scripts to drift**. Do **not** hand-maintain a186 custom `_lib.sh` overlay for auth (e.g. a Keycloak token-mint prepend) — the supported paths187 are `--auth-token` (static-bearer servers) or `auth login oidc-device` + no-`--auth-token`188 (OIDC/Keycloak servers). The device token is **not** minted per event: it's stored once in189 `auth.json` and refreshed headlessly at drain time.190 On Codex, confirm `~/.codex/hooks.json` contains the ai-memory lifecycle hooks and trust the191 new hook commands when Codex prompts on the next start. **Grok Build CLI** (`~/.grok/hooks/*.json`)192 is not yet a supported `--agent` — integrate manually, and note that its `SessionStart` ignores193 hook stdout (no handoff injection; use the MCP `memory_handoff_accept` instead). (Legacy qmd194 repos have per-repo `wiki-reindex` hooks; migration removes them.)195196### Hook config — per-platform paths + delivery model (macOS / Linux / Windows)197198`install-hooks` and `auth login oidc-device` both default to the platform **data dir**199(`dirs::data_local_dir()`), where `auth.json`, the `hook-spool/`, and the staged hook scripts200live — so the OIDC device token lands exactly where the hooks look for it. Same flow on all201three OSes; only the paths differ:202203| OS | data dir (`auth.json`, `hook-spool/`, `hooks/`) | Claude Code settings |204|---|---|---|205| **macOS** | `~/Library/Application Support/ai-memory` | `~/.claude/settings.json` |206| **Linux** | `~/.local/share/ai-memory` | `~/.claude/settings.json` |207| **Windows** | `%LOCALAPPDATA%\ai-memory` | `%USERPROFILE%\.claude\settings.json` |208209Hooks are **native** (`ai-memory hook --event …` calls the binary directly — no shell spawn;210Windows uses the `WindowsNative` config with the same spool + OIDC-token fallback). Codex →211`~/.codex/hooks.json`; OpenCode → its plugin config. If the binary can't find its vendored212scripts (e.g. a `cargo install` build), pass `--hooks-dir <data_dir>/hooks`. `auth.json` is213sensitive → `chmod 600` on POSIX (the CLI writes it that way).214215**Delivery is batched at session boundaries, not per event.** Per-event hooks216(`user-prompt-submit`, `pre/post-tool-use`, `pre-compact`, `stop`) only **enqueue** to217`<data_dir>/hook-spool` (instant, fire-and-forget). The spool is **drained** on218**`session-start`** (clears prior-session backlog) and **`session-end`** (the main delivery219point). So "captures don't show up immediately" is normal — they flush at the next boundary.220`resolve_bearer` at drain time: explicit `--auth-token` wins (static); else the OIDC token221from `auth.json` (refreshed if near expiry); else anonymous. The spool drops entries after 8222failed attempts or 7 days, capped at 10000 files. **Symptom of a wrong auth mode:**223`hook-spool` filling toward 10000 with `auth_mode: static` entries that 401 forever (a static224token against a Keycloak gateway) → switch to device flow and clear the dead backlog.225226Token lifetime (device flow): access token auto-refreshes; the refresh token lasts as long as227the issuer's offline-session idle window — after long inactivity, re-run `auth login228oidc-device`. Verify state any time with `ai-memory auth status`.229230### Keycloak-gated instance — the agent drives onboarding (mac / Linux / Windows)231232The developer should **not** hand-run setup scripts. When a dev needs hook233capture on a Keycloak/OIDC instance, **you (the agent) drive it**: gather the234inputs, check/create the device client, run the per-machine setup, and verify.235The dev only answers a couple of questions and approves a browser prompt once.236The [`scripts/`](scripts/) are your canonical, cross-platform command reference237(`*.sh` for macOS/Linux, `*.ps1` for Windows) — invoke them, or run the238equivalent `ai-memory` commands directly.239240**Inputs you need** — **read them from the repo's `.ai-memory.toml` `[instance]` block241first** (`issuer` / `server_url` / `client_id` / `agents`, see "What a wired repo has");242only ask the dev (or fall back to operator-global config) when that block is absent:243- `ISSUER` — realm URL, e.g. `https://kc.example/auth/realms/<realm>`.244- `SERVER_URL` — ai-memory server URL **including any base path**, e.g.245 `https://memory.example.dev` or `https://memory.example.dev/wiki`.246- `CLIENT_ID` — the device-flow client (default `ai-memory-cli`).247- which `AGENTS` the dev uses (`claude-code`, `codex`, `open-code`, …).248249When the `[instance]` block is present (e.g. committed in a private team repo), the agent250uses it and does NOT ask — `git clone` + `aim-init` is turnkey for the next developer.251252**1. Check the device-flow client exists** (read-only):253`GET <ISSUER>/.well-known/openid-configuration` (confirm `device_authorization_endpoint`),254then POST `client_id=<CLIENT_ID>&scope=openid` to that endpoint. A `device_code`255→ ready. `invalid_client` (no such client) or `unauthorized_client … device flow256is disabled` → the realm needs the client (step 2). The browser/MCP client is257PKCE-enforced and 400s with `Missing parameter: code_challenge_method`, so it258can't be reused — that's why a dedicated client exists.259260**2. Create the client IF missing — one-time per realm, needs Keycloak admin.**261If you have Keycloak admin access, use `scripts/kc-create-device-client.sh`262(idempotent; public, device-only, no PKCE). For a containerized Keycloak run it263INSIDE the pod (it runs `sh` there regardless of your OS), reusing the pod's own264admin env so no secret leaves the cluster:265```bash266# macOS/Linux/WSL:267kubectl exec -i <keycloak-pod> -- sh -c \268 'CLIENT_ID=ai-memory-cli KC_SERVER=http://localhost:8080/auth KC_REALM=<realm> \269 KC_ADMIN_USER="$KEYCLOAK_ADMIN" KC_ADMIN_PASS="$KEYCLOAK_ADMIN_PASSWORD" sh -s' \270 < scripts/kc-create-device-client.sh271# Windows PowerShell: Get-Content scripts/kc-create-device-client.sh | kubectl exec -i <pod> -- sh -c '…'272```273Adjust `KC_SERVER` to `…:8080` vs `…:8080/auth` per the realm's274`KC_HTTP_RELATIVE_PATH` (a `404 Not Found` on login means flip it). If you lack275admin access, ask the operator to run it once, then resume.276277**3. Run the per-machine setup yourself** — don't make the dev run a script. Per278the dev's OS:279- macOS/Linux: `ISSUER=… SERVER_URL=… CLIENT_ID=… AGENTS="…" sh scripts/dev-setup-hooks.sh`280- Windows: `pwsh scripts/dev-setup-hooks.ps1 -Issuer … -ServerUrl … -Agents "…"`281- Either way it runs `ai-memory auth login oidc-device --issuer <ISSUER>282 --client-id <CLIENT_ID>` (the dev approves the printed URL in their browser283 ONCE) then `ai-memory install-hooks --apply --agent <each> --server-url284 <SERVER_URL>` (NO `--auth-token`).285286**4. Verify:** `ai-memory auth status` → `oidc-device: logged in`. The dev needs287the realm role the server checks (`mcp:read`/`mcp:write`) — the same one MCP288login already requires.289290Record each instance's `ISSUER`/`SERVER_URL`/`CLIENT_ID` in **operator-global291config** (`~/.claude/CLAUDE.md` or a private runbook), NOT in this skill — the292scripts stay generic; these endpoints are operator/instance-specific.293294## Second / custom instance (e.g. a client Keycloak instance)295296Some repos talk to **two** ai-memory instances — a **personal** one and a **client/org** one297that authenticates with **OAuth/OIDC (Keycloak, RFC 9728)** rather than (or in addition to) a298static bearer. Both are **read + write**; keep them in sync, with the **personal instance as the299superset** (it accumulates everything any custom instance has).300301- **Hook capture works against an OAuth/Keycloak instance too.** A hook is headless and cannot run302 an *interactive* OAuth PKCE flow per event, so it uses a stored **OIDC device-flow token**: run303 `ai-memory auth login oidc-device --issuer <oidc-issuer> --client-id <public-device-client>`304 once (browser approval), and the hook (`resolve_bearer`) loads it from `auth.json` and refreshes305 it headlessly. Then `install-hooks` **without** `--auth-token`.306 - **A forwardAuth-JWT-only gateway rejects static bearers.** If the gateway answers307 `401 WWW-Authenticate: Bearer resource_metadata=…` (RFC 9728), a static hex token gets 401 —308 the upstream bearer is proxy-injected, not client-sendable — so **device flow is the only309 headless option** there. Only pass `--auth-token` when the instance genuinely accepts a static310 `HOOK_AUTH_TOKEN` on `/hook`.311 - **Device-flow client requirements (issuer side):** a **public** client with the312 device-authorization grant **enabled** and **no PKCE enforcement** (PKCE has no place in the313 device grant — there's no redirect; keep the browser/DCR/MCP client, which *does* enforce PKCE,314 as a **separate** client so you don't have to weaken it). The user needs the realm role the315 server checks (e.g. `mcp:read`/`mcp:write`) — those ride into the JWT via the realm-role mapper,316 not via a scope.317- **Dual-capture:** global hooks → personal instance; **project-level** hooks318 (`.claude/settings.local.json`) → the client instance. Each leg uses **that instance's** auth319 mode (static bearer or OIDC device token — they're independent). With both wired, every lifecycle320 event captures to both. (Or capture to one and reconcile via sync.)321- **Dual-write durable pages:** a `memory_write_page` in a two-instance project goes to **both**322 MCPs, same `(workspace, project)`.323- **Pass explicit `workspace`+`project`** on recall/write to the client instance — with `per_actor`324 + the scope-bleed fail-closed fix, an un-scoped call lands on that instance's baked default. The325 `.ai-memory.toml` marker gives the canonical scope.326- **Keep in sync** (bidirectional, on-demand): personal ⊇ everything; reconcile drift both ways327 preserving `(workspace, project)`.328329This operator-specific dual-instance wiring belongs in **operator-global config**330(`~/.claude/CLAUDE.md` or a shared memory rule), **not** the committed per-repo CLAUDE.md331`ai-memory` block — that block is generic + binary-owned, and in a shared/client repo it would332leak the dual-instance/capture setup and the project's information architecture.333334## doctor (read-only)335336Report, without writing:337- Is the `ai-memory` CLI available on `PATH`? What version? If missing/stale, report the native338 install path from the latest upstream release — download the `ai-memory-<os>-<arch>.tar.gz`339 asset (macOS ships `ai-memory-macos-aarch64`/`-x86_64`; Linux `ai-memory-linux-aarch64`/340 `-x86_64`), verify its `.sha256`; `cargo install` only if no asset for your arch. Do **not**341 fall back to the Docker wrapper unless the user wants Docker.342- Can the CLI reach the chosen server (`ai-memory status --json` with the right343 `AI_MEMORY_SERVER_URL` / auth env or flags)?344- **CLI version parity.** Compare `ai-memory --version` (local) to the **latest upstream release**345 (`gh release view -R akitaonrails/ai-memory --json tagName -q .tagName`). Flag a CLI behind it —346 it lacks newer subcommands (`auto-improve`, `pending-writes`, `curator`) and hook fixes — and347 recommend **refresh**. (The server's exact `.version` is admin-gated at `/admin/status`: a user's348 OIDC/JWT gets `401`, so don't rely on `ai-memory status` against the server for it; the latest349 release is the checkable target, and the MCP `initialize` handshake also carries it.)350- Is there a `.ai-memory.toml`? What workspace/project? Is its tracking policy intentional351 (committed for shared stable routing, or ignored local-only)?352- Is the routing snippet present in CLAUDE.md / AGENTS.md?353- **Does the snippet teach the search strategy?** Flag a stale snippet that lacks the354 "broaden when the current project misses" guidance — i.e. no mention of `scopes` /355 sibling projects, or no warning that `memory_query` returns snippets (not full page356 bodies). Re-installing the current template (below) backfills it. This matters because357 `memory_query` has **no global search**: an agent that searches only the current project358 and stops will miss cross-cutting knowledge that lives in a sibling (`infra`/`ops`) project.359- Is an ai-memory MCP entry present? Check the agent's **global** config FIRST — the MCP is360 usually installed there (`~/.claude.json` top-level `mcpServers`, e.g. `memory-personal`;361 `~/.codex/config.toml`; the OpenCode global config), *then* the **per-repo** `.mcp.json` /362 `opencode.json`. **Don't conclude "no recall" from an empty per-repo `.mcp.json`** — a global363 entry applies to every repo. If a per-repo `.mcp.json` IS used, inspect whether it contains364 only stable non-secret definitions or auth material, then confirm its tracking policy matches365 the repo's convention; tracked `.mcp.json` is valid for private/team repos with shared MCPs.366- For Codex, are global ai-memory hooks present in `~/.codex/hooks.json` (`SessionStart`,367 `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `PreCompact`, `Stop`) and do the staged368 scripts exist under the platform ai-memory hooks dir?369- **Are the hooks native + authenticated?** Flag any agent still calling hand-patched shell370 scripts under a custom overlay (e.g. `~/.config/ai-memory/hooks/` sourcing a Keycloak-mint371 `_lib.sh`) instead of the binary's `ai-memory hook --event …` form — those drift and miss372 upstream fixes (e.g. the `hookSpecificOutput` JSON wrapper + tab-escape fix that handoff373 injection depends on). If the server gates `/hook`/`/handoff`, confirm the hook carries the374 **right credential for that server's mode** (otherwise captures 401 **silently** — the CLI375 spools locally and returns `{}` regardless): a **static-bearer** server needs `--auth-token`;376 an **OIDC/Keycloak** gateway (`401 WWW-Authenticate: Bearer resource_metadata=…`) needs a377 device-flow token (`auth login oidc-device`) and the hooks installed **without** `--auth-token`378 — a static token is rejected there. Re-run `install-hooks --apply` (with or without379 `--auth-token` per mode) to migrate and remove any overlay dependency.380- **`hook-spool` at/near the 10000 cap — two DISTINCT causes; read `auth_mode` / `attempts` /381 `last_error` on a few entries to tell them apart:**382 - **Wrong auth** (fixable): `auth_mode: static` against a Keycloak/JWT gateway → `attempts` climb,383 `last_error` shows 401, entries drop after 8 tries. Fix: device flow (`auth login oidc-device`)384 + `install-hooks --apply` **without** `--auth-token`, and clear the dead backlog.385 - **Drain cadence** (NOT an auth bug): `auth_mode: oidc` (correct), `attempts: 0`,386 `last_error: null` — delivery works, but drains only fire at `session-start` / `session-end`, so387 a marathon session exceeds `MAX_SPOOL_FILES` (10000) before it ends and the **oldest are388 evicted**. Mitigate by draining at a boundary (start a fresh session, or run the `session-start`389 hook manually to drain) or shorter sessions; the durable fix is **mid-session incremental390 draining** (upstream — track the hook-spool delivery-reliability work). The spool is **shared391 across all agents** on the same data dir, so parallel long sessions accumulate together.392- **MCP auth (DCR):** if an agent's MCP login shows Keycloak **"Client not found"**, its cached393 Dynamic-Client-Registration id was orphaned (realm recreated/migrated). Clear the stale entry394 from the agent's MCP-auth cache (e.g. OpenCode's `~/.local/share/opencode/mcp-auth.json`) and395 re-auth to force a fresh DCR.396- **MCP OAuth discovery failure:** if a client's `reauth` errors **`Could not discover OAuth endpoints from server response`** (it doesn't chase the RFC 9728 → 8414 discovery — seen with **OMP**'s native MCP client), bridge the remote as a `stdio` server via `npx -y mcp-remote <url> <port>` in that client's own (git-excluded) config, keeping the committed `.mcp.json` portable `type: http`. If discovery+authorization then pass but the **token POST 404s**, the SDK looked up auth-server metadata at the **domain root** and dropped a path-based issuer's `/<base>/realms/<realm>` — a native client (Claude Code) connects via issuer-relative OIDC, so it's not a dead endpoint and not fixable by `mcp-remote` version/flags; fix with a server-side root `.well-known/oauth-authorization-server` (+ `openid-configuration`) alias to the realm, else use the native client. Full recipe: `references/usage.md` §MCP OAuth compatibility.397- **Two-instance (e.g. client Keycloak) repo?** If the repo registers a second MCP (a client/org398 instance), confirm: both are treated as **read+write** with the **personal** instance as the399 superset; durable writes are dual-written; if auto-capture to the client instance is wanted,400 project-level hooks point at it with a static `HOOK_AUTH_TOKEN` **or** a stored OIDC device-flow401 token (`ai-memory auth login oidc-device …` → `resolve_bearer` refreshes headlessly) — capture is402 feasible, not "impossible"; recall/write pass explicit `workspace`+`project` (per_actor403 fail-closed). Flag any committed CLAUDE.md that hard-codes this dual-instance/capture wiring —404 it belongs in operator-global config, not the shared per-repo block.405- **Legacy qmd?** Flag any of: a `qmd` MCP entry, a `wiki/` dir with `CONVENTIONS.md`, per-repo406 `*/hooks/wiki-reindex.sh`, a "Wiki (`wiki/`)" block in CLAUDE.md/AGENTS.md, `.wiki-guardrails.yml`.407408## install (greenfield)4094101. Confirm workspace + project with the user.4112. Ensure the native `ai-memory` CLI is installed and functional (see "CLI prerequisite" above).412 Install/upgrade from upstream release assets (macOS ships `ai-memory-macos-<arch>.tar.gz`,413 Linux `ai-memory-linux-<arch>.tar.gz`; verify the `.sha256`); `cargo install`/source build414 only if no asset for your arch. Avoid the Docker wrapper unless explicitly requested.4153. Decide `.ai-memory.toml` tracking policy from the repo context and user input. For a416 private/team repo with stable shared routing, write it and leave it tracked/committable.417 For public repos, personal forks, or per-operator routing, add it to the repo-local418 `.git/info/exclude`. Do not use the global `core.excludesfile`, and do not untrack an419 existing marker unless the user explicitly chooses local-only routing.4204. Obtain the canonical routing block from the binary (`memory_install_self_routing`, or421 `ai-memory install-instructions`) and write it into `CLAUDE.md` and `AGENTS.md` (idempotent422 — between the `<!-- ai-memory:start -->`/`<!-- ai-memory:end -->` markers; replace if423 present). Don't paste a hand-maintained copy.4245. Add the ai-memory MCP entry to the agent configs the repo uses. If the entry is stable and425 contains no credentials, it may live in a tracked per-repo `.mcp.json`; if it is personal or426 auth-bearing, install it in the agent's global/user config or keep it local-only via427 `.git/info/exclude`. Do not run `git rm --cached .mcp.json` unless the user explicitly asks428 for local-only MCP config.429 For Codex, prefer `ai-memory install-mcp --client codex ... --apply` so the server lands in430 `~/.codex/config.toml`.4316. Ensure global auto-capture hooks are installed for the active agents. For Codex, run432 `ai-memory install-hooks --agent codex --server-url https://memory.example.dev --apply` and verify433 `~/.codex/hooks.json` plus the staged `ai-memory/hooks/codex` scripts.4347. Tell the user auto-capture is now live (global hooks + the new marker); recall is via the435 session-start handoff + the routing snippet.436437## migrate (brownfield: qmd → ai-memory)438439Run **doctor** first; then, with the user's confirmation:4404411. **Marker** — write `.ai-memory.toml` (workspace/project) and preserve/choose the tracking442 policy: commit stable shared routing in private/team repos; keep personal/public routing443 local-only via `.git/info/exclude`.4442. **MCP** — in `.mcp.json` / `opencode.json` / `.codex/config.toml`, **replace** the `qmd`445 server entry with the ai-memory entry. Preserve the repo's existing tracking policy; commit446 stable non-secret server definitions when the repo intentionally shares MCPs, and keep447 auth-bearing or personal endpoints local/global.4483. **CLAUDE.md / AGENTS.md** — replace the "Wiki (`wiki/`)" / qmd-MCP block with the routing449 snippet. Drop instructions that tell the agent to query `qmd` or maintain `wiki/`.4504. **Remove ALL qmd-era artifacts.** The old qmd/wiki setup installs more than hooks —451 enumerate every one and remove it:452 - **`.wiki-guardrails.yml`** (guardrails config).453 - **Hooks** — `.claude/hooks/wiki-*.sh` (policy-check, reindex, drift-audit, suggest-ingest);454 `.codex/hooks/wiki-*.sh` (policy-check, reindex, drift-audit, consider) **+ `.codex/hooks.json`**;455 `.opencode/hooks/wiki-*.sh` **+ `.opencode/plugins/wiki-guardrails.js`**. Remove the matching456 hook entries from `.claude/settings.json`, and `[features] codex_hooks = true` from457 `.codex/config.toml` (it only existed to enable the codex wiki hooks).458 - **`opencode.json`** `permission.skill."wiki-*"` (swap to `"aim-*"` or drop).459 - **ANTIGRAVITY** (if the repo uses it) — the managed instruction block + any `.antigravity*`460 hooks/config.461 - Auto-capture now comes from the **global** ai-memory hooks — no per-repo hook scripts needed.4625. **Global QMD checkout/wrapper** (operator-level, outside the repo) — the per-project wrapper463 (`~/.local/share/skills/qmd/wrappers/<project>-qmd`, or the legacy464 `~/.local/share/essential-skills/qmd/...`) and the managed `qmd` checkout/cache are now465 orphaned. Remove the wrapper; remove the shared checkout only if no other repo still uses qmd.4666. **`wiki/` content** — leave it in place as history by default. If the user wants it in467 ai-memory, ingest the markdown into the chosen workspace/project (one page per file,468 redact any literal secrets) and then they can remove `wiki/`. Do **not** delete `wiki/`469 without explicit confirmation.4707. Re-run **doctor** to confirm: marker present + tracking policy intentional, snippet in471 CLAUDE/AGENTS, ai-memory MCP wired with no committed secrets, and **no qmd remnants** (no472 `.wiki-guardrails.yml`, no `wiki-*` hooks/plugins, no qmd MCP entry, no `codex_hooks`/473 `wiki-*` permission leftovers).474475## refresh (upgrade CLI + re-stage hooks to parity)476477For an **already-wired** environment that drifted from the server — CLI behind, or after the478server upgrades. No marker/snippet/MCP changes; **detect what's installed and bring it to parity**:4794801. **Detect the current state** (read-only): local `ai-memory --version` vs the **latest upstream481 release** (`gh release view -R akitaonrails/ai-memory --json tagName -q .tagName`; the server's482 own `.version` is admin-gated at `/admin/status` → `401` for a user, so the release is the483 checkable target); which agents have ai-memory hooks — `ai-memory install-hooks --help` lists the484 **canonical** supported agents (don't hardcode a stale subset): claude-code485 (`~/.claude/settings.json`), codex (`~/.codex/hooks.json`), gemini-cli (`~/.gemini/settings.json`),486 **antigravity-cli / `agy`** (`~/.gemini/config/hooks.json`), grok (`~/.grok/hooks/ai-memory.json`),487 cursor (`~/.cursor/hooks.json`), open-code (`~/.config/opencode/plugins/`), omp488 (`~/.omp/agent/extensions/`), openclaw; and `ai-memory auth status` (is the OIDC device token valid?).4892. **Upgrade the CLI** if behind the latest release (see "CLI prerequisite"): download the490 `ai-memory-<os>-<arch>.tar.gz` asset, verify its `.sha256`, **back up** the current binary, and491 install over `~/.local/bin/ai-memory`. Confirm `ai-memory --version` is now ≥ the server's.4923. **Re-stage hooks for every detected agent:** `ai-memory install-hooks --apply --agent <agent>493 --server-url <capture-url>` (OIDC/Keycloak server → **no** `--auth-token`; static-bearer →494 `--auth-token`). It re-verifies the staged scripts and refreshes the agent config (writes a495 backup). Native hooks (`ai-memory hook --event …`) pick up the new binary automatically; this496 refreshes the config + staged fallback scripts and drops any legacy `_lib.sh` overlay.4974. **Verify:** `ai-memory --version` matches the server; `ai-memory auth status` → logged in; the498 agent configs still call `ai-memory hook --event` natively; `<data_dir>/hook-spool` is well499 below the 10000 cap.5005. **≥ 2.0 notes:** `install-hooks --apply` now stores the bearer in `<data_dir>/auth-token`501 (0600) and renders a `resolveToken()` helper in generated TypeScript integrations; the502 managed Agent Skills changed — refresh them with `memory_install_self_routing` (or503 `ai-memory install-instructions`). Codex prompts *Hooks need review* on next start: trust504 them. A CLI ≥ 2.0 talks fine to a 1.3x server for hooks (`/hook/batch` unchanged), but505 upgrade the **server first** (`aim-ops upgrade`) and then clients, on the same day.506507`refresh` does **not** touch the marker, routing snippet, or MCP entry (version-agnostic, and the508snippet is binary-owned) — run **install** / **migrate** for those. Hooks are **global** (one data509dir for all agents), so refreshing once updates every wired repo, not just the current one.510511## Verify512513- `git check-ignore .ai-memory.toml` → ignored (not committed).514- Routing snippet present once (between the markers) in CLAUDE.md + AGENTS.md.515- No `qmd` MCP entry / `wiki-reindex` hooks remain (for migrate).516- A capture round-trips: a real agent session in the repo lands a page under the chosen517 workspace/project (check via `aim-status` / `memory_status` or `/api/v1/projects`).518519## Multi-user mode (optional)520521The instructions above cover the single-operator case (one human, one ai-memory instance).522When **multiple humans share an instance** — or one operator wants strict per-repo523isolation between several parallel Claude Code windows — switch the engine to multi-user524mode and onboard each user explicitly. The ai-memory CLI already ships the workflow.525526**Pre-req:** the engine must have `[auth].token_pepper` set (auto-generated by527`ai-memory init`; multi-user admin endpoints 503 otherwise). Confirm with528`curl <endpoint>/admin/status` or `ai-memory status`.529530### Per-user setup (canonical: one person, one identity)5315321. **Create the user** on the engine (root token required). Token is printed exactly once:533 ```bash534 ai-memory user add --username alice --email alice@home535 # → {"id": "...", "username": "alice", "token": "<COPY-THIS-NOW>"}536 ```537 Lifecycle subcommands: `user list` (no tokens surfaced), `user expire`/`revive`,538 `user rotate-token` (issues fresh plaintext once).5395402. **Install hooks stamped with that user's bearer** — one command per agent CLI the541 person uses (`claude-code`, `codex`, `cursor`, `gemini-cli`, `open-code`, `omp`,542 `openclaw`, `antigravity-cli`):543 ```bash544 ai-memory install-hooks --apply \545 --agent codex \546 --as-user alice \547 --auth-token <alice-token>548 ```549 Use `--agent claude-code` or `--agent open-code` for those harnesses.550 `--apply` mutates the agent config in place (idempotent; writes a timestamped backup).551 Defaults to the global hook config (`~/.claude/settings.json`, `~/.codex/hooks.json`,552 OpenCode plugin config, …).553 Pass `--config-file ./.claude/settings.json` to target project-level config instead554 (see "tokens per context" below).5555563. **Wire the bearer into the MCP entry** so MCP tool calls authenticate as the same557 user the hooks do:558 ```json559 {560 "mcpServers": {561 "memory-personal": {562 "type": "http",563 "url": "https://memory.example.dev/mcp",564 "httpHeaders": { "Authorization": "Bearer <alice-token>" }565 }566 }567 }568 ```569 `ai-memory install-mcp --apply --auth-token <alice-token>` writes this entry for you.5705714. **Run the engine in `per_actor` mode** (chart: `aiMemory.autoScope.mode: per_actor`).572 The active-project map keys by `(user, session_id)` with a user-only fallback —573 Alice never inherits Bob's last project on a session-less probe, and writes from574575…(truncated)