Redthread: Portable, Git-Backed Memory for AI Coding Agents
Every coding agent I use keeps its memory in a local folder — .claude/,
.cursor/, .agent/. That works until you switch machines, and then the
agent has amnesia. The context it built up about your project stays on the
laptop where it happened.
Redthread is a small tool I wrote to fix that. It stores agent memory in a git repo instead of a local folder, and exposes it over MCP, so every machine that clones the store sees the same memory. In this post, I will install it, connect it to an agent, and go through how to use it.
Install¶
pip install redthread # or: uv tool install redthread
Everything below needs redthread 0.6 or newer. That release reworked the agent-facing tools, and a few of the tool signatures changed with it.
Create a store¶
A store is a git repo with a declared phase pipeline — an ordered list
of whatever names fit your project. build,test,present and
train,eval,present are equally valid:
redthread init my-project --phases build,test,present --store ./my-store
Connect your agent¶
For Claude Code, one command:
claude mcp add redthread -- uvx redthread mcp-serve --store ./my-store
uvx fetches Redthread from PyPI on first launch, so this works even
without the install step above. If you already installed it, drop uvx:
claude mcp add redthread -- redthread mcp-serve --store ./my-store
Verify with /mcp inside Claude Code — redthread should show up as
connected with 17 tools. A quick smoke test is asking the agent to call
context_bootstrap. Note that MCP servers are only read at session start,
so if you added it mid-session you’ll need to restart Claude Code before
the tools appear.
Cursor uses an install deeplink rather than a CLI command, and VS Code has
code --add-mcp. The
per-client reference
covers Cursor, VS Code, Windsurf, Claude Desktop, Codex CLI, Gemini CLI,
and the Claude Agent SDK.
Make the agent actually use it¶
This is the part that’s easy to miss, and the part I spent the most time on. Registering the MCP server gives the agent the capability to use memory, but nothing tells it to actually reach for those tools. In my testing, agents often ignored them for a whole session.
I ended up fixing this in two places.
The first fix is a single tool that answers everything at once. A cold agent that wants to use memory still has to figure out which runs exist, which memory namespaces exist, and what’s in them. That takes several tool calls before it learns anything useful, and in practice it gives up before then. So there’s one tool that returns all of it together:
redthread bootstrap --store ./my-store
This prints the same payload the agent gets from context_bootstrap: the
phase pipeline, recent runs and their status, published handoffs, and an
index of every memory entry with a one-line description each. It also
returns a _next field telling the agent what to do with what it just got
— read these entries, or start a run, or write a session summary before
finishing. After one call, the agent knows the state of the store.
The second fix is a note in AGENTS.md (or CLAUDE.md), because that
file is the first thing most agents read. Redthread will write it for you:
ask the agent to call agents_md_bootstrap and it appends a short usage
policy to your project’s existing file. It’s idempotent, so it’s safe to
call every session.
One caveat if you use subagents: they don’t inherit the main agent’s
instructions, and they don’t read AGENTS.md. A subagent doing real work
will silently skip memory entirely. That’s why the guidance also lives in
the tool descriptions and responses, not just in a config file — that’s the
only channel that reaches every agent in every client. If you delegate
something worth remembering, tell the subagent to call context_bootstrap
too.
Make it portable¶
So far this is a local git repo. Give it a remote and sync:
git -C ./my-store remote add origin git@github.com:you/my-store.git
redthread sync --store ./my-store
sync is a one-shot commit + pull --rebase + push. Any other machine
that clones the store now sees the same memory, which is the whole point.
There’s also an auto-commit daemon (redthread daemon run) if you’d rather
not think about it.
If you don’t want to provision a second repo, Redthread can put the store on an orphan branch of a repo you already have, as a git worktree, without touching that repo’s checked-out branch:
redthread init my-project --phases build,test,present \
--store ./my-store --worktree-repo .
This writes a small .redthread.yaml marker into the host repo. Commit it,
and the next machine only needs to clone your code repo and register the
same MCP command — no flags to remember, no manual clone of the store.
Using the CLI¶
The same store also tracks multi-phase runs. This is useful outside the agent case too, for pipelines where one phase’s conclusions feed the next. One end-to-end pass looks like this:
# a run is one attempt through your declared phases
run_id=$(redthread run start --store ./my-store)
# append immutable context entries as a phase works
redthread log "$run_id" build note '{"msg": "kicked off build"}' --store ./my-store
# publish the build phase's curated handoff for the next phase
echo '{"headline": "build ok", "key_results": {"warnings": 0}}' > handoff.json
redthread handoff publish "$run_id" build handoff.json --store ./my-store
# the test phase reads only the handoff — never build's raw log
redthread handoff get "$run_id" build --store ./my-store
# full raw history, one JSON entry per line
redthread read "$run_id" --store ./my-store
The split between handoffs and the raw log is deliberate. Entries are append-only and immutable, so the raw log grows without bound. But a downstream phase reads the short curated handoff, not the transcript, so context windows don’t fill up with a previous phase’s noise.
One convenience: run_id is optional on every run-scoped tool on the MCP
side. Omit it and it resolves to the store’s newest active run, and the id
it picked is echoed back in the response, so an agent can’t quietly write
to the wrong run. Pass it explicitly when several runs are in flight across
machines.
Long-term memory¶
Long-term memory is separate from runs, because most of what’s worth remembering isn’t tied to one attempt. Each entry carries a one-line description, stored as YAML frontmatter:
redthread memory write notes toolchain.md ./note.md \
--description "Why this project uses uv, not conda" \
--tags toolchain --store ./my-store
redthread memory list --store ./my-store
Listing memory returns the descriptions instead of bare filenames, so an agent can tell which entries are worth opening without reading all of them first. Entries written without a description fall back to their first meaningful line, so old stores still list correctly.
When you’re after something specific, search covers keys, descriptions, tags, and bodies, and reports the line that matched:
redthread memory search uv --store ./my-store
The same reads are also exposed as MCP resources —
redthread://bootstrap, redthread://memory,
redthread://handoff/{run_id}/{phase}, and a few more — for clients that
can attach context directly instead of spending a tool call on it.
One warning¶
The store is a git repo, usually pushed to a shared remote. Never write secrets to it. Anything an agent puts in memory is committed to history and visible to everyone with access.
Links¶
- Repo: github.com/sina5/redthread
- Docs: sina5.github.io/redthread
- Quickstart · Full usage reference · Architecture
It’s MIT-licensed and works on Windows, Linux, and macOS (Python 3.11+). Issues and PRs welcome.