Plain-markdown durable memory for AI agents — any runtime, local, git-versioned, zero lock-in
Project description
draille
Plain-markdown durable memory for AI agents. Any runtime, local, git-versioned, hand-editable, zero lock-in.
Why « draille »
A draille is a transhumance trail in the mountains of southern France — a path not built, but worn into the land by herds walking it season after season. Nobody designed it; repeated passage made it, and it remembers the way for whoever comes next.
That is exactly what agent memory should be: not a database bolted on the side,
but a trace left by work itself — records worn in by sessions, ranked by how
often following them actually led somewhere (outcomes), readable by the next
traveler (human or agent) with no tooling at all.
What it is
Stdlib-only Python tools, no dependencies:
| Command | Role |
|---|---|
draille init |
scaffold memory/ (HANDOVER template, journal) and print the agent bootstrap block |
draille record |
write a durable record (markdown + frontmatter, stable content-hash id); --supersedes <id> marks a prior record obsolete |
draille prime |
rank all records (classification weight + outcome tally) into a budgeted digest for session start |
draille outcome |
append "this record demonstrably helped/failed" to an append-only log, keyed by immutable id |
draille search |
ranked full-text search over records (pure scan, no index) — or delegate to your own engine via DRAILLE_SEARCH_CMD (BYO backends) |
draille handover |
show/set the CORE block of memory/HANDOVER.md (atomic, Letta-style core memory) |
draille doctor |
health-check the store: corrupt records, orphan outcomes, dangling supersedes, unsafe scope homes, duplicate ids (exit 1 on any issue — CI-friendly) |
draille status |
fast persistence + health check — is memory uncommitted (dirty) or corrupt? exit 1 if so (for hooks/gates: `draille status |
draille migrate |
import legacy JSONL records into markdown |
Superseding — stale memory that stops misleading. Outdated facts are the
named failure of agent memory: "we use Postgres" lingers and misleads long after
the project moved to SQLite. draille record … --supersedes <old-id> marks the
old record obsolete; prime and search hide it by default (still on disk, still
in git history — search --all brings it back). One frontmatter line, no graph,
no TTL daemon: the markdown-shaped answer to temporal decay.
Each is also a standalone script (draille/<name>.py) you can copy anywhere — no install needed.
Storage is just files in your repo:
memory/
records/*.md # one record per file — human-editable, git-diffable
outcomes.jsonl # append-only; git is the WORM/recovery layer
draille vs. Claude Code's native memory
Claude Code now ships its own memory: a per-project auto memory directory indexed by MEMORY.md, but it's per-user and per-tool — it lives outside the repo and only Claude Code reads it. draille is project-owned: records live in the repo, travel with git clone, and are legible to any runtime (Claude Code, Codex, Cursor, or a human with cat). The two compose fine — native memory for personal prefs and scratch state, draille for what the project has learned.
Non-goals
- No per-turn auto-extraction. Frameworks like mem0/Letta watch every turn
and write memory automatically. draille doesn't, on purpose: automatic
extraction produces noisy memory, and the whole point here is a curated,
human-editable, git-diffable trail. Capture is a deliberate act (
record, or the session-end ritual), not a background process. If you want the agent to hold live working state mid-session, that's whathandover setis for. - No embedded vector database. Semantic recall is a
DRAILLE_SEARCH_CMDbackend you bring, not infrastructure draille ships (BYO backends). - No hosted service / multi-user layer. draille is a local, per-repo store. For memory-as-a-service at scale, that's a different tool.
Install
pip install draille # or: pipx install draille
# or: git clone and run the scripts directly — stdlib only, nothing to install
Quickstart
draille init # scaffold memory/ + print the agent bootstrap block
draille record decision foundational "Use Postgres" --body "Because RLS."
draille prime # ranked digest — paste/inject at session start
draille outcome <id> success --note "constrained the schema choice"
draille search postgres # ranked hits across all records
Root resolution: $MEMORY_ROOT env var, else the git root of the cwd.
Mono-project by default (memory/ at the repo root). Multi-scope routing via an
optional memory/scopes.json ({"scope": "relative/home", "central": "."}) —
homes must stay inside the root (absolute paths and .. are rejected).
--dir is an explicit escape hatch that bypasses root and scope routing
entirely; don't pass it untrusted input.
Agent bootstrap
Drop this in your AGENTS.md / CLAUDE.md (see AGENTS.md):
- Session start: run
draille prime, readmemory/HANDOVER.md. - Session end, triage three tiers:
- HOT → rewrite the CORE block of
memory/HANDOVER.md(≤15 lines, merge — never stack), - DURABLE →
draille record <type> <classification> "<title>" --body "…", - JOURNAL → append
memory/journal/<YYYY-MM-DD>.md.
- HOT → rewrite the CORE block of
- Commit
session-end: <date>. Never auto-push.
The full runtime-agnostic ritual — with the judgment criteria per tier and the
persistence check (draille status) — lives in PROTOCOL.md; the
block above is its condensed pointer.
The system is the ritual, not the tooling — the scripts just keep the trail walkable.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file draille-1.3.0.tar.gz.
File metadata
- Download URL: draille-1.3.0.tar.gz
- Upload date:
- Size: 22.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89e802a728d8aeabde8cd6c8218000f94ddb28cb192bdc73f5e82481a7ab9acc
|
|
| MD5 |
f72276c1bda747e546344f9ccb876f6e
|
|
| BLAKE2b-256 |
1d8cb920d4bf53c101b3df234e530f8d621a54fa9ea8fed227b56865feb89491
|
File details
Details for the file draille-1.3.0-py3-none-any.whl.
File metadata
- Download URL: draille-1.3.0-py3-none-any.whl
- Upload date:
- Size: 27.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c84e6328aaa399869dd5b50a87829244fa7164a1e97d74a1cf9871910a01c7f3
|
|
| MD5 |
f2eab89153ef7ef8754bff31df6733d4
|
|
| BLAKE2b-256 |
aec74e321e18123f6e4e014509c7cac939b8538193c1fec0d872a2bbe086809a
|