Markdown Base CLI (markbase)
A high-performance CLI tool for indexing and querying Markdown notes, designed for both AI agents and human users with Obsidian compatibility in mind.
Installation
Prerequisites: Rust 1.85+ (DuckDB is bundled)
Quick Start
Environment Variables
| Variable | Description | Default |
|---|---|---|
MARKBASE_BASE_DIR |
Vault directory | . (current directory) |
MARKBASE_OUTPUT_FORMAT |
Output format for query/template list | table |
Priority: CLI args > Environment variables > Defaults
Concepts
Note Properties
Each indexed note has two namespaces for properties:
File Properties (file.* prefix):
Access native database columns representing file metadata:
| Field | Type | Description |
|---|---|---|
file.path |
TEXT | File path relative to base-dir |
file.folder |
TEXT | Directory path relative to base-dir |
file.name |
TEXT | File name without extension |
file.ext |
TEXT | File extension |
file.size |
INTEGER | File size in bytes |
file.ctime |
TIMESTAMPTZ | Created time |
file.mtime |
TIMESTAMPTZ | Modified time |
file.tags |
VARCHAR[] | Tags from content (#tag) and frontmatter |
file.links |
VARCHAR[] | Wiki-links [[link]] + embeds ![[embed]] from body and frontmatter |
file.backlinks |
VARCHAR[] | Notes linking to this note (reverse of links) |
file.embeds |
VARCHAR[] | Embeds ![[embed]] from body only |
Note Properties (note.* prefix or bare):
Access YAML frontmatter fields:
---
title: My Note
author: John
status: in-progress
---
Query using explicit prefix or bare shorthand:
Tags
Tags are extracted from two sources:
Content tags (#tag in note body):
- Obsidian format:
#followed by alphanumeric characters, underscores, hyphens, and forward slashes - Must contain at least one non-numerical character (e.g.,
#1984is invalid,#y1984is valid) - Case-insensitive (e.g.,
#tagand#TAGare identical) - Supports nested tags using
/separator (e.g.,#project/2024/q1)
Frontmatter tags:
- YAML list format:
tags: [tag1, tag2]ortags: [project/2024]
All tags are merged into file.tags and can be queried with list_contains(file.tags, 'tag-name').
Field Resolution
| Syntax | Resolves To | Example |
|---|---|---|
file.* |
Native database column | file.name → name column |
note.* |
Frontmatter JSON extraction | note.author → properties->"author" |
| bare (no prefix) | Frontmatter JSON extraction (shorthand for note.*) |
author → properties->"author" |
The file.* and note.* namespaces are completely separate — no naming conflicts.
Name Uniqueness
Note names must be unique across the entire vault, regardless of their directory location.
- Index: When indexing, if two notes have the same name (different paths), a warning is shown and the duplicate is skipped
- Create: Creating a note fails if a note with that name already exists
- Rename: Renaming a note fails if a note with the target name already exists
Link Format (Obsidian Style)
Always use the filename only — no path, no extension:
[[中国移动]]
[[张三]]
[[entities/中国移动.md]]
[[people/张三]]
Wiki-links in frontmatter properties must additionally be wrapped in quotes:
# ✅ Correct
related_customer: "[[中石油]]"
attendees_internal:
# ❌ Wrong
related_customer:
attendees_internal:
Commands
index
Index Markdown notes to DuckDB.
Features:
- Incremental updates (skips unchanged files)
- Detects and removes deleted files
- Obsidian-compatible (wiki-links, embeds, frontmatter, tags)
query
Query indexed notes.
Two input modes:
# Expression mode (WHERE clause only)
# SQL mode (full SELECT statement)
Output formats:
Debug:
Type casts for non-string comparisons:
# or using bare shorthand:
note
Create and manage notes.
Create a note:
Rename a note:
Behavior:
- Looks up note by name (not path)
- Fails if name is ambiguous or new name exists
- Updates all
[[old-name]]links and![[old-name]]embeds across the vault (body and frontmatter) - Preserves aliases, section anchors, and block IDs
template
Manage MKS templates.
Templates are stored in templates/ under base-dir.
Query Syntax
markbase translates field names using explicit namespaces (file.* for file metadata, note.* or bare for frontmatter) to DuckDB queries. All DuckDB SQL keywords and operators are supported natively.
Commonly Used Functions:
list_contains(field, value)- Array containmentlist_contains(file.tags, 'todo')- file array field (native)list_contains(note.categories, 'work')- frontmatter array (cast to VARCHAR[])
Field Prefix Reference:
| Prefix | Namespace | Use For | Example |
|---|---|---|---|
file. |
File properties | Metadata columns | file.name, file.mtime, file.size |
note. |
Note properties | Frontmatter fields | note.author, note.status |
| (bare) | Note properties | Shorthand for note.* |
author, status |
Examples:
# File metadata queries (require file.* prefix)
# Frontmatter queries (note.* prefix or bare)
# Combined queries
License
MIT