Releases: keepnotes-ai/keep
v0.133.0
Continuous markdown sync
keep data export ~/vault --sync now registers a daemon-owned continuous mirror that keeps an Obsidian vault synchronized with your keep store. The daemon watches for mutations via a trigger-based sync outbox and incrementally re-exports only the affected notes — no periodic full scans, no manual re-runs.
What's new
Continuous one-way sync (--sync / --stop / --list)
keep data export ~/vault --sync # Export + register mirror
keep data export ~/vault --sync --include-parts # ...with analysis parts
keep data export ~/vault --sync --stop # Stop mirroring
keep data export --list # List active mirrorsThe CLI runs the initial export locally with a progress bar, then registers the directory with the daemon. From that point on, the daemon handles updates automatically:
- Incremental updates: content, tag, and edge changes rewrite only the affected note bundles (the changed note plus any notes whose inverse-edge frontmatter depends on it). A note insert or delete triggers a full replan.
- Trigger-driven: 15 SQLite triggers on documents, parts, versions, edges, and version_edges fire on every mutation and write to a
sync_outboxtable. The daemon polls the outbox on its existing timer loop — no table scans. - Debounced: rapid mutations coalesce within the configured interval before the daemon runs an export pass.
Content stability for markdown-authored notes
A new body-authority system (_body_authority tag) protects notes whose content is authored markdown — background summarization, description, and OCR won't overwrite the body. This is the gating dependency for future bidirectional sync (importing edits from the vault back into keep).
- Every body-writing code path now declares a write intent (
authoritative_input,derived_summary_replace,derived_description_append) - A central policy function decides whether the mutation is allowed based on the note's authority
- Default is
derived(existing behavior unchanged);markdownrejects all non-authoritative writes
Dependency service for relationship traversal
NoteDependencyService provides semantic note-dependency queries (current targets, current sources, archived targets, archived sources, structural surfaces) — the engine behind incremental export's ability to determine which notes are affected by a given mutation. Also used by the flow environment's edge traversal and the action context's find_referencing.
Mirror lifecycle
- Registrations persist as a
.markdown-mirrorssystem document — survives daemon restarts .keep-sync/map.tsvmaps vault paths to keep ids (plaintext, diffable).keep-sync/state.jsontracks operational bookkeeping- Path exclusivity: sync directories can't overlap with
keep put --watchdirectories, andkeep putrejects files inside a sync root - Stale files are automatically cleaned up when notes are deleted
Documentation
- KEEP-DATA.md rewritten: flat frontmatter shape, inverse edges, chain navigation, case-fold disambiguation, Obsidian usage, continuous sync section
- KEEP-PENDING.md (new): dedicated guide for
keep pendingflags, mirror visibility, and common workflows - EDGE-TAGS.md updated: new "When to promote a tag to an edge tag" section — the test is referential integrity, not cardinality
Upgrade notes
- Schema version 14 → 15: a
sync_outboxtable and 15 triggers are added on first startup. No data migration needed. - CI workflows now build the OpenClaw plugin bundle before
uv sync. keep data export --intervalflag removed (daemon uses a fixed 30s debounce).
What's next
Steps 3–4 of the markdown sync design are done. Step 5 (bidirectional import from the vault back into keep) is the next milestone, gated on the content-role policy that shipped in this release.
v0.132.1
v0.132.0
Obsidian-compatible markdown export
keep data export --format md now produces a directory of markdown files that Obsidian (and other wiki tools that recognize [[wikilinks]] in frontmatter) can open directly as a vault. Notes become nodes, edge-tag relationships become links, and the whole store renders as a navigable knowledge graph — the image above is a dev store of ~8000 notes opened straight in Obsidian, centered on SKILL.md.
What changed
Flat frontmatter. Exported .md frontmatter is now one flat top-level map. id is first, then any content_hash columns, then every tag (user + system) is its own top-level key. The nested tags: block is gone, and so are the duplicated created_at / updated_at / accessed_at column mirrors — those values already live in the _created / _updated / _accessed system tags. No data is lost; the representation is just honest.
Edge-tag linkages in both directions. Inverse edges (said, recipient_of, cited_by, referenced_by, …) are folded into the same flat frontmatter as multi-value YAML lists, with the source note's display name as the wikilink alias. Forward edge tags pass through as-is. Wiki tools render every value as a clickable link to the referenced note.
MediaWiki-style labeled refs. Edge-tag values now use canonical [[target|label]] syntax. This is what Obsidian, MediaWiki, and most other wiki systems understand as "link with display text", and it lines up neatly with the flat frontmatter above so the Properties pane renders it as a proper link field. A one-time startup migration rewrites any existing target[[label]] values on upgrade; parse_ref accepts both forms for backward compatibility.
Chain navigation as system frontmatter keys. Parent ↔ archived versions and parent ↔ analysis parts are linked via _prev_version / _next_version / _prev_part / _next_part system keys holding canonical part/version reference ids wrapped as [[id]] wikilinks. Chain navigation lives in the frontmatter graph, not in markdown links buried in the body — the body is now exactly the document's summary text.
Label propagation to target notes. When an edge-tag value carries a label (e.g. from: [[alice@example.com|Alice Smith]]), the label is observed and merged into the target note's name tag as a multi-valued set, append-only and preserving insertion order. Auto-vivified targets render in the graph with their human name instead of a bare machine id. Integrations that emit contact refs (Hermes, email extraction) produce canonical labeled refs by default.
Case-fold path safety. Two notes whose canonical paths collide case-insensitively (e.g. state-actions.md and STATE-ACTIONS.md on macOS APFS) are auto-disambiguated with a short hash suffix on the second one's stem, so the export runs clean on any filesystem.
Export format v3
The JSON export format is bumped to version 3. Older v1/v2 exports still import cleanly and are rewritten to canonical form on arrival.
Upgrade notes
First startup after upgrading runs a one-time migration that canonicalizes stored edge-tag values across documents, versions, and parts. It's gated by the existing _startup_maintenance_deferred flag so daemon startup is not blocked — the migration runs as part of background startup maintenance. A persistent labeled_ref_format_verified flag keeps subsequent startups at O(1).
Try it
keep data export ~/my-vault --format md --include-parts --include-versionsThen open ~/my-vault as an Obsidian vault. The graph view should look something like the image above.
v0.131.3
v0.131.2
v0.131.1
v0.131.0
What's new
- Refresh top-level docs: README, SKILL, Hermes integration
- Refresh CLI docs for accuracy
- Remove dead --with-parts list filter
- Refresh non-CLI docs for accuracy
- Update hermes prompt, the system now provides a guard block for isolation
- Improve reindex correctness and throughput
v0.130.1
v0.130.0
What's new
- Speed up daemon-heavy test coverage
- Fix OCR reembedding and sync design docs
- Simplify post-migration startup cleanup
- Unify stored summary and content text
- Remove legacy @p{0} overview parts
- Add markdown export mode for
keep data export - Stage only version-bump files in release.sh
- Fix file:// ID aliasing, directory unwatch, and path-decoding sweep
- Ship pre-built OpenClaw plugin bundle in the wheel
- Ensure store directory exists before ops-log setup
- Fix daemon startup races after remote integration
- Centralize provider model identity resolution
- Improve test isolation and Python CI coverage
- Rename CLI modules to match current roles
- Add flow-client round-trip interface tests
- Make CEL a hard runtime dependency

