A git-native, Markdown-only notes editor (Obsidian-style vault = a plain folder) with first-class Open Knowledge Format (OKF) support, plus a CLI and MCP server so local agents can work on the same vault with the same OKF rules.
Stack: Tauri 2 (Rust) · Vite + React 19 + TypeScript · shadcn/ui (Base UI) · Tailwind v4 · CodeMirror 6.
npm install
npm run tauri dev # desktop app
npm test && (cd src-tauri && cargo test) # unit tests
npm run build:cli # CLI / MCP server → dist-cli/notey.mjs
# UI-only in a browser:
npm run dev # in-memory demo vault, no git
NOTEY_VAULT=~/notes npm run dev # real folder + real git via a dev-only Vite bridgenpm run tauri build produces the .app/.dmg.
| Area | What you get |
|---|---|
| Vault & editor | Open any folder. File tree with git status colours and a context menu (new, rename, delete, rebuild index, history). CodeMirror Markdown editor with YAML-frontmatter highlighting and auto-save. Edit / split / preview modes; relative links in the preview open the target note. |
| Git | Branch switch and create, fetch / pull / push (push sets the upstream on first push), stage / unstage / discard per file, commit (⌘↵; commits everything if nothing is staged). Diffs for working copy and staged changes, unified or side by side (@codemirror/merge). |
| History | Per-file history that follows renames: "changes in this commit" or "compare with current", plus Restore this version. Repository history shows the changed files of each commit. |
| Conflicts | Merge a branch from the branch menu; pulls that conflict land here too. Each conflict hunk shows mine and theirs side by side with Keep mine / Take theirs / Both / Original (diff3) / Custom. Compare full versions shows git's stage-2 and stage-3 files. Mark resolved writes and stages the file; commit finishes the merge. Abort merge is supported. |
| OKF frontmatter | A Properties form replaces raw YAML for type (dropdown of OKF types plus types already used in the vault), title, description, status, stale_after, tags (chips), resource, generated, verified (Mark verified by me records human:<git email>) and sources. The trust tier is derived. Unknown keys and comments are preserved (yaml Document API). The preview renders a frontmatter card. |
| OKF bundle (no AI) | Initialize bundle: root index.md with okf_version: "0.2" and log.md. Rebuild index.md: per folder, grouped by type, sub-folders linked, descriptions reused. Hand-written text outside the <!-- okf:index:start/end --> markers is kept. Add log entry. Validate bundle: missing type, invalid status, stale concepts, frontmatter in non-root indexes, folders without index.md, broken links, non-ISO log headings. |
| Automation (toggles) | log.md gets Creation / Rename / Deletion entries, plus one Update per note per day (deduplicated). Folders that have an index.md are refreshed when a note in them is created, renamed, deleted, or changes its title, description, type or status. |
| Tabs & groups | VS Code behaviour. A single click on a note (tree, quick open, link) opens it in a preview tab (italic), and the next single click reuses that tab. Double-clicking the file or the tab, or editing the note, keeps it open. ⌘T (or + in the tab bar, or double-click on an empty tab bar) creates Untitled.md in the current note's folder in a new tab. Groups sit side by side: ⌘\ / the split button (Split right), dragging a tab onto another group's tab bar or onto the right third of an editor, or Move to new group in the tab menu. Up to 4 groups. ⌘W closes a tab (the macOS menu has no "Close Window" so ⌘W reaches the editor), ⌘⌥←/→ switches tabs, ⌘1–4 focuses a group, middle-click closes. Unsaved notes show a dot. The same note open twice shares one buffer. Tabs and groups are restored per vault. |
| Live preview | Obsidian-style: ## turns into a heading as you type. Bold, italic, code, |
| Attachments | Pasting or dropping images and other files (from the clipboard, or from Finder via Tauri drag & drop) stores them next to the note and inserts a relative link:  or [spec.pdf](spec.pdf). Clipboard screenshots get Obsidian-style names (Pasted image YYYYMMDD-HHMMSS.png) and names are deduplicated. Images render inline in the editor and the preview; images and PDFs open in a viewer; other files open in their default app (Open with default app / Reveal in Finder in the tree). |
| Search & tags | ⌘⇧F searches all notes (case-insensitive, results grouped per note with line snippets; Enter opens the first hit). #tag or tag:name lists the notes with that frontmatter tag; with an empty query the panel shows every tag with its count. Tag chips in Properties open that search. |
| Links | Typing [[ offers notes by title and inserts a standard Markdown link ([Title](../relative/path.md)), since OKF uses no wikilinks. Inside ]( every vault file's relative path is offered. Renaming or moving a note or folder rewrites every link pointing to it, plus the moved note's own relative links (code blocks are left alone; toggle in Settings → Files). The Outline tab (⌘⇧O) lists the note's headings and the notes linking to it; clicking either jumps to the line. |
| Outside the bundle | Top-level folders can be kept out of the OKF bundle (tree context menu Keep outside the bundle, or Settings → OKF bundle): no index.md, no log entries, no validation, not listed in the root index. Only root-level folders qualify, so a bundle never mixes checked and unchecked parts. The list is stored in .notey.json and committed with the vault. |
| .gitignore | Right-click a file or folder → Add to .gitignore (writes /path or /path/ to the root .gitignore; an already tracked file is untracked with git rm --cached, staged, file kept). Remove from .gitignore reverses it. Ignored paths show dimmed and italic in the tree and are outside the bundle as well: no index.md entries, log entries or validation. Settings → Git has a .gitignore editor with a syntax cheat sheet. |
| Terminal | ⌃** shows or hides a terminal below the editor, opened in the current note's folder (your login shell, $SHELL -l, in a real PTY via portable-pty). **⌃⇧ or + adds another one; Open in terminal in the tree's context menu starts one in that folder. Each terminal is a tab; shells keep running while the panel is hidden. In the browser dev mode (NOTEY_VAULT), scripts/devPty.py provides the PTY. |
| Settings | ⌘, opens sections for General (theme, vault), Editor (live preview, auto-save), Files, OKF bundle (automatic log.md / index.md), Git (add, edit and remove remotes, edit .gitignore, or initialize a repository) and Shortcuts. |
| File tree | index.md and log.md are shown dimmed; the eye button in the Explorer header (or Settings → Files) hides them, and a folder's index stays reachable via Open index.md in its context menu. Drag a file or folder onto another folder (or the empty area for the vault root) to move it; links are updated. New note from template (context menu or ⌘K) starts a note with sections for its OKF type, e.g. Decision, Meeting Note or Playbook. |
| Editing | ⌘B bold, ⌘I italic, ⌘⇧X strikethrough, ⌘⇧C inline code, ⌘⇧K link. Pasting a URL over selected text makes it a link. In tables, Tab / ⇧Tab move between cells and realign the columns, and Tab in the last cell adds a row; Insert table is in ⌘K. The status bar shows words and reading time. |
| File watcher | The desktop app watches the vault, so changes from outside (git pull in a terminal, other editors, agents) show up immediately: tree, git status and unmodified open notes. Changes inside .git (commits, checkouts) refresh the git panel. |
| Commands | ⌘K palette (quick open + every command). Shortcuts: ⌘N new note, ⌘E edit/preview, ⌘\ split, ⌘⇧H history, ⌘⇧G git, ⌘⇧I rebuild index, ⌘⇧L log entry, ⌘⇧V validate, F2 rename, ⌘O open vault. |
src/ React UI
lib/backend.ts typed Tauri commands (+ browser demo mock / dev bridge)
lib/store.ts zustand app state; OKF automation hooks into save/create/rename/delete
lib/okf.ts index.md / log.md generation, validation, link resolution (pure, tested)
lib/frontmatter.ts lossless frontmatter edits
lib/conflict.ts conflict-marker parsing / resolution (pure, tested)
lib/gitparse.ts porcelain status + log parsing (pure, tested)
lib/links.ts link rewriting on rename, backlinks, outline, search, tags (pure, tested)
lib/table.ts Markdown table formatting (pure, tested)
lib/vault.ts cached text of all notes for search / backlinks / completion
src-tauri/src/
fsops.rs vault-sandboxed file ops (no path escapes)
git.rs `git` CLI wrapper with a subcommand allow-list (uses your credentials, hooks, config)
watch.rs vault file watcher (`notify`), emits `vault://changed`
term.rs integrated terminal (`portable-pty`), emits `term://data` / `term://exit`
scripts/devBridge.ts dev-only Node implementation of the same commands
cli/vault.ts Node vault ops for the CLI / MCP (reuses lib/okf.ts + lib/links.ts)
cli/mcp.ts stdio MCP server (JSON-RPC, no SDK dependency)
cli/main.ts `notey` command line
Why the git CLI instead of libgit2: it reuses the user's credential helpers, SSH agent, hooks and merge config. No auth UI is needed.
notey gives scripts and local agents (Claude Code, Codex, Cursor, …) the same vault operations as the app:
validation, index.md / log.md maintenance, link rewriting on rename. Writes go through the same automation, so
log.md and folder indexes stay in sync no matter who edits the vault. It is a single Node script (Node 20+) with
no runtime dependencies.
npm run build:cli && npm link # puts `notey` on your PATH
# or grab notey.mjs from a GitHub release and run `node notey.mjs`
cd ~/notes
notey validate # exit 1 on errors (--strict: also on warnings), --json for agents
notey new decisions/use-okf --type Decision --title "Use OKF"
notey index --all # rebuild every index.md
notey rename guide.md guides/start.md # moves the note, rewrites links, logs it
notey search "#onboarding"
cat note.md | notey write concepts/note.md
notey log "Imported the old wiki"| Command | What it does |
|---|---|
validate [--strict] |
OKF bundle check, same rules as the app |
list [dir] · read <path> · search <query> · backlinks <path> |
Read-only queries |
new <path> [--type T] [--title T] |
New note from the template of an OKF type (notey types) |
write <path> |
Write a note from stdin |
rename <from> <to> [--no-links] |
Rename / move a note or folder and rewrite links |
index [dir] [--all] · log <text> [--kind K] |
index.md / log.md maintenance |
mcp |
MCP server over stdio |
Global options: --vault <dir> (default $NOTEY_VAULT or the current directory), --json, --no-log, --no-index.
Folders excluded in .notey.json and paths ignored by git are outside the bundle, as in the app.
notey mcp --vault <dir> serves the tools validate_bundle, list_notes, read_note, search_notes, backlinks,
create_note, write_note, rename_note, rebuild_index and append_log.
claude mcp add notes -- notey mcp --vault ~/notes # Claude Code{ "mcpServers": { "notes": { "command": "notey", "args": ["mcp", "--vault", "/Users/me/notes"] } } }Paths are vault-relative and can't escape the vault. The app's file watcher picks up changes made through the CLI immediately.
- The browser dev mode has no file watcher; there the tree, git status and open notes refresh on window focus.
- Wikilinks (
[[...]]) are intentionally not stored, because OKF specifies standard Markdown links;[[only triggers completion. - Rebase, stash UI, and blame are not implemented yet.