[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-78438":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":9,"rankLanguage":9,"license":9,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":9,"pushedAt":9,"updatedAt":25,"readmeContent":26,"aiSummary":27,"trendingCount":15,"starSnapshotCount":15,"syncStatus":28,"lastSyncTime":29,"discoverSource":30},78438,"ai-memory","akitaonrails\u002Fai-memory","akitaonrails","Solution for long term memory for agent coding CLIs and to facilitate handoff between different agent vendors",null,"Rust",569,61,10,1,0,8,48,542,36,92.38,false,"main",true,[],"2026-06-12 04:01:23","\u003Cp align=\"center\">\n  \u003Cpicture>\n    \u003Csource media=\"(prefers-color-scheme: dark)\" srcset=\"docs\u002Flogo-dark.png\">\n    \u003Cimg alt=\"ai-memory\" src=\"docs\u002Flogo.png\" width=\"480\">\n  \u003C\u002Fpicture>\n\u003C\u002Fp>\n\n> Long-term memory for AI coding agents. Quit Claude Code mid-task,\n> start OpenAI Codex in the same directory, continue without\n> re-explaining the architecture, the failed approaches, or the open\n> questions.\n\n[![status: v0.2 milestones complete](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fstatus-v0.2--complete-green)](docs\u002FARCHITECTURE.md)\n[![Rust](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Frust-1.95+-blue)](rust-toolchain.toml)\n[![License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-blue)](LICENSE)\n\n## What it is\n\nLLM coding agents lose all context when a session ends. ai-memory\ngives them a shared, persistent wiki: every prompt, tool call, and\ndecision is captured automatically; when a session ends, the relevant\npages get rewritten as a coherent narrative; when the next agent\nstarts (Claude Code, Codex, OpenCode, …) it sees a handoff with\n\"where you left off\" already prepended.\n\nThe wiki is plain markdown in a git repo - `grep`-able, openable in\nObsidian, backed up with `rsync`. No vector database to babysit, no\n`write_note` ceremony, no manual context-loading. The full design is\nin [`docs\u002FARCHITECTURE.md`](docs\u002FARCHITECTURE.md); the influences and\npriors are at the [bottom](#influences-and-prior-art).\n\n## Key features\n\n- **Zero-friction capture.** Lifecycle hooks fire-and-forget every\n  prompt + tool call + session boundary. You never type `write_note`.\n- **Cross-agent handoffs.** Quit Claude Code mid-task, start Codex\n  in the same directory hours later - the next agent sees a\n  \"where you left off\" block before its first prompt.\n- **Per-project isolation by construction.** Each project lives at\n  `\u003Cwiki_root>\u002F\u003Cworkspace_id>\u002F\u003Cproject_id>\u002F…` keyed by stable UUIDs.\n  Same page path can exist in two projects without collision; a\n  rename is one column update; a purge is one `rm -rf`.\n- **Karpathy-style LLM wiki.** Pages are compiled from observations\n  at session-end (or PreCompact), not retrieved over raw logs.\n  Supersession chain + git-versioned markdown means you can\n  time-travel with `git log`.\n- **Built-in `\u002Fweb` browser.** Read-only HTML UI for the wiki -\n  project list, folder tree, FTS5 search, markdown rendering, dark\n  mode. Mounted on the same axum server as MCP.\n- **Multi-agent + multi-machine ready.** Supported clients: Claude\n  Code, Codex, OpenCode, Cursor, Claude Desktop (via `mcp-remote`),\n  Gemini CLI, OpenClaw. Server runs local (loopback) OR on a homelab\n  box (LAN\u002FVPN\u002Fcloud) with bearer-token auth.\n- **Thin-client CLI.** `ai-memory bootstrap`, `purge-project`,\n  `rename-project`, `lint`, `embed`, `forget-sweep`, `backup` are\n  all HTTP clients of the running server - never touch SQLite or\n  wiki files directly. Server is the single source of truth.\n- **LLM is opt-in.** Zero-LLM mode still gives you FTS5 search +\n  rule-based summarisation. Add a provider when you want consolidated\n  pages and lint contradictions.\n\n## Use cases\n\n- **\"Quit at 4 PM, pick up at 9 AM in a different agent.\"** The\n  classic. SessionStart hook in the next agent (any of the\n  supported CLIs) prepends a typed handoff with the open questions,\n  next steps, and a session summary.\n- **\"What did we decide about X six weeks ago?\"** Type\n  `memory_query X` from the agent (or `ai-memory search X` from a\n  terminal) - FTS5 over the wiki. Pages are LLM-consolidated, so\n  the hit is a coherent decision page, not a raw chat log.\n- **\"This new project has months of history before ai-memory.\"**\n  `cd ~\u002FProjects\u002F\u003Cthat-project> && ai-memory bootstrap` collects\n  `git log`, README, `docs\u002F`, module headers, project rules and\n  one-shot-summarises them into seed wiki pages. Future sessions\n  build on top.\n- **\"Run one ai-memory for the whole household.\"** Stand the server\n  up on a homelab box at `0.0.0.0:49374` with a bearer token; every\n  laptop\u002Fdesktop talks to it. Per-cwd routing keeps each project's\n  pages cleanly separated; the `\u002Fweb` UI is reachable from a\n  browser anywhere on the LAN.\n- **\"Audit what landed before sharing with a teammate.\"** Browse\n  the wiki at `http:\u002F\u002F\u003Cserver>:49374\u002Fweb` - HTTP Basic dialog if\n  auth is on, paste the token as password. Per-project tree view,\n  rendered markdown, supersession chain visible per page.\n- **\"Drop an experiment, keep the rest.\"**\n  `ai-memory purge-project --project experimental --confirm`.\n  Atomic: that project's DB rows cascade away, its wiki subdir gets\n  `rm -rf`'d, every sibling project is untouched by construction.\n\n## Quick start\n\nYou need: Docker + an agent CLI (Claude Code, Codex, OpenCode, Cursor,\nor anything else that speaks MCP).\n\nThe default quick-start has **no authentication** - the server binds\nto loopback only, so on a single-user laptop nothing else can reach\nit. Adding a bearer token is a one-line change once you're ready to\nexpose the server on the LAN; see [Security](#security) below.\n\n```bash\n# 1. Install the ai-memory CLI wrapper (a ~3 KB shell script that\n#    runs the binary inside docker with your $HOME mounted). This is\n#    the only thing that needs to live on the host filesystem.\nmkdir -p ~\u002F.local\u002Fbin\ncurl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002Fakitaonrails\u002Fai-memory\u002Fmain\u002Fbin\u002Fai-memory \\\n    -o ~\u002F.local\u002Fbin\u002Fai-memory\nchmod +x ~\u002F.local\u002Fbin\u002Fai-memory\n# Most distros put ~\u002F.local\u002Fbin on PATH automatically. If `which\n# ai-memory` comes up empty, add this to ~\u002F.bashrc \u002F ~\u002F.zshrc:\n#     export PATH=\"$HOME\u002F.local\u002Fbin:$PATH\"\n\n# 2. Start the server. `--restart unless-stopped` makes it come back\n#    on docker daemon restart and on machine boot (provided your\n#    docker service is enabled at boot — `sudo systemctl enable\n#    docker` on most distros). Loopback-only bind (`127.0.0.1:49374`)\n#    so nothing outside this machine can reach it. Omit the LLM \u002F\n#    EMBEDDING lines for zero-LLM mode — FTS5 search still works\n#    without any keys.\ndocker run -d --name ai-memory \\\n    --restart unless-stopped \\\n    -p 127.0.0.1:49374:49374 \\\n    -v ai-memory-data:\u002Fdata \\\n    -e AI_MEMORY_LLM_PROVIDER=anthropic \\\n    -e ANTHROPIC_API_KEY=sk-ant-... \\\n    -e AI_MEMORY_EMBEDDING_PROVIDER=openai \\\n    -e OPENAI_API_KEY=sk-... \\\n    akitaonrails\u002Fai-memory:latest\n\n# 3. Wire your agent CLI in two commands. The wrapper takes care of\n#    mounts + auto-detecting ~\u002F.claude\u002Fsettings.json. Re-run with\n#    `--agent codex`, `--agent opencode`, `--client cursor`, etc.\n#    for additional agents; full list in docs\u002Finstall.md.\nai-memory install-mcp   --client claude-code --apply\nai-memory install-hooks --agent  claude-code --apply\n```\n\nThat's it. Start a Claude Code session as usual - every prompt and\ntool call now lands in ai-memory, and the next session you open in\nthis project will see a handoff with where you left off.\n\nThe `install-mcp` \u002F `install-hooks` commands default to\n`http:\u002F\u002F127.0.0.1:49374` (matching the server above) and no bearer\ntoken. Both are idempotent - re-runs replace ai-memory's entry,\npreserve every other server \u002F hook you have configured, and write a\ntimestamped `.bak-\u003Cts>` next to the file before each modifying\nwrite. The hook scripts are staged into\n`~\u002F.local\u002Fshare\u002Fai-memory\u002Fhooks\u002F\u003Cagent>\u002F` automatically; re-running\noverwrites them so future image updates ship updated hooks. Drop\n`--apply` to print the snippet instead of mutating.\n\n> **Prefer docker compose?** Clone the repo and run\n> `docker compose -f docker\u002Fdocker-compose.yml up -d` instead of\n> step 2. The bundled compose file already has\n> `restart: unless-stopped`, a healthcheck, and the named volume\n> wired up; step 3 is identical.\n\n> **Server on a different machine?** Replace `-p 127.0.0.1:49374:49374` with\n> `-p 0.0.0.0:49374:49374` on the server's docker run; on the client, set\n> `export AI_MEMORY_SERVER_URL=http:\u002F\u002F\u003Cserver-ip>:49374` and add\n> `--server-url \u003Csame>` to the install-mcp \u002F install-hooks commands.\n> See [Security](#security) — anything non-loopback should also have a bearer token.\n\n### Keeping ai-memory up to date\n\nThe wrapper checks Docker Hub at most once every 24 hours and prints\na one-line warning to stderr when a newer image is available. To\nupgrade:\n\n```bash\nai-memory upgrade\n```\n\nIn order: (1) self-upgrades the wrapper script itself by re-fetching\n`bin\u002Fai-memory` from GitHub (validated against the shebang; falls\nback gracefully if curl is missing or perms block the in-place\nreplacement), (2) `docker pull`s the latest image, (3) re-stages\nhook scripts under `~\u002F.local\u002Fshare\u002Fai-memory\u002Fhooks\u002F\u003Cagent>\u002F` for\nevery agent you've configured, and (4) tells you how to restart the\nserver container so the new binary is picked up. The hook refresh\nis idempotent - re-running `install-hooks --apply` replaces the\nseven keys ai-memory owns and leaves every other hook the user has\nwired up alone. Set `AI_MEMORY_NO_VERSION_CHECK=1` to silence the\ndaily check, or `AI_MEMORY_WRAPPER_URL=\u003Curl>` to pin the self-upgrade\nsource (e.g. a fork or a tagged release).\n\n> **If your server runs on a different host** (scenarios C\u002FD), `ai-memory upgrade`\n> only refreshes the local wrapper, your local image, and your hook scripts. You\n> still need to redeploy the server separately — run `bin\u002Fdeploy` on the homelab\n> box (or `docker compose pull && docker compose up -d` in your deploy dir) so\n> the server picks up the new binary too.\n\n> **Inside ai-jail (or any bwrap sandbox)?** The wrapper at\n> `~\u002F.local\u002Fbin\u002Fai-memory` works fine — the sandbox bind-mounts\n> `~\u002F.local` read-only, so the script is visible from inside, and\n> `\u002Fvar\u002Frun\u002Fdocker.sock` is already passed through. Run the\n> `install-*` commands *outside* ai-jail (they need to write to\n> `~\u002F.local\u002Fshare\u002Fai-memory\u002Fhooks\u002F`, which the sandbox keeps\n> read-only); daily use from inside the sandbox needs no binary at\n> all (agents reach ai-memory over MCP).\n\n**For everything else** - Codex, OpenCode, Cursor, Claude Desktop,\nGemini CLI, OpenClaw, the curl-based hook installer (no docker\nneeded), running ai-memory without docker, the full subcommand\nreference, the homelab deploy pattern, security hardening - see\n[**`docs\u002Finstall.md`**](docs\u002Finstall.md).\n\n## Security\n\nThe default Quick start runs **without authentication** because the\nserver is bound to loopback (`127.0.0.1:49374`) - no process outside\nthis machine can reach it. That's the safest default for a personal\nlaptop and matches the \"single-user, single-machine\" use case the\nproject is optimised for.\n\n### When you need bearer auth\n\nEnable bearer auth if **any** of these are true:\n\n- The server is exposed beyond loopback (LAN, VPN, reverse proxy, cloud).\n- More than one untrusted process runs on the same machine.\n- The data dir contains observations from sensitive projects you\n  wouldn't want any local user to read.\n\n### Enabling bearer auth (turn-key recipe)\n\n```bash\n# 1. Generate a token (one-time; save the output somewhere).\nTOKEN=$(ai-memory generate-auth-token)\necho \"$TOKEN\"   # 64 hex chars\n\n# 2. Pass it to the server on startup. Note the bind is now 0.0.0.0\n#    so remote clients can reach it; only do this with a token set.\ndocker run -d --name ai-memory \\\n    --restart unless-stopped \\\n    -p 0.0.0.0:49374:49374 \\\n    -v ai-memory-data:\u002Fdata \\\n    -e AI_MEMORY_AUTH_TOKEN=\"$TOKEN\" \\\n    -e AI_MEMORY_LLM_PROVIDER=anthropic \\\n    -e ANTHROPIC_API_KEY=sk-ant-... \\\n    akitaonrails\u002Fai-memory:latest\n\n# 3. Set the same token in every client environment that needs to\n#    reach this server:\nexport AI_MEMORY_AUTH_TOKEN=\"$TOKEN\"\n\n# 4. Re-run install-mcp \u002F install-hooks so the agent configs pick\n#    up the new token + URL.\nai-memory install-mcp   --client claude-code --apply \\\n    --server-url \"http:\u002F\u002F192.168.0.90:49374\u002Fmcp\" \\\n    --auth-token \"$TOKEN\"\nai-memory install-hooks --agent  claude-code --apply \\\n    --server-url \"http:\u002F\u002F192.168.0.90:49374\" \\\n    --auth-token \"$TOKEN\"\n```\n\nWhen the server has `AI_MEMORY_AUTH_TOKEN` set, every request to\n`\u002Fmcp`, `\u002Fhook`, `\u002Fhandoff`, `\u002Fadmin\u002F*`, and `\u002Fweb\u002F*` must present\nthe token. HTTP clients (MCP, hooks, CLI) send an\n`Authorization: Bearer \u003Ctoken>` header; the `\u002Fweb\u002F*` browser flow\nuses HTTP Basic auth (see below). Token comparison uses\n`subtle::ConstantTimeEq` to rule out timing-based recovery.\n\nWhen the server has **no** `AI_MEMORY_AUTH_TOKEN` set AND binds to a\nnon-loopback address, it logs a loud `warn` on startup. That's the\nsignal to either lock the bind back to `127.0.0.1` or set a token.\n\nSee [`docs\u002Fdeploy.md`](docs\u002Fdeploy.md) for the full homelab pattern\n(bearer + TLS via cloudflared + reverse proxy).\n\n### DNS-rebinding guard (`AI_MEMORY_ALLOWED_HOSTS`)\n\nAny non-loopback bind also needs `AI_MEMORY_ALLOWED_HOSTS` set to the\nhost\u002FIP the clients will use, otherwise the HTTP server rejects\nexternal `Host` headers with 403 before they reach `\u002Fmcp`, `\u002Fhook`,\n`\u002Fhandoff`, `\u002Fadmin\u002F*`, or `\u002Fweb\u002F*`:\n\n```bash\n-e AI_MEMORY_ALLOWED_HOSTS=\"\u003Cserver-ip>,localhost,127.0.0.1\"\n```\n\nClients hitting the server by IP or hostname will be accepted;\nrequests with an unrecognised `Host` are refused. This guards against\nDNS-rebinding attacks where a malicious page tricks the browser into\nsending requests to the server using a different hostname.\n\n### Browser access to `\u002Fweb`\n\nWhen the server has **no** bearer token set, visit\n`http:\u002F\u002F\u003Chost>:49374\u002Fweb` in any browser - no prompt.\n\nWhen `AI_MEMORY_AUTH_TOKEN` is set, the browser shows a native HTTP\nBasic dialog on first visit. Leave the username blank (or any value)\nand paste the token as the password. The server sets a cookie that\npersists for 30 days so subsequent navigation doesn't re-prompt.\n\nBrowsers cannot pass a Bearer header in normal navigation, which is\nwhy the `\u002Fweb` routes use HTTP Basic rather than Bearer. MCP and hook\nclients continue to use `Authorization: Bearer \u003Ctoken>`.\n\n## Configuring the CLI\n\nThe `ai-memory` binary is a thin HTTP client. It never opens the\nwiki or SQLite directly - every state-touching command goes through\nthe running server, which is the sole writer.\n\nConfiguration is two environment variables, both **optional**:\n\n| Variable | Default | When to set it |\n|---|---|---|\n| `AI_MEMORY_SERVER_URL` | `http:\u002F\u002F127.0.0.1:49374` | When the server runs somewhere other than this machine (e.g. a homelab at `http:\u002F\u002F192.168.0.90:49374`). |\n| `AI_MEMORY_AUTH_TOKEN` | unset (no auth) | When the server has bearer auth enabled - see [Security](#security). |\n\nFor the **single-laptop local case** (scenarios A\u002FB) you don't need\neither env var: the CLI talks to the loopback server and just works.\nScenario B (loopback + token) only requires `AI_MEMORY_AUTH_TOKEN` in\nthe env.\n\nFor a **remote \u002F homelab** server (scenarios C\u002FD), set both in your\nshell rc (or a `.envrc` if you use direnv):\n\n```bash\nexport AI_MEMORY_SERVER_URL=\"http:\u002F\u002F192.168.0.90:49374\"\nexport AI_MEMORY_AUTH_TOKEN=\"b9a5075d…\"   # only when server has auth enabled\n```\n\nExplicit flags (`--auth-token`, `--server-url`) on `install-mcp` \u002F\n`install-hooks` override env vars when both are set - useful when\nyou're generating configs for a client that talks to a different\nserver than the CLI default.\n\nThe `init`, `serve`, `install-*`, `generate-auth-token`, and\n`setup-agent` subcommands don't need these env vars - they either\nset up local files or start the server itself.\n\n## How it works in practice\n\nYou mostly don't think about it. Hooks capture every prompt + tool\ncall + session boundary automatically. The agent gains awareness of\nprior work without you typing anything special. A few patterns are\nworth knowing:\n\n### Cross-agent handoff\n\n```\n$ claude\n> \"Working on the auth refactor. JWT rotation story is broken; trying\n   session cookies as an alternative.\"\n[work for an hour]\n> \u002Fexit\n\n$ codex   # in the same directory, hours or days later\n[SessionStart hook fetches the handoff; the next agent sees it.]\n> \"Picking up: you were investigating session cookies as an\n   alternative to broken JWT rotation. Continuing?\"\n```\n\nYou did nothing special. Handoff created automatically on Claude\nCode's session-end, surfaced automatically on Codex's session-start.\n\n### Compaction recovery\n\nWhen Claude Code or Codex compact their working context, the\n`PreCompact` hook fires and ai-memory writes a fresh\n`sessions\u002F\u003Cid>.md` page summarising the session so far. After\ncompaction, the agent can recover the summary via `memory_recent`\neven though its raw history is gone.\n\n### Adopting ai-memory mid-project: bootstrap\n\nIf you're installing ai-memory in a project you've been working on\nfor months, the wiki starts empty and the first few sessions are\nnet-zero - you're populating, not retrieving. `ai-memory bootstrap`\nsolves that by LLM-summarising your existing `git log`, README,\n`docs\u002F`, and module-level doc-comments into seed wiki pages.\n\n```bash\n# Run from your project's repo root. The CLI collects sources locally\n# (git log, README, docs\u002F, module headers) and POSTs them to the server\n# at AI_MEMORY_SERVER_URL, where the LLM call and wiki writes happen.\n# Requires an LLM provider configured on the server. Budget caps at\n# 50k input tokens (~$0.05 with Claude Haiku 4.5).\nexport AI_MEMORY_SERVER_URL=\"http:\u002F\u002Flocalhost:49374\"\nai-memory bootstrap\n```\n\nThe workspace defaults to `default` and the project defaults to the\ncurrent directory's basename - that's almost always what you want, so\nomit `--workspace` \u002F `--project` unless you're deliberately overriding.\n\nBootstrap produces a per-project `bootstrap.md` manifest (under\n`\u003Cwiki>\u002F\u003Cworkspace>\u002F\u003Cproject>\u002F`) listing every page generated + a\none-paragraph rationale. Run with `--dry-run` first to preview which\nsources would be sent without paying for the LLM call. Re-running on\nthe same project requires `--force`.\n\nSee [`docs\u002Finstall.md`](docs\u002Finstall.md#bootstrap-mid-project) for\nthe full flag reference + per-source priority order.\n\n### Spelunking your own history\n\n```bash\ndocker exec ai-memory ls \u002Fdata\u002Fwiki\u002Fsessions\u002F\ndocker exec ai-memory cat \u002Fdata\u002Fwiki\u002Fsessions\u002F\u003Cuuid>.md\n\n# Open in Obsidian \u002F any markdown viewer:\ndocker cp ai-memory:\u002Fdata\u002Fwiki .\u002Fmy-ai-memory-wiki\n\n# Time-travel:\ndocker exec ai-memory git -C \u002Fdata\u002Fwiki log --oneline\n```\n\n### Browse the wiki in a browser\n\nFor a more navigable view, start the server with `--enable-web` and\nopen `http:\u002F\u002F\u003Chost>:49374\u002Fweb` in any browser. Project-list homepage,\nper-project page tree with breadcrumbs, rendered markdown with syntax\nhighlighting and metadata (tier, kind, pinned, supersedes chain),\nplus FTS5 search - all read-only, no editing. Light\u002Fdark theme\nfollows your OS setting via `prefers-color-scheme`.\n\n![Project list homepage with the LLM-optimised banner; four projects (distrobox-gaming, ai-memory, nes-to-sms, .config) shown as cards with page counts + last activity.](docs\u002Fweb-projects-home.png)\n\nDrill into any project for the folder tree (`concepts\u002F`, `decisions\u002F`,\n`gotchas\u002F`, `sessions\u002F`) and the recent-activity column:\n\n![distrobox-gaming project view: left sidebar shows the folder tree grouped by kind; right column lists recent pages with kind badges and relative timestamps.](docs\u002Fweb-project-view.png)\n\n```bash\nai-memory serve --transport http --bind 127.0.0.1:49374 --enable-web\n# or, if you run via docker compose, add it to the command line in\n# docker-compose.yml: [\"serve\", \"--transport\", \"http\", \"--bind\",\n# \"0.0.0.0:49374\", \"--enable-web\"]\n```\n\nThe web routes are mounted at `\u002Fweb` on the same axum server as the\nMCP endpoint. When the server has bearer auth enabled, the browser\nshows a native HTTP Basic dialog on first visit - leave the username\nblank (or any value) and paste the token as the password; the cookie\npersists for 30 days so subsequent navigation doesn't re-prompt.\nLoopback-bound servers with no token need no credentials at all. See\n[Security → Browser access to \u002Fweb](#browser-access-to-web) above.\n\n### Rules vs facts - ai-memory tells you when something belongs in CLAUDE.md\n\nWhen you type something like \"don't forget to never add a function\nwithout a unit test\", that's a **durable project rule**, not a\nsession-level observation. Rules need to fire on every relevant\naction - that's what your project's `CLAUDE.md` \u002F `AGENTS.md` is for\n(it's loaded into the agent's system prompt every turn), while\nai-memory queries only fire when the agent thinks to call them.\n\nThe consolidator now classifies each compiled observation as\n`decision | fact | rule | gotcha`. Rule-tagged pages are auto-routed\nto `wiki\u002F_rules\u002F\u003Cslug>.md`, and the next time you run `memory_lint`\nthe agent sees a suggestion:\n\n> **rule_suggestion**: Page `_rules\u002Fnever-ship-code-without-test.md`\n> looks like a durable project rule. Consider copying it into your\n> project's CLAUDE.md \u002F AGENTS.md so the agent sees it on every\n> turn, not just when it remembers to call memory_query.\n\nai-memory never edits your `CLAUDE.md` itself - the suggestion is\nthe whole UX. You copy what's useful, ignore what isn't.\n\n### When the agent reaches for memory\n\nHooks handle *capture* (every prompt + tool call + session boundary)\nand *handoff resume* (SessionStart auto-fetches pending handoffs)\nwithout you typing anything. **Proactive querying** - the agent\nreaching into the wiki on its own - depends on the agent knowing\n*when* to call which tool. Concrete patterns once the routing\nsnippet is installed (see \"Nudge the agent\" below):\n\n| You say… | Agent calls… | Effect |\n|---|---|---|\n| \"Have we discussed X?\" \u002F \"search memory for Y\" | `memory_query` | FTS5 over consolidated wiki pages; returns ranked snippets with `\u003Cmark>`-highlighted hits. |\n| (before proposing architecture) implicit | `memory_query` | The routing snippet tells the agent to check prior decisions \u002F gotchas before proposing anything, so you don't get contradicted-by-its-own-history suggestions. |\n| \"Catch me up\" \u002F \"I've been away\" | `memory_explore` | Prose digest. Verbosity auto-scales to time-since-last-activity: \u003C 1 h → one line, > 30 days → full catchup. |\n| \"Where did we leave off?\" | (handoff already prepended) | SessionStart hook already prepended the handoff before your first prompt; the agent just reads from that block. |\n| \"Save context for the next session\" | `memory_handoff_begin` | Writes a terse handoff with `open_questions` + `next_steps` for whichever agent opens this project next. |\n| \"Consolidate this session\" | `memory_consolidate` | Manual trigger of what session-end normally does automatically (compile observations into wiki pages). |\n| \"Audit the wiki\" \u002F \"any contradictions?\" | `memory_lint` | Runs the stale-page \u002F contradiction \u002F rule-suggestion pass. |\n| \"How big is the wiki?\" \u002F \"stats?\" | `memory_status`, `memory_briefing` | Counts + recent activity windows. |\n\nThe mapping is in the [routing snippet](#nudge-the-agent-to-use-memory-proactively)\nthe next section installs.\n\n### Nudge the agent to *use* memory proactively\n\nThe capture side works automatically. For the *query* side, the\nagent needs a routing table in the project's rules file\n(`CLAUDE.md` for Claude Code; `AGENTS.md` for Codex \u002F OpenCode \u002F\nCursor \u002F Gemini CLI). Two ways to install it - pick whichever's\neasier in the moment:\n\n**From the agent** (no terminal needed):\n\n> \"Install ai-memory routing into this project.\"\n\nThe agent calls the `memory_install_self_routing` MCP tool, gets\nback the canonical snippet + a per-agent filename map, then uses\nits own Write\u002FEdit tool to land the block in the right rules file\n(Claude Code → CLAUDE.md, everyone else → AGENTS.md). Idempotent:\nthe block is wrapped in `\u003C!-- ai-memory:start -->` \u002F\n`\u003C!-- ai-memory:end -->` markers so the agent replaces in place on\nre-runs.\n\n**From the terminal**:\n\n```bash\nai-memory install-instructions          # auto-detects CLAUDE.md vs AGENTS.md\nai-memory install-instructions --target AGENTS.md   # force a specific file\nai-memory install-instructions --print              # preview without writing\n```\n\nThe CLI's auto-detect: if `$PWD\u002FCLAUDE.md` exists, extend that;\nif `$PWD\u002FAGENTS.md` exists, extend that; if both exist, write to\nboth (multi-agent project - keep both files in sync); if neither\nexists, create `CLAUDE.md` and print a hint about `--target\nAGENTS.md` for non-Claude agents.\n\nBoth paths produce the same block. Both replace existing markered\nblocks in place rather than duplicating, so you can re-run safely\nwhenever the snippet evolves (e.g. when a new MCP tool ships).\n\n## LLM provider - recommended defaults\n\nYou can run ai-memory entirely without an LLM (FTS5 search +\nrule-based summaries, $0). When you *do* configure one, the\noptions below are ranked by fitness for ai-memory's\nconsolidation workload - see\n[`docs\u002Fllm-provider-comparison.md`](docs\u002Fllm-provider-comparison.md)\nfor the empirical writeup behind this ranking.\n\n> **TL;DR.** Use **Claude Haiku 4.5** as your default. Switch\n> to **GPT-5.4-mini** if you want the same quality cheaper +\n> faster. Switch to **qwen3:32b on Ollama** if you have a\n> local LLM server and prefer $0 \u002F fully-self-hosted. The\n> three are interchangeable; pick once and forget.\n\n### Option 1 - Claude Haiku 4.5 *(recommended default)*\n\nBest balance of speed (~7 s), restraint, and classification\nquality. The only model that consistently classifies durable\nproject rules as `kind: rule` so the consolidator auto-routes\nthem to `_rules\u002F\u003Cslug>.md`. ~$0.02 per consolidation; cost\nis negligible for personal use.\n\n```bash\nAI_MEMORY_LLM_PROVIDER=anthropic\nAI_MEMORY_LLM_MODEL=claude-haiku-4-5\nANTHROPIC_API_KEY=sk-ant-…\n```\n\nOr via OpenRouter (handy if you already have an OpenRouter\naccount and want one bill):\n\n```bash\nAI_MEMORY_LLM_PROVIDER=openai-compat\nAI_MEMORY_LLM_BASE_URL=https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\nAI_MEMORY_LLM_MODEL=anthropic\u002Fclaude-haiku-4.5\nLLM_API_KEY=sk-or-v1-…\n```\n\n### Option 2 - OpenAI GPT-5.4-mini *(cheaper alternative)*\n\n~5× cheaper than Haiku, ~2× faster (~4 s avg). Same parse\nreliability, same faithfulness. One known weakness: mild\nover-classification on trivial sessions (will sometimes\nmanufacture an extra `decisions\u002F` page for a thin\nsession). Acceptable for most users.\n\n```bash\nAI_MEMORY_LLM_PROVIDER=openai\nAI_MEMORY_LLM_MODEL=gpt-5.4-mini\nOPENAI_API_KEY=sk-…\n```\n\nOr via OpenRouter:\n\n```bash\nAI_MEMORY_LLM_PROVIDER=openai-compat\nAI_MEMORY_LLM_BASE_URL=https:\u002F\u002Fopenrouter.ai\u002Fapi\u002Fv1\nAI_MEMORY_LLM_MODEL=openai\u002Fgpt-5.4-mini\nLLM_API_KEY=sk-or-v1-…\n```\n\n### Option 3 - Local Ollama qwen3:32b *(free \u002F self-hosted)*\n\n$0 per consolidation. Requires a machine with at least ~24 GB\nof unified or VRAM memory to keep the Q4_K_M weights warm\n(~20 GB) plus headroom. Strix Halo \u002F Apple Silicon \u002F a\nrecent NVIDIA card all work. Latency is ~90 s but\nconsolidation is a background job - users never see it.\n\nOne-time setup on the Ollama host:\n\n```bash\nollama pull qwen3:32b\nollama pull nomic-embed-text   # for embeddings; see below\n# Recommended Ollama env:\n#   OLLAMA_KEEP_ALIVE=20m       (keep models warm between consolidations)\n#   OLLAMA_FLASH_ATTENTION=1\n#   OLLAMA_KV_CACHE_TYPE=q8_0   (halves KV memory)\n```\n\nai-memory env:\n\n```bash\nAI_MEMORY_LLM_PROVIDER=openai-compat\nAI_MEMORY_LLM_BASE_URL=http:\u002F\u002F\u003Collama-host>:11434\u002Fv1\nAI_MEMORY_LLM_MODEL=qwen3:32b\nLLM_API_KEY=ollama-local                  # any non-empty value; Ollama doesn't validate\n```\n\nIf you bind ai-memory to a non-loopback address, also set\n`AI_MEMORY_ALLOWED_HOSTS` - see [Security → DNS-rebinding guard](#dns-rebinding-guard-ai_memory_allowed_hosts).\n\n### What we don't recommend\n\n- **Claude Sonnet 4.5** - strictly dominated by Haiku for\n  this task: same parse reliability, 3× cost, hallucinated\n  details before the prompt was tightened. Use it only if\n  you specifically need extended reasoning (e.g. cross-page\n  lint sweeps).\n- **Reasoning-mode models** (Kimi-K2.6, Claude with extended\n  thinking enabled, GPT-o3, Gemini \"thinking\" variants) -\n  these models burn `max_tokens` budget on internal\n  reasoning before emitting visible content; with the\n  strict-JSON consolidation prompt they hang or emit empty\n  responses. If you must use one, turn reasoning off.\n\n### Embedding provider\n\nThe LLM provider drives consolidation + lint. Embeddings are\na *separate* concern (hybrid retrieval over the wiki - BM25\n+ vector RRF). Defaults when `AI_MEMORY_EMBEDDING_PROVIDER`\nis set:\n\n| Provider | Default model | Dim |\n|---|---|---|\n| `openai` | `text-embedding-3-small` | 1536 |\n| `voyage` | `voyage-3` | 1024 |\n\nFor the local stack, point the OpenAI embedder at Ollama:\n\n```bash\nAI_MEMORY_EMBEDDING_PROVIDER=openai\nAI_MEMORY_EMBEDDING_BASE_URL=http:\u002F\u002F\u003Collama-host>:11434\u002Fv1\nAI_MEMORY_EMBEDDING_MODEL=nomic-embed-text\nAI_MEMORY_EMBEDDING_DIM=768\nOPENAI_API_KEY=ollama-local\n```\n\nSkipping the embedding provider entirely is fine -\n`memory_query` falls back to pure FTS5 (BM25) and still\nworks; you just lose vector re-ranking.\n\nPer-tier feature breakdown + the openai-compat \u002F Ollama setup\nis in [`docs\u002Finstall.md`](docs\u002Finstall.md#llm-provider-tiers).\n\n## Architecture in 60 seconds\n\nA single Rust binary, optionally containerised. Runs as an\n[MCP](https:\u002F\u002Fmodelcontextprotocol.io\u002F) server over stdio + HTTP.\nOwns a data directory containing:\n\n```\n\u003Cdata_dir>\u002F\n├── wiki\u002F    # markdown source of truth (git-versioned)\n├── raw\u002F     # immutable session log archive\n├── db\u002F      # SQLite (FTS5 + page_embeddings) — derived index\n├── models\u002F  # reserved for local embedding model (v0.3+)\n└── logs\u002F    # rolling daily tracing output\n```\n\nAgent lifecycle hooks fire-and-forget POST to the server's HTTP\ningress. The server queues writes through a single SQLite writer\n(no `database is locked`). On session end an optional LLM-driven pass\nrewrites pages atomically with supersession (`is_latest=false` +\n`supersedes` chain) and opens a typed handoff for the next agent.\nThe retention sweep decays unused episodic content while semantic\nconcept pages compound forever; pinned pages are exempt. Retrieval\nis FTS5 by default; when an embedder is configured, hybrid RRF over\n`page_embeddings` joins the FTS5 ranks.\n\nSee [`docs\u002FARCHITECTURE.md`](docs\u002FARCHITECTURE.md) for the canonical\ndata-flow diagram + crate breakdown + cross-cutting invariants.\n\n## Docs\n\n| File | What it is |\n|---|---|\n| [`docs\u002Finstall.md`](docs\u002Finstall.md) | **Installation cookbook.** Every agent CLI, every alternative (curl, source build, no-docker, no-auth), and the server-on-a-different-machine (homelab\u002FLAN) walkthrough. Read after the Quick start if your setup doesn't match the happy path. |\n| [`docs\u002Fmcp-install.md`](docs\u002Fmcp-install.md) | Per-client MCP config snippets (Cursor, Claude Desktop, Gemini CLI, OpenClaw, pi). |\n| [`docs\u002Fdeploy.md`](docs\u002Fdeploy.md) | Homelab deploy: bin\u002Fdeploy, bearer-token auth, TLS via cloudflared. |\n| [`docs\u002Flifecycle-ops.md`](docs\u002Flifecycle-ops.md) | **Read before running purge \u002F rename \u002F backup \u002F restore \u002F reset.** Safety matrix for the state-touching commands, per-project disk layout (how isolation actually works), and operator workflows for \"fresh start\", \"snapshot before risky op\", \"drop one project\". |\n| [`docs\u002FARCHITECTURE.md`](docs\u002FARCHITECTURE.md) | Operational summary: data flow, crate layout, cross-cutting invariants, schema. |\n| [`docs\u002Fdesign-decisions.md`](docs\u002Fdesign-decisions.md) | The full v1 spec. |\n| Research docs under `docs\u002F` | Karpathy LLM Wiki notes, agentmemory \u002F basic-memory \u002F cognee deep-dives, lessons-learned from upstream issues. |\n\n## Influences and prior art\n\n- **[Karpathy LLM Wiki](https:\u002F\u002Fgist.github.com\u002Fkarpathy\u002F442a6bf555914893e9891c11519de94f)** - the compile-not-retrieve pattern.\n- **[agentmemory](https:\u002F\u002Fgithub.com\u002Frohitg00\u002Fagentmemory)** - most of the right ideas; this project is the Rust successor.\n- **[basic-memory](https:\u002F\u002Fgithub.com\u002Fbasicmachines-co\u002Fbasic-memory)** - the markdown-on-disk source-of-truth model.\n- **[cognee](https:\u002F\u002Fgithub.com\u002Ftopoteretes\u002Fcognee)** - pipeline composition and triplet embeddings.\n- **[A-MEM](https:\u002F\u002Farxiv.org\u002Fabs\u002F2502.12110)** - Zettelkasten-style atomic notes with link evolution.\n\n## License\n\nMIT - see [LICENSE](LICENSE).\n\n## Acknowledgements\n\nThis codebase is being built collaboratively with Claude Code\n(Anthropic Claude Opus 4.7) following the plan documented in\n`docs\u002Fdesign-decisions.md`.\n","ai-memory 是一个为AI编码代理提供长期记忆的解决方案，旨在解决不同代理供应商之间切换时上下文丢失的问题。其核心功能包括零摩擦捕获、跨代理交接以及基于项目的隔离等，通过将所有交互记录自动保存到一个共享且持久化的wiki中（以纯markdown格式存储于git仓库），使得开发者可以在中断后无缝继续工作而无需重复解释背景信息。该项目采用Rust语言开发，具有良好的性能和稳定性。适用于需要频繁切换AI编码工具或希望保持项目历史记录清晰可追溯的研发团队使用。此外，它还支持多客户端接入，并提供了一个内置的Web浏览器来方便地查看和搜索wiki内容。",2,"2026-06-11 03:56:49","CREATED_QUERY"]