Skip to main content

sessionmemory

Durable memory for coding agents, one folder of searchable pages per project.

An agent starts every session knowing nothing about the last one. Yesterday's session worked around a trap, rejected the obvious approach for a reason, and found the one flag that makes a library behave. All of it ends with the session, and the next one works it out again from nothing. sessionmemory keeps what a project learned and hands it back when the next session starts. Each project keeps its own memory, and no page is ever shared between projects.

Part What it is
The vault Your knowledge as markdown pages, one folder per project
The sessionmemory plugin Claude Code hooks that feed a session at its start and record it at its end
The sessionmemory CLI Searches pages by meaning and creates them. Everything else is a file you edit

The pages follow the memoryfield format by Cal Paterson, described in his article and defined in the memoryfield spec. Each project's learnings/ folder is one field in that format: a flat directory of markdown pages beside one vector index file. You can export that folder, share it, or read it with any tool that speaks the format.

Requirements

  • uv. It installs the CLI, and the plugin runs its hooks through it.
  • Python 3.13 or 3.14. uv downloads one when none is installed.
  • git. The vault is a git repository, and a project is registered by its git remote.
  • Claude Code, to run the plugin. The CLI works on its own without it.

The first search downloads the nomic-embed-text-v1.5 embedding model, about 520MB, and caches it under ~/.cache/sessionmemory/models. Nothing else touches the network.

Install

Installation is three steps: the CLI, the plugin, and a vault for them to share.

1. Install the CLI

The CLI is published on PyPI. Install it as a tool, which puts sessionmemory on your PATH:

uv tool install sessionmemory

To upgrade it later, run uv tool upgrade sessionmemory.

2. Install the Claude Code plugin

In Claude Code, add this repository as a marketplace and install the plugin from it:

/plugin marketplace add natelandau/sessionmemory
/plugin install sessionmemory@sessionmemory

The hooks run the sessionmemory from step 1 when its version is at or past the plugin's own. Keep the two in step: after /plugin update sessionmemory@sessionmemory, run uv tool upgrade sessionmemory as well. A plugin newer than the tool falls back to a copy of the CLI it carries, in a Python environment of its own that the first such session builds.

3. Create a vault

The vault is its own directory. Make it a git repository of its own, so your pages and this code do not share a history. Then initialize it:

mkdir -p ~/repos/my-vault
cd ~/repos/my-vault
git init
sessionmemory init ~/repos/my-vault

sessionmemory init writes the three files a vault needs. A marker in _system/vault.toml identifies the directory as a vault. A .gitignore keeps the derived index out of your history. A README explains the layout to whoever opens the vault later. sessionmemory init never overwrites a file, so you can run it again safely.

Then tell the CLI and the hooks where the vault is. Both read the same two places, in the same order: the SESSIONMEMORY_VAULT environment variable, then vault.root in ~/.claude/sessionmemory.toml. Record the root in the file, since a session launched from a GUI or an IDE does not read your shell profile:

[vault]
root = "~/repos/my-vault"

The variable wins when both are set, so exporting it in one shell points that shell at a different vault without touching the file. The plugin documentation lists every other key that file accepts.

Until a directory holds the marker that sessionmemory init writes, every command refuses to touch it. The refusal protects you. If the root points at your home directory by mistake, the first page written scatters a projects/ tree into it.

Nothing commits the vault on a timer. The plugin commits it when a session starts and again when a session ends, so a page reaches git within the session that wrote it. Pushing that history to a remote stays yours to do.

Note: To bring an existing directory of notes under the CLI, run sessionmemory init --force ~/repos/my-vault once. --force means only that the directory already has contents. Nothing existing is overwritten.

Register a project

A project gets memory when its repository is registered. With the plugin installed, the first session you open in a git repository registers it. The session begins with one line that names the slug:

This repository was registered with the vault as project 'invoice-api'.

The slug comes from the git remote, or from the directory name when the repository has no remote. There is nothing else to choose: no tags, no scope, no note type.

Only a git repository is registered for you. A slug is permanent once pages carry it. A session opened in your home directory or a scratch folder must not leave a project named after it in the vault. To register a directory outside git, or to choose the slug yourself, run the command once:

cd ~/repos/invoice-api
sessionmemory project --register --cwd .
✓ registered 'invoice-api'
  └─ root: ~/repos/invoice-api

From then on, a session that starts in a registered repository receives that project's memory. A session that ends or compacts hands its transcript to a background pass, which records what was worth keeping.

Search and write pages

The CLI does two things. It finds pages by meaning, and it creates pages. Reading and editing a page is a job for your editor or your agent's own tools.

sessionmemory search "stripe event delivered twice" --cwd .
~/repos/my-vault/projects/invoice-api/learnings/stripe-retries-a-webhook-for-72-hours-so-the-handler-must-be-idempotent.md
  Stripe retries a webhook for 72 hours, so the handler must be idempotent
  Stripe redelivers an unacknowledged webhook for up to 72 hours, so the handler records the event id and ignores a repeat.

A result is a path, a title, and a summary. A paraphrase finds the page, because search ranks by meaning and not by words in common. A hit has to stand out from the rest of the project's pages, so a query that nothing answers returns no results rather than the nearest pages. Pass --read to print every hit in full.

Specs are searched the same way with --specs, which finds a design record by its title, summary, and headings:

sessionmemory search "queue for rendering PDFs" --specs --cwd .
~/repos/my-vault/projects/invoice-api/specs/2026-10-13-render-invoice-pdfs-in-a-worker-pool.md
  Render invoice PDFs in a worker pool
  Rendering a large invoice PDF blocks the request thread. Render in a pool of worker processes instead of a queue.
sessionmemory new learning \
  --title "Stripe retries a webhook for 72 hours, so the handler must be idempotent" \
  --summary "Stripe redelivers an unacknowledged webhook for up to 72 hours, so the handler records the event id and ignores a repeat." \
  --cwd .
~/repos/my-vault/projects/invoice-api/learnings/stripe-retries-a-webhook-for-72-hours-so-the-handler-must-be-idempotent.md

The vault path is shortened to ~ here; the command prints absolute paths.

The command writes the frontmatter and prints the path. Write the body into that file, or pass it with --body-file. The title is what every future session sees at its start, and the summary is what a search result shows. Both state the fact and not the topic.

What a session sees

sessionmemory inject prints the block a session starts with. This is the block for a project holding one learning, three specs, one reference doc, one runbook, and four open backlog items:

## Using this vault

Durable memory for this project lives in a vault of markdown pages. Nothing below is
loaded for you: the titles are what the vault holds, and each is one `sessionmemory search`
away. The project's folder has `learnings/` and `logs/`, searched by meaning, beside
`specs/`, `runbooks/`, `reference/`, and `backlog.md`, which are ordinary files you Read
and Edit. `sessionmemory path <kind>` prints one path, where kind is project, learnings,
logs, specs, runbooks, reference, or backlog; `sessionmemory project --json` prints
them all for a script.

  - Before assuming nothing was written down, search: `sessionmemory search "<words>"`
    prints each hit's path, title, and summary, and `--read` prints every hit's whole
    page in one call. Search with a few distinctive words, such as names, identifiers,
    or error text, rather than a sentence. No hits means nothing is recorded, not that
    the query needs loosening.
  - Past sessions, one page each: `sessionmemory search "<words>" --logs`. Search them
    for why something was decided, what happened the last time an area changed, or
    whether a fix was already tried.
  - Open work: read `backlog.md`. Any top-level bullet is an open item. Under a
    `## <kind>` heading (feat, fix, refactor, perf, docs, test, build, ci, decide), the
    full shape is sized S, M, or L: `- [S] <imperative description> - <YYYY-MM-DD> [#topic]`.
    Add one with
    `sessionmemory new backlog --kind <kind> --size <S|M|L> --title "..." --topic <topic> --cwd .`,
    which writes that shape and creates the file or heading when missing. Questions
    for the user to answer go under `## decide`. Delete a finished line, and one
    that will never be done, directly; never tick or annotate it. Git history is the
    record of what was finished.
  - Specs: `specs/` holds one design record per feature, named `<date>-<topic>.md`
    and kept after the work ships. Before designing or changing a feature, search
    them: `sessionmemory search "<words>" --specs` (listing the directory works too), so a
    decision already made is not made again. `sessionmemory new spec --title "..." --cwd .`
    creates one and prints its path. Open every spec with a short paragraph directly
    under the `# ` title saying what it is for. A search shows the frontmatter
    `summary`, else a goal or purpose section's first paragraph, else that opening one.
    A spec carries a `status`: `draft`, `shipped`, `superseded`, or `abandoned`. Set
    `status: shipped` when its work ships; a spec with no frontmatter block gets one
    holding only `status`. Replace one with
    `sessionmemory new spec --title "..." --supersedes <old filename> --cwd .`, which marks
    the old spec superseded.
  - Runbooks: `runbooks/` holds step-by-step procedures to follow by hand, such as
    configuring a service, rotating a credential, or recovering from an outage.
    Whenever you write one, create it with `sessionmemory new runbook --title "..." --cwd .`,
    which prints its path, never in another folder or the repository.
  - Reference: `reference/` holds this project's private docs, such as hosting
    settings, where each credential lives, or a data dictionary, kept out of its
    repository. Any it holds are listed below; before working in an area, read the one
    whose name matches. Keep a doc current by editing it in place. Create one with
    `sessionmemory new reference --title "..." --cwd .`, which prints its path. Public
    documentation stays in the repository.
  - The hooks commit the vault. Do not commit it, and do not tell the user vault
    changes are left uncommitted.
  - Learnings are captured at session end, not by you mid-session. When the user asks
    you to remember something about this project, or to keep a learning now, never
    use your own memory files: `sessionmemory new learning --title "..." --summary "..." --cwd .`
    creates the page and prints the path to write prose into. Title and summary state
    the fact, not the topic. Keep a page under 8KB; more detail is another page.

## What this project knows

  - Stripe retries a webhook for 72 hours, so the handler must be idempotent

## Reference docs

  reference/hosting.md

## Runbooks

  runbooks/rotate-the-stripe-webhook-signing-secret.md

## Open work

  4 open backlog items
  3 specs in specs/

A page body never enters that block, so its cost grows with the number of pages and not with their length. The titles say what exists. sessionmemory search returns what they say.

Add rules to your CLAUDE.md

The guidance block above tells a session how to read and write the vault. It does not control where other skills and plugins write their files. A planning skill writes its plans and specs to the path it chose for itself, often under the repository's docs/. Rules in a CLAUDE.md file send that work to the vault.

Put the rules in your user file, ~/.claude/CLAUDE.md, so that they apply in every registered project. A project CLAUDE.md is committed with the code. Other contributors read it, and they have no access to your vault.

Copy this block into the file:

## Session memory

- Write every plan, spec, and design document into the sessionmemory vault, including
  the ones a skill or plugin generates. Create them with
  `sessionmemory new spec --title "..." --cwd .` and
  `sessionmemory new plan --title "..." --cwd .`. Never write them under the
  repository's `docs/`, which holds public documentation.
- Write every runbook (a step-by-step procedure to follow by hand) with
  `sessionmemory new runbook --title "..." --cwd .`.
- Keep documentation that describes the code and is safe to publish in the repository.
  Put private documentation about a project, such as hosting settings or where each
  credential lives, in the vault with `sessionmemory new reference --title "..." --cwd .`,
  and edit that file in place afterward.

If you use the CLI without the plugin, no hook injects the guidance block. Add this rule to the same block, so that each session reads the guidance itself:

- At the start of each session in a registered project, run
  `sessionmemory inject --cwd .` and follow the guidance it prints.

Documentation

Page What it covers
Concepts Pages, fields, the index, and the layout of a vault
CLI reference Every command, its options, and its output
The Claude Code plugin The hooks, the sweep, every setting, and the slash commands
Vault health What sessionmemory doctor reports, and what to do about it

Development

git clone https://github.com/natelandau/sessionmemory
cd sessionmemory
uv sync                  # install dependencies
uv run duty lint         # ruff, ty, typos, yamllint, shellcheck, prek
uv run duty test         # pytest with coverage

CLAUDE.md records the conventions this project holds itself to.

License

MIT. See LICENSE.

Metadata

Release files for sessionmemory 0.11.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sessionmemory 0.11.0
File Size Uploaded
sessionmemory-0.11.0.tar.gz 62.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sessionmemory 0.11.0
File Interpreter ABI Platform
sessionmemory-0.11.0-py3-none-any.whl Python 3 none any Details

Total release size: 138.2 kB

Release files / sessionmemory-0.11.0.tar.gz

Download URL sessionmemory-0.11.0.tar.gz
Size 62.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6e6622d2ab6787fda251b0a53c414cf119b3b953584706ed230ed9175e54d503
BLAKE2b-256 checksum
How to use checksums
d55aaf560ba30fed6d765b6d3052dd1c78d3a6a5c1158071f99888787c3a72a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / sessionmemory-0.11.0-py3-none-any.whl

Download URL sessionmemory-0.11.0-py3-none-any.whl
Size 75.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3505ec3beddec0bf1be673c57c2e46245140dcc3d383f76ebc24efc9befa553b
BLAKE2b-256 checksum
How to use checksums
1b2215890da8072e0290a1e859dc8395091396020ddebc4fc4e18663c61dfeb1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.11.0 This release

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page