"I'm overwhelmed by the flood of AI generated knowledge managers, so let's create yet another one."
Obviously heavy WIP, don't expect miracles.
Could be any of:
- a bad pun on German/French accented "the"
- an abbreviated "Zettel(kasten)"
- Zommuter's knowledge manager
- "Zero knowledge manager" — irony in case it doesn't work
Also feel free to pronounce zkm simply "ze-kem".
Requires Python 3.11+ and uv.
git clone <this-repo> ~/src/zkm
cd ~/src/zkm
uv sync
uv run zkm --versionFor a system-wide install:
uv tool install .
zkm --versionexport ZKM_STORE=~/knowledge # default if unset
zkm initzkm init creates ~/knowledge/ with inbox/, notes/, originals/, a
git repo, zkm-config.yaml for committed config, and a gitignored
.zkm-secrets.yaml for credentials.
Plugins convert sources into markdown. Two install paths:
Released plugins (recommended for end users):
uv tool install zkm --with zkm-eml --with zkm-photo # add one or moreDev / local plugins (for development or custom plugins):
# bundled plain-text/markdown importer
zkm plugin add ./examples/zkm-notes
# or a git-hosted plugin
zkm plugin add https://github.com/yourname/zkm-mypluginList installed plugins (shows origin: entry-point or filesystem path):
zkm plugin list# plain notes importer: set the source directory in zkm-config.yaml
cat >> $ZKM_STORE/zkm-config.yaml <<EOF
notes:
source_dir: $HOME/Documents/notes
EOF
zkm convert notesThe command walks the source, writes frontmatter-tagged markdown into the store, and auto-commits. Re-running is safe — files are deduped by sha256.
Use --reprocess to re-derive files whose processor_version differs from
the current plugin version, or --reprocess-all to re-derive everything.
Build (or refresh) the BM25 search index:
zkm indexThe index lives in $ZKM_STORE/.zkm-index/bm25.pkl (gitignored). Re-running
is incremental — only files whose mtime changed are re-tokenised.
zkm search "electricity bill"
zkm search "apple" --top-k 5
zkm search "recipe" --jsonOutput: ranked hits with score, date, and a text snippet.
Point zkm at any OpenAI-compatible endpoint via $ZKM_STORE/zkm-config.yaml
(non-secret) and $ZKM_STORE/.zkm-secrets.yaml (gitignored, chmod 0600):
# zkm-config.yaml
core:
llm:
endpoint: http://localhost:11434 # Ollama
model: llama3# .zkm-secrets.yaml
core:
llm:
key: sk-...Then:
zkm query "what bills are due this month?"Migrating from an existing .env? Run zkm config migrate --apply once.
The answer streams to stdout with a sources list at the end.
Plugins (source → md) → Store (md + git) → Index (BM25) → Query (CLI)
- No vector DB in Phase 1. BM25 over markdown. Embeddings are Phase 2.
- Git history = temporal index. HEAD = current state; log/diff = evolution.
- LLM at query time only, via any OpenAI-compatible API.
See docs/ for design notes, plugin spec, and future-phase plans.
The two most fully-fledged converters are zkm-eml (email) and zkm-whatsapp (chat). The full set of available plugins:
| Plugin | Description |
|---|---|
| zkm-eml | Convert Maildir / .eml files to markdown with thread modeling and attachment extraction |
| zkm-whatsapp | Convert a decrypted WhatsApp msgstore.db to per-chat-day transcript markdown (media + call logs) |
| zkm-ner | Amender: extract named entities (persons, orgs, locations, contacts) into frontmatter |
| zkm-notmuch | Amender: merge notmuch Xapian tags into mail-message frontmatter |
| zkm-pdf | Import text-extractable PDFs into the knowledge store |
| zkm-photo | Import JPEG photos with EXIF metadata into the knowledge store |
| zkm-scan | OCR scanned images and PDFs (tesseract) into the knowledge store |
To install a plugin, clone it into plugins/ inside your zkm checkout (it is auto-discovered):
git clone https://github.com/zommuter/zkm-eml.git plugins/zkm-emlNote: PyPI names are reserved (
pip install zkm-emletc.) but currently ship 0.0.1 placeholder stubs only — functional plugin code requires the git-clone path above. Entry-point–based plugin discovery (pip-installable plugins) is planned for zkm 1.0.
See docs/plugin-spec.md and the reference
implementation in examples/zkm-notes/.
A plugin is a directory (or git repo) containing:
plugin.yaml # name, version, config_schema, creates_dirs
convert.py # def convert(store_path, config, *, progress=None) -> list[Path]
Install locally during development (creates a symlink in plugins/):
zkm plugin add ./my-pluginFor released plugins: uv tool install zkm --with zkm-<name>
~/knowledge/
├── inbox/ # drop zone — unsorted items
├── notes/ # manual notes, diary, zettelkasten
├── originals/ # binary originals (git-annex / git-lfs / plain)
├── zkm-config.yaml # non-secret config (committed)
├── .zkm-secrets.yaml # credentials (chmod 0600, gitignored)
└── .zkm-index/ # BM25 index (gitignored)
Plugins create additional directories on first run (e.g. mail/, messages/).
| Phase | Status | Features |
|---|---|---|
| 1 — MVP | done | init, plugins, convert, index, search, query |
| 2 — Richer search | planned | embeddings, NER, store management commands |
| 3 — Integration | planned | FastAPI WebUI, entity pages, Zelegator integration |
| 4 — Temporal | planned | git-history queries, memory compaction |
MIT — see LICENSE