Reference

Mental CLI reference

I type mental. After mental install, agents write on your behalf (journal, decisions, residue). You type mental when you want the pulse yourself. Agents always pass --json. Named commands are one-shot: print or write, then exit.

mental                 # heartbeat on a TTY; help + exit 2 otherwise
mental -h              # Daily commands
mental --help          # all commands, grouped
mental heartbeat --json
mental where --json
mental schema --json

Non-TTY (pipes, agents) with no args prints help and exits 2. mental --json with no command is a heartbeat.

Global flags

Flag Meaning
--json { "ok": true, "data": … } or { "ok": false, "error": { "code", "message", "hint?" } }. Optional sibling update when npm is ahead
--dir <path> Override resolve (same as MENTAL_DIR)
-h Short Daily help
--help Full grouped usage. After a command: that command’s help (examples first)
-v, --version Print version
--plain, --no-color No emoji / ANSI (also NO_COLOR any non-empty, TERM=dumb)

MENTAL_ASCII=1 strips emoji from TTY output. --json is always ASCII. MENTAL_SKIP_UPDATE_CHECK=1 skips the npm newest-version check. When npm is ahead, every --json envelope includes a sibling update (current, latest, hint). Ordinary commands read ${XDG_CACHE_HOME:-~/.cache}/mental/npm-latest.json: 24h TTL while this CLI matches cache (so a ship is visible by the next day), 7d TTL only while already behind. A failed npm view backs off 15 minutes without burning that TTL. TTY prints the hint at most once per day. doctor still checks live. install recopies skills; it does not npm view.

Commands

Command What it does
mental Heartbeat (TTY): resume, last outcome, git, hops today, Needs eyes, In the air, Later, unsettled + settled; then exit
mental heartbeat Same cheap reload; agents pass --json. Does not write the pulse watermark. With time tracking on it updates last_seen unless --passive. It may write jev-hint.json and npm-latest.json, and it may run a synchronous npm view (about 5s) when the update cache is stale, including on --json. MENTAL_SKIP_UPDATE_CHECK=1 skips that. Delta is counts only. hopsToday is parks since local midnight. later / laterCount are --status later residue. JSON also includes needYouCount, agingCount, directionCount, and freshEyesCount. --fields keeps only the named keys
mental pulse Cross-project compact rows from bindings.json (id, name, resume, attentionCount, openDecisionCount). No journal bodies. Writes watermark for the active bundle
mental where Active bundle: root, id, mode, reason, gitRoot (read-only; does not create identity)
mental status Git + resume + residue + open/deferred decisions + notes; writes status/current.md; first write creates identity
mental search <q> Query the derived index (--type, --status, --tag, --kind, --any, --project, --all-projects); journal hops as path#HH:MM; JSON includes tokens + op. Every backend matches word prefixes. Hits are ordered by type, then title weight, then path. A search rebuilds the index when files changed outside Mental. Pages of 50 via --limit, --offset, --all, plus --since and --on
mental list List concepts (--type, --status, --tag, --kind, --since, --on; pages of 50 via --limit, --offset, --all; --project <id|name> or --all-projects to cross projects)
mental move <path> --to <project> Re-file one attention/decision/note into another bound project, keeping timestamp, frontmatter and body; warns on dangling links
mental why <path|sha|path:line> Decisions and hops anchored to that path or commit, plus Mental-Decision trailers. JSON matches search: hits with path, type, title, head
mental show <path> One OKF file relative to the bundle root (includes backlinks; with a decision-model key also suggestedLinks and, for agent-directed text, flags; writes may add advisory data.guard)
mental relink [path] [--apply] Optional (needs a Jev key). Suggest links for a file, or the 5 most recent non-journal files, each labelled with how it relates (supports, supersedes, contradicts, depends_on; plain related is unlabelled). Dry run by default; --apply appends only high-confidence links under ## Related (- supersedes: [Title](path))
mental retag [path] [--apply] [--limit N] Optional (needs a Jev key). Propose one topic tag for untagged files from existing tags plus frequent title words (bootstrap, * marks a new topic). Dry run by default; --apply writes only the tags: line of confident matches. Does not run on the personal bundle unless mental option decide personal on
mental supersede <path> --by <newer> / mental obsolete <path> (--restore undoes) Mark a note or decision as replaced by a newer one, or no longer true. Only frontmatter changes (status, superseded_by); the file is kept and the newer one gets a supersedes: link. Journals are refused. Marked files leave the heartbeat, brief and open-decision lists and rank last in search. No model needed; doctor can also propose these marks (--apply writes)
`mental brief [–hops N] [–find WORD] [–since park handoff]`
mental extract <file|-> [--apply --tag slug] Optional (needs a Jev key). Propose actions, decisions and concerns from pasted meeting notes. Dry run by default; --apply writes them (needs --tag). The raw text is never stored
mental search <q> --rank Optional (needs a key). Re-order hits by relevance, favoring the file type the question points at (decision, attention, note); never drops a hit. With zero hits and a key, search also lets the model read the freshest files (broad: true)
mental reindex Rebuild ${XDG_CACHE_HOME:-~/.cache}/mental/<uuid>.sqlite
mental park --resume Encode at an interruption (default title "Parked"). Optional --attention + --kind (and --from, --against, --via, --ref). Requires --resume. Then heartbeat; writes watermark
mental handoff --share <dir> After the journal entry, pack this one project (never personal files or hours) into a directory outside the worktree, with HANDOFF.md. --since YYYY-MM-DD limits journal days. --no-journal packs decisions and residue only. --allow-secrets writes the pack even if a credential shape is present
mental import <dir> Merge a share into the binding with the same git origin. A second import does not duplicate hops. --allow-secrets merges even if a credential shape is present
mental option trailer on Install prepare-commit-msg (chains an existing hook). It appends Mental-Decision: <id>/<path> and never fails the commit. Default off
mental trailer Read-only list of those trailers. The hook calls it
mental handoff --title --resume Planned boundary: journal then heartbeat. Both flags required. JSON { path, heartbeat }. Writes watermark. --via optional
mental journal --title --body --resume [--against] [--via] Append today’s journal section. --resume is required (same as park/handoff)
mental schema [command] Dump the command catalog as JSON (mental schema heartbeat --json for one command)
mental completion bash|zsh|fish Print a completion script (do not auto-write shell rc files)
mental attention --title --kind Create or update residue (direction | concern | thread | verify; --status later is come-back-to-this, not a note; --status resolved closes). Create requires --tag (1–3 topic slugs). Omit --tag on update. --via optional
mental decide --title --body Create or update a decision. Create requires --body (the why) and --tag (1–3 topic slugs). Same --title without --body updates; omit --tag on update so existing tags stay. --status decided closes by title; --path targets a file. --via optional
mental note --title Scaffold a durable fact (not “come back to this” — that is attention --status later). Create requires --tag (1–3 topic slugs)
mental move <path> --to <project> Re-file one attention/decision/note into another bound project’s bundle, keeping timestamp and body. Same as attention --title … --move-to <project>. Warns about dangling links; a name clash gets -2
(create commands) --project <id|name> attention, decide and note can file into another bound project from any cwd (never journal/park/handoff)
mental local [--import | --move] Project ./.mental after ignore check. --move --delete-home removes the home slice after the copy
mental remap [--to id] List or retarget this clone’s UUID. --from <id> is the same retarget as --to
mental split [--copy] New UUID for this clone (mental new is an alias)
mental link --to <id> Point this clone at an existing UUID
mental backup --out <dir> Pack this HOME’s OKF + identities into a portable directory (outside the git worktree; no sqlite, no hours, no machine paths). Default includes personal
mental restore --from <dir> Merge a backup per UUID. Dest-ahead hops and dest-only slices stay. --replace --confirm REPLACE wipes packed slices only
mental install User skill + rule; ~/.mental skeleton; CLI on PATH. Recopies skills/rules (--force when dest version already matches). Does not npm-upgrade the CLI. --mcp registers MCP; --hooks / --track only after the user says yes this turn. JSON includes optionals[] (needsConsent: true)
mental uninstall Remove installed skill / rule / hooks / MCP / mental-track copies
mental option List or set optional features (track per UUID; mcp / hooks user-global). --all sets track default on. --this before a UUID is usage
mental track Optional automated project-time record (off until option track on): private/customer copy, wall/billable, several clocks, dated client export. Full contract
mental hooks on|off Optional session hooks (default off; alias of option hooks)
mental option decide [status|on|off|key <KEY>|key -|key clear|provider <P>|url|model|account <v>|fallback <P|off>|personal on|off|budget <tokens/day>|budget off] [--provider P] Optional decision model: TypeSafe Jev, OpenAI Decisions, Cloudflare Clef, or a custom endpoint. Status (with today’s usage), mute, store keys (config only, mode 0600, never env), pick the active provider and a fallback, allow or block personal-slice notes, cap daily input tokens. option jev is an alias. Never in MCP
mental serve Optional MCP stdio (session verbs: heartbeat, journal, park, …). Identity/setup stay CLI
mental dashboard Optional read-only localhost explorer (127.0.0.1:3847; a busy port falls back unless --port is set). Views: Today, Attention, Library, Decisions, Map, Sit-downs (track on), All projects. --no-open prints the URL. --idle-exit quits after idle minutes. Loopback only; GET/HEAD; no CORS. Full page: Dashboard. Agents still --json
mental doctor PATH, bindings, ignore, skills, npm update, host plugin / skill-copy lag, decision budget, stale residue, optionals[], time.sqlite never git-tracked (exit 3). --fix recopies home skills/rules and git excludes (never --project or optionals). --fix-ignore is ignore-only. --days <n> overrides the 14-day stale threshold (warn only; exit 0 if only warns). TTY: ✓ ok, ⚠ failed warn, ✖ error; ASCII fallback OK / ! / X. Footer next: is a portable command (no &&). JSON data.next. MENTAL_SKIP_HOST_PLUGIN_CHECK=1 skips host CLIs. With a decision-model key it also runs warn-only content checks (jev-stale, jev-decisions, jev-handoff, jev-untagged, jev-secrets, plus a jev self-check); --offline skips them. With a key, plain doctor also previews model-proposed tags and links for historical files (jev-repair); --apply writes them, --limit N files per run (default 10, max 200, progress resumes). Personal-slice notes are not sent unless mental option decide personal on.

Writes

Same --title updates the existing decision or attention file (paths are identities). --path targets a specific file.

Create for decide, attention, and note requires --tag (1–3 topic slugs). Omit --tag when the same title updates an existing file. Do not pass --tag on journal or park.

mental journal --title "What landed" --body "Evidence git cannot see." --resume "Exact next action — open loops: none" --against PLAN.md --via cursor
mental decide --title "Heartbeat only, no standing TUI" --body "Default TTY mental prints a one-shot heartbeat and exits." --status decided --tag continuity --via cursor
mental attention --title "Tom said ship the pointer not the dump" --kind direction --from Tom --tag continuity --via cursor
mental attention --title "Come back to MCP" --kind thread --status later --tag continuity --via cursor
mental attention --title "Resolver tests not reviewed" --kind verify --tag continuity --via cursor
mental attention --title "Tom said ship the pointer not the dump" --status resolved
mental note --title "Identity is a UUID in bindings.json" --tag continuity
mental track start --title-internal "Auth callback" --via cursor
mental park --resume "Exact next action — open loops: none"
mental track report --since 2026-08-01 --until 2026-08-31

Journal contract (one section per task, not per chat turn):

## HH:MM — <outcome>
<what changed, evidence, decisions git cannot explain>

Against: <optional repo-relative plan>
Resume: <one exact next action> — open loops: <none or list>

Lookup

mental status
mental heartbeat --json
mental where
mental search overlay
mental list --type Decision --kind direction
mental show notes/some-fact.md
mental show journal/2026-08-29.md#22:36

Typed filters (--type, --status, --tag, --kind) apply before the result cap. Search uses SQLite FTS5 with bm25 when Node’s node:sqlite includes FTS5; otherwise it uses SQLite LIKE (or a markdown scan if sqlite is missing). Title matches rank above buried body mentions; Decision/Note rank above Journal. Space-separated words are AND prefixes (overlay leftover must match both) unless --any (OR). Quotes are shell glue, not a phrase operator. JSON includes tokens and op. Journal hops index as journal/YYYY-MM-DD.md#HH:MM. Search one concept per query; before proposing an approach, search that name. MCP q may be a string array (union).

Exit codes

Code Meaning
0 ok
1 resolve / not-found / unknown command
2 usage (missing flags, unknown flags) or non-TTY no args without --json
3 doctor found error-level problems (ok: false in JSON; warn-only stays ok: true)

Layout

OKF files under ~/.mental/projects/<uuid>/ (or ./.mental after mental local):

journal/YYYY-MM-DD.md
decisions/YYYY-MM-DD-slug.md
attention/YYYY-MM-DD-slug.md
notes/slug.md
status/current.md          # disposable cache, not SoT
time.sqlite                # optional hours (off until `mental option track on`; never git)

Index: ${XDG_CACHE_HOME:-~/.cache}/mental/<uuid>.sqlite (rebuildable). Hours are not in the index — they live in bundle time.sqlite.

Optional timers are default off. When on, agents generate private and customer-ready title/body fields from current work and refresh them at park, journal, or handoff. track start is ensure-running (machine-wide); --new starts another clock; billable defaults to wall (--billable suggested uses last_seen). Report includes running clocks. Missing customer copy returns structured review JSON. Customer export rows contain date, work description, wall, and billable. TTY heartbeat and pulse never show hours. Full Track contract.

Pulse watermark: ${XDG_CACHE_HOME:-~/.cache}/mental/<uuid>.pulse.json ({ at: iso } — rebuildable, not SoT). Written after delta by pulse / park / handoff; heartbeat never writes it.

Heartbeat lists (attention and open decisions) are capped at 7; JSON includes attentionCount / openDecisionCount / laterCount. Extra later: mental list --type Attention --status later. Extra open decisions: mental list --type Decision --status open. JSON stale (and a Stale (> 14d) text block) lists open/deferred decisions and open/later attention untouched for 14+ days, oldest first, capped at 5 per kind with a true count.

See identity for UUID / local / leftover import, agents for the --json contract, dashboard for the localhost page, decision models for optional model assist, and optional time tracking for what hours can and cannot do.