Notes apps with a CLI: ten terminal note tools compared
A notes app with a CLI, compared: nb, zk, iwe, jrnl, dnote, Joplin, the Obsidian CLI, NotesMD, Basic Memory and Jotura, with platforms and JSON output.
The right notes app with a command line interface (CLI) depends on who else will touch your notes. For a terminal-only setup, nb keeps every notebook in git, and zk or iwe give Neovim and VS Code a language server over plain Markdown files. jrnl suits a dated journal, and dnote a stash of snippets in one SQLite file.
Jotura fits best for notes that a desktop app shares with your scripts or AI agents. Its jotura command edits the same Markdown folder as the desktop app, open or closed, and can refuse a write when the note changed since the script read it. Joplin’s terminal app works on the same synced notebooks as the Joplin apps, and the official Obsidian CLI drives a running copy of Obsidian. Basic Memory is an MCP server for AI assistants that also answers on the command line.
Every fact below was checked against each project’s documentation and release page on October 10, 2026. The nb, zk, iwe, jrnl, dnote, Joplin and Jotura commands were run that day on Windows 11 (nb under Git Bash), and the outputs shown are what they printed. The Obsidian CLI, NotesMD CLI and Basic Memory examples come from their official docs.
The comparison table
| Tool | Platforms | Where notes live | Search | Sync | Latest release |
|---|---|---|---|---|---|
| nb | macOS, Linux; Windows through WSL, MSYS or Cygwin | Plain text files, Markdown by default; each notebook is a git repository | Full text with regex, tags, AND, OR, NOT | Any git remote, automatic once set | 7.25.5, Aug 12, 2026 |
| zk | macOS, Linux, Windows | Markdown files plus a SQLite index in .zk/ |
Full text, tags, link filters | None built in | v0.15.6, Jul 26, 2026 |
| iwe | macOS, Linux, Windows | Markdown files in a folder | Full text, fuzzy, link graph | None built in | 0.26.1, Oct 8, 2026 |
| jrnl | macOS, Linux, Windows (Python 3.11 or later) | One text file, or a folder of dated files | Text, tag and date filters | None built in | v4.6, Aug 7, 2026 |
| dnote | macOS, Linux, FreeBSD, Windows | One SQLite file | Full text | A Dnote server you host | CLI 0.16.0, Nov 8, 2025 |
| Joplin terminal app | macOS, Linux; Windows through WSL | Markdown notes inside a SQLite database | In the terminal interface only | Joplin Cloud, Joplin Server, Nextcloud, WebDAV, S3, Dropbox, OneDrive, a folder | 3.7.1 on npm, Sep 7, 2026 |
| Obsidian CLI | Windows, macOS, Linux, with the app running | Markdown files in a folder | Through the running app | Obsidian Sync or any file sync | Obsidian 1.14.4, Oct 5, 2026 |
| NotesMD CLI | Windows, macOS, Linux | An Obsidian vault’s Markdown files | Full text | None built in | v0.3.7, Sep 1, 2026 |
| Basic Memory | Anywhere Python 3.12 runs, through uv | Markdown files plus a local SQLite index | Text, vector or hybrid | Basic Memory Cloud, or git yourself | v0.23.2, Aug 25, 2026 |
| Jotura | macOS, Windows, Linux; the Android app reaches notes only through Sync | Markdown files in a folder | Full text; meaning-based modes query the desktop’s index | Jotura Sync, end-to-end encrypted, run by the desktop and Android apps | 0.11.1, Oct 6, 2026 |
| Tool | Install | JSON output | Editor integration | License and cost |
|---|---|---|---|---|
| nb | brew install xwmx/taps/nb or npm install -g nb.sh |
None | Opens notes in $EDITOR; nb browse |
AGPL-3.0, free |
| zk | brew install zk or a release binary |
--format json and jsonl |
Language server; Neovim and VS Code plugins | GPL-3.0, free |
| iwe | brew install iwe-org/iwe/iwe or npm install -g @iwe-org/iwe |
-f json on find |
Language server for Neovim, Helix, VS Code, Zed | Apache-2.0, free |
| jrnl | pipx install jrnl |
--format json |
Opens entries in your editor | GPL-3.0, free |
| dnote | brew install dnote or the getdnote.com script |
None | Opens $EDITOR when you omit -c |
Apache-2.0, free |
| Joplin terminal app | npm install -g joplin |
ls --format json |
External editor setting | AGPL-3.0-or-later; Joplin Cloud from €2.40 a month billed yearly |
| Obsidian CLI | A toggle in Settings, General (installer 1.12.7 or later) | format=json on most listing commands |
It is the Obsidian app, plugins included | Proprietary, free; Sync from $4 a month billed annually |
| NotesMD CLI | brew install yakitrak/yakitrak/notesmd-cli, or Scoop |
--format json on content search |
--editor opens $EDITOR |
MIT, free |
| Basic Memory | uv tool install basic-memory --prerelease=allow |
--json on search, read and context commands |
Any MCP client; Obsidian reads the same files | AGPL-3.0, free locally; Cloud $15 a month in beta |
| Jotura | The desktop installer, or the script on the download page | Global --json flag; errors always JSON |
The Jotura desktop app; any editor sees plain files | Closed source, free; Sync £4 or $5 a month |
nb
nb is a single Bash script for notes, bookmarks, to-dos and web archives. Each notebook is a git repository, and nb commits every add, edit and delete in the background. Once a notebook has a remote (nb remote set), commands in it sync automatically, at most once every NB_AUTO_SYNC_SECONDS (60 by default), and nb sync forces one. A line changed on two machines ends up with both versions between git conflict markers.
nb add --title "Printer quote" --content "Quote came in at 4,200. See [[Vendors]]. #invoice"
# Added: [1] printer_quote.md "Printer quote"
nb search "#invoice" --list
# [1] Printer quote
nb show 1 --print
nb remote set https://github.com/you/notes.git
After two adds, git log showed [nb] Add: vendors.md and [nb] Add: printer_quote.md with no commit typed by hand. nb reads wikilinks and #tags, and --encrypt protects a single note. It runs wherever Bash and git do, and Git Bash on Windows 11 worked for this guide. Scripts have to parse text, since nb has no JSON output.
zk
zk is a plain text note-taking assistant for a Zettelkasten or personal wiki, with no editor of its own. Notes are Markdown files with wikilinks, tags and YAML frontmatter, indexed in a SQLite file under .zk/.
zk init notes && cd notes
echo "Quote came in at 4,200. #invoice" | zk new --title "Printer quote" --interactive --print-path
zk list --match printer --format json
zk list --tag invoice --format "{{title}} {{path}}"
--interactive makes zk new read the body from standard input, and new files get a short random name such as 8798.md unless you set a pattern. Each JSON result carries title, path, lead, body, tags, dates and a checksum. zk list also filters by links, finding orphans, notes that link to a given note, and broken links. zk graph --format json exports the whole graph.
zk lsp runs a language server, giving Neovim, VS Code, Emacs and other LSP editors link completion and navigation. Sync is left to git or a file sync tool. On Windows, the README warns that Defender has flagged some prebuilt binaries and recommends building from source.
iwe
iwe describes itself as a Markdown knowledge graph: a language server for your editor, plus a CLI and an MCP server for agents, over a plain folder of notes. A native app, iWe for Mac, opens the same folder.
iwe init
iwe find --lexical "4,200" -f json
iwe rename vendors suppliers
# Renaming 'vendors' to 'suppliers'
# Updated 1 document(s)
find returned each matching note’s key and title with its outgoing and incoming references. rename rewrote the Markdown link in the note that pointed at the old name. The language server (iwes) covers Neovim, Helix, VS Code and Zed, iwec is the MCP server, and there is no sync.
jrnl
jrnl writes timestamped journal entries from the command line. It reads plain-English dates, so jrnl yesterday: files an entry under the previous day, and words starting with @ become tags.
jrnl yesterday: Finished the @proposal draft.
jrnl Call with the printer about @invoice 4471
jrnl -contains printer
jrnl --format json
The JSON export returned a tags count and an entries array, each entry with title, body, date, time, tags and starred. A journal is one text file or a folder of dated files. Single-file journals can be AES-encrypted, and the docs warn that nobody can recover a lost password; folder journals cannot be encrypted. Every record is a dated entry with no links between entries, and nothing syncs beyond whatever moves the file.
dnote
dnote is a single Go binary for short notes filed into books, handy for commands and how-tos. Everything lives in one SQLite file. The project dates from 2017 and is quiet now: the last CLI release, 0.16.0, shipped on November 8, 2025, and later commits are dependency updates.
dnote add linux -c "find . -name '*.log' -mtime +7 -delete"
dnote add git -c "git commit --fixup HEAD~2, then git rebase -i --autosquash"
dnote view
# git (1)
# linux (1)
dnote find "rebase"
Leave out -c and dnote opens your editor, or pipe text in on standard input. Sync needs a Dnote server you host yourself, a single binary or Docker image, and dnote login. There is no JSON output, though dnote view <book> <id> --content-only prints a note’s text for a script.
Joplin’s terminal app
Joplin is a full notes app with desktop, mobile and web clients, and its terminal app reads and writes the same notebooks. Run joplin alone for a three-pane interface inside the terminal, or pass a command for one-shot use. It syncs to more targets than anything else here, and joplin e2ee manages end-to-end encryption.
NPM_CONFIG_PREFIX=~/.joplin-bin npm install -g joplin
joplin mkbook "Work"
joplin use "Work"
joplin mknote "Printer quote"
joplin set "Printer quote" body "Quote came in at 4,200."
joplin ls --format json
ls --format json returns each note’s id, title, notebook id and timestamps, and batch runs a file of commands in order. In shell mode, joplin search replies that the command only works in GUI mode, meaning the terminal interface, so scripts cannot search. Notes are Markdown stored inside a SQLite database in the profile folder, and other tools see them as files only after joplin export with --format md.
Joplin fits anyone who needs one open-source app on every platform, iPhone included. The docs list macOS, Linux and Windows through WSL, and the commands above also ran in a native Windows terminal. Joplin desktop 3.7 added an MCP server for AI clients, and Joplin Cloud starts at €2.40 a month billed yearly after a 14-day trial. Jotura vs Joplin compares the two apps.
The official Obsidian CLI
Obsidian added a command line tool in version 1.12, and it needs the 1.12.7 or later installer. Turn on Command line interface under Settings, General, and Obsidian registers an obsidian command on your PATH. Parameters use name=value syntax, and obsidian alone opens an interactive prompt with autocomplete and history.
obsidian search query="printer quote" format=json
obsidian create name="Printer quote" content="Quote came in at 4,200." template=Vendor
obsidian daily:append content="- [ ] Call the printer"
obsidian vault=Work tasks todo format=json
obsidian rename file="Printer quote" name="Printer quote 2026"
Your plugins, templates and settings apply because the CLI acts through the app, and no other tool here has a plugin ecosystem to match. move and rename update internal links when Automatically update internal links is on. Most listing commands, including search, tags, tasks, backlinks and properties, take format=json.
The CLI requires Obsidian to be running, and the first command launches it if it is closed. A headless server, or a job that runs while you are logged out, needs something else.
Obsidian Headless, in open beta, fills part of that gap. Installed with npm install -g obsidian-headless (Node.js 22 or later), its ob command syncs a vault through Obsidian Sync without the app, with no note editing.
Obsidian is free for any use. Sync Standard costs $4 a month billed annually, or $5 monthly, for one vault and 1 GB. Sync Plus costs $8 or $10 for ten vaults and 10 GB to 100 GB.
NotesMD CLI for vaults without the app
NotesMD CLI, formerly the unofficial obsidian-cli, works on an Obsidian vault’s files directly, so Obsidian never needs to run. It took the new name when the official CLI arrived. On a server without Obsidian you register the vault yourself, and daily notes follow .obsidian/daily-notes.json when it exists.
notesmd-cli add-vault /home/me/vaults/work --set-default
notesmd-cli search-content "printer" --format json
Basic Memory
Basic Memory is an MCP server (Model Context Protocol, the standard AI assistants use to call tools) that stores what your assistant learns as Markdown files you can edit too. A local SQLite index handles text, vector and hybrid search. Its bm tool commands give the shell the same note operations, and search-notes, read-note, build-context and recent-activity take --json.
uv tool install basic-memory --prerelease=allow
bm tool write-note --title "Printer quote" --folder vendors --content "Quote came in at 4,200."
bm tool search-notes "printer" --json
Notes use a format of observations and relations that an assistant walks with bm tool build-context. bm tool edit-note supports append, find_replace and replace_section, and find_replace takes an --expected-replacements count that defaults to 1.
Local use is free under AGPL-3.0. Basic Memory Cloud adds sync, mobile access and backups at a beta price of $15 a month, locked in for life, against a regular $19, with a 7-day trial. By default the CLI checks for updates daily and applies them silently, and anonymous promotion telemetry runs until you set BASIC_MEMORY_NO_PROMOS=1.
Jotura
Jotura is a local-first Markdown notes app for macOS, Windows, Linux and Android. The Windows and .deb installers put its jotura command on your PATH, and on macOS and the AppImage one click in Settings does it. The vault is an ordinary folder of .md files, an Obsidian vault opens as it is, and the CLI works with the app closed. When the CLI writes, the open app’s file tree, search and tabs refresh within about a second.
In this run against a test note, TODO appears twice and the last edit reuses a hash from before the second edit:
jotura edit "Projects/Printer quote.md" --replace "TODO" --with "DONE"
# exit 4, on stderr:
# {"code":"Ambiguous","message":"ambiguous match for `TODO`: 2 occurrences","lineHits":[3,4],"needle":"TODO","occurrences":2}
HASH=$(jotura read "Projects/Printer quote.md" --json | jq -r .hash) # assumes jq
jotura edit "Projects/Printer quote.md" --replace "Status: TODO" --with "Status: ordered" --if-hash "$HASH"
# exit 0
jotura edit "Projects/Printer quote.md" --replace "4,200" --with "4,350" --if-hash "$HASH"
# exit 2, on stderr:
# {"code":"HashConflict","message":"hash conflict: expected b76df3463af77a054dacb4884beb978c, actual 66875304ec316f6b6081d6fd10a005fd","actual":"66875304ec316f6b6081d6fd10a005fd","actualHash":"66875304ec316f6b6081d6fd10a005fd","expected":"b76df3463af77a054dacb4884beb978c","expectedHash":"b76df3463af77a054dacb4884beb978c","path":"Projects/Printer quote.md"}
A replace that matches more than once is refused with its line numbers, and --all or --nth 2 makes the choice explicit. With --if-hash, a write is refused when the file on disk changed after the script read it, and the CLI and desktop app share a per-vault write lock around that check. A script that skips --if-hash overwrites like any other tool, and text the app has not yet saved is not on disk to protect.
Edit commands leave frontmatter alone, and frontmatter set and tag add change it. Errors are single-line JSON on stderr with stable exit codes. The editing docs and the CLI reference cover the rest, including today --append, tasks --json, backlinks and watch.
There is no standalone CLI download, and the CLI never syncs on its own, since the desktop and Android apps run Jotura Sync. The Android app reaches your notes only through Sync. There is no editor plugin, meaning-based search needs an index only the desktop builds, and Jotura is closed source with no plugins and no iOS app. The app and CLI are free to download, and Sync costs £4 or $5 a month, or £40 or $50 a year, with no free trial, close to Obsidian Sync’s price.
Which one fits
| You want | Best fit |
|---|---|
| A desktop app and scripts editing the same Markdown, with stale writes refused | Jotura |
| An AI coding agent reading and editing your notes from a shell | Jotura, with its agent skills; iwe and the Obsidian CLI also work |
| JSON from every command, errors included | Jotura; zk, iwe, jrnl, Joplin, the Obsidian CLI and Basic Memory on some commands |
| An Obsidian vault edited with Obsidian closed | Jotura, with link-aware renames; NotesMD CLI without installing an app |
| Obsidian’s plugins, scripted while the app is open | Obsidian CLI |
| Every change versioned in git, from one portable script | nb |
| Linked notes with completion inside Neovim, VS Code or Emacs | zk; Jotura has no editor plugin |
| A link-aware note graph shared by your editor and an agent | iwe |
| A dated journal with tags | jrnl |
| A quick stash of shell commands and snippets | dnote |
| Open source, on every platform including iPhone | Joplin; Jotura is closed source with no iOS app |
| Sync driven from the command line on a headless machine | nb or Joplin; Jotura’s CLI never syncs |
| A memory server for MCP-based AI assistants | Basic Memory; Jotura has no MCP server |
Jotura has no iOS app. Joplin reaches an iPhone with open-source apps and the widest choice of sync targets, and Obsidian does with plain files and plugins. Jotura vs Obsidian covers that decision in detail.
AI agents reach notes in one of two ways. Jotura, iwe and the Obsidian CLI give the agent a shell command, and Basic Memory and iwe’s iwec serve notes as MCP tools. CLI tools vs MCP servers for agents compares the two on cost and safety.
Frequently asked questions
Which terminal notes apps store plain Markdown files?
nb, zk, iwe, NotesMD CLI, the Obsidian CLI, Basic Memory and Jotura all keep notes as Markdown files in a folder that any other tool can read. zk and Basic Memory add a SQLite index beside the files. jrnl uses plain text files. Joplin and dnote store notes inside a SQLite database.
Which command line note-taking apps have built-in sync?
nb syncs through any git remote, and dnote through a server you host. Joplin syncs to Joplin Cloud, Joplin Server, Nextcloud, WebDAV, S3, Dropbox, OneDrive or a folder. Obsidian, Basic Memory and Jotura sell hosted sync, and Jotura’s runs in its desktop and Android apps, never in the CLI. zk, iwe, jrnl and NotesMD CLI leave sync to you.
Which notes CLI works best with AI agents?
Jotura fits best for an agent editing notes you also edit in a desktop app. With --if-hash it refuses a write to a note that changed after the agent read it, and its skills teach the agent to read before every edit. Any tool with plain files and non-interactive commands works with a coding agent, and zk, iwe, jrnl, the Obsidian CLI, NotesMD CLI and Basic Memory print JSON too. Basic Memory and iwe also offer MCP servers for assistants that connect that way.
Can I use the Obsidian CLI without Obsidian running?
No. The official Obsidian CLI requires the app to be running, and the first command launches it if it is closed. Obsidian Headless, in open beta, syncs a vault without the app but does not edit notes. NotesMD CLI, an independent MIT-licensed tool, reads and writes vault files directly with Obsidian closed.
Are these terminal note tools free?
nb, zk, iwe, jrnl, dnote, NotesMD CLI, Basic Memory and Joplin’s terminal app are free and open source. Obsidian and its CLI are free but proprietary, with Sync from $4 a month billed annually. Jotura’s app and CLI are free and closed source, with Sync at £4 or $5 a month. Joplin Cloud starts at €2.40 a month billed yearly, and Basic Memory Cloud costs $15 a month in beta.