Docs / The CLI / Install and setup
Install and setup
How the jotura command line tool reaches your PATH on each platform, how it picks a vault, and what its global flags and exit codes do.
jotura is the command line interface to the same vault the desktop app uses. Vault files are plain markdown on disk, so there is no unlock step and no daemon to start. Every data command works the moment the binary is on your PATH and a vault is selected.
This page covers getting the binary installed, checking that it works, telling it which vault to use, and the flags and exit codes that apply to every command. The individual commands are documented in the rest of this section, starting with reading and searching.
The CLI ships inside the desktop app
There is no separate CLI download. Every desktop installer carries the jotura binary next to the app executable. You can get the installers from the download page. How the binary reaches your PATH depends on the platform.
| Platform | Where the binary lands | How it reaches your PATH |
|---|---|---|
| macOS (.dmg or .app) | Jotura.app/Contents/MacOS/jotura | Open Settings, go to Advanced, and use the Command-line tool card. Installing creates /usr/local/bin/jotura as a symlink to the bundled binary, through an admin-privileged step, so macOS asks for your password. |
| Windows (.msi) | <install dir>\jotura.exe | The MSI adds its install folder to the system PATH during installation. The command resolves in any new terminal with no further action. |
| Linux (.deb) | /usr/bin/jotura | Already on the standard PATH. Nothing to install. |
| Linux (AppImage) | Inside the AppImage, under usr/bin/ | The Command-line tool card copies the binary to ~/.local/bin/jotura. That directory has to be on your PATH for the command to resolve. |
Windows PATH
The MSI adds the install folder to the system PATH, so jotura resolves in any terminal you open after installing. Already-open terminals need a restart to pick the change up. If the app’s Settings card offers to install the command-line tool even though jotura already works in your terminal, you can decline: the command is available either way.
The Settings card and the startup prompt
The Command-line tool card is rendered only on the platforms that need it, which are macOS, Windows, and the Linux AppImage. A .deb install never shows it. On Windows the card writes your user PATH through the environment API directly rather than using setx, because setx truncates long PATH values. If that write fails, the card surfaces the error text from PowerShell, or the literal message Could not update PATH. when PowerShell reported nothing.
On Windows and the AppImage the app also shows a one-time startup prompt offering to do the same job as the card. It is skipped entirely when the status already says installed, which is why most people never see it. The app records that it asked whichever way you answer, so the prompt does not return. Use the Settings card instead. The settings reference covers the rest of that panel.
To remove the tool later, undo whichever route installed it:
sudo rm /usr/local/bin/jotura
rm ~/.local/bin/jotura
On Windows, edit PATH under System Properties, then Environment Variables.
The one-line installer
If you would rather do the whole thing from a terminal, an install script handles it. Both scripts perform the same six steps:
- Fetch the release manifest from
releases.jotura.io. - Download the installer artifact for your operating system and CPU.
- Verify the artifact’s SHA-256 checksum against the manifest before touching anything.
- Install the desktop app and put the
joturaCLI on your PATH. - Install the Jotura agent skill for any AI coding agents found on the machine.
- Launch the app.
On macOS and Linux:
curl -fsSL https://jotura.io/install.sh | bash
On Windows, in PowerShell:
irm https://jotura.io/install.ps1 | iex
Both scripts are readable before you run them. The links open the raw script files rather than pages: install.sh and install.ps1. Rerunning either one upgrades an existing install in place. If a script fails at any point it tells you to fall back to the download page.
What each platform ends up with
The phrase “install the app and put the CLI on your PATH” means something different on each platform, and only one of the three is fully automatic.
| Platform | App location | CLI location | Privileges needed |
|---|---|---|---|
| macOS | /Applications/Jotura.app, with the quarantine flag cleared | Symlink at /usr/local/bin/jotura | sudo for the symlink, and also for the app copy when /Applications is not writable by your user |
| Linux, AppImage (default) | ~/.local/share/jotura/Jotura.AppImage, plus an icon and a jotura.desktop menu entry | Copied to ~/.local/bin/jotura | None |
Linux, --deb | System package, launched as jotura-desktop | /usr/bin/jotura | sudo, and the install fails without it |
| Windows | Install folder chosen by the MSI | jotura.exe in that folder, on the system PATH | A Windows UAC elevation prompt from msiexec /passive |
Three privilege details are worth knowing before you run anything. On macOS, the script prints that sudo is needed because /usr/local/bin is system-owned, and if sudo is unavailable or declined it only warns: the CLI then stays inside the app bundle and off your PATH. On Linux with --deb, sudo is a hard requirement and the script stops with advice to rerun without --deb for a user-level AppImage install. On the AppImage path the script warns when ~/.local/bin is not on your PATH, and prints the export PATH=... line for your shell profile, but adding it is your job.
One more Linux tradeoff: the AppImage auto-updates itself from inside the app, and the .deb does not. To upgrade a .deb install, rerun the script.
On Windows, already-open terminals need restarting before they see the new PATH. If msiexec returns code 3010 the script reports that the install succeeded and Windows wants a reboot to finish. That is a success, not a failure.
Installer flags
The macOS and Linux script accepts flags after -s --:
| Flag | Effect |
|---|---|
--no-launch | Do not launch the app when the install finishes. |
--skills <mode> | Which agent skills to install. Accepts all, claude, codex, gemini, copilot, or none. The default detects agents by looking for ~/.claude, ~/.codex, ~/.gemini, and ~/.copilot. |
--deb | Linux only. Install the .deb system package instead of the AppImage. |
-h, --help | Print the script’s help and exit. |
For example, to install without launching the app and without touching any agent config:
curl -fsSL https://jotura.io/install.sh | bash -s -- --no-launch --skills none
The PowerShell script takes the same choices as parameters, but only when you download the file and run it rather than piping it into iex:
| Parameter | Effect |
|---|---|
-NoLaunch | Do not launch the app when the install finishes. |
-Skills <mode> | One of detect, all, claude, codex, gemini, copilot, or none. The default is detect. |
Skill installation is a convenience of the installer, not a requirement. You can wire agents up at any time with the skill and agents commands, which are covered in the agent surface.
Check that it worked
Open a new terminal window so it picks up the updated PATH, then ask for the version:
jotura --version
The output is the command name followed by the version number, for example jotura 0.7.0. That version comes from the binary itself, so it tells you which build you are actually running.
Then confirm the command surface loads:
jotura --help
If either command is not found, the PATH change has not reached that shell yet. Close the terminal, open a fresh one, and try again.
When the CLI is older than the app
A CLI left over from an older install can be a full release behind the app, which matters because commands are added over time. The agents family, for instance, does not exist in 0.6.1. Three things help:
- Run
jotura doctor, which reports bothcli-version(the running binary) anddesktop-app-version(read from the desktop settings file). Seeing them side by side is the quickest way to confirm a mismatch. - On macOS, the Settings card’s button reads
Reinstallonce a tool is present. When the symlink no longer points at the current app bundle, the status line readsInstalled at <path> (points elsewhere; reinstall to update). That is what a stale CLI looks like after an app update. - Rerunning either install script upgrades in place.
Fuller diagnostics
jotura doctor reports the executable path, whether its directory is on your PATH, the resolved vault, sync state, share health, document conversion health, and semantic search prerequisites:
jotura doctor
Doctor always exits 0, even when a check reports fail, so scripts must parse the statuses rather than test the exit code. Ask for JSON to do that:
jotura doctor --json
The full check list is in watching and diagnostics.
How the CLI picks a vault
Almost every command resolves a vault before it does anything else. The resolution order is fixed:
| Order | Source | Notes |
|---|---|---|
| 1 | --vault <path> | The global flag. It wins over everything else, for that one invocation only. |
| 2 | $JOTURA_VAULT | An environment variable, useful for a shell session or a service that always works in one vault. |
| 3 | The desktop app’s last-opened vault | Read from the lastVaultPath key in the desktop app’s settings.json. This is why the CLI usually just works on a machine that also runs the app. |
Whatever the source, the path is canonicalized: symlinks are resolved and the result is made absolute, so it matches what the desktop app stores. On Windows the canonical form is the extended-length one, so commands report paths beginning with \\?\C:\....
If nothing resolves, the path is inaccessible, or the path is not a directory, the command exits 6 with a NoVault error on stderr.
jotura --vault "C:/Users/you/Documents/my-vault" status
export JOTURA_VAULT="$HOME/Documents/my-vault"
jotura status
The desktop settings file lives in the standard config location for each platform:
| Platform | Settings path |
|---|---|
| Windows | %APPDATA%\jotura\settings.json |
| macOS | ~/Library/Application Support/jotura/settings.json |
| Linux | ~/.config/jotura/settings.json |
One failure mode to know about: reading that file is best-effort at every step. An unreadable file, invalid JSON, or a missing lastVaultPath key all produce the same result as having no settings file at all. The command then exits 6 saying no vault was specified, which points you at --vault and $JOTURA_VAULT and gives no hint that the file was found but unusable. If source 3 stops working for no apparent reason, open the settings file and check that it parses.
Commands that need no vault
A few commands are handled before vault resolution runs, so they work on a machine with no vault configured and can never exit 6: completion, hook install, skill install, skill list, skill show, and the hidden path completer. doctor also never exits 6. It resolves the vault itself and reports a failure as a check result instead of an error.
Global flags
Three flags are declared globally, so clap accepts them at any position and they appear in the options block of every subcommand’s help. jotura --json read notes/a.md and jotura read notes/a.md --json are the same command. Two more flags are worth listing here even though they are not global.
| Flag | Type | Default | What it does |
|---|---|---|---|
--vault <VAULT> | path | none | Global. Overrides vault selection for this invocation, ahead of $JOTURA_VAULT and the desktop’s last-opened vault. Honored by every command, including doctor. |
--json | boolean | off | Global. Asks for structured JSON on stdout. Errors are JSON on stderr regardless of this flag. See the next section for which commands actually branch on it. |
-v, --verbose | boolean | off | Global. Logs progress to stderr. In practice this is one line, vault: <resolved path>, emitted after vault resolution. The short -v is taken globally, which is why grep has no invert-match flag. |
-h, --help | boolean | none | Per-command, the clap default. Prints help for the command or subcommand you attached it to. |
-V, --version | boolean | none | Top level only. jotura read --version is an unrecognized-argument error, not a version print. |
Which commands honor --json
This is the most surprising part of the surface, so it is worth stating plainly. Roughly half the commands emit JSON whether or not you ask, because every mutating command already speaks JSON. There are three behaviors, and the table below gives the behavior for every command.
| Command | --json behavior |
|---|---|
agents status | Branches on --json, emitting newline-delimited JSON |
agents sync | Branches on --json, emitting newline-delimited JSON |
backlinks | Branches on --json |
batch | Ignores --json, always pretty JSON |
context | Branches on --json |
conventions | Branches on --json |
create | Always JSON |
delete | Always JSON |
doctor | Branches on --json |
edit | Always JSON |
frontmatter delete | Always JSON |
frontmatter get | Branches on --json |
frontmatter set | Always JSON |
grep | Branches on --json |
hook install | Always JSON |
import | Always JSON |
links | Branches on --json |
ls | Branches on --json |
ls-documents | Branches on --json |
memory list | Branches on --json |
memory log | Always JSON |
memory recall | Branches on --json |
memory resolve | Always JSON |
memory supersede | Always JSON |
quick-open | Branches on --json |
read | Branches on --json |
regenerate-md-store | Always JSON |
rename | Always JSON |
search | Branches on --json |
search-documents | Branches on --json |
shares list | Branches on --json |
skill install | Ignores --json, always newline-delimited JSON |
skill list | Branches on --json |
skill show | Ignores --json, always the raw skill file |
status | Always JSON |
sync drop-path | Branches on --json |
sync login | Always JSON |
sync logout | Always JSON |
sync status | Branches on --json |
tag add | Always JSON |
tag list | Branches on --json |
tag remove | Always JSON |
templates create | Always JSON |
templates delete | Always JSON |
templates list | Branches on --json |
templates show | Branches on --json |
today --read | Branches on --json |
today (path and append forms) | Always JSON |
trash <path> | Always JSON |
trash empty | Always JSON |
trash list | Branches on --json |
trash purge | Always JSON |
trash restore | Always JSON |
vault info | Always JSON |
watch | Ignores --json, always compact newline-delimited JSON |
write | Always JSON |
Three commands emit newline-delimited JSON rather than one pretty document: agents status, agents sync, and skill install. So does watch. That matters if you pipe output into a parser expecting a single object.
Every error the CLI itself raises is a single-line JSON object on stderr, in every mode, with a stable code field. Argument-parsing errors are the exception: those come from clap and are plain text, as described under exit codes below. The envelope and its per-code extra fields are documented in exit codes and JSON output.
Environment variables
These are the variables the jotura binary itself reads. The two the install scripts read, JOTURA_INSTALL_DRYRUN and JOTURA_MANIFEST_URL, exist for continuous integration and are documented in the script headers.
| Variable | Purpose |
|---|---|
JOTURA_VAULT | Default vault path, second in the resolution order above. |
JOTURA_SESSION_DIR | Overrides the sync key cache directory, which defaults to <config>/jotura/sessions/. It exists for test isolation. Leave it unset in normal use. |
JOTURA_MODEL_DIR | Overrides the embedding model directory used by semantic search. An empty value falls through to the default. |
ORT_DYLIB_PATH | macOS only. An explicit path to libonnxruntime.dylib for a standalone build. The bundled CLI resolves the runtime relative to its own executable and sets this itself. |
JOTURA_DISABLE_SANDBOX | Runs the document converter without a sandbox. doctor surfaces it as a warning. |
JOTURA_ALLOW_UNSANDBOXED_CONVERSION | The same effect under a different name, and likewise surfaced by doctor as a warning. |
JOTURA_CONFIG_DIR | Overrides the config directory the CLI reads, which is where the desktop settings file and the session cache live. It is a test seam, because the platform API ignores HOME and XDG_CONFIG_HOME on Windows. Leave it unset in normal use. |
JOTURA_HOME_DIR | Overrides the home directory the CLI reads, for the same testing reason. Leave it unset in normal use. |
Shell completion
jotura completion <shell> prints a completion script to stdout for one of five shells: bash, elvish, fish, powershell, and zsh. Anything else is a usage error. The script is generated under the command name jotura.
Where you save that output is a matter of your shell’s conventions. Common choices are below.
For bash:
jotura completion bash > ~/.local/share/bash-completion/completions/jotura
For zsh, with ~/.zfunc added to your fpath:
jotura completion zsh > ~/.zfunc/_jotura
For fish:
jotura completion fish > ~/.config/fish/completions/jotura.fish
For elvish:
jotura completion elvish > ~/.config/elvish/lib/jotura.elv
For PowerShell, add this line to your $PROFILE:
jotura completion powershell | Out-String | Invoke-Expression
The generated script completes commands, subcommands, and flags. Note paths are a separate matter. The CLI carries a hidden helper, jotura __complete-paths [PREFIX], that prints one vault-relative path per line for every synced file starting with the prefix. It exists for completion scripts that want to complete note paths. It always exits 0 and never reports an error, even with no vault selected or a failed walk, so a broken completion never surfaces in your shell.
Exit codes at a glance
Every command returns one of these. They are the public contract, and they are the reason agent scripts can branch on failure without parsing prose.
| Code | Symbol | Meaning |
|---|---|---|
| 0 | Success. | |
| 1 | Error | Generic error, including unclassified input and output failures. |
| 2 | HashConflict | An --if-hash precondition did not match, or a compare-and-swap write lost the race. In a batch the same code covers one or more entries failing preflight, and the error carries a conflicts array rather than a single diff. |
| 3 | NotFound | A file was missing, a --replace needle matched nothing, or sync drop-path was given a path with no synced row. |
| 4 | Ambiguous | A --replace needle matched more than once and neither --all nor --nth was given. |
| 5 | OutOfRange | A line number, line range, or --nth value was out of bounds. |
| 6 | NoVault | No vault resolved, the path was inaccessible, or it was not a directory. |
| 8 | PermissionDenied | A filesystem permission error, or a path that resolves outside the vault. |
| 9 | InvalidArgs | A bad flag combination or an invalid value, including a wrong sync password or an invalid recovery phrase. |
| 11 | MultiEditOpFailed | One operation in an edit --apply or batch sequence failed. |
| 12 | SyncNotEnabled | sync login, sync logout, or sync drop-path ran against a vault with no sync configured. sync status never exits this way: it reports enabled: false instead. |
| 13 | SemanticUnavailable | Semantic or smart search ran without the desktop app’s vector index in place. |
| 14 | TargetMissing | An --if-hash write found its target deleted or moved since the read. The file is deliberately not recreated. |
| 15 | ReadOnlyShare | The target path is inside a view-only share mount. |
| 16 | AgentConflict | agents sync found files changed on both the vault and the system side. |
Codes 7 and 10 are retired. They belonged to the encrypted-vault era, when vaults could be locked, and the numbers will not be reused.
One collision to guard against: clap’s own usage errors also exit 2. A missing required argument, an unknown flag, or an invalid enum value exits 2 with human-readable text on stderr, not the JSON error envelope. A caller that sees exit 2 should check whether stderr parses as JSON before concluding it hit a hash conflict.
The full per-code error payloads, including the fields each one carries, are in exit codes and JSON output.
The only credential step is sync
Nothing about local reading, writing, or searching needs a login. The one exception is optional cloud sync, whose key cache is managed by jotura sync login and jotura sync logout, and read by jotura sync status. The desktop app shares that same cache, so signing in from either surface resumes the other.
jotura sync status
Sync itself is a paid subscription, priced on the pricing page. The app and the CLI are free. Enabling sync, setting or rotating the password, and restoring a vault all happen in the desktop app. See sync and shares for what the CLI does and does not do.
Where to go next
- Reading and searching covers
read,ls,search, andgrep, including the hash you need before an edit. - Editing notes covers the safe read-then-write loop and the strict
--replacerules. - The agent surface covers
skill install,agents sync, memory, and conventions. - Getting started covers the desktop side of the same vault.