A simple, file-based system that gives persistent inter-session memory to Zoo Code, Claude Code, Cursor, or any code agent that can read files. No dependencies, no third-party tools, nothing to install β just conventions + a "rules" file that Zoo Code automatically injects into the system prompt at the start of every session.
Open a new Zoo Code conversation in your workspace and paste this single sentence:
Read https://github.com/pguedon-ai/zoo-code-memory and install the Zoo memory
system in this workspace. Detect the projects present, fill in the global
memory and one ZOO.md per project with my real environment, create .clinerules,
add the git-exclusion block to each existing .gitignore, and tell me when
you're done and how to verify it works.
That's it β the agent does the rest. It will:
- Read this repo (README +
templates/). - Inspect your workspace, OS, tool versions, and existing
.gitignores. - Create
.zoo/ZOO.md(global memory) and<project>/ZOO.md(one per project) filled with your real environment β not template placeholders. - Create
.clinerulesat the workspace root β the trigger Zoo Code auto-injects at every session. - Append the git-exclusion block to each repo's
.gitignoreso the memory is never committed. - Tell you when it's done and how to verify it (see How to verify it works).
Why this works: Zoo Code can read a GitHub repo, write files, run shell commands, and edit your .gitignores β so a single sentence is enough to bootstrap the whole system. You don't need to copy templates by hand or read the rest of this README.
Prefer to do it by hand, or want to audit what the agent did? See Manual setup (optional) at the bottom β every step is verifiable.
- A global memory file (
.zoo/ZOO.md) + a per-project memory file (<project>/ZOO.md). - A rules file (
.clinerules) that Zoo Code automatically injects into the system prompt at every session, instructing the agent: "read the memory before answering, update it when you're done". - The memory is local and never version-controlled (added to
.gitignore) β it lives on the volume, not on GitHub.
Result: from one session to the next, the agent no longer forgets the project's architecture, the pitfalls it hit, the decisions you made. It reads the relevant ZOO.md before answering β without you having to ask.
| Layer | Location | Content |
|---|---|---|
| Global | .zoo/ZOO.md (at the workspace root) |
User preferences, server environment (OS, tools, versions), cross-project conventions, project inventory, session history |
| Per-project | <project>/ZOO.md (at each project's root) |
Project architecture, decisions made, known pitfalls, code locations, DB schema, project-specific history |
| Trigger | .clinerules (at the workspace root) |
Automatically injected by Zoo Code into the system prompt at every session (setting zoo-code.useAgentRules, enabled by default). This is what makes reading the memory automatic. |
Layout in a workspace where projects live directly at the root:
.zoo/ZOO.md # global memory
.clinerules # trigger (injected at every session)
projectA/ZOO.md # project A memory
projectB/ZOO.md # project B memory
If the agent is not Zoo Code, use the equivalent rules file:
| Agent | Rules file |
|---|---|
| Zoo Code | .clinerules |
| Claude Code | CLAUDE.md |
| Cursor | .cursorrules |
| Generic agents | AGENTS.md |
| Fallback | paste the rules into the tool's custom system instructions |
Don't trust the setup until you've run this end-to-end test. It's the only way to know the agent actually reads the memory before answering.
# 1. Files exist where they should
ls .zoo/ZOO.md .clinerules <project>/ZOO.md # all should exist
# 2. Memory is not tracked by git (in each repo)
git check-ignore -v ZOO.md .clinerules .zoo/ZOO.md # should match .gitignore
git ls-files | grep -E 'ZOO\.md$|\.clinerules$|\.zoo/' # should be empty
# 3. The memory contains real info, not template placeholders
head -20 .zoo/ZOO.md
head -20 <project>/ZOO.md- Open a brand-new Zoo Code session (close the current one and start fresh β the injection happens at session start).
- Ask a question about a project, without telling the agent where to look:
- "What's the stack of
<project>?" - "What do you know about my environment?"
- "What's the stack of
- The agent should answer using facts that only exist in the
ZOO.mdfiles β and you should see in the UI trace that it readZOO.mdbefore answering (e.g. "Zoo wants to read this file"). - If the answer is correct and cites info from the memory β the system works.
- Check that
.clinerules(or your agent's equivalent) exists at the workspace root β not inside a project folder. - Check that
zoo-code.useAgentRulesis on (VSCode Settings β searchzoo-code.useAgentRules). - As a last resort, paste the
.clinerulescontent into the tool's custom system instructions.
On a Linux server with Zoo Code + GLM 5.2, after install:
git check-ignore -v ZOO.mdmatched.gitignorein all 3 repos in the workspace βgit ls-files | grep ZOO.mdwas empty (no memory file tracked) β- New session, question about a project's stack β the agent read that project's
ZOO.mdspontaneously (visible in the trace: "Zoo wants to read this file" + recursive folder read) and answered correctly without being told where to look β
- Read
.zoo/ZOO.mdbefore any action β even for a simple question. - If the task mentions a project β also read its
ZOO.md. - Never re-ask the user for info that's already in the memory.
Write to the memory as soon as a piece of info is important and durable:
- an architecture or design decision
- a discovered pitfall (bug, undocumented behavior, tool limitation)
- a clarification from the user (e.g. "this DB is shared between X and Y")
- environment facts (installed version, unavailable tool, blocked network)
- a working rule agreed with the user (e.g. "commits OK, pushes forbidden")
Do NOT write: ephemeral details, raw file contents, anything that can be derived from the code itself.
- Dated entry
[YYYY-MM-DD]at the top of the "Session history" section (most recent first). - Dense summary: what, where (relative links to files), why.
- Pitfalls ALSO go into a dedicated "Known pitfalls" section of the project.
- Human-readable and editable directly in your editor (VSCode, vim, etc.).
- Optionally versionable (decision here: NOT version-controlled β see Limitations).
- Zero dependencies, no third-party tool, nothing to install.
- The agent reads it with its usual file-reading tool (
read_file/cat/ etc.).
Unlike Claude's native "memory" (which persists on Anthropic's servers), this system lives on disk, on your volume. You stay the owner β but it relies on the agent's discipline to read and maintain the files.
- Reading discipline is still manual:
.clinerulesis injected, but nothing forces the agent to obey. In practice, recent models (GLM 5.2, Claude) follow the rule; a weaker model might ignore it. - The memory is not version-controlled: it survives a container rebuild only if the local volume persists. This is a deliberate choice (no secrets in git).
- No Zoo-side memory backend: unlike Claude (which persists memory on Anthropic's servers), here everything is files on your disk β you stay the owner, but there's no automatic sync between machines.
- Sanitize before sharing: if you ever want to share a
ZOO.md(like this repo does for the system), strip every hostname, path, secret, or personal info before committing.
- π«
apply_diffcorrupts emojis in SEARCH/REPLACE blocks (the character becomesU+FFFD). Never put an emoji inside a Zoo Code diff; if an emoji must be inserted near a change, usesedby line number (grep -nto locate, thensed "Ns|.*|..."). - π«
gitrejects spaces in branch names (git check-ref-format --branchexits 1). Convention: hyphens, no spaces, no accents. β οΈ If several git repos coexist (one at the root + one per project), project commits happen inside the project folder, not from the workspace root.β οΈ A.envreferenced in aZOO.mdis just a hint β the real file stays gitignored and lives elsewhere (e.g. asecrets/folder).
README.md # This guide (generic, sanitized)
LICENSE # MIT
.gitignore # Ignores memory files (repo contains only the guide)
templates/
.clinerules # Trigger to copy at the workspace root
ZOO-global.md # Global memory template to fill in
ZOO-project.md # Per-project memory template to fill in
gitignore-block.txt # Block to append to each repo's .gitignore
Prefer to do it by hand, or want to audit what the agent did after the one-sentence install? Here's every step, each verifiable.
- Global memory β
mkdir -p .zoo, copytemplates/ZOO-global.mdto.zoo/ZOO.md, fill in your real OS/tools/projects. Verify:cat .zoo/ZOO.mdshows your real env, not placeholders. - Per-project memory β for each project, copy
templates/ZOO-project.mdto<project>/ZOO.md, fill in architecture/DB/pitfalls/history. Verify:ls <project>/ZOO.mdexists and describes the real architecture. - Trigger β copy
templates/.clinerulesto the workspace root; edit the "User preferences" block. Verify: a new session's system prompt should mention the rules (ask the agent "what rules are in your system prompt?"). - Git exclusion β append the block from
templates/gitignore-block.txtto every repo's.gitignore. Verify:git check-ignore -v ZOO.mdmatches,git ls-files | grep ZOO.mdis empty. - Enable injection β VSCode Settings β
zoo-code.useAgentRulesβ on (default). Verify: see step 3.
Then jump to How to verify it actually works for the end-to-end test.
MIT β do whatever you want, including commercial use. Credit is appreciated but not required.
If this system is useful to you, consider starring the repo or opening an issue to suggest improvements.