Skip to main content

Zettelkasten Control — CLI utility for managing a Zettelkasten note-taking system

Project description

ztlctl

CI PyPI version License: MIT

Zettelkasten Control — a CLI utility and agentic note-taking ecosystem that combines zettelkasten, second-brain, and knowledge-garden paradigms into a single tool designed for both human users and AI agents.

ztlctl manages your knowledge vault as structured markdown files backed by a SQLite index, connected through a weighted knowledge graph, and accessible via CLI, MCP server, or direct Python API.

Table of Contents

Installation

pip install ztlctl

Or with uv:

uv tool install ztlctl

For MCP server support (optional):

pip install ztlctl[mcp]

Quick Start

# 1. Initialize a new vault
ztlctl init my-vault --topics "python,architecture,devops"

# 2. Enter the vault directory
cd my-vault

# 3. Start a research session
ztlctl agent session start "Learning Python async patterns"

# 4. Create notes as you learn
ztlctl create note "Asyncio Event Loop" --tags "lang/python,concept/concurrency"
ztlctl create note "Async vs Threading" --tags "lang/python,concept/concurrency"

# 5. Create references to external sources
ztlctl create reference "Python asyncio docs" --url "https://docs.python.org/3/library/asyncio.html"

# 6. Track tasks that emerge
ztlctl create task "Refactor API to use async" --priority high --impact high

# 7. Let the graph grow — reweave discovers connections
ztlctl reweave --auto-link-related

# 8. Search your knowledge
ztlctl query search "async patterns" --rank-by relevance

# 9. Close the session when done
ztlctl agent session close --summary "Mapped async patterns, identified refactoring task"

Core Concepts

Content Types

ztlctl manages four content types, each with its own lifecycle:

Type Purpose Initial Status ID Format
Note Ideas, knowledge, decisions draft ztl_<hash>
Reference External sources (articles, tools, specs) captured ref_<hash>
Task Actionable work items inbox TSK-NNNN
Log Session journals (JSONL) open LOG-NNNN

Content Subtypes

Notes and references can be further classified:

  • Note subtypes: knowledge (long-lived insight), decision (architectural/design choice)
  • Reference subtypes: article, tool, spec
  • Garden maturity: seed (raw capture) → budding (developing) → evergreen (polished)

Lifecycle States

Each content type follows a defined state machine:

Note:      draft → linked (1+ outgoing link) → connected (3+ outgoing links)
Reference: captured → annotated
Task:      inbox → active → done | blocked | dropped
Decision:  proposed → accepted → superseded
Log:       open ↔ closed (reopenable)

Status transitions are enforced — you cannot skip states or make invalid transitions.

Vault Structure

my-vault/
├── ztlctl.toml          # Configuration
├── .ztlctl/
│   └── ztlctl.db        # SQLite index + FTS5 + graph edges
├── self/
│   ├── identity.md      # Agent identity (generated from config)
│   └── methodology.md   # Agent methodology
├── notes/
│   ├── python/          # Topic subdirectories
│   │   └── ztl_a1b2c3d4.md
│   └── architecture/
│       └── ztl_e5f6g7h8.md
└── ops/
    ├── logs/
    │   └── LOG-0001.jsonl
    └── tasks/
        └── TSK-0001.md

Tags

Tags use a domain/scope format for structured categorization:

--tags "lang/python"        # domain=lang, scope=python
--tags "concept/concurrency" # domain=concept, scope=concurrency
--tags "status/wip"         # domain=status, scope=wip

Unscoped tags (e.g., python) work but generate a warning — the domain/scope format enables powerful filtering.

Knowledge Graph

Every content item is a node. Edges are created through:

  • Frontmatter links: Explicit links: in YAML frontmatter
  • Wikilinks: [[Note Title]] references in body text
  • Reweave: Automated link discovery via 4-signal scoring (BM25 lexical similarity, Jaccard tag overlap, graph proximity, topic match)

The graph powers ztlctl graph commands for traversal, analysis, and structural insight.

Tutorial: Building Your Knowledge Vault

Step 1: Initialize Your Vault

ztlctl init research-vault --name "Research Notes" --topics "ml,systems,papers"
cd research-vault

This creates the directory structure, config file, SQLite database, and agent identity files. The --topics flag pre-creates subdirectories under notes/.

Options:

  • --name TEXT — Vault display name
  • --client [obsidian|vanilla] — Client integration (Obsidian adds .obsidian/ config)
  • --tone [research-partner|assistant|minimal] — Agent personality for self/ files
  • --topics TEXT — Comma-separated topic directories
  • --no-workflow — Skip workflow template setup

Step 2: Capture Knowledge

Create a note — your primary unit of knowledge:

ztlctl create note "Transformer Architecture" \
  --tags "ml/transformers,concept/architecture" \
  --topic ml

Create a reference — link to an external source:

ztlctl create reference "Attention Is All You Need" \
  --url "https://arxiv.org/abs/1706.03762" \
  --subtype article \
  --tags "ml/transformers,papers/seminal"

Quick capture with garden seed — when you want minimal friction:

ztlctl garden seed "Idea: attention mechanisms for code review" \
  --tags "ml/attention" --topic ml

Seeds start at seed maturity and can grow to buddingevergreen as you develop them.

Step 3: Work with Tasks

ztlctl create task "Read BERT paper" --priority high --impact high --effort low
ztlctl create task "Implement attention visualization" --priority medium

View your prioritized work queue:

ztlctl query work-queue

Tasks are scored by priority × impact ÷ effort and presented in actionable order.

Step 4: Connect Knowledge

Automatic link discovery — reweave analyzes all content and suggests connections:

ztlctl reweave --auto-link-related

Dry run to preview what would change:

ztlctl reweave --dry-run

Target a specific note:

ztlctl reweave --id ztl_a1b2c3d4

Reweave uses a 4-signal scoring algorithm:

  1. BM25 (35%) — lexical similarity between content bodies
  2. Tag Jaccard (25%) — tag overlap between items
  3. Graph Proximity (25%) — existing network distance
  4. Topic Match (15%) — shared topic directory

Step 5: Query and Explore

Full-text search:

ztlctl query search "transformer attention" --rank-by relevance
ztlctl query search "python async" --rank-by recency --type note
ztlctl query search "architecture" --rank-by graph  # PageRank-boosted

List with filters:

ztlctl query list --type note --status draft
ztlctl query list --tag "ml/transformers" --sort recency
ztlctl query list --maturity seed --since 2025-01-01
ztlctl query list --include-archived --sort title

Get a specific item:

ztlctl query get ztl_a1b2c3d4

Decision support — aggregate context for a decision:

ztlctl query decision-support --topic architecture

Step 6: Analyze the Graph

Find related content via spreading activation:

ztlctl graph related ztl_a1b2c3d4 --depth 2 --top 10

Discover topic clusters:

ztlctl graph themes

Find the most important nodes (PageRank):

ztlctl graph rank --top 20

Find the shortest path between two ideas:

ztlctl graph path ztl_a1b2c3d4 ztl_e5f6g7h8

Find structural gaps — orphan notes with no connections:

ztlctl graph gaps --top 10

Find bridge nodes — key connectors between clusters:

ztlctl graph bridges --top 10

Step 7: Update and Evolve

Update metadata:

ztlctl update ztl_a1b2c3d4 --title "New Title" --tags "new/tag"
ztlctl update ztl_a1b2c3d4 --status linked
ztlctl update ztl_a1b2c3d4 --maturity budding  # Grow a garden note

Archive — soft-delete that preserves graph edges:

ztlctl archive ztl_a1b2c3d4

Supersede a decision:

ztlctl supersede ztl_old_decision ztl_new_decision

Step 8: Export and Share

Export markdown — portable copy of all content:

ztlctl export markdown --output ./export/

Generate indexes — type and topic groupings:

ztlctl export indexes --output ./indexes/

Export the knowledge graph:

ztlctl export graph --format dot --output graph.dot  # For Graphviz
ztlctl export graph --format json --output graph.json # For D3.js / vis.js

Step 9: Maintain Integrity

Check vault health:

ztlctl check

Auto-fix detected issues:

ztlctl check --fix
ztlctl check --fix --level aggressive  # More thorough repairs

Full rebuild — re-derive the entire database from files:

ztlctl check --rebuild

Rollback to the last backup:

ztlctl check --rollback

Command Reference

Global Options

Every command supports these flags:

Flag Description
--version Show version and exit
--json Structured JSON output (for scripting and agents)
-q, --quiet Minimal output
-v, --verbose Detailed output with debug info
--no-interact Non-interactive mode (no prompts)
--no-reweave Skip automatic reweave on creation
-c, --config TEXT Override config file path
--sync Force synchronous event dispatch

Most commands also support --examples to show usage examples.

Commands at a Glance

Command Purpose
init [PATH] Initialize a new vault
create note TITLE Create a note
create reference TITLE Create a reference
create task TITLE Create a task
create batch FILE Batch create from JSON
query search QUERY Full-text search
query get ID Get item by ID
query list List with filters
query work-queue Prioritized task queue
query decision-support Decision context aggregation
graph related ID Find related content
graph themes Discover topic clusters
graph rank PageRank importance
graph path SRC DST Shortest path between nodes
graph gaps Find structural holes
graph bridges Find bridge nodes
graph materialize Compute and store graph metrics
agent session start TOPIC Start a session
agent session close Close with enrichment pipeline
agent session reopen ID Reopen a closed session
agent session cost Token cost tracking
agent session log MSG Append log entry
agent context Token-budgeted context payload
agent brief Quick orientation summary
agent regenerate Re-render self/ files
check Integrity check/fix/rebuild
update ID Update metadata or body
reweave Automated link discovery
archive ID Soft-delete content
supersede OLD NEW Mark decision as superseded
extract SESSION_ID Extract decision from session
export markdown Export as portable markdown
export indexes Generate type/topic indexes
export graph Export graph (DOT or JSON)
garden seed TITLE Quick-capture seed note
serve Start MCP server
upgrade Run database migrations

Search Ranking Modes

The --rank-by option on query search supports three modes:

Mode Algorithm Best For
relevance BM25 with time decay General search
recency Modified date descending Finding recent work
graph BM25 × PageRank boost Finding well-connected content

Query Filters

The query list command supports composable filters:

# Combine any of these:
--type note|reference|task|log
--status draft|linked|connected|inbox|active|done|...
--tag "domain/scope"
--topic "python"
--subtype knowledge|decision|article|tool|spec
--maturity seed|budding|evergreen
--space notes|ops|self
--since 2025-01-01
--include-archived
--sort recency|title|type|priority
--limit 50

Agentic Workflows

ztlctl is designed to be operated by AI agents as a first-class use case. Every command supports --json output for structured parsing, and the session/context system provides token-budgeted context windows.

Session-Based Agent Workflow

Sessions are the primary organizational container for agentic work:

# Agent starts a focused research session
ztlctl agent session start "API design patterns" --json

# Agent creates notes as it discovers knowledge
ztlctl create note "REST vs GraphQL trade-offs" \
  --tags "architecture/api" --session LOG-0001 --json

# Agent logs its reasoning and costs
ztlctl agent session log "Analyzed 5 API frameworks" --cost 1200 --json
ztlctl agent session log "Key insight: GraphQL better for nested data" --pin --json

# Agent checks token budget
ztlctl agent session cost --report 50000 --json

# Agent requests context for continued work
ztlctl agent context --topic "api" --budget 4000 --json

# Agent closes session, triggering enrichment pipeline
ztlctl agent session close --summary "Mapped API paradigms" --json

Context Assembly (5-Layer System)

The agent context command builds a token-budgeted payload with 5 layers:

Layer Content Budget
0 — Identity self/identity.md + self/methodology.md Always included
1 — Operational Active session, recent decisions, work queue, log entries Always included
2 — Topic Notes and references matching the session topic Budget-dependent
3 — Graph 1-hop neighbors of Layer 2 content Budget-dependent
4 — Background Recent activity, structural gaps Budget-dependent

The system tracks token usage per layer and reports pressure status (normal, caution, exceeded).

# Get full context with default 8000-token budget
ztlctl agent context --json

# Focus on a topic with custom budget
ztlctl agent context --topic "architecture" --budget 4000 --json

# Quick orientation (no session required)
ztlctl agent brief --json

Session Close Enrichment Pipeline

When a session closes, ztlctl automatically runs:

  1. Cross-session reweave — discovers connections for all notes created in the session
  2. Orphan sweep — attempts to connect orphan notes (0 outgoing edges)
  3. Integrity check — validates vault consistency
  4. Graph materialization — updates PageRank, degree, and betweenness metrics

Each step can be toggled in ztlctl.toml:

[session]
close_reweave = true
close_orphan_sweep = true
close_integrity_check = true

Decision Extraction

Extract decisions from session logs into permanent decision notes:

# Extracts pinned/decision entries from the session log
ztlctl extract LOG-0001 --title "Decision: Use GraphQL for nested queries"

This creates a decision note (subtype=decision, status proposed) linked to the session via a derived_from edge.

MCP Server Integration

ztlctl includes a Model Context Protocol (MCP) server for direct integration with AI clients like Claude Desktop:

ztlctl serve --transport stdio

12 MCP tools: create_note, create_reference, create_task, create_log, update_content, close_content, reweave, search, get_document, get_related, agent_context, session_close

6 MCP resources: self/identity, self/methodology, vault overview, work queue, topics, full context

4 MCP prompts: research_session, knowledge_capture, vault_orientation, decision_record

Add to Claude Desktop's claude_desktop_config.json:

{
  "mcpServers": {
    "ztlctl": {
      "command": "ztlctl",
      "args": ["serve"]
    }
  }
}

Batch Operations

For programmatic creation, use batch mode with a JSON file:

echo '[
  {"type": "note", "title": "Concept A", "tags": ["domain/scope"]},
  {"type": "reference", "title": "Source B", "url": "https://example.com"},
  {"type": "task", "title": "Follow up on C", "priority": "high"}
]' > items.json

ztlctl create batch items.json --json
ztlctl create batch items.json --partial  # Continue on individual failures

Scripting with JSON Output

Every command supports --json for structured output:

# Create and capture the ID
ID=$(ztlctl create note "My Note" --json | jq -r '.data.id')

# Query and process results
ztlctl query search "python" --json | jq '.data.items[].title'

# Check vault health programmatically
ERRORS=$(ztlctl check --json | jq '.data.issues | map(select(.severity == "error")) | length')

Knowledge Paradigms

ztlctl integrates three complementary note-taking paradigms:

Zettelkasten (Atomic Notes + Links)

The zettelkasten method treats each note as a single, self-contained idea connected to others through links.

How ztlctl supports it:

  • Each note gets a unique content-hash ID (ztl_a1b2c3d4)
  • Notes link to each other via [[wikilinks]] in body text and explicit frontmatter links
  • ztlctl graph related implements spreading activation to find connected ideas
  • ztlctl graph path traces connection chains between any two notes
  • Note status evolves automatically: draftlinked (1+ outgoing) → connected (3+ outgoing)

Workflow:

# Capture atomic ideas
ztlctl create note "Immutability reduces bugs" --tags "concept/fp"
ztlctl create note "Pure functions are easier to test" --tags "concept/fp"

# Let reweave connect them
ztlctl reweave --auto-link-related

# Explore the connections
ztlctl graph related ztl_abc123 --depth 2

Second Brain (PARA + Capture Everything)

The second brain approach (inspired by Tiago Forte's PARA) captures everything and organizes by actionability.

How ztlctl supports it:

  • Projects → Tasks with priority/impact/effort scoring and work-queue
  • Areas → Topic directories (--topic) for ongoing domains
  • Resources → References with URL, subtype (article/tool/spec), and tags
  • Archiveztlctl archive soft-deletes while preserving graph edges
  • Sessions provide temporal organization — every piece of content links to its creation session

Workflow:

# Capture everything during a research session
ztlctl agent session start "System design research"

# Resources (articles, tools, specs you encounter)
ztlctl create reference "CAP Theorem Explained" --subtype article --url "..."
ztlctl create reference "Redis Documentation" --subtype tool --url "..."

# Knowledge (your synthesized understanding)
ztlctl create note "Trade-offs in distributed caching" --subtype knowledge

# Tasks (actions that emerge)
ztlctl create task "Evaluate Redis vs Memcached" --priority high --impact high

# Review your work queue
ztlctl query work-queue

Knowledge Garden (Seeds → Evergreen)

The digital garden metaphor treats notes as living things that grow over time through tending.

How ztlctl supports it:

  • Maturity levels: seed (raw capture) → budding (developing) → evergreen (polished)
  • ztlctl garden seed — quick capture with minimal friction
  • ztlctl update --maturity budding — promote as you develop ideas
  • ztlctl graph gaps — find notes that need tending (no outgoing links)
  • Garden notes protect body content from accidental overwrites

Workflow:

# Quick-capture seeds throughout the day
ztlctl garden seed "Idea: use event sourcing for audit trail"
ztlctl garden seed "Question: how does CRDT conflict resolution work?"

# Later, tend your garden — find seeds that need attention
ztlctl query list --maturity seed --sort recency

# Develop a seed into a budding note
ztlctl update ztl_abc123 --maturity budding \
  --tags "architecture/event-sourcing,pattern/cqrs"

# Promote to evergreen when fully developed
ztlctl update ztl_abc123 --maturity evergreen

Combining Paradigms

These paradigms work together naturally:

  1. Capture with garden seeds and references (Second Brain + Garden)
  2. Connect via reweave and wikilinks (Zettelkasten)
  3. Develop by promoting seeds through maturity levels (Garden)
  4. Act on tasks surfaced by the work queue (Second Brain)
  5. Decide by extracting decisions from sessions (Zettelkasten + Second Brain)
  6. Review via graph analysis to find gaps and bridges (All three)

Configuration

ztlctl uses a ztlctl.toml file at the vault root. Settings can be overridden via CLI flags or ZTLCTL_* environment variables.

Key Configuration Sections

[vault]
name = "my-vault"
client = "obsidian"  # or "vanilla"

[agent]
tone = "research-partner"  # or "assistant", "minimal"

[agent.context]
default_budget = 8000      # Token budget for context assembly
layer_2_max_notes = 10     # Max notes in topic layer
layer_3_max_hops = 1       # Graph traversal depth

[reweave]
enabled = true
min_score_threshold = 0.6  # Minimum score to suggest a link
max_links_per_note = 5
lexical_weight = 0.35      # BM25 weight
tag_weight = 0.25          # Tag Jaccard weight
graph_weight = 0.25        # Graph proximity weight
topic_weight = 0.15        # Topic match weight

[garden]
seed_age_warning_days = 7
evergreen_min_key_points = 5
evergreen_min_bidirectional_links = 3

[search]
half_life_days = 30.0      # Time-decay half-life for recency ranking

[session]
close_reweave = true       # Reweave on session close
close_orphan_sweep = true  # Connect orphan notes on close
close_integrity_check = true

[check]
backup_retention_days = 30
backup_max_count = 10

[git]
enabled = true
auto_push = true
commit_style = "conventional"

[mcp]
enabled = true
transport = "stdio"

Environment Variables

Any setting can be overridden with a ZTLCTL_ prefix:

ZTLCTL_REWEAVE__MIN_SCORE_THRESHOLD=0.4 ztlctl reweave
ZTLCTL_AGENT__CONTEXT__DEFAULT_BUDGET=16000 ztlctl agent context

MCP Server

The MCP (Model Context Protocol) server exposes ztlctl's full functionality to AI clients.

Setup

# Install with MCP support
pip install ztlctl[mcp]

# Start the server
ztlctl serve --transport stdio

Available Tools

Tool Description
create_note Create a note with title, tags, topic
create_reference Create a reference with URL
create_task Create a task with priority/impact/effort
create_log Start a new session
update_content Update content metadata
close_content Archive or close content
reweave Run link discovery
search Full-text search with ranking
get_document Retrieve content by ID
get_related Graph-based related content
agent_context Token-budgeted context payload
session_close Close session with enrichment

Available Resources

Resource Description
self/identity Agent identity document
self/methodology Agent methodology document
vault/overview Vault statistics and structure
vault/work-queue Prioritized task list
vault/topics Available topic directories
vault/context Full assembled context

Available Prompts

Prompt Description
research_session Start a structured research session
knowledge_capture Guided knowledge capture workflow
vault_orientation Orient to the current vault state
decision_record Record an architectural decision

Development

uv sync --group dev                              # install all dev dependencies
uv run ztlctl --help                             # run the CLI
uv run pytest --cov --cov-report=term-missing    # run tests with coverage
uv run ruff check .                              # lint
uv run ruff format .                             # format
uv run mypy src/                                 # type check
uv run pre-commit run --all-files                # run all pre-commit hooks

Architecture

ztlctl follows a strict 6-layer package structure:

src/ztlctl/
├── domain/          # Types, enums, lifecycle rules, ID patterns
├── infrastructure/  # SQLite/SQLAlchemy, NetworkX graph, filesystem
├── config/          # Pydantic config models, TOML discovery
├── services/        # Business logic (create, query, graph, reweave, ...)
├── output/          # Rich/JSON formatters
├── commands/        # Click CLI commands
├── plugins/         # Pluggy hook specs and built-in plugins
├── mcp/             # MCP server adapter
└── templates/       # Jinja2 templates for content creation

Dependencies flow downward: commands → output → services → config/infrastructure → domain.

Contributing

This project uses conventional commits. All commit messages and PR titles must follow the format:

<type>(<scope>): <description>

Common types: feat, fix, docs, test, refactor, ci, chore

Workflow

  1. Branch from develop: git checkout -b feature/<name> develop
  2. Make changes and commit with conventional messages
  3. Open a PR targeting develop
  4. Releases are automated when develop is merged to main

License

MIT

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

ztlctl-1.0.0.tar.gz (276.1 kB view details)

Uploaded Source

Built Distribution

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

ztlctl-1.0.0-py3-none-any.whl (130.2 kB view details)

Uploaded Python 3

File details

Details for the file ztlctl-1.0.0.tar.gz.

File metadata

  • Download URL: ztlctl-1.0.0.tar.gz
  • Upload date:
  • Size: 276.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ztlctl-1.0.0.tar.gz
Algorithm Hash digest
SHA256 eb8e165e78877ecb2527c01f4ccff3b24ec3c78a199bddf60d94c9ef3d9448e9
MD5 9295465bf5573d7a7ab3c16302f67e88
BLAKE2b-256 fa11ed744b23b72b3ccd0075c449b64395c977be039e5f100ed64301c2221232

See more details on using hashes here.

Provenance

The following attestation bundles were made for ztlctl-1.0.0.tar.gz:

Publisher: publish.yml on ThatDevStudio/ztlctl

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

File details

Details for the file ztlctl-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: ztlctl-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 130.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ztlctl-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c77f186c18f7401eb0c469c6ea1544ed29c24ba7ca2f6ce185d3f8c39c2d0fe2
MD5 aad1ef94e8cbeb936fd5375e34ba6f27
BLAKE2b-256 62948c8b6fbd889252ed1e388fd4e4fa56562ab83f82b3de9e3db95690b69aa9

See more details on using hashes here.

Provenance

The following attestation bundles were made for ztlctl-1.0.0-py3-none-any.whl:

Publisher: publish.yml on ThatDevStudio/ztlctl

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 Pingdom Monitoring Sentry Error logging StatusPage Status page