Reference

Mental CLI for agents

Agents write Mental CLI on the user’s behalf. After mental install, the skill plus the always-on rule tell them when to journal, decide, and record residue. The human does not keep a second journal. I type mental. They may still type mental for the pulse.

The CLI is the contract. Humans type mental. Agents call mental … --json. They do not grep .mental, ~/.mental, or YAML frontmatter.

This is not a hidden hook and not every chat turn. Hooks stay off until the user asks. Write at a task boundary, when a decision constrains the future, or the moment residue surfaces. Fail open if Mental CLI is missing.

If mental is on PATH for daily work, follow the Mental skill copied by install — not the plugin bootstrap (skills/mental-setup). If JSON includes update, or the user asks to upgrade, run npm i -g @balacode/mental, then mental install, then mental doctor. npm owns the executable; install recopies skill/rule, does not turn on optionals, and does not refresh the host plugin cache. Existing Windows installs from Mental 0.8.1 or older run mental-repair.cmd once after npm updates. If PowerShell blocks npm-generated .ps1 launchers, use npm.cmd / mental.cmd; never change execution policy for the user. If the CLI is missing, continue the coding task (fail open) and mention the install block. Never execute a .mjs file directly. The plugin does not start MCP.

Always

mental where --json
mental heartbeat --json

heartbeat is the cheap mid-chat reload: resume, last outcome, git, hops today, residue (Needs eyes / In the air / Later), unsettled + settled (lists capped at 7; counts via attentionCount / needYouCount / agingCount / directionCount / freshEyesCount / openDecisionCount / needsEyesCount / laterCount / guardrailCount / hopsToday; stale lists decisions and attention untouched 14+ days). JSON also includes id and mode. --fields keeps only the named keys. Use mental status --json when you also need notes. Do not call pulse every turn. Flag grammar: mental <cmd> --help or mental schema --json. Unknown flags fail (error.code unknown-flag, error.hint lists legal flags). Journal requires --resume (same as park/handoff). Decide create requires --body (same --title without --body updates). Create for decide, attention, and note also requires --tag (1–3 topic slugs). Omit --tag on update. Do not pass --tag on park or journal.

If JSON includes data.track.enabled, follow the Mental Track skill. track start --via <host> if runningCount is 0 (start twice is ensure-running). If tracking is off, do not enable it. After mental install or mental doctor, ask about optionals (needsConsent: true) with a one-liner each: hooks (session-start status), MCP (mental serve for clients that cannot shell the CLI), time tracking (per-project sit-down clock). Check whether MCP is needed. Never run mental option … on or install --hooks|--mcp|--track until the user says yes this turn.

Park vs handoff vs heartbeat vs pulse

When Command
Cheap reload / other agents may have written mental heartbeat --json
Interrupted mid-hop / switching context mental park --resume "…" --json
Come back to this / note that for later mental attention --title "…" --kind thread --status later --json
Planned task boundary (journal + heartbeat) mental handoff --title "…" --resume "…" --json
Cross-project compact overview mental pulse --json

park encodes at an interruption (default title "Parked"; optional --attention + --kind). handoff is sugar for journal then heartbeat — both --title and --resume required. pulse returns compact rows from bindings (id, name, resume, counts) — no journal bodies, no merged dump. Heartbeat stays read-only for the pulse watermark; park / handoff / pulse write it after computing since-last-pulse counts.

Pass your host’s token as --via (claude-code, cursor, codex, copilot, opencode); the rule mental install writes for each host names it.

mental park --resume "…" --via <host> --json
mental handoff --title "…" --resume "…" --via <host> --json
mental pulse --json
mental journal --title "…" --body "…" --resume "…" --against PLAN.md --via <host> --json
mental attention --title "…" --kind concern --status open --via <host> --json
mental attention --title "…" --kind thread --status later --via <host> --json
mental attention --title "…" --kind verify --status open --via <host> --json
mental decide --title "…" --body "…" --status open --via <host> --json
mental decide --title "…" --status decided --json
mental search overlay --json
mental list --type Decision --kind direction --json
mental show notes/some-fact.md --json
mental status --json

Mid-chat, not just start/finish:

  • Before proposing a concrete approach, flag, crate, or env var: mental search <that name> --json as its own query (space-separated words are AND; --any is OR). Also mental list --type Decision --json (all statuses). If id is null, those catalogs are empty (unbound); use pulse for other repos.
  • Record attention the moment residue surfaces. “Come back to this” / “for later” is --status later, never note. Settled facts (“DONE …”, “CORRECTION …”) are not residue: use note/journal or resolve the original item (attention warns on those titles).
  • Park when interrupted mid-hop; handoff only at a planned close
  • Re-call mental heartbeat --json whenever other agents may have written — it derives git live. On this repo’s bench machine a CLI heartbeat is 51 ms p50; in-process (MCP) it is 11 ms. See benchmarks.
  • Use pulse for multi-repo orchestration, not as a per-turn dump

Skill + rule

mental install copies:

  • The skill — procedure (when to journal, CLI contract, privacy). Model-invocable and user /mental.
  • A tiny always-on rule — pointer, not the full lifecycle. One source: rules/mental.mdc. Form follows the destination (.mdc with frontmatter vs .md body).
Host Path Form
Claude Code ~/.claude/rules/mental.md body, no paths: (loads every session)
Claude Code ~/.claude/CLAUDE.md managed block body (kept for older Claude Code and OpenCode fallback)
Cursor ~/.cursor/rules/mental.mdc .mdc with alwaysApply: true. doctor fails if this file is missing, unless the Cursor plugin already ships the rule. If both exist, doctor warns that Cursor loads the rule twice, and mental install drops the home copy
Cursor .cursor/rules/mental.mdc same .mdc, only with mental install --project (project-only; no home recopy; Cloud/CLI / git-shared; doctor warns)
Codex ~/.codex/AGENTS.md managed block body
OpenCode ~/.config/opencode/AGENTS.md managed block body, only if that file already exists (creating it would drop the CLAUDE.md fallback)
Convention ~/.agents/AGENTS.md + ~/.agents/rules/mental.md body

mental uninstall removes each of those, including a leftover empty AGENTS.md/CLAUDE.md after the managed block is stripped. mental doctor fails when a load-true host file is missing. mental doctor --fix recopies the home dests and git excludes; it does not write the project Cursor rule.

Hosts that read .agents/skills

mental doctor --json lists detected hosts under hosts (path and ok / missing). CI checks the skill frontmatter. It does not launch Cursor, Claude, Codex, Copilot, OpenCode, Windsurf, or Cline.

Host User-level Project-level /mental Rule
Cursor ~/.agents/skills/mental <repo>/.agents/skills/mental yes ~/.cursor/rules/mental.mdc
Claude Code ~/.agents/skills/mental <repo>/.agents/skills/mental yes ~/.claude/rules/mental.md
Codex ~/.agents/skills/mental <repo>/.agents/skills/mental yes ~/.codex/AGENTS.md block
Copilot ~/.agents/skills/mental <repo>/.agents/skills/mental yes host rule from install
OpenCode ~/.agents/skills/mental <repo>/.agents/skills/mental yes AGENTS.md block if the file exists
Windsurf ~/.agents/skills/mental <repo>/.agents/skills/mental yes host rule from install
Cline ~/.agents/skills/mental <repo>/.agents/skills/mental yes host rule from install

node scripts/sync-project-skills.mjs --emit <project> copies skill/mental to <project>/.agents/skills/mental and prints gitignore guidance. It does not edit .gitignore. Before a manual host check, confirm name: mental, a description of at most 1024 characters, and compatibility naming Node >=22.13. Record the CLI version and the date. Layout is what CI verifies. Host UI is not launched in CI.

Before changing code you did not write, run mental why <file>. When the host exposes a session link, pass --ref (https or a repo-relative path). Mental stores it and never fetches it.

Source: skill/mental/SKILL.md and rules/mental.mdc.

Mental CLI is not a todo app. Do not store transcripts. Do not duplicate PLAN.md. Create a decision only when it constrains the future — including a rejected approach titled with the words a later agent will search. Attention is residue, not a backlog.

Mental receipt

Turns that invoked mental end with 🧠 **Mental** then a markdown bullet list: - emoji **Kind** › *action* › title (lowercase italic action). Read with no title omits the third slot (- 🔍 **Read** › *heartbeat*). Do not wrap with ────────. Do not emit </br>.

🧠 **Mental**
- 📓 **Journal** › *recorded* › Resolver landed
- 🚦 **Attention** › *recorded* › Tom said ship

Full table and read-only shape: the skill.

Agent Plugins 1.0.0

The repo root is the plugin root (spec):

  • plugin.json — closed manifest
  • skills/mental-setup/ — bootstrap: install the CLI (immediate child of skills/)
  • skill/mental/ — full procedure, copied by mental install (not plugin-discovered)

Compatible clients (Cursor, VS Code, GitHub Copilot, ChatGPT/Codex, OpenCode, Kiro) load the same directory. Native plugin does not start MCP. Rules and hooks stay client-specific and install via mental install / mental hooks on. Optional MCP is mental serve / mental install --mcp only.

When releasing this repo, git tag vX.Y.Z, package.json, and npm view @balacode/mental version must be the same string. Follow .cursor/rules/release.mdc. README restates that; the agent install paste does not.

In-session panel (Claude Desktop)

The plugin ships claude-mod/, a Claude Code mod: an ES module that draws UI inside the host. It is a host UI API, not MCP, and it only reads: mental heartbeat --json --passive, mental brief --json --hops 8 when the Desktop pane is open, plus mental list --json and mental show <path> --json. The surface decides the look: desktop gets rich cards and a footer button, the terminal gets the plain text pane, mobile is left alone. Park and Hand off send a prompt so the agent writes through the skill. Hide dismisses a concern from the footer only. User steps: Host UIs. The localhost page: Dashboard.

Details for editing it live in .cursor/rules/claude-mod.mdc. Validate with claude plugin validate . --strict. Codex and DeepSeek panels are separate, later work, one host at a time.

Optional MCP

Default off. Skill + rule are enough for agents that can shell.

mental serve            # stdio: heartbeat, pulse, where, status, search, list, show, park, handoff, journal, attention, decide, note, why, brief, move, option, track
mental install --mcp    # register in ~/.cursor/mcp.json + ~/.claude.json (+ ~/.codex/config.toml if present)

MCP is how tool-only agents (parallel sessions, orchestrators) re-pulse and record mid-chat. Tools wrap the same handlers as the CLI. Search/list accept type / status / tag / kind. Tool JSON is compact. option is a tool, but decision-model keys are refused: the user sets those in a terminal. mental uninstall removes only Mental’s MCP entry. Contract: Decision models.

Optional hooks

mental hooks on         # Cursor sessionStart + Claude SessionStart/PreCompact → mental status --json
mental hooks off

Never Stop auto-journal. The skill + rule remain the real contract — Cursor additional_context can drop. Hooks do not start or stop a timer.

Optional time tracking

Default off. If heartbeat JSON includes data.track.enabled, follow the Mental Track skill. Start with short AI-generated internal and customer-ready title/body when runningCount is 0; start twice is ensure-running (a second session joins the existing clock). At park/handoff/journal, regenerate customer copy from the completed work on that same command. Billable defaults to wall; stop --billable suggested uses last_seen elapsed. --accept-stale is a no-op. Ask only when client identity, billable treatment, or safe wording is genuinely ambiguous. Renderer-safe question = one plain-text draft prompt, 2–3 short { id, label } options, (Recommended) in the first label, and allow_multiple: false; use the host’s structured question tool or numbered text fallback. Never invent a start clock or smaller billable duration.

mental track start --title-internal "Auth retry" --body-internal "Tracing retry state." --title-external "Login reliability" --body-external "Improving login retry behavior." --via <host> --json
mental handoff --title "Auth retry fixed" --body "Original error is preserved." --title-external "Improved login reliability" --body-external "Corrected retry handling for consistent login results." --resume "Commit when asked — open loops: none" --via <host> --json

What it cannot do: reconstruct hours from git, fill a missed start from chat, invent a smaller billable duration, or print hours on TTY heartbeat / pulse. Missing customer copy returns structured review JSON; generate and amend it before export. Usage “Time tracking is off” is not permission to run mental option track on.

Human contract: What time tracking can and cannot do. Agent procedure: optional/mental-track/SKILL.md.

JSON envelope

Stable:

{ "ok": true, "data": { } }
{ "ok": false, "error": { "code": "usage", "message": "…" } }

When this CLI is behind npm, a sibling is added (omitted when current or skipped):

{ "ok": true, "data": { }, "update": { "current": "0.4.0", "latest": "0.5.0", "hint": "CLI 0.4.0; npm 0.5.0. Run `mental install` or `npm i -g --force @balacode/mental`." } }

If update is present, tell the user once this session and suggest npm i -g @balacode/mental then mental install then mental doctor. Do not block work. Do not put it on the Mental receipt.

mental doctor may also warn that a Claude Code / Copilot plugin, or a copied skill, is behind this CLI. That is a second channel: native plugin caches are not refreshed by mental install. Suggest the host’s plugin update command. Do not block work.

where data: { root, id, mode, reason, gitRoot }. Write commands may include imported and indexed when a leftover ./.mental/ was ingested.