Skip to main content

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

Project description

draille

tests PyPI

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.

Task guard — pending tasks don't silently erode. LLM rewrites of the CORE block are lossy: a task nobody closed can just vanish on the next handover set. Tag it - [t] <task> and set stamps a - [t-xxxx] id on write; close it explicitly with closed: <reason> on the line. If a pending id disappears anyway, the write still happens (soft, by design — a blocking guard kills a headless agent) but it's logged to task-guard.jsonl and warned on stderr, and draille status surfaces the open-drop count.

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

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):

  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 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


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.4.0.tar.gz (24.4 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.4.0-py3-none-any.whl (29.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for draille-1.4.0.tar.gz
Algorithm Hash digest
SHA256 a3dfb3ce03964a0be8fb22b1579702405294db39563ad95e69172acc0b7c8ad2
MD5 080eb3935cbe867efb59e28e7f426e53
BLAKE2b-256 b9dcf8ece244a1eff7e0612bddd9b3fefa164bec3d547964bfb815cc625b9517

See more details on using hashes here.

File details

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

File metadata

  • Download URL: draille-1.4.0-py3-none-any.whl
  • Upload date:
  • Size: 29.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.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0f9877ea937c74e25263b19601e07dcd73b7e9e7eaab77695a664789d0070ecd
MD5 557eee2ab3c11d39d288d8d663f1390b
BLAKE2b-256 5e486788a9978cd337ef6e1381251e3a0a92316b96ccf4e367027a7d725543a8

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