Filesystem-based memory tree for AI agents. Persist conversation context on disk in a tree structure grouped by topic — agents keep top-level summaries in context and load deeper levels on demand.
AI agents lose context as conversations grow and get compressed. memtree gives them a structured place to save and retrieve knowledge across sessions. The CLI is designed to be called directly by agents (via tool use / shell commands), and the included Claude Code skills provide ready-made /memtree-save and /memtree-load slash commands.
This project is built almost entirely with Claude Code (this includes the READMEs and all other text in this repo) and is an exploration of how tree-based memory might improve on Claude's existing flat-file auto-memory. It's a working experiment, not a polished tool.
graph TD
root[".memtree/"]
root --- rust["rust/"]
root --- python["python/"]
root --- tools["tools/"]
rust --- errors["errors.md<br/><i>Error handling patterns</i>"]
rust --- async["async/"]
rust --- lifetimes["lifetimes.md<br/><i>Ownership & borrowing rules</i>"]
async --- tokio["tokio.md<br/><i>Tokio runtime usage</i>"]
async --- streams["streams.md<br/><i>Async iterator patterns</i>"]
python --- decorators["decorators.md<br/><i>Decorator patterns</i>"]
python --- typing["typing.md<br/><i>Type hint cheatsheet</i>"]
tools --- git["git/"]
tools --- docker["docker.md<br/><i>Dockerfile best practices</i>"]
git --- rebase["rebase.md<br/><i>Interactive rebase workflow</i>"]
git --- hooks["hooks.md<br/><i>Useful git hooks</i>"]
style root fill:#f9f,stroke:#333
style rust fill:#ffd,stroke:#333
style python fill:#ffd,stroke:#333
style tools fill:#ffd,stroke:#333
style async fill:#ffd,stroke:#333
style git fill:#ffd,stroke:#333
style errors fill:#fff,stroke:#333
style lifetimes fill:#fff,stroke:#333
style tokio fill:#fff,stroke:#333
style streams fill:#fff,stroke:#333
style decorators fill:#fff,stroke:#333
style typing fill:#fff,stroke:#333
style docker fill:#fff,stroke:#333
style rebase fill:#fff,stroke:#333
style hooks fill:#fff,stroke:#333
Directories (yellow) hold topic summaries. Leaves (white) hold the actual memories with frontmatter.
cargo install --path .# Store a memory
memtree store --path rust/errors --summary "Error handling patterns" \
--content "Use thiserror for library errors, anyhow for applications." \
--tags rust,errors
# Store a directory summary
memtree store --path rust --summary "Rust programming topics"
# List the tree
memtree ls --depth 2
# rust/ Rust programming topics
# errors.md Error handling patterns
# Print the entire tree
memtree inspect
# Recall a memory
memtree recall rust/errors
# Search across all memories
memtree search thiserror
# Move
memtree move rust/errors rust/error-handling
# Delete
memtree delete rust/error-handlingmemtree ships with two Claude Code skills that give agents slash commands for saving and loading context. Copy the skills/ directory into your project or symlink it.
Invoked manually when you want Claude to persist everything from the current conversation. The agent audits the full conversation, extracts every piece of information (decisions, code changes, errors, architecture, etc.), and stores them as leaves in the tree. It also writes a ## Memtree index to CLAUDE.md with @import syntax so future sessions auto-discover the tree.
> /memtree-save
# Claude will:
# 1. Run memtree inspect to check existing tree
# 2. Extract every topic from the conversation
# 3. Store each as a leaf with full verbatim content
# 4. Add directory summaries
# 5. Update CLAUDE.md with @import tree index
# 6. Print the final tree
Invoked with a prompt describing what you're working on. The agent searches the tree, recalls matching leaves, and presents a structured summary.
> /memtree-load working on the OAuth integration
# Claude will:
# 1. Run memtree ls --depth 2 for tree overview
# 2. Search for keywords: "OAuth", "integration", "auth"
# 3. Recall full content of matching leaves
# 4. Present a structured summary grouped by topic
Resolved in order:
--root <path>flagMEMTREE_ROOTenvironment variable.memtree/in the current working directory (default)
The root is auto-created on first write.
<root>/
├── _summary.md # root summary (plain text)
├── rust/
│ ├── _summary.md # "Rust programming topics"
│ ├── errors.md # leaf with YAML frontmatter + body
│ └── async/
│ ├── _summary.md
│ └── tokio.md
└── python/
├── _summary.md
└── decorators.md
Leaf files have YAML frontmatter:
---
summary: "Rust error handling patterns"
created: 2026-03-09T12:00:00Z
updated: 2026-03-09T12:00:00Z
tags: [rust, errors]
---
Use `thiserror` for library errors
and `anyhow` for application errors.Directory summaries (_summary.md) are plain text.
| Command | Description | Locks |
|---|---|---|
store --path <path> --summary <text> [--content <text>] [--tags t1,t2] |
Create/update a leaf (with content) or directory summary (without) | Yes |
recall <path> [--full] |
Print leaf body or directory summary + children | No |
ls [path] [--depth N] |
Tree listing with summaries | No |
inspect |
Print entire tree with all summaries (no leaf content) | No |
search <query> |
Case-insensitive substring search across all leaves | No |
move <src> <dst> |
Move a leaf or subtree | Yes |
delete <path> [--force] |
Remove a leaf or subtree | Yes |
Write commands acquire an exclusive flock on <root>/.memtree.lock. Read commands don't lock. Writes are atomic (temp file + rename), so readers never see partial content.
Storing a nested path under an existing leaf automatically promotes it to a directory. For example, storing at rust/errors/handling when rust/errors.md exists moves it to rust/errors/errors.md and creates the rust/errors/ directory.
Use --content - to read content from stdin:
echo "Memory content" | memtree store --path notes/idea --summary "An idea" --content -