A local-first, AI-native CLI for card-based writing.
mf treats your knowledge base as a codebase. Articles are assembled from
composable Blocks, every piece of state lives in plain files on disk, and the
CLI is shaped so both humans and Agents can drive it.
Three ideas guide every decision in mf:
Knowledge is meant to spread. Capture it once as a Block, then let it diffuse — through articles, glossary terms, builds, and downstream publishers like Yuque or static sites. The same atomic unit can land in a report today and a paper tomorrow, without copy-paste drift.
flowchart LR
subgraph Capture
B((Block))
S[Source]
T[Term]
end
subgraph Compose
A1[Article: Report]
A2[Article: Paper]
end
subgraph Ship
P1[Publisher: Yuque]
P2[Publisher: Local]
end
B --> A1
B --> A2
S --> A1
T --> A2
A1 --> P1
A1 --> P2
Your writing follows the same discipline as your infrastructure:
declarative YAML configs (minds.yaml, mind.yaml, mind-index.yaml),
schema validation, deterministic builds, and full git auditability.
If you can review a PR, you can review a chapter.
mf is designed first for AI Agents, not for human terminal sessions.
Every command speaks a JSON envelope ({ status, command, data }), exits
with stable codes, and produces deterministic output contracts.
Build a pipeline with shell, Make, or an LLM — the contract is the same.
This is an independent philosophy, not a subset of DaC: AI Native CLI rejects interactive prompts, colored output designed for human eyes, and inconsistent exit codes. The tool is a reliable API for an LLM to call.
Local-first underpins all three: no cloud, no lock-in, plain markdown and YAML you can edit in any editor.
Requires Rust 1.75+.
git clone https://github.com/alswl/mind-forge.git
cd mind-forge
cargo install --path .Or run from source while iterating:
cargo run -- --helpShell completion:
mf completion zsh # or bash | fish | powershell | elvish# 1. Create a Mind Repo
mkdir my-repo && cd my-repo
mf init # creates minds.yaml + projects/
# 2. Create a project (path-based identity, Unicode/emoji/dates supported)
mf project new blog
mf project new workspaces/team/projects/2026-W21
# 3. Create an article
mf article new "First Post" --project blog
# 4. Add sources, assets, and terms
mf source new https://example.com/ref --file-kind web --project blog
mf asset new diagram.png --project blog
mf term new "Zettelkasten" --definition "A note-taking method" --project blog
# 5. Index, build, and publish
mf article index --project blog
mf build "First Post" --project blog
mf publish run "First Post" --target local --project blogSee docs/manual.md for the full user manual, including repo layout, shared CLI contracts, scripting patterns, and end-to-end workflows for projects, articles, sources, assets, terms, builds, publishing, templates, and configuration.
| Concept | What it is |
|---|---|
| Mind Repo | A directory rooted at minds.yaml. The outermost unit of organization. |
| Project | A subdirectory with mind.yaml. Default layout: docs/, sources/, assets/, outputs/. |
| Article | A document — either a single Markdown file or a directory of ordered files. |
| Block | An atomic, reusable unit of content composed into articles. |
| Source | An external reference (web page, PDF, RSS feed, file) tracked per project. |
| Asset | A binary or non-text resource attached to a project. |
| Index | mind-index.yaml per project — the source of truth for everything above. |
| Publisher | A target (e.g. local, yuque-prompt) that ships built output somewhere. |
All on-disk YAML follows the mind 0.3.0 format (schema: "1"), so repos move
freely between mf and other mind-compatible tools.
How the pieces fit on disk:
flowchart TD
Repo[Mind Repo<br/><code>minds.yaml</code>]
Repo --> P1[Project: blog<br/><code>mind.yaml</code>]
Repo --> P2[Project: papers<br/><code>mind.yaml</code>]
P1 --> Docs[docs/]
P1 --> Sources[sources/]
P1 --> Assets[assets/]
P1 --> Outputs[outputs/]
P1 --> Idx[<code>mind-index.yaml</code>]
Docs --> A1[essay.md<br/>file article]
Docs --> A2[2026-review/<br/>directory article]
A2 --> A2a[01-intro.md]
A2 --> A2b[02-details.md]
A typical loop:
flowchart LR
C[Capture<br/><code>mf source</code><br/><code>mf asset</code><br/><code>mf term</code>]
D[Draft<br/><code>mf article new</code>]
I[Index<br/><code>mf … index</code><br/><code>mf project lint --fix</code>]
B[Build<br/><code>mf build</code>]
P[Publish<br/><code>mf publish run</code>]
C --> D --> I --> B --> P
P -. new insights .-> C
- Capture —
mf source newandmf asset newpull raw material into a project.mf term newrecords vocabulary. - Draft —
mf article new <TYPE> <TITLE> [--template <S>] [--file]scaffolds a directory article (default) or single file (--file) underdocs/. The default template isblank;--template arch|prd|blogselects another built-in scaffold, and--template <path>reads a project-local Markdown template. New articles automatically get Typora front matter (typora-copy-images-to) pointing to the project assets directory (disable withplugins.typora-front-matter.enabled: falseinmind.yaml). Edit in any Markdown editor. - Index —
mf source index,mf article index, andmf project lint --fixreconcilemind-index.yamlwith the filesystem. - Build —
mf build <article>assembles output (directory articles merge their files in filename order) intooutputs/<article>.md. - Ship —
mf publish run … --target <publisher>pushes to a configured target.
Every step is idempotent and pipe-friendly. Pass --json to any command to
get a machine-readable envelope.
These flags are available on most commands:
| Flag | Description |
|---|---|
--root <PATH> |
Mind Repo root directory |
--config <PATH> |
Config file path |
-p, --project <PROJECT> |
Project selector for project-scoped commands |
-v, --verbose... |
Verbose output (repeatable) |
-q, --quiet |
Silence non-error output |
-o, --output <text|json> |
Output format (default: text) |
--json |
Shorthand for --output json |
--no-color |
Disable colored output |
-h, --help |
Show help |
-V, --version |
Show version |
Shared flag families (uniform across all commands they apply to):
| Flag family | Applies to | Description |
|---|---|---|
--dry-run |
every mutating command (new, rename, remove, archive, update, index, lint --fix) |
Preview without writing |
-f, --force |
every new/rename/remove/archive |
Proceed despite safety checks: overwrite an existing target, or remove an entity referenced by others |
-y, --yes |
every remove and archive |
Confirm destructive action non-interactively |
--no-headers, --no-trunc |
every list |
Suppress table header / disable column truncation |
--fix, --rule <RULE>, --severity <LEVEL>, --max-warnings <N> |
every lint |
Auto-fix, restrict rule, filter severity, fail on warnings > N |
--project is available on project-scoped commands (article, asset,
source, term, build, publish run, etc.) and accepts repo-relative
paths or project names. When running inside a project directory,
--project can be omitted — the CLI auto-detects the current project.
When run outside a project directory without --project, mf article list
automatically matches all projects and sorts articles by most recently
modified.
Bootstrap a directory as a Mind Repo (creates minds.yaml and the
default projects/ container). Defaults to the current directory.
| Subcommand | Description |
|---|---|
new <PATH> |
Create a project. Accepts cwd-relative or repo-relative paths with Unicode, emoji, dates, spaces. --template <TEMPLATE> |
list (ls) |
List projects |
show <PATH> |
Show project details |
update <PATH> |
Update project metadata. --description <TEXT>, --clear-description |
rename <OLD_PATH> <NEW_PATH> |
Rename a project |
remove <PATH> (rm) |
Remove a project (interactive confirmation in TTY) |
archive <NAME_OR_PATH> |
Archive a project to _archived/ (interactive confirmation in TTY) |
lint |
Lint project(s). Rules: missing_directory, stale_index_entry, name_convention, missing_manifest, duplicate_key. Requires -p, --project <PROJECT> |
index |
Index projects (mf extension) |
import <DIRECTORY> |
Import a directory as a project. --type <TYPE>, --source <DIR>, --assets <DIR>, -y, --yes |
| Subcommand | Description |
|---|---|
new <TITLE> |
Create an article. -t, --template blank|arch|prd|blog|<path>, --file, --tag <TAG>, --draft |
list (ls) |
List articles. Omitting --project outside a project dir lists all articles across all projects, sorted by most recently modified. |
show <PATH> |
Show article details |
update <PATH> |
Update article metadata. --status draft|published |
rename <OLD_PATH> <NEW_PATH> |
Rename an article |
remove <PATH> (rm) |
Remove an article (interactive confirmation in TTY) |
lint |
Lint articles |
convert |
Convert article shape between directory and single-file. --to-single-file, --to-directory, --dry-run |
index |
Index articles (mf extension). --dry-run previews without writing |
| Subcommand | Description |
|---|---|
new <INPUT> |
Add a source. -n, --name <NAME>, --file-kind auto|pdf|file|rss|web, --source-kind yuque|meeting|misc, --link |
list (ls) |
List sources. --filter <PATTERN>, -t, --type <KIND> |
show <PATH> |
Show source details |
update <PATH> |
Update a source (mf extension). --url <URL>, --rename <NAME> |
rename <OLD_PATH> <NEW_PATH> |
Rename a source |
remove <NAME_OR_PATH> (rm) |
Remove a source. --keep-file |
index |
Index sources (mf extension) |
clean |
Clean stale index entries |
| Subcommand | Description |
|---|---|
new <PATH> |
Add an asset. --name <NAME>, --tag <TAG>, --copy/--link |
list (ls) |
List assets. --filter <PATTERN>, --type image|video|audio|other |
show <PATH> |
Show asset details |
update [PATH] |
Update assets. --set-url <URL>, --channel <CHANNEL>, --all |
rename <OLD_PATH> <NEW_PATH> |
Rename an asset |
remove <PATH> (rm) |
Remove an asset |
index |
Index assets (mf extension). --refresh-metadata |
clean |
Clean stale index entries |
| Subcommand | Description |
|---|---|
new <TERM> |
Create a term (mf extension). --definition <TEXT>, --description <TEXT>, --confidence <N>, --alias <TEXT>, --tag <TAG>, --misrecognition <TEXT> |
list (ls) |
List terms. --filter <PATTERN> matches term name (substring); --tag <TAG> / --alias <ALIAS> match their respective fields. --has-correction, --scope project|global|all (AND semantics; default merges project + global fallback) |
show <TERM> |
Show term details |
update <TERM> |
Update term metadata and corrections. --definition <TEXT>, --description <TEXT>, --confidence <N>, --alias <TEXT>, --tag <TAG>, --clear-description, --clear-confidence, --delete-alias <TEXT>, --delete-tag <TAG>, --add-correction <ORIGINAL> (repeatable), --correction-match <ORIGINAL:KIND>, --correction-fix <ORIGINAL:KIND>, --correction-pinyin <ORIGINAL:PINYIN>, --delete-correction <ORIGINAL> (repeatable), --dry-run |
correction <SUBCOMMAND> |
Manage corrections as a first-class subresource: add <TERM> <ORIGINAL> <CORRECT> (idempotent; reports created: true|false; --match, --fix, --boundary, --pinyin), list <TERM>, show <TERM> <ORIGINAL>, update <TERM> <ORIGINAL>, remove <TERM> <ORIGINAL> (--dry-run) |
move <TERM> (mv) |
Move a term between scopes. --to-global, --to-project <PROJECT>, --from-global, --force (overwrite destination; source preserved on rejection), --dry-run |
rename <OLD_TERM> <NEW_TERM> |
Rename a term. --keep-alias keeps the old name as an alias |
remove <TERM> (rm) |
Remove a term (interactive confirmation in TTY) |
lint [PATH] |
Lint term consistency in project docs. CJK matching uses jieba word segmentation (word default — both edges must align with token boundaries); substring bypasses jieba; pinyin for tone-less scan. --fix, --term <NAME> (repeatable), --include-suggested, --article <SLUG> or a Markdown PATH to target a single article/file. Non-TTY --fix exits 2 without -y/--yes. |
fix [PATH] |
First-class alias for term lint --fix. Same flags as lint + --include-suggested for suggested corrections. Accepts repeatable --term <NAME> to scope to one or more terms (case-sensitive exact canonical match; unknown → exit 2). |
Global terms (created without --project) are stored in minds-terms.yaml at
the repo root. Project-scoped terms live in each project's mind-index.yaml.
Corrections follow two paths. mf term lint/fix deterministically applies the
declared glossary corrections — the closed-set, recurring domain terms — to
project docs under guardrails (no edit inside a protected term occurrence,
declared-correction precedence, non-overlapping edits, atomic write after
diff/confirm). Open-domain ASR errors that no fixed list can enumerate are
corrected by the driving agent, then persisted via mf term correction add once
they recur. mf owns the deterministic guardrails; the agent owns the
open-domain judgment.
-o, --output <PATH>, --dry-run. ARTICLE may be an indexed name/slug or
a repo-relative path prefixed with @ (e.g. @projects/blog/docs/2026-03-review/).
Directory articles are built by merging Markdown files in filename order. Relative
image/link/reference paths are automatically rewritten to resolve from the output
directory; paths inside fenced code blocks, absolute paths, and URLs are left unchanged.
| Subcommand | Description |
|---|---|
run <ARTICLE> |
Publish to a target (supported: local, yuque-prompt). --target <TARGET> (optional when publish.default_target is configured). File-based publishers discovered for both explicit and default targets. Local publishers honor config.prefix. |
update <ARTICLE> |
Update a publish record. --target <TARGET> (required), --status draft|published|archived, --target-url <URL>, --set <KEY=VALUE> |
target list |
List publish targets and diagnostics |
target show <NAME> |
Show publish target details |
| Subcommand | Description |
|---|---|
list |
List built-in and project-local render templates |
show <NAME> |
Show template details + preview |
| Subcommand | Description |
|---|---|
schema |
Show config JSON schema. --output-format json|yaml (default: json) |
show |
Show effective config (canonical JSON envelope). --output-format json|yaml (default: yaml) |
generate |
Generate effective config file. --output-format json|yaml (default: yaml), -o, --output <PATH> |
default |
Show default config values. --output-format json|yaml (default: yaml) |
terminal |
Show terminal capability diagnostics (hyperlink support, color depth, terminfo probing). Respects TERM, COLORTERM, TERM_PROGRAM, NO_COLOR, MF_FORCE_HYPERLINKS, MF_NO_HYPERLINKS. |
Supported shells: bash, zsh, fish, powershell, elvish
Text output includes commit / build_date / rustc. JSON envelope adds
target_triple. Pass --json for the machine-readable form.
- Repo bootstrap —
mf init [PATH]createsminds.yamland.mind/ - Project lifecycle —
mf project new | list | show | update | rename | remove | archive | lint | index | import; path-based identity supports Unicode, emoji, dates, spaces - Project auto-detection — running inside a project directory auto-injects
--project;mf article listwithout--projectoutside a project dir auto-matches all projects, sorted by most recently modified; cwd-relative paths normalized to repo-relative canonical identity - Article management —
mf article new | list | show | update | rename | remove | lint | index; directory articles by default,--filefor single-file shape;--template blank|arch|prd|blogor custom project-local template path - Sources —
mf source new | list | show | update | rename | remove | index | clean;--file-kind auto|pdf|file|rss|web,--source-kind yuque|meeting|misc - Assets —
mf asset new | list | show | update | rename | remove | index | clean;--copy/--linkfor copy vs symlink - Glossary —
mf term new | list | show | update | correction | move | rename | remove | lint; global terms inminds-terms.yaml, project-scoped inmind-index.yaml - Build — config-driven assembly, directory-article merging,
--dry-run,--output, and@path/-style article addressing - Publish —
mf publish run | update | target list | target showagainst per-target publishers (local,yuque-prompt); project-level local targets resolve relative paths from project root - Render templates —
mf render template list | showcovers built-in and project-local templates - Config —
mf config schema | show | generate | default; centralized defaults fordocs/,sources/,assets/,_archived/, andoutputs/ - Plugins —
mind.yamlsupports apluginsblock for forward-compatible plugin configuration; thetypora-front-matterplugin is enabled by default and injectstypora-copy-images-tofront matter into new articles - Compatibility — reads and writes mind 0.3.0 YAML; tolerates older
schema_versionand list-based shapes on read - Shell completion —
mf completion <SHELL>for bash, zsh, fish, powershell, elvish - Version —
mf versionincludes commit / build_date / rustc / target_triple - Terminal intelligence —
mf config terminalfor capability diagnostics; automatic OSC 8 hyperlink rendering in list/show/verb outputs when supported; broad terminal emulator detection (iTerm2, Ghostty, Kitty, VS Code, Warp, Terminal.app, tmux, WezTerm, etc.) plusMF_FORCE_HYPERLINKS/MF_NO_HYPERLINKSoverrides; file:// URI encoding for paths with spaces or Unicode - Output contracts —
textby default,--jsonfor{ status, command, data }envelopes; canonical per-verb shapes; identity round-trip between list and show; remove/archive use a TTY confirmation protocol; stable exit codes
Every mf command adheres to shared text-layout and JSON-envelope contracts. These are documented in the feature specification:
| Contract | Description |
|---|---|
| List layout | Unified table format for all mf <noun> list commands |
| Show layout | Unified key-value block format for all mf <noun> show commands |
| Verb envelopes | Per-verb JSON shapes (create, rename, remove, update, index, lint) |
| Flag conventions | Required flags every command must accept |
| Confirmation protocol | TTY-only interactive prompt for destructive verbs |
Key rules:
datais always a JSON object — no bare arrays, strings, ornull- Text output adapts to TTY (headers + ANSI) vs pipe (no headers, no ANSI, same row shape)
- Every resource carries an
identityfield that round-trips between list and show --dry-runis available on every mutating command- Remove and archive require confirmation in TTY; non-TTY exits 1 without
--yes/--force
See specs/ for detailed specifications and the feature evolution plan.