Mental CLI benchmarks
Mental CLI is a pulse, not a daemon. The number that matters is “how long until I know where I left off?”
These numbers are measured, not estimated. Reproduce them:
npm test
npm run bench
scripts/bench.mjs builds an isolated git repo under a temp HOME, seeds continuity + N notes, and reports p50 / p95. It never writes into your real ~/.mental.
Snapshot (this repo)
Measured 2026-08-27 on Node v22.14.0 (linux/x64, 4 CPUs), Mental CLI 0.2.3. Each row is the p50 of 21 runs after 3 warmup runs. p95 in parentheses.
This Node build’s node:sqlite has no FTS5 module. The index still writes a SQLite concepts + links table; search uses LIKE with title-first ranking. When FTS5 is present, search uses MATCH + bm25 instead. Either way, --type / --status / --tag / --kind apply before the result cap.
| Surface | p50 (p95) | Notes |
|---|---|---|
node spawn (node -e 0) |
14.0 ms (14.6 ms) | Process overhead |
mental where --json |
43.8 ms (45.2 ms) | Fresh Node process |
mental heartbeat --json |
50.5 ms (51.2 ms) | The agent pulse |
mental status --json |
60.1 ms (61.1 ms) | Pulse + notes + writes status/current.md |
mental list --type Decision --json |
44.4 ms (46.4 ms) | Typed filter |
collectHeartbeat (in-process) |
11.2 ms (12.0 ms) | Same function MCP calls after mental serve |
mental search --json (100 notes) |
45.5 ms (46.6 ms) | 6 hits, --type Note |
searchBundle (100 notes, in-process) |
0.47 ms (0.58 ms) | |
mental search --json (500 notes) |
45.9 ms (47.2 ms) | 30 hits |
searchBundle (500 notes, in-process) |
0.64 ms (0.82 ms) | |
mental search --json (2000 notes) |
46.2 ms (47.5 ms) | 50 hits (result cap) |
searchBundle (2000 notes, in-process) |
1.17 ms (1.28 ms) |
Read of the table: a fresh mental heartbeat --json is about 51 ms on this machine, and 14 ms of that is Node starting. The pulse itself is 11 ms. Search at 2,000 notes stays in the same CLI band (~46 ms) because process spawn dominates; in-process search is 1.2 ms.
That is why agents that can keep mental serve warm (MCP) should: the work is cheap; starting Node is most of the CLI cost.
What we measure
- CLI rows —
spawnSync(process.execPath, ["bin/cli.mjs", …]). This is how humans and shell-using agents invoke Mental CLI. - In-process rows —
importofcollectHeartbeat/searchBundle. This is howmental serveruns after the MCP process is up. - Search — query
needlewordwith--type Noteagainst N seeded notes (every 17th note is a hit). Hits reported are what the CLI returned, capped at 50.
What we do not claim
- Not a comparison against another product. Mental CLI’s job is continuity, not grep-replacement throughput.
- Not an FTS5 bm25 number on this host. This Node binary has no
fts5module. The fallback is tested and the numbers above use it. - Not your laptop. Cold disk, antivirus, and a larger journal will move the p50. Run
npm run benchand believe that output.
Tests
npm test
Automated tests on this revision: identity (move, SSH ≡ HTTPS, two clones, fork, worktree, monorepo), leftover import, search filters and title ranking, plugin schemas, version lockstep, install / doctor / uninstall, heartbeat, MCP tools, optional time tracking. FTS5 versus the LIKE fallback is covered; Windows timings are not measured here.
Zero runtime npm dependencies. Node >=22.13.