Jotura

Guides / AI agents / 2026-08-30

How to give AI agents persistent memory

How to give AI agents persistent memory using plain markdown files the agent writes and reads back, so decisions and facts survive between sessions.

To give an AI agent persistent memory, give it a durable place outside the model to write to, and a habit of reading that place before it acts. A context window is emptied when the session ends, so nothing the agent worked out yesterday survives unless it was written to a file, a database, or a service the model does not own.

The simplest version is a folder of markdown files on disk. At the end of a piece of work, the agent writes one short note per decision or fact. At the start of the next session, it searches those notes for the current task and reads the top few.

Several assistants now ship a memory setting you can switch on, including ChatGPT and Claude. Those stores live inside one vendor’s product, you cannot open them as ordinary files, and they do not follow you to a different agent. If you want memory you own and can move, you build it around the agent instead.

Short-term context and long-term memory

The context window is short-term memory: it holds the current task and disappears with the session. Long-term memory is a small set of durable statements kept outside the model, retrieved only when they match what you are doing now.

A long chat history is not long-term memory. It is a transcript: it grows without bound, and the useful parts get buried. For the failure mode in detail, see why your AI agent forgets everything between sessions.

Where agents can keep long-term memory

ApproachGood forTrade-off
Built-in assistant memory (ChatGPT, Claude, Claude Code)Zero setup, works out of the boxLocked to one vendor, not readable as files, does not move between tools
Instruction files (CLAUDE.md, AGENTS.md, GEMINI.md)Stable rules and project conventionsLoaded every session, so a growing log gets expensive and stale
A folder of markdown notes with searchDecisions, facts, and follow-ups accumulated over monthsYou have to build the write and read habit yourself
An MCP memory server (mem0, Letta, the reference memory server)Structured recall shared across several clientsAnother process to run, contents harder to inspect
A vector store (Chroma, pgvector)Fuzzy recall across very large memory setsReal infrastructure, opaque contents, hard to audit

Built-in memory is the right pick if you live in one assistant and never want to think about this again. An MCP memory server earns its extra process when several clients need the same structured store. MCP memory server versus plain markdown files works through that trade.

A vector store is the right call when recall has to be fuzzy across tens of thousands of entries. Most agent memory never gets there. Vector database versus markdown for agent memory compares the two directly.

Skip files if your agent has no shell in its loop, or if your store is already heading past tens of thousands of entries. Otherwise start with files and move when search stops finding things.

What belongs in memory

Write down decisions, facts, and follow-ups. A decision records a choice and the reasoning behind it, which is the expensive part to reconstruct later. A fact records something true about the system that took effort to discover, such as a build quirk or a real rate limit. A follow-up records work you deliberately deferred, so it can be closed rather than forgotten.

Keep three things out. Do not store secrets: memory files get synced, shared, and printed into agent transcripts and logs, so anything in them should survive being seen. Do not store personal traits about the people involved. Do not store whole session transcripts, because a memory system that stores everything retrieves nothing useful.

What a coding agent should remember

Persistent memory for coding agents has a narrower shape than general assistant memory. The work itself produces the knowledge worth keeping. That is the library you rejected and why, the root cause behind a day-long bug, the build quirk that bites on one platform only, and the refactor you deferred until after the release.

That knowledge recurs. The same library gets proposed six months later, and without memory the agent argues for it again. Write the decision down once, and the next session starts where the last one ended.

Build it with plain files first

You do not need a product for the loop. Three directories and a shell command get you most of the way:

mkdir -p memory/decisions memory/facts memory/followups

cat > "memory/decisions/2026-08-30 postgres-advisory-locks.md" <<'EOF'
# Use Postgres advisory locks for the job queue

Redis was rejected. We already run Postgres and the queue is low volume.
EOF

Recall is a search over that folder:

rg -l "job queue" memory/
rg -n "rate limit" memory/facts/

Then add two lines to whichever instruction file your agent already loads, telling it to search memory/ before proposing an approach and to write a note when work concludes. That instruction is the behavior change.

This version has real gaps. Search returns matches in file order rather than by relevance. Nothing marks a decision as reversed, so old answers sit beside current ones. Two agents writing at once can overwrite each other, and review is manual.

What a memory tool adds

Jotura is a local-first markdown notes app, built for a person’s own notes, and it ships a first-party command-line tool called jotura alongside the desktop app. The memory commands came later, on top of the plain-file vault that was already there.

jotura memory log --kind decision \
  --title "Use Postgres advisory locks for the job queue" \
  --project "Payments" \
  --body "Redis was rejected. We already run Postgres and the queue is low volume."

jotura memory recall "job queue locking" --limit 5
jotura memory recall "rate limits" --kind fact --json

Notes land at <root>/<Decisions|Facts|Followups>/<date> <slug>.md, so that first command writes Memory/Decisions/2026-08-30 use-postgres-advisory-locks-for-the-job-queue.md. The filenames contain spaces, so quote them. Recall ranks by relevance, freshness, and importance, and hides superseded or resolved notes unless you pass --include-stale. The store stays a folder of .md files you can open in the editor or commit to a git repository.

Making recall automatic

A memory system that depends on you saying “check your memory” has not solved the problem. One command returns the memories most relevant to the task at hand:

jotura context "refactor the sync queue"

Automatic injection at session start is Claude Code only today. jotura hook install claude writes a SessionStart entry into ~/.claude/settings.json that runs jotura context and feeds the result into the opening context. Codex, Gemini CLI, and Copilot get the same loop by putting the jotura context instruction in their prompt file, which the agent follows rather than the runtime guaranteeing it.

Keeping memory honest as it grows

Reversed memory is worse than no memory, because an agent will act confidently on a decision you overturned months ago. Give reversals an explicit representation instead of deleting the old note:

jotura memory supersede "Memory/Decisions/2026-03-11 use-redis-for-the-job-queue.md" \
  --by "Memory/Decisions/2026-08-30 use-postgres-advisory-locks-for-the-job-queue.md"

jotura memory resolve "Memory/Followups/2026-04-02 remove-the-deprecated-identity-endpoints.md"
jotura memory list --stale

Superseding links both notes, so the history of the decision stays readable while only the current answer is recalled. The --stale flag surfaces active memories older than 180 days, which makes a decent monthly review queue.

Writes that do not clobber your file

Agents write to the same files you do, sometimes at the same moment. You read the file’s content hash first, then pass it back on the write. If the file changed in between, the CLI refuses with exit code 2 instead of overwriting silently:

NOTE="Memory/Facts/2026-06-04 stripe-api-rate-limit.md"
HASH=$(jotura read "$NOTE" --json | jq -r .hash)
jotura edit "$NOTE" --replace "100 requests per second" --with "25 requests per second" --if-hash "$HASH"

Replacements must match exactly once by default, writes are atomic, and body edits never touch the YAML frontmatter. Full command reference on the CLI page.

Sharing memory across machines and agents

Across two machines, the memory folder has to travel. Syncthing, Dropbox, iCloud Drive, or a private git repository all work, and all of them are free or already paid for. They win on cost, and git gives you a diffable history of every memory change for nothing.

Jotura Sync costs 4 pounds per month or 40 pounds per year, with no free trial. What you get for it is end-to-end encryption where the server never sees your filenames, paths, or content, plus encrypted sharing of a folder with other people. If you sync only your own machines and trust the encryption your current provider gives you, keep what you have.

Different agents can share one store because the store is plain files. Only the instruction files differ per tool, so Jotura keeps them in an Agents/ folder in the vault and writes them out on demand:

jotura agents sync

That covers Claude Code, Codex, Gemini CLI, and Copilot instructions from one edited source.

Honest limits

The app and the CLI are free on Mac, Windows, Linux, and Android. Sync is the only paid part, and it has no free trial. There is no iOS app.

Jotura is closed source and has no plugin system, so you cannot extend the memory format with community add-ons. Automatic session-start injection works with Claude Code only. Semantic search runs entirely on your machine, but you enable it in the desktop app’s settings, and the CLI can only query that index rather than build it.

Common questions

Does this work with any agent? Writing and reading work on any agent that can run a shell command. Automatic recall at session start is Claude Code only; other runtimes need the instruction in their prompt file.

How much should the agent write? A few sentences per memory. If a note needs headings, it is a document, and it belongs in your notes rather than your memory index.

Will memory grow too large to search? Volume is rarely the problem. Keyword search across a 10,000-note vault runs in roughly 8 milliseconds in a release build, and recall adds a ranking pass over the top matches. Stale entries hurt long before size does, which is why superseding matters more than pruning.

Download Jotura

Free. Plain markdown files, yours forever.