djalmajr/skills

aim-init

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, …

First seen May 28, 2026

Installation

$ npx skills add djalmajr/skills --skill aim-init

Summary

  • 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.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from djalmajr/skills · top by installs.

npx skills add djalmajr/skills

Browse all from djalmajr/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Declared
Cursor Not declared
Codex Declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Declared

Repository health

Stars 4
Default branch main
Open issues 0
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Declared agents claude-code codex opencode
More metadata
short-description
Initialize/migrate a repo into ai-memory

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 45,057 B
  • docs SUMMARY.md 660 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 76 installs

SKILL.md

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](references/usage.md).
Server side (install a new instance, upgrade the engine, providers, consumer keys,
backup/restore) is [aim-ops](../aim-ops/SKILL.md) — 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:

  1. 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.

  1. Workspace — personal → default; org repo → its own workspace (e.g. acmecorp).
  2. 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:

  1. 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.

  1. 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-x8664.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, -x8664 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>.

  1. 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

  1. .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. ```toml workspace = "default" project = "<repo-name>"

# Optional routing/policy (defaults shown; uncomment to change — commented = no-op): # projectstrategy = "repo-root" # collapse worktrees/subdirs to the git root (default: basename) # dropsubagent_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>"; serverurl = "https://memory.example.dev/wiki"; # ai-memory URL incl. base path clientid = "ai-memory-cli" # device-flow client agents = ["claude-code"] # agents to wire hooks for ```

  1. 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: memoryinstallself_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.

  1. 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](templates/mcp-entry.json.tmpl)). Prefer the CLI installer so each agent gets its native global config: ``bash 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.

  1. 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. AIMEMORYAUTHTOKEN 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 (KEYSDB). 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 actoruser. 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 resourcemetadata=… (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 resolvebearer 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>/hook401 with a WWW-Authenticate: Bearer resourcemetadata= header means OIDC/Keycloak (device flow); a plain 401/200 without that header means static-bearer/open. GET <server>/keys/whoami200 with {identity, canissue} 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): ``bash # static-bearer server: TOKEN=<HOOKAUTHTOKEN> 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 memoryhandoff_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::datalocaldir()), 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 <datadir>/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. resolvebearer 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/](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 needread them from the repo's .ai-memory.toml [instance] block first (issuer / serverurl / clientid / 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 deviceauthorizationendpoint), then POST clientid=<CLIENTID>&scope=openid to that endpoint. A devicecode → ready. invalidclient (no such client) or unauthorizedclient … device flow is disabled → the realm needs the client (step 2). The browser/MCP client is PKCE-enforced and 400s with Missing parameter: codechallenge_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 KCSERVER to …:8080 vs …:8080/auth per the realm's KCHTTPRELATIVEPATH (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=… SERVERURL=… CLIENTID=… 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 <CLIENTID> (the dev approves the printed URL in their browser ONCE) then ai-memory install-hooks --apply --agent <each> --server-url <SERVERURL> (NO --auth-token`).

4. Verify: ai-memory auth statusoidc-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/SERVERURL/CLIENTID 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 (resolvebearer) 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 resourcemetadata=… (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 HOOKAUTHTOKEN 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 memorywritepage 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/-x8664; Linux ai-memory-linux-aarch64/ -x8664), 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

AIMEMORYSERVER_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 memoryquery returns snippets (not full page bodies). Re-installing the current template (below) backfills it. This matters because memoryquery 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 resourcemetadata=…) 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 /

lasterror on a few entries to tell them apart: - Wrong auth (fixable): authmode: static against a Keycloak/JWT gateway → attempts climb, lasterror 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): authmode: oidc (correct), attempts: 0, lasterror: null — delivery works, but drains only fire at session-start / session-end, so a marathon session exceeds MAXSPOOL_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 HOOKAUTHTOKEN or a stored OIDC device-flow token (ai-memory auth login oidc-device …resolvebearer refreshes headlessly) — capture is feasible, not "impossible"; recall/write pass explicit workspace+project (peractor 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)

  1. Confirm workspace + project with the user.
  2. 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.

  1. 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.

  1. Obtain the canonical routing block from the binary (memoryinstallself_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.

  1. 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.

  1. 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.

  1. 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:

  1. 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.

  1. 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.

  1. 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/.

  1. 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.

  1. 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.

  1. 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.

  1. 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:

  1. 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/status401 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?).

  1. 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.

  1. 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.

  1. 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.

  1. ≥ 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 memoryinstallself_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)

  1. Create the user on the engine (root token required). Token is printed exactly once:

``bash 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).

  1. 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): ``bash 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).

  1. Wire the bearer into the MCP entry so MCP tool calls authenticate as the same

user the hooks do: ``json { "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.

  1. Run the engine in peractor mode (chart: aiMemory.autoScope.mode: peractor).

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 Alice never publish to Bob's slot.

Tokens per context (parallel-session isolation without agent hooks)

Same person, two Claude Code windows open in different repos at the same time → MCP tool calls don't forward the hook's session_id, so the engine collapses both windows onto one user-only slot. Workaround: treat each repo as its own logical user.

ai-memory user add --username alice-foo --email [email protected]   # token T-foo
ai-memory user add --username alice-bar --email [email protected]   # token T-bar

In each repo's project-level .claude/settings.json (Claude Code merges project-level over global), pin a distinct token:

{
  "mcpServers": {
    "memory-personal": {
      "type": "http",
      "url": "https://memory.example.dev/mcp",
      "httpHeaders": { "Authorization": "Bearer <T-foo>" }
    }
  }
}

Install hooks scoped the same way:

cd ~/repo-foo && ai-memory install-hooks --apply \
  --as-user alice-foo --auth-token T-foo \
  --config-file ./.claude/settings.json

Now the two windows authenticate as distinct users to the engine; per_actor keying isolates their slots automatically. Operational cost: N tokens per real person × M repos that need isolation. The trade-off is acceptable when you really need parallel isolation; otherwise the per-user setup above is enough.

Server-side scope-guard (defense in depth)

The engine ships an admission webhook chain. The ops chart includes an optional scope-guard webhook that enforces per-user write boundaries — an ACL of user → [(workspace pattern, project pattern), …]. Even if a token is pasted into the wrong .ai-memory.toml or MCP config, the engine rejects the write at admission time:

# values.yaml
webhooks:
  scopeGuard:
    enabled: true
    rules:
      alice:   [{ workspace: "alice|shared", project: ".*" }]
      client-x: [{ workspace: "client-x|shared", project: ".*" }]

**Enabling scope-guard ⇒ the write's actor must be non-empty, or the ACL rejects everything. The ACL keys on ActorContext.user. An OIDC client carries it in the JWT (preferredusername), so multi-user instances match rules naturally. But a client that authenticates hooks with the static HOOKAUTHTOKEN lands with an empty actor unless you also set mcpAuth.hookAuth.username (→ HOOKAUTHUSERNAME, the username mcp-auth stamps on hook-token requests) to a user the ACL allows. Miss it and every static-token hook capture is silently 403'd at admission — data loss visible only in the engine logs (scope-guard: user '' not allowed to writepage …). Note aiMemory.rootUsername does not** cover this: it stamps the rung-1/MCP path, not the hook path. So a single-operator static-token instance must set mcpAuth.hookAuth.username (= the ACL user) when it enables scopeGuard; multi-user instances rely on OIDC instead (never a shared hook username).

Reads are NOT gated — the engine has no read-side admission chain. For real read privacy (adversarial users), run separate ai-memory instances per trust boundary.

Doctor check for multi-user

In addition to the single-user checks above, doctor for a multi-user install should report:

  • [auth].token_pepper is set on the engine
  • ai-memory user list returns the expected users
  • The agent MCP config (~/.claude/settings.json, ~/.codex/config.toml, OpenCode config,

or project-level equivalent) has Authorization: Bearer …

  • The active hook config is stamped with --as-user (every hook call carries the user's

bearer, not the root token)

  • If autoscope is persession and the MCP client doesn't forward sessionid, warn

that the mode is silently degrading to single slot and recommend per_actor instead

See [references/usage.md](references/usage.md) for the recall model (handoff per project, auto-capture, the MCP tools) and [aim-query](../aim-query/SKILL.md) / [aim-write](../aim-write/SKILL.md) for manual recall/write.