Skip to main content

kvault

Persistent, structured memory for AI agents — plain Markdown, a CLI, zero services.

pip install knowledgevault

Your agent creates nodes (people, projects, notes), keeps every parent summary a rollup of what's below, and orients itself with one cheap command:

$ kvault tree
. « Knowledge Base » [3 children, 11 total] ~2026-06-07
  notes [1 children, 1 total] ~2026-04-11
    reading_list ~2026-04-11
  people [2 children, 5 total] ~2026-06-02
    contacts [2 children, 2 total] ~2026-06-02
      mike_torres ~2026-01-20
      sarah_chen ~2026-06-02
    friends [1 children, 1 total] ~2026-03-14
      alex_rivera ~2026-03-14
  projects [2 children, 2 total] ~2026-06-07
    launch_plan « Launch Plan — v2 » ~2026-06-07
    website_redesign ~2026-05-28

One outline line per node: title, size, and most-recent activity — about 15 tokens each, so a several-hundred-node KB orients an agent for a few thousand tokens. Anything pruned by --depth or --max-children is called out in place (…74 nodes below), so a partial view can never silently hide content.

Built for developers using AI coding tools who want their agent to remember things between sessions — contacts, projects, meeting notes, research — in a structured, navigable format. kvault needs no API keys, no hosted service, no database: any agent that can run shell commands can use it.

How it works

  • A node is a directory containing a single _summary.md — YAML frontmatter plus Markdown. Leaf nodes are entities (a person, a project); parent nodes summarize their descendants.
  • Parent summaries are the index. Every level is a comprehensive rollup of the subtree below it, written by the agent itself. Navigation is top-down reading, not blind grepping.
  • Writes propagate. kvault write returns the full ancestor chain so the agent rewrites those summaries in one follow-up call — the "2-call write workflow."
  • The KB instructs the agent. kvault init generates an AGENTS.md with the workflow, the rules (search before create, never fabricate, propagate everything), and a periodic maintenance playbook.

Quickstart (30 seconds)

pip install knowledgevault
kvault init ./my_kb --name "Your Name"

Then tell your agent:

"Use kvault CLI commands to manage my knowledge base at ./my_kb"

The agent reads the generated AGENTS.md and starts working.

Tool Setup
Project-instruction agents Keep AGENTS.md in the KB root so the agent reads the workflow automatically
Terminal agents Tell the agent: "Read AGENTS.md for the kvault workflow, then use shell commands to manage ./my_kb"
Custom-instruction agents Paste the generated AGENTS.md workflow into the workspace or system instructions

Agent skill included. skills/kvault/SKILL.md carries the full workflow in the portable SKILL.md agent-skills format, so the agent loads it on demand from any directory — no per-KB setup. Install it wherever your tool discovers skills:

# Claude Code
cp -r skills/kvault ~/.claude/skills/kvault

# OpenClaw (per workspace)
cp -r skills/kvault ~/.openclaw/workspace/skills/kvault

# Other agents: copy into your tool's skills directory, or paste the
# SKILL.md body into its custom instructions

Already have data? Point your agent at an export from a chat, email, or notes tool — see docs/importing-data.md.

The 2-call write workflow

# Call 1: write the node (stdin = frontmatter + markdown body)
kvault write people/contacts/sarah_chen --create --reasoning "Met at NeurIPS" --json --kb-root ./my_kb <<'EOF'
---
source: manual
aliases: [Sarah Chen, sarah@example.com]
---
# Sarah Chen
Research scientist at Acme AI...
EOF
# → {"success": true, "changed": true, "did": "created people/contacts/sarah_chen",
#    "notes": [{"code": "autofilled", "text": "name=Sarah Chen", ...}],
#    "ancestor_paths": ["people/contacts", "people", "."],
#    "ancestors": [{path, current_content}, ...], "journal_logged": true}

# Call 2: the agent rewrites the returned ancestors, including root
kvault update-summaries --json --kb-root ./my_kb <<'EOF'
[
  {"path": "people/contacts", "content": "# Contacts\n...updated..."},
  {"path": "people", "content": "# People\n...updated..."},
  {"path": ".", "content": "# Knowledge Base\n...updated..."}
]
EOF

In human mode the same write narrates its decisions under the receipt:

Created: people/contacts/sarah_chen
  autofilled  name=Sarah Chen
Journal: journal/2026-08/log.md
Ancestors to update: 3  (people/contacts, people, .)

Re-sending identical content is a detected no-op — the file is not rewritten, mtime and created/updated stay put, so the recency signal in the tree stays honest:

Unchanged: people/contacts/sarah_chen
  unchanged   body and metadata identical — file not rewritten, created 2026-08-10, updated 2026-08-10 preserved
Journal: journal/2026-08/log.md

Required frontmatter: source, aliases — kvault stamps created/updated automatically.

What kvault tells you

kvault reports what it decided, not what you asked for. A note is emitted only when kvault invented a value, deliberately changed nothing, half-failed, hid something, or fell back — silence means the operation went exactly as asked. Notes render as indented lines under the receipt (human mode) and as a notes array in --json and over MCP, each {code, text, level} — a note's why/next ride in the JSON at every tier, and print in human mode at --explain. Batch commands collapse repeated notes by code ({code, count, examples}), so a 40-ancestor maintenance run emits one line, not forty. The vocabulary is closed — 10 codes:

Code Contract
autofilled kvault invented a value you did not supply
unchanged the operation ran and deliberately changed nothing
partial part succeeded, part did not; manual repair needed
created something came into existence as a side effect
removed something was destroyed, with a count
truncated you are not seeing everything that matched or exists
skipped kvault could not read something and continued without it
waited kvault blocked on, or broke, another process's lock
guessed an input was unusable and a fallback was chosen
propagate ancestor summaries are stale because of this operation

Tiers. -q/--quiet (receipt and warnings only) → normal → --explain (adds each note's why and the exact next command) → --trace (adds lock waits and mechanics). Flags work before or after the subcommand; KVAULT_VERBOSITY=quiet|normal|explain|trace sets the tier for hooks and cron jobs (flags win; a typo silently means normal). partial notes survive even --quiet — silencing a half-failure on request is a footgun.

--strict exits 3 when any warning-class note (partial, skipped, a broken lock) was emitted — for CI and unattended runs. check rejects it: its exit codes are already a contract, and its human output is frozen.

Durable ops log. Every successful mutating command (CLI and MCP) appends one row to .kvault/logs.db: op, path, did, notes, changed/partial flags, duration, session. kvault log tail shows what this KB's other agents and sessions did recently; KVAULT_SESSION groups the commands of one logical task, KVAULT_OPS_LOG=0 disables. A failed append can never fail a write — the CLI surfaces the miss as a skipped note.

Full 0.13.0 detail — every new JSON field, per-command changes, frozen surfaces — is in the CHANGELOG.

The maintenance loop

KBs rot without pruning. The tree annotations make refactor triggers deterministic instead of aspirational — agents read them off the orientation pass:

Signal Action
Branch with >10 children ([N children, ...]) Split into subgroups; kvault move entities; re-propagate
Branch ~updated_max older than ~6 months Review for stale or dead content
SUMMARY: warnings from kvault check Rewrite flagged parents as comprehensive rollups
Near-duplicate titles or aliases Verify identifiers, merge, delete the duplicate

kvault check also catches stale propagation, and works as a pre-prompt hook:

{
  "hooks": {
    "UserPromptSubmit": [
      {"type": "command", "command": "kvault check --kb-root /absolute/path/to/my_kb"}
    ]
  }
}

CLI reference

Category Commands
Orient & discover kvault tree [path] [--depth N] [--max-children N] [--gist], kvault search "<query>"
Nodes kvault read, kvault write (stdin), kvault list, kvault delete, kvault move
Summaries kvault read-summary, kvault write-summary (stdin), kvault update-summaries (stdin JSON), kvault ancestors
Quality kvault validate, kvault check
Journal & artifacts kvault journal, kvault artifact daily, kvault log tail, kvault log summary
Lifecycle kvault init, kvault status

Agent-facing commands accept --json for machine-readable output and --kb-root (auto-detected from cwd by default), before or after the subcommand — as do the output flags -q/--quiet, --explain, --trace, and --strict (see What kvault tells you).

MCP server (optional)

The CLI is the primary interface. For MCP-native clients, a stdio compatibility server ships with the [mcp] extra (Python 3.10+), bound to one KB root per process:

pip install "knowledgevault[mcp]"
kvault-mcp --kb-root /absolute/path/to/my_kb
{
  "mcpServers": {
    "kvault": {
      "command": "kvault-mcp",
      "args": ["--kb-root", "/absolute/path/to/my_kb"]
    }
  }
}

It exposes the same operations as the CLI (kvault_tree, kvault_search, kvault_read_node, kvault_write_node, summary/journal/validation tools, kvault_log_tail for the ops log), plus a strict parent-summary workflow with stale-write detection. Results carry the same did/notes decision reporting as --json, placed before the bulk payload. The write tools (kvault_write_node, kvault_write_entity) accept ancestors="content"|"paths": "paths" keeps ancestor_paths but omits the full ancestors[].current_content payload, which can exceed 45,000 characters on a mature KB. The default stays "content" in 0.13.x and flips to "paths" in 0.14.0. Set KVAULT_ALLOWED_ROOTS to pin allowed roots on shared runtimes. Protocol details: ARCHITECTURE.md.

It's just files

kvault produces Markdown with YAML frontmatter in a plain directory. No proprietary format, no database to export from. Your existing tools work out of the box:

Want to... Use
Semantic search Embed the .md files with any vector tool
Exact text search rg -n "phrase" ./my_kb
Visual browsing Open the KB directory in Obsidian or Logseq
Publish as a site Point Hugo, Jekyll, or Astro at the directory
CI validation Run kvault validate or kvault check in a GitHub Action
Bulk export find . -name _summary.md + yq over the frontmatter

Python API

from pathlib import Path
from kvault.core import operations as ops

kg_root = Path("my_kb")
outline = ops.build_outline(kg_root, depth=2)          # annotated tree as nested dict
node = ops.read_node(kg_root, "people/contacts/sarah_chen")
result = ops.write_node(kg_root, "people/contacts/new_person", "# Content", create=True)
matches = ops.search_nodes(kg_root, "sarah follow up")

Development

pip install -e ".[dev,mcp]"
pytest -q
ruff check .
black --check kvault/ tests/
mypy kvault/ --ignore-missing-imports

License

MIT

Download files

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

Source Distribution

knowledgevault-0.13.0.tar.gz (131.8 kB view details)

Uploaded Source

Built Distribution

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

knowledgevault-0.13.0-py3-none-any.whl (102.7 kB view details)

Uploaded Python 3

File details

Details for the file knowledgevault-0.13.0.tar.gz.

File metadata

  • Download URL: knowledgevault-0.13.0.tar.gz
  • Upload date:
  • Size: 131.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for knowledgevault-0.13.0.tar.gz
Algorithm Hash digest
SHA256 fb582359200a923585f648e53ec6756bc30c601d7d5027f9c94270b5866af069
MD5 95dd14bf6ed347a25e3b1d05590e22b2
BLAKE2b-256 55ea3a0168e3344699781b518400ee936c33e1fefab4a24e2cdccd910a342496

See more details on using hashes here.

Provenance

The following attestation bundles were made for knowledgevault-0.13.0.tar.gz:

Publisher: publish.yml on cimo-labs/kvault

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file knowledgevault-0.13.0-py3-none-any.whl.

File metadata

  • Download URL: knowledgevault-0.13.0-py3-none-any.whl
  • Upload date:
  • Size: 102.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for knowledgevault-0.13.0-py3-none-any.whl
Algorithm Hash digest
SHA256 34fbeec00a656b02c91c0366afad7aba6a9c4e568bd41912b781ba3aceae93ae
MD5 66e81e2eb6ca5026ccf15a223fc5d848
BLAKE2b-256 7dc80569fd3749e6fe82e46bf2a0a9963ef38618c2b7f0e3285cab8c1034fece

See more details on using hashes here.

Provenance

The following attestation bundles were made for knowledgevault-0.13.0-py3-none-any.whl:

Publisher: publish.yml on cimo-labs/kvault

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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