Skip to main content

Plain-markdown durable memory for AI agents — any runtime, local, git-versioned, zero lock-in

Project description

draille

tests

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 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 what handover set is for.
  • No embedded vector database. Semantic recall is a DRAILLE_SEARCH_CMD backend 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

pipx install git+https://github.com/MathiasFranceschi/draille
# or: pip install git+https://github.com/MathiasFranceschi/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):

  1. Session start: run draille prime, read memory/HANDOVER.md.
  2. Session end, triage three tiers:
    • HOT → rewrite the CORE block of memory/HANDOVER.md (≤15 lines, merge — never stack),
    • DURABLEdraille record <type> <classification> "<title>" --body "…",
    • JOURNAL → append memory/journal/<YYYY-MM-DD>.md.
  3. Commit session-end: <date>. Never auto-push.

The system is the ritual, not the tooling — the scripts just keep the trail walkable.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

draille-1.2.0.tar.gz (20.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

draille-1.2.0-py3-none-any.whl (24.6 kB view details)

Uploaded Python 3

File details

Details for the file draille-1.2.0.tar.gz.

File metadata

  • Download URL: draille-1.2.0.tar.gz
  • Upload date:
  • Size: 20.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for draille-1.2.0.tar.gz
Algorithm Hash digest
SHA256 8ae67c3f38ea5d3b8e5ca88689ed11aa8688576c6bfee21a277688b604a8eecf
MD5 5ea3df4c30f19a039bc183792c8c79e5
BLAKE2b-256 6b5aa486fef3bf3dc272e29edf9b651c1b78c6e77328f3a31ac4a291cca0df86

See more details on using hashes here.

File details

Details for the file draille-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: draille-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 24.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for draille-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 91f2dbd260d588498f2aa1607f74963c1977ee8bb5f5c16b8398807ff1fa0638
MD5 fb75869080ded47c3a84bb936ae72727
BLAKE2b-256 bec184eabcfa1cf24395d2310ca06ab3dc292d75e8cac8add8eb2572c13c82eb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page