Skip to content

Export Memory to Files

Materialize a project's memory into .sibyl/memory/, a directory of Markdown files that any filesystem-native agent can read and grep without talking to a Sibyl server.

The graph stays the source of truth. This is a curated projection of it: structured substrate, flat-file surface.

Usage

bash
sibyl export memory
sibyl export memory --project sibyl
sibyl export memory --output docs/memory --notes 100

With no --project, the command uses the project linked to the current directory (see sibyl project link).

Why Files

Every major coding agent in 2026 reads Markdown from the repository it is working in. A repo-local .sibyl/memory/ is legible to all of them with zero integration work, costs nothing per read, and keeps working when the server does not. Recording usage still goes over the API (sibyl cite <id>), so the citation signal stays honest.

Layout

.sibyl/memory/
├── README.md         # what this is, how to read it, how to cite it
├── index.md          # every materialized memory, grouped by type, with ids and paths
├── handbook.md       # distilled project handbook, composed from cited memories
├── recent.md         # recency-ordered digest with previews
├── tasks.md          # open work with task ids
├── notes/            # one file per memory, full text
│   └── <type>-<title-slug>.md
└── manifest.json     # per-file SHA-256 plus a digest over the whole projection

Three tiers, deliberately: README.md and handbook.md orient, index.md / recent.md / tasks.md are the greppable middle, and notes/ holds the detail an agent opens once grep points at it.

handbook.md is composed during the export from the project's own memories, one synthesis run over the graph. Every line in it cites the memory it came from, and a heading with nothing behind it says so rather than being padded. Pass --no-handbook to skip the composition; the file is then simply absent, so nothing downstream has to special-case a placeholder.

It is built per export rather than cached. The pipeline is deterministic search and rendering with no model call, so recomputing is cheap and the handbook cannot drift out of step with the graph it describes.

File Format

Every Markdown file opens with YAML frontmatter carrying a type, which makes the projection OKF v0.1-compatible: Markdown with YAML frontmatter in a git-shippable directory. Sibyl-specific keys are namespaced under sibyl_.

markdown
---
type: "Sibyl Pattern"
title: "Pool Surreal connections per org"
description: "Each org gets a dedicated connection-pooled client scoped to its namespace…"
timestamp: "2026-07-22T09:30:00Z"
okf_version: "0.1"
sibyl_schema_version: "sibyl-memory-files-v1"
sibyl_kind: "memory_note"
sibyl_id: "pattern_a1b2c3d4"
sibyl_entity_type: "pattern"
sibyl_project_id: "project_05eb5c8c782a"
---

# Pool Surreal connections per org

...full memory body...

## Provenance

- Sibyl id: `pattern_a1b2c3d4`
- Entity type: `pattern`
- Cite with: `sibyl cite pattern_a1b2c3d4`

This differs from sibyld export okf, which embeds a lossless JSON payload in frontmatter so an archive can round-trip back into a graph. That format is for backup and portability. This one is for reading, so it carries only the fields a reader needs.

Determinism

Re-running against an unchanged graph rewrites byte-identical files. The directory is meant to live in git, and a projection that churned on every run would bury real changes in noise.

Three rules make that hold:

  • No wall-clock values. Nothing samples datetime.now(). Timestamps in the output come from the data. manifest.json carries a content digest instead of an export time.
  • Whitelisted fields. Retrieval counters, last-recalled stamps, embedding metadata, and internal record handles all change when memory is merely read. None of them reach the files.
  • Total ordering. Records sort by id for file placement and by recency (id as tiebreak) for reading order, so the order the server returned them in cannot leak into the bytes.

Verify a materialized directory at any time:

bash
sibyl export memory --output /tmp/check --json | jq -r .content_digest
jq -r .content_digest .sibyl/memory/manifest.json

Options

OptionShortDefaultDescription
--project-plinked projectProject id or name to materialize
--output-o.sibyl/memoryDirectory to write into
--notes-60Maximum memories to include
--tasks-25Maximum open tasks to include
--task-status-doing,blockedComma-separated task statuses to materialize
--type-Tten memory typesMemory entity type to include (repeatable)
--hydrate-trueRefetch each entity for its full body
--writable-falseLeave files writable instead of read-only
--force-falseWrite into a populated directory that is not an export
--json-jfalsePrint the export receipt as JSON

The note budget is spread across the requested types in round-robin rather than spent on whichever type the backend happens to order first, so a 40-item export covers decisions, patterns, procedures, claims, and the rest instead of forty decisions.

--hydrate costs one extra request per memory. Turning it off is faster and yields 500-character previews, which defeats the point for anything but a smoke test.

Reading the Result

bash
# what does this project know about connection pooling?
rg -i 'connection pool' .sibyl/memory/notes

# every decision, by file
rg -l 'type: "Sibyl Decision"' .sibyl/memory/notes

# what is open right now?
cat .sibyl/memory/tasks.md

Files are written read-only. Edit the graph, not the directory: the next export overwrites everything Sibyl manages.

manifest.json is the only authority on what Sibyl owns, which makes the write path safe in two ways. A file this exporter never wrote is never deleted, wherever it sits, so your own notes under notes/ survive a re-export. And a populated directory holding no manifest.json is refused rather than overwritten, so --output . in a repo root cannot eat that repo's README.md. Pass --force when you genuinely mean to write into one.

Released under the Apache-2.0 License.