CLI Reference
Complete command-line interface documentation for NeuralMind.
Table of Contents
- Overview
- Global Options
- Commands
- build
- scan-for-secrets
- query
- wakeup
- search
- benchmark
- probe
- stats
- validate
- doctor
- eval
- ingest-content
- learn — document ingestion
- learn (deprecated, pre-v1.11.0)
- self-improve status
- next
- memory
- cognition-loop
- skeleton
- structural
- last
- recap
- install-hooks
- install-hermes-plugin
- init-hook
- decisions
- drift
- compliance
- ci-check
- export
- watch
- serve
- daemon
- savings
- review
- onboarding
- team governance
- optimize-docs
- license — issuer-side
- Exit Codes
- Environment Variables
- Examples
Overview
NeuralMind provides a command-line interface for building neural indexes, querying codebases with natural language, and managing knowledge graphs.
neuralmind [OPTIONS] COMMAND [ARGS]
Getting Help
# General help
neuralmind --help
# Command-specific help
neuralmind build --help
neuralmind query --help
Global Options
| Option | Description |
|---|---|
--help |
Show help message and exit |
--version |
Show version number |
Commands
audit recent (v3.1.4+)
Query recent audit trail events.
Arguments
| Argument | Required | Default | Description |
|---|---|---|---|
-n |
No | 10 |
Number of events to show |
--category |
No | — | Filter by category (backend, compliance, audit) |
--action |
No | — | Filter by action (build, query, export) |
--actor |
No | — | Filter by actor |
--since |
No | — | Filter by timestamp (ISO 8601) |
Output
Recent audit events in tabular format. (v4.13.0+) A Client column names
the MCP client that made each mcp_call (the clientInfo name it sent when
it connected, e.g. claude-code or hermes), so two agents pointed at one
project can be told apart; it shows - for CLI, hook and daemon events. The
full {"name", "version"} is in details.client (--json, audit export).
Examples
# Show last 10 audit events
neuralmind audit recent
# Show last 20 events since a specific date
neuralmind audit recent -n 20 --since "2026-08-01"
# Show only backend builds
neuralmind audit recent --category backend --action build
Prerequisites
Requires an initialized project with audit trail.
audit verify
Check the hash chain of .neuralmind/audit_events.jsonl.
neuralmind audit verify [project_path] [--json]
Arguments
| Argument | Required | Default | Description |
|---|---|---|---|
project_path |
No | . |
Project root |
--json, -j |
No | — | Print the result as JSON |
Exit codes
| Code | Meaning |
|---|---|
0 |
Every hashed record checks out |
1 |
A line fails: it isn’t a JSON object, its hash or prev_sha256 doesn’t match, or it has no hash after the chain started |
Output
✓ Audit trail integrity OK (N events). Extra lines say how many records come
before the hash chain (written by versions before v0.46.2, so the chain doesn’t
cover them), and which rotated archive the log continues, if any, and whether
its link to that archive was checked (it can’t be once the archive is pruned).
On failure, the line number in the file and the reason go to stderr. --json
prints ok, first_bad_line, total, unchained, continues_from,
archive_checked and reason. first_bad_line is null when the file can’t
be read at all.
v4.8.0 and earlier accepted a record without a hash anywhere in the log,
skipped lines they couldn’t parse, and never compared prev_sha256. So records
edited or appended at the end with their hash removed, and garbage appended to
the log, passed.
What it can’t detect
Records deleted from the end, or a chain recomputed by anyone who can write the
file (the hash has no secret key). Keep an exported copy off the host with
neuralmind audit export . -o audit.jsonl. See the
Security Guide.
build
Build or rebuild the neural index from a knowledge graph.
neuralmind build <project_path> [OPTIONS]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root containing graphify-out/graph.json |
Options
| Option | Default | Description |
|---|---|---|
--force, -f |
False | Force re-embedding of all nodes, even if unchanged |
--content-type |
auto |
What kind of project to build: auto (detect — book mode kicks in when markdown outweighs code ≥3:1, counting both recursively with vendored/state dirs pruned), book (force book mode: code scope for the engine, content scope for chapters), code, content |
--dry-run |
False | Scan the project and estimate token savings without building the index (v0.39.0+). Since v4.5.0 it scans the files a build would index (.gitignore applied), reports Excluded by .gitignore: ~N nodes in M files (dir/ count, …), and divides by the measured token count of those files |
--naive-50k |
False | (v4.5.0+) With --dry-run, divide by the fixed 50,000-token estimate instead |
--redact-secrets |
False | Replace detected credentials with a [REDACTED:kind] marker in text entering the index, on all three backends. Equivalent to NEURALMIND_REDACT_SECRETS=1. Off by default because redacting costs recall on legitimately secret-shaped identifiers — run neuralmind scan-for-secrets first; removing and rotating the credential is the actual fix. |
--regenerate-graph |
False | Always rebuild .neuralmind/graph.json with the built-in tree-sitter backend, whatever graph exists — the way out of a stale graphify graph. graphify-out/ is never written (v4.4.0+) |
--strict |
False | Exit 3 before embedding when the code graph FAILs the freshness check (v4.4.0+) |
--prune |
False | Remove orphaned vectors even when they are more than half the store; otherwise they’re kept with a warning as a safety valve against a graph that shrank by mistake (v4.4.0+) |
--archive-graphify |
False | (v4.13.0+) First rename an unused graphify-out/graph.json to graph.json.archived-<UTC timestamp> (the rest of graphify-out/ is left alone; renaming it back restores it), then build. Refused under graph_source: graphify, where that file is the graph |
--json, -j |
False | Emit structured JSON output (for --dry-run) |
Output
The first line names the graph the build used (v4.4.0+):
Graph: .neuralmind/graph.json (built-in, incremental, 8,351 nodes)
Graph: graphify-out/graph.json (graphify, read-only, 3,197 nodes)
When the graph isn’t in step with the files on disk, the freshness report
follows (see doctor). Then the build statistics:
- Number of nodes processed
- Delta: new / updated / skipped / removed —
removedcounts vectors purged because their node left the graph (v4.4.0+) - Number of communities indexed
- (v4.13.0+) The synapse store after the build, and how it changed since the
previous build (what queries, edits and the build’s own seeding added, less
decay):
Synapses: 3,612 edges (+47 since last build), 12 transitions (+12). The first build after upgrading shows totals only. With 0 transitions it says where they come from: the files an agent reads and edits in turn (the Claude Code hooks; the Hermes plugin, from reads and edits) or that change together (neuralmind watch). Queries strengthen edges but add no transitions. - Build time elapsed
(v4.13.0+) The closing hint for files that change without a commit is now
neuralmind watch . --reindex. Earlier versions suggested plain
neuralmind watch ., which learns from edits but doesn’t re-index them, so
the index went on drifting.
Graph source (v4.4.0+)
graph_source in .neuralmind.yaml picks which graph the index is built from:
| Value | Behaviour |
|---|---|
auto (default) |
.neuralmind/graph.json when present, else graphify-out/graph.json. A graphify graph that FAILs the freshness check is replaced by a built-in graph at build time (when there is code on disk to parse), and the build prints why |
builtin |
Always the tree-sitter graph; graphify-out/ is never read |
graphify |
Only graphify-out/graph.json; the build fails if it’s missing |
NeuralMind never switches source silently: if the previous build used the
built-in graph and it has gone, the build stops and asks for
--regenerate-graph or graph_source: graphify.
(v4.13.0+) Under auto, once .neuralmind/graph.json exists a
graphify-out/graph.json left from a graphify-era build (v4.3.x and earlier
built from it) is never read again. Every build now says so, until it is dealt
with:
Ignoring graphify-out/graph.json: graph_source: auto uses .neuralmind/graph.json once it exists, so nothing reads the graphify graph. Archive it with `neuralmind build . --archive-graphify`, or set graph_source: builtin (or graphify) in .neuralmind.yaml.
doctor’s Graph source check warns about the same file. Setting
graph_source explicitly is a choice already made, so neither warns then.
What gets indexed: .gitignore (v4.5.0+)
The index covers what git covers. Inside a git repo the file list comes from
git ls-files --cached --others --exclude-standard — nested .gitignore,
.git/info/exclude and the global excludes apply exactly as in git — minus
tracked files that match an ignore rule (force-added copies under an ignored
directory). Outside git, or when the project sits in a directory an enclosing
repository ignores (a scratch dir, a vendored checkout), the project’s own
top-level .gitignore is applied with the same pattern rules. .neuralmindignore narrows further and, since v4.5.0, uses
gitignore semantics too (! negation, / anchoring, **; * doesn’t cross
/). The first build after upgrading prints what was excluded, once.
# .neuralmind.yaml
respect_gitignore: false # index ignored files again (v4.4 behaviour)
include_ignored: # or pull specific ignored paths back in
- "generated/api/**"
How long the index may lag: stale_after (v4.13.0+)
# .neuralmind.yaml
stale_after: 30m # s, m, h or d; a bare number is refused
A value that isn’t a duration (stale_after: 30) turns no gate on, and says
so: doctor’s Code graph check warns stale_after: '30' in .neuralmind.yaml
isn't a duration, and health prints the same on stderr and returns it as
stale_after_invalid in --json.
The index is behind from the moment a file it hasn’t seen is written: a new
file, or one changed since the graph was built (dated by its modification
time; a graph changed after the last build, by the graph’s). A file rewritten
while it waits doesn’t restart the clock: with a limit set, health and
doctor keep the time each unindexed file was first seen in
.neuralmind/drift_seen.json (the only thing either writes, and only when
.neuralmind/ exists), drop a file once it is indexed, and delete the record
when nothing drifts. stale_after
says how long that may last before the gates trip:
neuralmind doctorfails the Code graph check (exit1) once the index has been behind for longer, addingbehind for 3h, over stale_after 30m. Within the limit the check is as before: WARN for small drift, FAIL past 10% of files.neuralmind healthexits0with statuscatching_upwhile the index is behind for no longer than that, and1after. Withoutstale_afterit exits1on any drift, as before.
Drift with no date of its own (files deleted from disk, a graph built on
another OS) is never “within” the limit, and doctor says so instead of a
time. --stale-after on either command overrides the setting; the MCP
neuralmind_health tool follows the setting too. For a project whose files change without commits, run
neuralmind watch . --reindex and set stale_after a little above its
debounce.
Git can’t re-include a file under an excluded directory: to ignore a directory
except one file, write projects/* then !projects/keep.py, not projects/.
Examples
# Basic build
neuralmind build /path/to/project
# Force complete rebuild
neuralmind build /path/to/project --force
# Replace a stale graphify graph with the built-in one (v4.4.0+)
neuralmind build /path/to/project --regenerate-graph
# CI: stop with exit 3 if the code graph is out of step with the code
neuralmind build /path/to/project --strict
# Estimate token savings without building (Gap 1: 1-click setup)
neuralmind build /path/to/project --dry-run
# → NeuralMind dry run — my-project
# → Files scanned : 142
# → Lines of code : 18,342
# → Languages : 89 Python, 53 TypeScript
# → Est. token reduction : ~42x per query
# → No index was built. Run `neuralmind build .` to activate these savings.
Prerequisites
As of v0.15.0, none beyond pip install neuralmind. When no
graphify-out/graph.json exists, build auto-generates .neuralmind/graph.json
with the bundled tree-sitter backend (neuralmind/graphgen.py) and prints:
Graph: .neuralmind/graph.json (built-in, generated, 1,240 nodes)
Backend precedence:
- A real graphify graph always takes priority where present (
graphify update /path/to/project). - Otherwise the built-in tree-sitter backend generates the graph. It indexes Python, TypeScript, Go, Rust, Java, C, C++, C#, Ruby, and PHP (
.py,.ts/.tsx,.go,.rs,.java,.c/.h,.cpp/.cc/.cxx/.hpp/.hh/.hxx,.cs,.rb,.php) out of the box (Java added in v0.28.0; C and C++ in v0.32.0; C# in v0.35.0; Ruby in v0.36.0; PHP in v0.37.0); more grammars register behind theSUPPORTED_SUFFIXESseam. A mixed-language repo is indexed in one pass. Schema artifacts (v0.40.0+) are indexed alongside code asdocumentnodes: OpenAPI/AsyncAPI specs (.yaml/.ymlwith anopenapi/asyncapi/swaggerkey) emit nodes per path+method, schema component, and channel; SQL DDL (.sql) emits one node perCREATEobject; Protocol Buffers (.proto) emit nodes permessage,service,rpc, andenum. Plain YAML config files are silently skipped. --forcere-embeds; it never touches the graph source.--regenerate-graph(v4.4.0+) replaces a graphify graph with a built-in one in.neuralmind/, leavinggraphify-out/untouched, andgraph_source(above) pins the choice.- An empty/non-code project writes no graph, so you still get the “no graph” guidance rather than a silent 0-node success.
- Optional precision (v0.17.0+): set
NEURALMIND_PRECISION=1and place a*.scipindex (fromscip-python/scip-typescript/scip-go) in the project root to replace the built-in backend’s heuristiccalls/inheritsedges with compiler-accurate ones for the files the index covers. Off by default. -
Secret redaction (opt-in):
--redact-secrets(orNEURALMIND_REDACT_SECRETS=1) replaces detected credentials with[REDACTED:<kind>]in embedded text — document chunks and node descriptions — on all three backends. Off by default, because redacting costs recall on legitimately secret-shaped identifiers.What it does not cover. Node labels,
graphify-out/graph.jsonand.neuralmind/index_ir.jsonare written before the embedding step, so a credential inside a symbol name or docstring still reaches those files verbatim (on every backend — this is not backend-specific). Verify withgrep -r '<the-secret>' graphify-out/ .neuralmind/after a build if it matters to you.The flag is a backstop for text that reaches the vector store, not a guarantee that no artifact under the project holds the credential. Run
neuralmind scan-for-secrets .first — removing and rotating is the actual fix. The build also warns if git is already tracking files under.neuralmind/.
scan-for-secrets
Report credentials present in a project’s files, so they can be removed and rotated before they reach the index, a commit, or an agent’s context.
neuralmind scan-for-secrets [PROJECT_PATH] [--high-confidence-only] [--strict]
[--include-ignored] [--use-neuralmindignore] [--json]
| Option | Effect |
|---|---|
PROJECT_PATH |
Project root (default .). |
--high-confidence-only |
Report only vendor-shaped credentials; suppress the generic-assignment heuristic. |
--strict |
Exit non-zero on heuristic findings too (default: high-confidence only). |
--include-ignored |
Also scan files .gitignore excludes, for a full sweep (v4.15.0). By default a scan inside a git repository covers what can be committed and what build indexes. |
--use-neuralmindignore |
Also apply .neuralmindignore globs. Off by default — see below. |
--json / -j |
Machine-readable output. |
NeuralMind secret scan — /home/dev/myproject
[HIGH ] scratch.py:1 anthropic-api-key (sk-a…(43 chars)) [untracked]
[HIGH ] src/config.py:8 aws-access-key-id (AKIA…(20 chars)) [tracked]
[maybe] src/db.py:34 generic-secret-assignment (9f8K…(28 chars)) [tracked]
2 high-confidence (1 in tracked files), 1 heuristic.
...
Skipped 1 linked worktree (.worktrees/a1): other checkouts of a repository; run the scan inside one to check it.
Skipped 1 file that .gitignore excludes (never committed or indexed). --include-ignored scans them too.
What a scan covers (v4.15.0). Inside a git repository, the files
build indexes, from the same git ls-files listing: tracked files, and
untracked files .gitignore doesn’t exclude. A gitignored .env can’t be
committed and isn’t indexed, so it isn’t reported unless you pass
--include-ignored; the report always ends with how many ignored files it
skipped, so a clean scan never hides them. Linked worktrees inside the
checkout (.worktrees/<id>/, .claude/worktrees/<id>/, any directory whose
.git file names a git worktree add checkout) are skipped even with
--include-ignored, because each is another copy of a repository and would
repeat every finding; they are listed instead. Submodules and nested
repositories are scanned. Outside a git repository everything is read.
Each finding says whether git tracks its file (v4.15.0): [tracked] or
[untracked], and "tracked": true | false in --json (null outside a
repository). A credential in a tracked file is already in history, or one
commit from it, so rotate that one first. --json also reports
gitignore_respected, ignored_files_skipped and skipped_worktrees.
Two confidence tiers. HIGH matches a vendor-specific shape and
effectively never fires on prose: Anthropic and OpenAI keys, AWS access
key IDs, secret keys and session tokens (including the
"SecretAccessKey" / "SessionToken" JSON the AWS CLI prints, v4.8.1),
GitHub tokens and fine-grained PATs, GitLab PATs and Hugging Face tokens
(v4.8.1), Slack tokens, Google API keys, Stripe keys, PyPI and npm
tokens, PEM private-key blocks, JWTs, Authorization: Bearer/Basic
headers, and passwords embedded in database or http(s)://user:password@host
URLs. maybe matches a generic SECRET=value assignment, including a quoted
JSON or dict key such as {"password": "…"} (v4.8.1), that cleared a
Shannon-entropy threshold and a placeholder denylist — so
password = "changeme", api_key = os.environ["X"], and KEY=${VAR} are
not reported.
Previews never include the tail of a secret — only a short prefix and a length — so scan output is safe to paste into an issue or a CI log.
Exit codes, so this works as a CI gate:
| Condition | Exit |
|---|---|
| No findings | 0 |
| Heuristic findings only | 0 (1 with --strict) |
| Any high-confidence finding | 1 |
| Path does not exist | 2 |
The scanner reads files directly rather than going through the indexer, so
it sees a .env that isn’t gitignored, and other files build never parses
(a gitignored one with --include-ignored). It skips the usual
vendored/build directories (node_modules, .venv, dist, target,
.git, .neuralmind), binary files, and anything over 5 MB.
.neuralmindignore is not applied by default. That file is tuned for
retrieval quality, not security — NeuralMind’s own copy excludes docs/,
*.md and tests/ because markdown dilutes code retrieval. A scanner that
inherited it would skip exactly where credentials tend to sit and report a
false all-clear. Pass --use-neuralmindignore if you want those globs
applied anyway.
Related: neuralmind build . --redact-secrets scrubs detected credentials
out of indexed text as a backstop — it does not remove the credential from
your working tree, and it is not a substitute for rotating a key that has
already been exposed.
query
Query the codebase with natural language and get optimized context.
neuralmind query <project_path> "<question>" [OPTIONS]
Type Filter (v3.1.4+)
Steer the ranking toward a node type (results are ranked, not filtered, so other types can still appear):
| Flag | Description |
|---|---|
--type code |
Rank source code first |
--type docs |
Rank documentation first |
--type auto |
Auto-detect intent (default) |
(v4.8.1+) code and docs replace the detected intent before L3 is ranked.
Before v4.8.1 they re-boosted the hits after the context was built, so the
returned context didn’t change.
Cross-Project Search (v3.1.4+)
Query across multiple indexed projects:
neuralmind query --projects /repo/a,/repo/b "authentication logic"
Results are tagged with source project and deduplicated by ID.
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root |
question |
Yes | Natural language question about the codebase |
Options
| Option | Default | Description |
|---|---|---|
--json, -j |
False | Output results as JSON |
--trace |
False | (v0.23.0+) Attach a per-layer retrieval trace (see below) |
--trace-verbose |
False | (v0.23.0+) With --trace, keep full candidate/hit lists |
--explain |
False | (v0.39.0+) Human-friendly breakdown of token savings, layers used, top hits, and synapses that fired (implies --trace). (v4.6.0+) Also prints the query intent L3 ranked with and how it was decided, and labels each top hit with its name and file. (v4.13.0+) With --json, the same breakdown is an explain object (budget, layers, intent, communities_loaded, top_hits with scores, synapses_fired); before, --json dropped it |
--relevance |
False | (v0.41.0+) With --json, attach a structured relevance sidecar (per-file, per-node score/synapse-boost/recall + line spans) so a downstream compressor can protect the load-bearing spans (see below) |
--mode |
default |
(v3.8.0+) default uses the context selector against one scope; unified searches the content scope and the code scope together and merges results — for a project that mixes prose (via ingest-content) and code |
--scope-bias |
balanced |
(v3.8.0+) With --mode=unified: content, code, or balanced — weights which scope’s hits rank higher in the merged results |
--chapter |
None | (v3.8.0+) With --mode=unified: filter results to a specific chapter tag, e.g. --chapter="Chapter 2 — The Corner Pub" |
--no-learn |
False | (v4.5.0+) Read-only query: synapse recall still shapes the context (you get exactly what a normal query returns), but nothing is reinforced, no query log is written, and the synapse database is opened read-only (mode=ro). Never builds — on an unbuilt project it says so. For evals, benchmarks and CI; same as NEURALMIND_NO_LEARN=1 |
Unified search mode (v3.8.0+)
For a project indexed with both neuralmind build (code) and
neuralmind ingest-content (prose — see v3.4.0), --mode=unified queries
both scopes and merges the results instead of picking one by default:
neuralmind query . "how does the corner pub scene end?" --mode=unified --scope-bias=content
neuralmind query . "what handles the payment retry?" --mode=unified --scope-bias=code
neuralmind query . "who visits the pub?" --mode=unified --chapter="Chapter 2 — The Corner Pub"
Output
Returns:
- Optimized context text for AI consumption
- Token count and breakdown by layer
- Reduction ratio compared to full codebase
- Communities/modules loaded
Retrieval traces (v0.23.0+)
--trace explains why a result came back (PRD 3) — useful when retrieval
surprises you. It records, layer by layer:
- candidates — the raw vector-search pool (ids + scores);
- cluster_scores — per-cluster score with vector-vs-synapse attribution (how much of each cluster’s score came from learned co-activation);
- synapse_boost — individual co-activation boosts;
- hits — the final ranked hits, flagging which were synapse-recalled;
- budget — tokens per layer + reduction ratio.
Plain --trace prints a compact per-layer summary; --json includes the full
trace object (bounded, and path-redactable via the RetrievalTrace API for
sharing in bug reports). Tracing is off by default and zero-overhead. The
daemon’s /query honors trace too, so daemon and direct mode return the same
attribution.
Relevance sidecar (v0.41.0+)
--relevance (with --json) attaches a structured relevance block so a
downstream compressor can tell which spans are load-bearing and must
survive compression. NeuralMind already computes a vector score, a learned
synapse boost, and a recall flag per retrieved node; the sidecar exposes
them as machine-readable metadata keyed by source file (plus best-effort line
spans from the graph), built from the post-boost L3 hits so it reflects the
same signals the rendered context used:
{
"relevance": {
"version": 1,
"files": {
"auth/handlers.py": {
"max_score": 1.02,
"nodes": [
{"node_id": "…", "label": "authenticate", "score": 0.87,
"synapse_boost": 0.15, "recalled": true, "lines": [42, 68]}
]
}
}
}
}
The version field guards the wire shape; the stable files{} / node_id
keys let a tool running after NeuralMind re-associate the signal regardless of
pipeline order. The same block is available over MCP via
neuralmind_query(include_relevance=true). Off by default — query output is
unchanged unless requested. --relevance and --explain both run in direct
mode (they need the full result the daemon’s thin response omits).
Examples
# Basic query
neuralmind query /path/to/project "How does authentication work?"
# JSON output
neuralmind query /path/to/project "What are the main API endpoints?" --json
# Explain the retrieval path (raw trace)
neuralmind query /path/to/project "How does billing work?" --trace
neuralmind query /path/to/project "How does billing work?" --trace --json
# Human-friendly explanation of why this context was chosen (v0.39.0+)
neuralmind query /path/to/project "auth flow" --explain
# → Why this context?
# → Token budget breakdown:
# → L0 identity : 142 tokens
# → L1 summary : 513 tokens
# → L2 communities: 800 tokens
# → L3 search : 980 tokens
# → Total used : 2,435 tokens
# → Est. saved : 47,565 tokens (20.5x reduction)
# → Query intent : code (by classifier)
# → Top search hits (L3, 4 nodes):
# → 0.912 authenticate (auth/handlers.py)
# → 0.887 JWTMiddleware (auth/middleware.py)
Query intent (v4.6.0+). --explain says which intent a question was
given and how it was decided. (v4.12.0+) Only a requested intent
(--type code|docs, or query_type over MCP) re-weights L3’s candidates — a
docs request multiplies doc hits by 2.0 and code hits by 0.7. A detected one is
shown but changes no score unless NEURALMIND_AUTO_INTENT_BOOST=1:
| Shown as | Decided by |
|---|---|
code (by classifier) / docs (by classifier) |
the v3.9.0 pattern classifier |
… (by keywords) |
the older keyword count, when the classifier calls the question hybrid |
code (by question shape) / docs (by question shape) |
the opt-in intent rules, NEURALMIND_INTENT_RULES=1 (see Environment Variables) |
Before v4.12.0 a detected docs intent multiplied doc hits by 2.0 and code by
0.7 within L3’s four hits, which is why a README could rank above the
implementation of a “how does X work” question; v4.12.0 stopped that (the
retrieval eval lost 3 questions with it) and widened L3 to eight hits chosen
from 20 candidates. The hit list prints each hit’s label and file; before
v4.6.0 it printed raw node ids.
Sample Output
=== Query: How does authentication work? ===
[Context]
# Project: MyApp
MyApp is a web application with JWT-based authentication...
[Authentication Module]
The auth module handles user login, token generation, and validation...
[Relevant Code]
- auth/jwt_handler.py: JWT token generation and validation
- auth/middleware.py: Authentication middleware for routes
- models/user.py: User model with password hashing
---
Tokens: 847 | Reduction: 59.0x | Layers: L0, L1, L2, L3 | Communities: [5, 12]
wakeup
Get minimal wake-up context for starting a new conversation.
neuralmind wakeup <project_path> [OPTIONS]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root |
Options
| Option | Default | Description |
|---|---|---|
--json, -j |
False | Output results as JSON |
Output
Returns L0 (Identity) + L1 (Summary) context, typically ~600 tokens, suitable for:
- Starting new AI conversations
- Providing project context to coding assistants
- Initial system prompts
Examples
# Get wake-up context
neuralmind wakeup /path/to/project
# Redirect to file
neuralmind wakeup /path/to/project > context.md
# JSON format
neuralmind wakeup /path/to/project --json
Sample Output
=== Wake-up Context ===
# Project: MyApp
MyApp is a full-stack web application for task management built with React and Node.js.
## Architecture Overview
### Core Components
- **Frontend**: React 18 with TypeScript, Tailwind CSS
- **Backend**: Node.js/Express REST API
- **Database**: PostgreSQL with Prisma ORM
- **Auth**: JWT-based authentication
### Main Modules
1. User Management (users/) - Registration, profiles, settings
2. Task Engine (tasks/) - CRUD operations, scheduling, notifications
3. API Layer (api/) - REST endpoints, middleware, validation
---
Tokens: 412 | Layers: L0, L1
search
Perform direct semantic search across codebase entities.
neuralmind search <project_path> "<query>" [OPTIONS]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root |
query |
Yes | Semantic search query |
Options
| Option | Default | Description |
|---|---|---|
--n |
10 | Maximum number of results. (v4.8.2+) Must be at least 1; 0 or a negative value is a usage error (it used to return one result) |
--json, -j |
False | Output results as JSON |
Output
Returns matching code entities with:
- Entity name and type
- Similarity score (0-1)
- File path
- Community membership
Examples
# Basic search
neuralmind search /path/to/project "authentication"
# Limit results
neuralmind search /path/to/project "database connection" --n 5
# JSON output
neuralmind search /path/to/project "API endpoint" --json
Sample Output
=== Search: authentication ===
1. authenticate_user (function) - Score: 0.92
File: auth/handlers.py
Validates user credentials and returns JWT token
Community: 5 (Authentication)
2. AuthMiddleware (class) - Score: 0.87
File: auth/middleware.py
Express middleware for JWT validation
Community: 5 (Authentication)
3. hash_password (function) - Score: 0.81
File: utils/crypto.py
Securely hashes passwords using bcrypt
Community: 5 (Authentication)
---
Results: 3 | Query time: 45ms
benchmark
Run performance benchmark with sample queries.
neuralmind benchmark <project_path> [OPTIONS]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root |
Options
| Option | Default | Description |
|---|---|---|
--json, -j |
False | Output results as JSON |
--naive-50k |
False | (v4.5.0+) Divide by the fixed 50,000-token estimate instead of the measured token count of the code the index covers — for comparison with pre-v4.5.0 numbers |
--contribute |
False | Print your numbers and a schema-ready entry for docs/community-benchmarks.json. Nothing is uploaded. From v4.5.0 the entry is v2: avg_reduction_ratio against the fixed 50K estimate every row compares on, plus measured_avg_reduction_ratio and full_codebase_tokens |
--quality |
False | (v0.23.0+) Quality-eval mode — see below |
--suite |
(all) | (v0.23.0+) With --quality, run one suite: python / typescript / go |
--baseline |
— | (v0.23.0+) With --quality, a saved suite JSON to compare against (reports metric deltas) |
--public |
False | (v0.31.0+) Public-benchmark mode — reproduce the honest vs-alternatives comparison on pinned real repos (see below). Ignores project_path (it uses the pinned corpus, not your project) and requires a source checkout — the evals/public harness ships in the repo, not the PyPI wheel |
--repo |
(all) | (v0.31.0+) With --public, scope to one corpus repo: requests / click |
--seeds |
1 |
(v0.31.0+) With --public, the seed count recorded in the report. The pipeline is deterministic (synapse injection off), so variance across seeds is exactly 0 — recorded honestly rather than padded with artificial noise |
--judge |
False | (v0.34.0+) With --public, also run the opt-in answerability arm — each backend answered from its real window by a pinned model (claude-opus-4-8), graded vs. the def-site gold anchor (0–2 + grounded). A clearly-labeled secondary signal. Needs ANTHROPIC_API_KEY; never runs in CI; the recall table is byte-identical with or without it; skips cleanly without a key (see below) |
--judge-out |
bench/public/judge |
(v0.34.0+) With --public --judge, where to write the raw answerability transcripts (question, context tokens, answer, verdict, rationale) |
Output
Comprehensive benchmark report including:
- The baseline it divides by (v4.5.0+) —
Baseline: measured: 94,069 tokens in 36 indexed code files: the token count of every code file the index covers, measured at build, at the same ~4 chars/token as the context. Prose (Markdown, reStructuredText, plain text) is left out unless the index holds nothing else, and an index built before v4.5.0 is measured on the spot without writing anything. Before v4.5.0 every ratio divided by a fixed 50,000-token guess whatever the repo’s size - The legacy ratio beside it (v4.5.0+) —
Legacy reduction: 41.8x (vs the fixed 50K-token estimate used before v4.5.0), so older numbers and the community table stay comparable;--naive-50kmakes the fixed estimate the headline instead - Which questions it asked — the ones in
.neuralmind.eval.yamlwhen the project has one (seeeval), else five generic questions - Token counts and reduction ratios for each query
- Benchmark queries are read-only (v4.5.0+): they never train the synapse layer
--json carries avg_reduction_ratio with the baseline it used (baseline,
and full_codebase_tokens), plus legacy_avg_reduction_ratio against
estimated_full_codebase_tokens (always 50,000); every entry in results has
its own reduction and legacy_reduction.
Examples
# Run default benchmark
neuralmind benchmark /path/to/project
# JSON output
neuralmind benchmark /path/to/project --json
On psf/requests v2.32.3 (verbatim, v4.5.0):
Project: requests
Baseline: measured: 94,069 tokens in 36 indexed code files
Questions: generic
Wake-up tokens: 510
Avg query tokens: 1200.6
Avg reduction: 78.6x
Legacy reduction: 41.8x (vs the fixed 50K-token estimate used before v4.5.0)
Summary: 78.6x average token reduction vs measured: 94,069 tokens in 36 indexed code files
Quality-eval mode (v0.23.0+)
--quality switches the command from token-reduction benchmarking to
retrieval-quality measurement: does NeuralMind surface the right code,
not just less of it? It scores precision@k, recall@k, MRR, and
answerability over golden query suites (Python / TypeScript / Go — 30
queries with expected-module labels) and exits non-zero if a suite regresses
past its floor, so CI can gate retrieval-affecting changes.
Like neuralmind eval, this is a contributor/CI self-test that runs against
the golden suites shipping with the source repo (the evals/quality/
package), not the installed wheel. The pure metrics live in
neuralmind.quality (from neuralmind import quality).
# Score all golden suites
neuralmind benchmark --quality
# One language, machine-readable
neuralmind benchmark --quality --suite go --json
# Compare against the committed measured baseline (reports metric deltas)
neuralmind benchmark --quality --baseline evals/quality/baseline.json
# Dependency-free validation of the suites + metric math (no embeddings)
python -m evals.quality.runner --selfcheck
Sample (markdown) output — measured on the committed fixtures:
## NeuralMind retrieval-quality eval
| Suite | Queries | MRR | Answerability | Recall@5 | Precision@5 | Gate |
|-------|--------:|----:|--------------:|---------:|------------:|:----:|
| `go` | 10 | 0.950 | 100% | 0.833 | 0.603 | PASS |
| `python` | 10 | 0.900 | 100% | 0.833 | 0.612 | PASS |
| `typescript` | 10 | 0.900 | 100% | 0.800 | 0.562 | PASS |
**Overall: PASS**
The exit code is non-zero if any suite drops below the floors in
evals/quality/harness.py (DEFAULT_THRESHOLDS), so CI can gate on it. The
measured baseline lives at evals/quality/baseline.json; the self-benchmark
workflow runs this on every PR (where real embeddings are available) and posts
the table + baseline deltas as a PR comment.
Public-benchmark mode (v0.31.0+)
--public runs the honest public benchmark: a reproducible, no-cherry-picking
comparison of how much context different approaches put in an agent’s window to
answer a real code question, and whether the objectively-correct file actually
makes it in. It clones real, pinned OSS repos at fixed commit SHAs
(requests @0e322af877, click @874ca2bc1c) and scores cost and
correctness together against strong baselines: full-file paste, ripgrep,
a same-encoder embedding-rag, and neuralmind’s progressive disclosure.
Gold-file recall is an objective def-site oracle — each query’s gold file is
the definition site of a named symbol, verifiable with one rg; there is no
LLM judge. Scoring reuses neuralmind/quality.py verbatim — the same metric
code the CI quality gate runs. Pre-registered queries live in
evals/public/manifest.json; every one is reported, losses included.
# Clone the pinned repos and print the full table
neuralmind benchmark --public
# Scope to one repo
neuralmind benchmark --public --repo click
# Machine-readable output (for CI / further analysis)
neuralmind benchmark --public --json
# Equivalent module entrypoint (from a clone)
python -m evals.public.run
The run is deterministic — synapse injection is OFF (session-dependent
learning can’t be a fixed, reproducible public number; its lift is
measured separately by the synapse A/B eval, tests/benchmark/run.py Phase 2).
This reuses the same NEURALMIND_SYNAPSE_INJECT=0 toggle documented in the
Environment Variables table. Re-running on the same
machine matches the published table to the token. Across machines, recall and
found-rate have matched exactly, while token counts differ slightly on the
GitHub-hosted CI runners without AVX-512 (up to 0.8% on a per-repo mean for the
v4.10.0 run; an Apple M3, also without AVX-512, matched exactly; the v4.12.0
run hasn’t been compared across machines yet);
NEURALMIND_ORT_THREADS=1 matches CI’s configuration — see
how exactly a re-run reproduces.
Honest headline: against what agents actually do today — paste files or grep
— NeuralMind finds the gold file on 40 of 40 queries at 51–242× fewer
tokens than pasting every source file, and beats ripgrep on
cost on every repo; on recall it’s ahead on 3 of 4 repos and ties on the fourth.
The benchmark also reports, without hiding it, that NeuralMind’s own vector index
with nothing added (embedding-rag) ranks the gold file higher on flask and
rich at fewer tokens. Forty queries is a small sample: the 150-question
retrieval eval misses 10, published. Full methodology,
results, honest caveats, and “where NeuralMind loses” are published at
docs/benchmarks/public.md;
raw per-query data is committed at bench/public/results.json, and the forkable
runner is .github/workflows/bench-public.yml.
Competitor head-to-head (v0.33.0+)
The benchmark also ships a live, reproducible row vs. codebase-memory-mcp
0.8.1 (the obvious incumbent), on the same pinned repos, questions, def-site
gold, and quality.py scorer, at retrieval depth matched to embedding-rag
(top-8). It lives in a separate module and is off the default run because
it downloads an external binary — invoke it explicitly from a clone:
pip install codebase-memory-mcp==0.8.1 # pins 0.8.1 — on-device embeddings, no API key
python -m evals.public.competitor # prints the competitor row; fails closed without the binary
On reproducible retrieval ranking NeuralMind reaches 100% gold-file recall
and ranks the right file far higher (MRR 0.96 vs 0.23 on requests, 0.60
vs 0.50 on click), while the competitor surfaces the gold file in its top-8
only ~half the time, at an order of magnitude more read cost. Honest framing:
this measures pure retrieval — no LLM agent loop on either side, exactly how we
test NeuralMind’s own search; we used the competitor’s most-favorable
reproducible keyword mapping; and we cite its published LLM-agent numbers (~90%
of an “Explorer” agent; C at 0.58) as-is rather than reproduce them. Per-query
traces and pinned REPRODUCE.md are committed under bench/public/competitor/;
full caveats are in the “Competitor head-to-head” section of
docs/benchmarks/public.md.
Answerability arm — --judge (v0.34.0+)
Gold-file recall measures locating the right file, not answering the question. The opt-in answerability arm adds the answering signal — a clearly-labeled secondary to the recall headline:
ANTHROPIC_API_KEY=… python -m evals.public.run --judge # off by default
For each query it answers from the real context each backend would put in the
window (whole files / retrieved chunks / compact L0–L3 context) using a pinned
model (claude-opus-4-8), constrained to that context only, then a separate
judge call grades the answer against the same def-site gold anchor on a 0–2 scale
plus a grounded flag. It needs ANTHROPIC_API_KEY (and the anthropic
package), never runs in CI, and the recall table is byte-identical with or
without --judge — absent a key it skips cleanly and the recall benchmark still
runs. The answerer prompt, judge rubric, pinned model id, and every raw
transcript are committed under bench/public/judge/ (transcripts written to
--judge-out, default bench/public/judge). Full framing + caveats: the
“Answerability arm” section of
docs/benchmarks/public.md.
Sample Output
=== NeuralMind Benchmark ===
Project: MyApp
Total Nodes: 241
Communities: 93
Estimated Full Codebase: 50,000 tokens
| Query Type | Tokens | Reduction | Latency |
|------------|--------|-----------|----------|
| Wake-up context | 341 | 146.6x | 45ms |
| How does authentication work? | 739 | 67.7x | 187ms |
| What are the main API endpoints? | 748 | 66.8x | 192ms |
| Explain the database models | 812 | 61.6x | 201ms |
|------------|--------|-----------|----------|
| **Average** | **660** | **85.7x** | **156ms** |
---
Benchmark completed in 625ms
probe (v0.27.0+)
Run a retrieval self-probe on your own codebase: does the index actually
find the right file when the agent asks about a symbol? Unlike benchmark
(which measures token reduction) and benchmark --quality (which scores ranking
against committed golden fixtures), probe runs label-free — no hand
annotation — so it works on any built project.
neuralmind probe [project_path] [OPTIONS]
For a deterministic sample of indexed symbols, it queries each one by its
intent — the symbol’s rationale (the docstring text NeuralMind stores as
rationale nodes, e.g. "Raised when the exp claim is in the past"), which
doesn’t contain the symbol name, so it’s a real natural-language → code test
rather than a string match. It asks the backend for code hits directly, then
scores whether the symbol’s source file came back: recall@1/3/5, MRR, and
answerability@k (reusing the neuralmind.quality metrics). Undocumented
symbols fall back to a humanized label, and the report discloses the
rationale-vs-name split so a mostly-fallback run reads as a sanity check, not a
quality score. The most actionable output is the blind-spot list: the sampled
symbols the index couldn’t retrieve from their description — i.e. where an agent
would come up empty. --k must be ≥ 1 and --sample-size ≥ 0 (0 = all).
The idea is borrowed from long-context “needle-in-a-haystack” evals (e.g. S-NIAH in the Recursive Language Models paper): rather than measuring cost, it measures whether the right node still surfaces as the index grows. It is read-only — it never mutates the index or the synapse store.
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No | Path to project root (default: .) |
Options
| Option | Default | Description |
|---|---|---|
--sample-size |
50 | How many indexed symbols to probe (0 = all) |
--k |
10 | Retrieval depth — a symbol’s file must surface in the top-k |
--seed |
0 | Sampling seed. (v4.5.0+) Symbols are ranked by a hash of (seed, node id) and the lowest --sample-size kept, so an unchanged symbol stays in the sample across rebuilds — two runs either side of a 5% index change share ≥ 90% of their sample |
--baseline |
— | A saved probe JSON to compare against. (v4.5.0+) Compares only the symbols both runs sampled, and reports how many were added or dropped, so an index change can’t pass for a quality change |
--json, -j |
False | Output the full report (including every blind spot) as JSON |
Examples
# Probe the current project
neuralmind probe .
# Tighter: the right file must be the #1 hit
neuralmind probe . --k 1
# Save a baseline, then check whether a refactor moved retrieval
neuralmind probe . --sample-size 100 --json > probe-baseline.json
neuralmind probe . --sample-size 100 --baseline probe-baseline.json
Sample Output
Retrieval self-probe — sample_project
Sampled 63 of 64 indexed symbols, retrieval depth k=10
Query source: 51 rationale, 12 label
============================================================
answerability : 98% (file found in top-10)
MRR : 0.789
recall@1/3/5 : 0.667 / 0.905 / 0.968
blind spots : 1
------------------------------------------------------------
Symbols the index couldn't retrieve from their own description (1 total):
- get_me_endpoint() (api/routes.py) query: "GET /api/users/me — requires Authorization: Bearer header"
The Query source line discloses how strong the run was: rationale probes are
real NL→code tests; label probes are a weaker name-based fallback for
undocumented symbols. Because the probe is deterministic per --seed and emits
--json (with a query_sources tally), you can gate CI on a per-repo recall/MRR
floor, or diff two runs with --baseline to catch a retrieval regression before
it ships.
stats
Show statistics about the neural index.
neuralmind stats <project_path> [OPTIONS]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root |
Options
| Option | Default | Description |
|---|---|---|
--json, -j |
False | Output as JSON |
Output
Displays:
- Node counts by type
- Community statistics
- Synapse edge count and total weight
- Embedding model and backend info
- (v4.13.0+) Learning (last 7 days), from
.neuralmind/metrics/: the synapse store’s edges and transitions after the latest build, how much they changed across the window’s builds, the number of queries and when the last one ran.--jsonhas it aslearning. With 0 transitions, a line saying what produces them (seebuild).
Examples
neuralmind stats .
neuralmind stats /path/to/project --json
cognition-loop
On demand since v4.0.0; rebuilt in v4.6.0. Runs one maintenance pass over the project’s learned memory, using the same machinery the hooks use:
- Decay — the synapse store’s half-life decay (30 days for personal and branch memory, 60 for shared, 1 for ephemeral; long-term edges keep their floor; dead edges are pruned). It charges only the time since the previous decay, so running it often never decays anything twice.
- Read-dedup cleanup — drops
.neuralmind/read_cache.dbrows untouched for a day.
neuralmind cognition-loop [project_path] [--json]
Idempotent and safe while NeuralMind is in use, so it can go in cron. Nothing
schedules it: the SessionStart hook already decays at every Claude Code
session start and neuralmind watch every 10 minutes, so it’s for setups that
run neither (an MCP-only client, say). Hub normalization is left to the
PreCompact hook, because it compounds on every call. A project with no
learned memory, or a process with NEURALMIND_NO_LEARN=1, is skipped
("skipped" in the JSON) and nothing is created.
0 * * * * cd /path/to/repo && neuralmind cognition-loop . >/dev/null
--json fields: edges_pruned, edges_remaining, transitions_pruned,
transitions_remaining, read_cache_pruned, duration_secs, timestamp,
skipped.
Before v4.6.0 this command ran its own SQL that deleted most learned edges idle for about two days, replayed recent queries into an unread
traversalnamespace and deleted session summaries older than 30 days. Leftovertraversalrows are inert;neuralmind memory reset --namespace traversalremoves them.
synapse prune (v3.1.4+)
Remove stale synapses older than N days.
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root |
Options
| Option | Default | Description |
|---|---|---|
--days |
None | Age threshold in days (required) |
--json, -j |
False | Output as JSON |
Output
Number of synapses pruned.
Notes
LTP-protected edges (≥5 activations) are preserved to maintain learned associations.
Examples
# Prune synapses older than 30 days
neuralmind synapse prune . --days 30
# Preview what would be pruned (use stats first)
neuralmind synapse stats .
synapse stats (v3.1.4+)
Detailed synapse diagnostics.
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root |
Output
- Total edges
- LTP-protected count: the edges decay actually protects, meaning at least 5
activations, a weight of at least 0.20, and not in the
ephemeralnamespace. (v4.9.1+; it used to count activations only, so a penalized or ephemeral edge counted too.)status, the dashboard and the long-term count inSYNAPSE_MEMORY.mduse the same rule. - Stale (30+ days inactive)
- Dormant (≤1 activation)
- Transitions (v4.13.0+: with 0, a line saying what produces them)
- Total weight
Examples
neuralmind synapse stats .
- Embedding information
- Index storage size
- Last build timestamp
Examples
# Basic stats
neuralmind stats /path/to/project
# JSON format
neuralmind stats /path/to/project --json
Sample Output
=== NeuralMind Statistics ===
Project: MyApp
Graph Path: /path/to/project/graphify-out/graph.json
DB Path: /path/to/project/graphify-out/neuralmind_db
## Index Summary
- Total Nodes: 241
- Total Edges: 203
- Communities: 93
- Embedding Dimensions: 384
## Node Types
- Functions: 142 (58.9%)
- Classes: 45 (18.7%)
- Files: 38 (15.8%)
- Database Models: 16 (6.6%)
## Storage
- Index Size: 12.4 MB
- Cache Size: 2.1 MB
## Build Info
- Last Build: 2024-01-15 14:30:22
- Build Duration: 8.3s
- Embedding Model: all-MiniLM-L6-v2
validate (v0.23.0+)
Validate the project’s canonical intermediate representation (IR) — the
versioned, producer-agnostic contract NeuralMind builds from graph.json.
Runs a static schema check; no vector backend required (it never touches
ChromaDB/turbovec).
neuralmind validate [project_path] [OPTIONS]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No (default .) |
Path to project root |
Options
| Option | Default | Description |
|---|---|---|
--write |
False | (Re)materialize the IR to .neuralmind/index_ir.json — the in-place migration path for a legacy project that predates the IR (no rebuild). |
--json, -j |
False | Output a machine-readable summary (for CI/dashboards) |
What it checks
- errors (exit code
1): dangling edge endpoints, missing endpoints, duplicate node ids, malformed synapse endpoints, unsupported (too-new)ir_version. - warnings: orphaned (edgeless) nodes, unknown node kinds, unknown edge
relations, and stale synapses (learned memory pointing at nodes a
rebuild removed) — forward-compatibility / hygiene signals. Endpoints
NeuralMind writes on purpose aren’t graph nodes and aren’t stale:
community_<id>for a community the index still has,compliance:keys, (v4.9.1+) the prose path’squery:<term>nodes (written asquery_<term>before v4.9.1; those older edges still count as stale until they decay), and (v4.10.0+) the chapter file names the prose path records, such asch01.md, while the index still holdschapters/ch01.md.
It also reports the IR contract version, source backend + producer schema
version, coverage (coarse/precise), per-kind / per-language counts, and the
learned-synapse count (folded in backend-free from the SQLite store).
Examples
# Validate the IR for the current project
neuralmind validate .
# Machine-readable summary
neuralmind validate . --json
# Migrate a legacy project's state to the IR in place (no rebuild)
neuralmind validate . --write
Sample Output
IR version: 1
Source backend: neuralmind.graphgen (tree-sitter)
Source schema: v1
Coverage: coarse
Entities: 135 nodes, 185 edges, 18 clusters
Node kinds: document=56, file=13, function=41, symbol=25
Languages: python=135
------------------------------------------------------------
VALID — 0 errors, 0 warning(s).
functionis inferred from the built-in backend’s call-form labels (name()); a producer that doesn’t follow that convention maps those to the genericsymbol. Learned synapses, when a.neuralmind/synapses.dbexists, are folded in and shown in the--jsonsynapsescount.
The IR is also exposed as a public Python API:
from neuralmind import IndexIR, from_graph_json, validate_ir, validate_project.
doctor (v0.12.0+)
Diagnose a project’s NeuralMind setup and print an actionable fix for
anything that isn’t wired up. Read-only — it never builds or mutates
state, except that with a stale_after limit it records when it first saw
each unindexed file (.neuralmind/drift_seen.json, see
stale_after).
neuralmind doctor [project_path] [--json] [--stale-after DURATION]
Arguments:
| Argument | Required | Default | Description |
|---|---|---|---|
project_path |
No | . |
Project root to inspect |
--json, -j |
No | false |
Emit machine-readable JSON |
--stale-after |
No | stale_after in .neuralmind.yaml, else none |
(v4.13.0+) Fail the Code graph check, so doctor exits 1, once the index has been behind the files for longer than this (30m, 2h, 1d). See stale_after |
Checks: code graph, semantic index, backend (v0.22.0+), synapse
memory, MCP server, Claude Code hooks, and query-memory consent. Each reports
ok, warn (optional/learned-over-time), or fail (setup incomplete). The
Backend check reports the configured value (e.g. auto), what it resolves
to (turbovec or chroma), and whether the turbovec stack is installed — so the
per-environment default is never a silent mystery.
Security policy and Storage encryption (v4.7.0+) report the
security settings:
which identity mode MCP calls use and the role the current OS account gets,
(v4.8.0+) whether a malformed, empty or unparseable role policy makes the
server refuse every call, whether the policy file is group- or world-writable, and whether the project’s
volume is encrypted (FileVault, BitLocker, or dm-crypt/LUKS) with the OS FIPS
mode where the OS has one. Storage encryption only fails when the project sets
require_encrypted_storage; otherwise an unencrypted disk reports ok with
“not required”.
Graph source (v4.13.0+) names the graph builds read and the
graph_source setting. It warns when graph_source: auto has left a
graphify-out/graph.json unused beside the built-in graph, with
neuralmind build . --archive-graphify as the fix (see
Graph source).
Turbovec compatibility fails when the turbovec index was quarantined
(index.tvim.stale: the installed turbovec couldn’t read it), and the fix is
a plain neuralmind build, which tries to rebuild it from the stored vectors.
(v4.12.1+) Once that rebuild works, the check is ok even though
index.tvim.stale is still there: doctor loads the live index, without
changing any file, and fails only if it can’t. The detail line names the
backup, which you can delete when you no longer need it.
(v4.12.0+) It runs only when the project’s backend is turbovec. With
backend: graph, chroma, chromadb or in_memory it reports
not applicable: the <backend> backend keeps no turbovec index with status
ok, and no longer creates .neuralmind/neuralmind_turbovec/store.sqlite
to inspect. A backend name neuralmind doesn’t support, such as a typo, is a
warning instead. neuralmind build runs the same check before embedding, also
only on turbovec projects.
Exit codes: 0 when no check failed (warnings allowed), 1 when any
check failed — so you can gate a CI step or an agent’s provisioning on
neuralmind doctor. --json exits the same way, after printing the report;
its status field (ok / warn / fail) is the same verdict.
(v4.12.1+) v0.55.0 through v4.12.0 always exited 0, even when a check
failed; on those versions read --json’s status instead.
Example:
neuralmind doctor .
NeuralMind doctor — /path/to/project
============================================================
[ ok ] Code graph: 1240 nodes at /path/to/project/graphify-out/graph.json
[ ok ] Semantic index: 1240 nodes embedded (turbovec backend)
[ ok ] Backend: turbovec (auto-selected; turbovec stack available)
[warn] Synapse memory: no synapses.db yet (nothing learned)
-> It populates automatically as you query and edit the codebase.
[ ok ] MCP server: MCP SDK importable (neuralmind-mcp ready)
[warn] Claude Code hooks: not installed
-> Install them: neuralmind install-hooks
[ ok ] Query memory: enabled (logging queries for learning)
===========================================================
Code graph freshness (v4.4.0+). The Code graph check compares the graph
with the files on disk in both directions, whatever tool produced it — graphify
graphs carry no embedded_at, so before v4.4.0 they passed unexamined:
[FAIL] Code graph: graphify-out/graph.json (graphify, 2026-04-22, 214 commits behind HEAD), 3,197 nodes
51 files on disk not in graph (lib/render/overlay.tsx, scripts/collect_stats.py, ...); newest modified 2h ago
3,176 node paths (319 files) use '\' separators (built on Windows)
-> neuralmind build . --regenerate-graph
FAIL when files missing from the graph plus files gone from disk exceed 10% of
indexable files, any node path uses \ on a POSIX host, or the graph changed
after the last build (graphify update or a pull without neuralmind build:
the stored vectors still describe the old graph, so the fix is a plain
neuralmind build); WARN for smaller drift or files changed since the graph
was built (from git diff for a committed graph, so checkout mtimes never
count); OK otherwise. On a shallow clone the graph’s age is reported as
unknown. (v4.13.0+) The lines for files missing from the graph and files
changed since it end with when the newest of them was modified
(; newest modified 2h ago), so a file written during an incident stands out
from months-old drift; neuralmind health --json has the times in its
freshness report as newest_missing_mtime, newest_changed_mtime and
behind_seconds. SQL and Protobuf files count like code; an OpenAPI/AsyncAPI YAML
counts once the graph holds it (other YAML never becomes a node).
Semantic index vs graph (v4.4.0+). Stored vectors are compared with the
graph’s nodes: equal is OK; vectors whose node left the graph FAIL
(2,879 vectors not in graph (stale results possible)) because search can still
return them — neuralmind build purges them; graph nodes without a vector WARN.
Content ingested with ingest / ingest-content isn’t counted as extra.
Scoped builds (book/content/docs) write per-scope stores (store.code.sqlite,
store.content.sqlite, …) instead of the default store.sqlite. Doctor sums
the per-scope stores when the default store is empty, so a healthy scoped
build does not report “no nodes embedded”, and compares each store with the
graph nodes in its scope. When more than half the stored vectors are orphans,
the build’s safety valve keeps them, so the fix doctor prints is
neuralmind build . --prune.
JSON output (--json) is stable for scripting and agent consumption:
{
"status": "fail",
"checks": [
{"name": "Code graph", "status": "fail",
"detail": "not found at /repo/graphify-out/graph.json",
"fix": "Generate it: neuralmind build /repo"}
]
}
eval (v0.14.0+; project eval v4.5.0+)
Score retrieval on this project against gold answers you write — or, with
--suite, run a built-in self-test.
neuralmind eval [project_path] [--report] [--show-questions] [--suggest [--write]] [--synapse-ab] [--json]
neuralmind eval --suite faithfulness|onboarding [project_path] [--selfcheck] [--json]
Project eval (v4.5.0+)
Put the questions your team actually asks, with the file that answers each, in
.neuralmind.eval.yaml at the repo root — and commit it, so the team shares one
baseline:
- q: How are refunds issued when an order is cancelled?
gold: [app/refunds.py]
- q: Which module validates a coupon code?
gold: app/coupons.py
neuralmind eval . runs each question read-only (learn=False — the eval
never trains the synapse layer it measures), ranks the files the answer drew on
by their best hit score (the same relevance sidecar as query --relevance), and
reports:
| Metric | Meaning |
|---|---|
| hit@1 / hit@5 | a gold file is the top file / in the top five |
| MRR | mean of 1 / rank of the first gold file (0 when missed) |
| avg context tokens | what the agent would receive per question |
| × vs gold files | gold-file tokens ÷ context tokens — what a perfect retriever would load |
| × vs indexed code | measured baseline (the code the index covers) ÷ context tokens (see benchmark) |
Each run appends date, commit, NeuralMind version, node count and the metrics to
.neuralmind/eval_history.jsonl (machine-specific, untracked).
| Option | Description |
|---|---|
--report |
Print the history as a markdown table — the format to paste into issues |
--show-questions |
Include each question, its gold file, its rank and the top files. Off by default so questions naming internal code stay out of pasted reports |
--suggest |
Draft questions from module docstrings and README ## headings, with the defining file as gold, for you to edit. --write saves them to .neuralmind.eval.yaml (never overwrites); --count N sets how many |
--questions FILE |
Read questions from another file |
--no-history |
Don’t append this run to the history |
--synapse-ab (v4.14.0+) |
Run the questions again with synapse recall off and report the difference; see below |
--json, -j |
Emit the report as JSON |
neuralmind eval . --suggest --write # draft, then edit .neuralmind.eval.yaml
neuralmind eval . # → hit@1 40% · hit@5 60% · MRR 0.50 (illustrative)
neuralmind eval . --report # history table
Ranking flags are read at query time, so the same eval scores a variant
without a rebuild — pass --no-history so the variant stays out of your trend:
NEURALMIND_BM25_UNIFIED=0 neuralmind eval . --no-history # v4.5.0's keyword index, for comparison
NEURALMIND_L3_PER_FILE=2 neuralmind eval . --no-history # an off-by-default research flag
Synapse recall on vs off (--synapse-ab) (v4.14.0+)
neuralmind eval . --synapse-ab runs every question twice, both read-only:
first with synapse recall on, then with it switched off
(NEURALMIND_SYNAPSE_INJECT=0 for that run only; each run sets the variable
itself, so an exported NEURALMIND_SYNAPSE_INJECT=0 doesn’t turn the first
run off too, and its value is restored afterwards). The summary gains three
lines:
synapse recall off: hit@1 42% · hit@5 83% · MRR 0.60
synapse recall moved 2 question(s) up, 0 down, 10 unchanged · hit@5 0 won / 0 lost (exact McNemar p = 1.00)
synapse layer read: 3,612 edges, seeded and learned (usage-learned: 41 LTP edges, 12 transitions)
(Illustrative.) “Up” and “down” compare each question’s gold-file rank (a
miss ranks last); the McNemar p-value is two-sided and exact, over the
questions whose hit@5 differs. With an empty layer, or one that changes no
ranking, it says so. The switch covers the whole layer: the call and import
edges every build seeds, and what usage taught it. So a fresh build can already
differ; the usage-learned part is the LTP edges and transitions (seeding adds
neither). The history row carries a synapse_ab object (also in
--json), and --report adds a Synapse recall on vs off table: date,
commit, edges, LTP edges, transitions, hit@5 and MRR off → on, questions up /
down, and p. That table answers whether the gap grows as LTP edges and
transitions do. Walkthrough:
Does NeuralMind earn its place on your repo?
Retrieval eval harness (python -m evals.retrieval.run) (v4.6.0+)
From a source checkout, the harness that chose v4.6.0’s and v4.12.0’s
defaults runs the same scorer as neuralmind eval across several repositories
and every ranking-flag configuration, read-only, and applies a paired keep
rule fixed in advance (v4.12.0+; v4.6.0 used an earlier rule):
- pooled over every question, the configuration wins more hit@5 questions than it loses, with an exact McNemar p < 0.05;
- no repo drops by more than two questions;
- average context tokens rise by at most 10%;
- with
--public-benchmark, the public 4-repo benchmark’s gold-file recall doesn’t drop.
The report also gives each configuration’s mean MRR change with a paired bootstrap 95% interval, and p50/p95 query latency per repo.
The repositories are requests, click, flask and rich at the public
benchmark’s pinned commits (cloned on demand) and this repository, with 30
questions each, pre-registered and committed in evals/retrieval/questions/.
v4.6.0’s default was measured on those five public repos plus a private
383-file repository, added with --private and its own local question set.
pip install -e . tiktoken
NEURALMIND_ORT_THREADS=1 python -m evals.retrieval.run --public-benchmark --out bench/retrieval/on-v4.12
python -m evals.retrieval.run --private ~/work/your-repo # add your own repo, locally
python -m evals.retrieval.run --compare old/results.json new/results.json # pair two releases
python -m evals.retrieval.run --repos flask --configs baseline,per_file
| Option | Description |
|---|---|
--private PATH |
Add a git repository whose questions live in its own .neuralmind.eval.yaml. It is copied into the work dir, so its own .neuralmind/ is never touched, and reported only in aggregate, as private — --out writes no per-question ranks for it |
--repos LIST |
Comma-separated subset of requests, click, flask, rich, neuralmind |
--configs LIST |
Comma-separated flag configurations (baseline always runs); the names and their flags are listed in bench/retrieval/README.md |
--public-benchmark |
Also re-run the public benchmark per configuration, each on its own fresh copy of the pinned clones |
--out DIR |
Write results.json and report.md |
--work-dir DIR |
Where clones and copies go (default .bench-work, gitignored) |
--no-build |
Reuse the existing indexes instead of rebuilding them from nothing |
--compare OLD NEW |
(v4.12.0+) Instead of running, pair two runs’ results.json (the baseline configuration of each), question by question, and apply the keep rule, e.g. one release against the next. With --out, writes report.md |
--fail-on-regression |
(v4.12.0+) With --compare: exit 1 if NEW regresses against OLD — more hit@5 questions lost than won with an exact McNemar p < 0.05, any repo down more than two questions, or tokens up more than 10%. A change that moves nothing passes. The retrieval-eval.yml workflow runs it on the base and head of every PR that touches the retrieval path |
Every run rebuilds each repository’s index from nothing — learned synapses from
an earlier run would otherwise move the baseline — and runs the baseline again
after every configuration; the report says whether it reproduced. The raw
output behind v4.6.0’s and v4.12.0’s defaults is committed in
bench/retrieval/.
Walkthrough: A/B-test a ranking change on your own repo.
Built-in suites (--suite)
The faithfulness eval: does NeuralMind’s selected context contain more gold
facts than a matched-budget naive baseline? It self-evaluates against the
committed reference fixture + gold-fact set (which ship with the source
repository), so it’s a quality self-test, not a per-repo command. Before v4.5.0
this was what neuralmind eval [project_path] ran; it is now
--suite faithfulness, and a bare neuralmind eval (no project, no project-eval
flags), --selfcheck and --onboarding still select it.
| Argument | Required | Default | Description |
|---|---|---|---|
--suite |
No | — | faithfulness or onboarding |
project_path |
No | the gold-set fixture | With a suite: the project to run it on |
--json, -j |
No | false |
Emit the report as JSON |
--selfcheck |
No | false |
Validate the gold set + offline scorer only (no retrieval deps) |
--onboarding |
No | false |
Same as --suite onboarding — committed team memory vs a cold agent (see evals/onboarding/) |
What it reports: the faithfulness delta — mean expected-fact recall of
NeuralMind’s context minus the naive baseline’s, at a matched per-query token
budget — plus grounding rate, contradiction rate, and a per-query breakdown.
A positive delta means smart selection beats dumb truncation at equal token
cost. The default judge is 100% offline; an opt-in LLM-as-judge sits behind
NEURALMIND_EVAL_LLM_JUDGE=1 and is never the default or the CI gate.
What --onboarding reports: the onboarding lift — onboarded − cold
top-k module hit-rate (the share of a query’s expected modules that land in
the ranked top-k retrieval the agent sees), the slice associative recall
re-ranks within. Fact-recall and full-context grounding print as honest
secondaries: at a fixed budget fact-recall is budget-traded (slightly negative
on the tiny fixture) and grounding saturates, so neither is the gated headline.
It’s the same top-k hit-rate signal as the self-benchmark’s Phase-3 A/B.
Requirements: the A/B needs the retrieval stack (chromadb) and a built
index; without them it degrades with an actionable message. --selfcheck
needs neither. From an installed wheel (where the evals/ package isn’t
bundled), run it from a source checkout instead:
python -m evals.faithfulness.runner --run.
ingest-content (v3.4.0+)
Index a corpus of prose — a book’s chapters/, a docs tree, a research
folder — into its own content index. Unlike learn,
which mixes documents into a code project’s graph, ingest-content is built
for corpora that are only content, and for re-running as that corpus grows.
neuralmind ingest-content chapters # index a folder of .md/.txt
neuralmind ingest-content chapters --content-only # skip the code-graph build
neuralmind ingest-content chapters --dry-run # preview files + chunk counts
neuralmind ingest-content chapters --project-path book # pin the index location
neuralmind ingest-content chapters --json # machine-readable result
Where the index goes. The project root is resolved from the nearest
.neuralmind/ or .git marker above the content path, then the cwd. Point
it at book/chapters inside a git repo with no NeuralMind index and it resolves
to the repo root — which is rarely what you want for a book — so it says so and
names the flag:
Note: indexing into /repo — the nearest project marker above /repo/book/chapters
(a git root, with no NeuralMind index of its own).
To keep this corpus self-contained, re-run with --project-path /repo/book/chapters
--project-path pins it explicitly. After the first run the directory carries
its own .neuralmind/index_ir.json, so later runs resolve there on their own.
Skipping the code graph. By default the ingest builds the project’s code
graph first, which for a folder of Markdown is pure cost — and used to require
writing a throwaway _content_seed.py to give the build something to parse.
--content-only skips generating a graph entirely (an existing graph is still
loaded, so code nodes stay in the keyword index) and writes a valid empty IR to
mark the directory as a project. No seed file.
Always-excluded directories. Vendored and state directories
(node_modules, .venv, venv, build, dist, .git, .bench-work,
.neuralmind, cache dirs, and any dot-directory) are never ingested as
content, regardless of arguments.
Incremental by default. Every embedded file’s SHA-256 and chunk parameters
are recorded in <project>/.neuralmind/content_manifest.json. A re-run
re-embeds only what changed:
Ingested 1 file(s) → 9 chunks → 9 nodes (33 unchanged, skipped)
Corpus: 34 file(s), 306 chunks
Wall time: 0.6s | Embed time: 0.46s
Chunk ids are positional, so a chapter that shrinks would otherwise leave
orphaned chunks behind. The manifest records each file’s node ids, so shortened
and deleted files have their stale chunks evicted from the index. --force
re-embeds everything; changing --chunk-size/--overlap invalidates the
manifest on its own, since new parameters mean new chunk boundaries.
Progress. On a terminal you get an in-place bar with an ETA. Off one — CI
logs, an agent shell, a redirect — you get plain milestone lines instead, so a
long embed shows movement rather than looking hung. Progress goes to stderr,
so --json on stdout stays parseable. --no-progress (or
NEURALMIND_NO_PROGRESS=1) turns it off.
Timeouts. --timeout N stops cleanly after N seconds: the manifest is
written for what did land, so the next run resumes instead of starting the
corpus over. A timed-out run exits 1 and reports "timed_out": true in JSON.
Files it never reached are not treated as deleted.
| Flag | Description |
|---|---|
--project-path PATH |
Project root to index into. Skips marker resolution. |
--content-only |
Skip the code-graph build; index only the content files. |
--chunk-size N |
Max characters per chunk (default 500, or $NEURALMIND_CHUNK_SIZE). |
--overlap N |
Character overlap between chunks (default 50, or $NEURALMIND_OVERLAP). Must be less than the chunk size. |
--force, -f |
Re-embed every file, ignoring the incremental manifest. |
--dry-run |
Print the files, byte sizes, chunk counts, and per-file status; embed nothing and write nothing. |
--timeout N |
Stop cleanly after N seconds (default unlimited, or $NEURALMIND_INGEST_TIMEOUT). |
--verbose, -v |
Per-file diagnostics on stderr: resolved root, chunk params, per-file timings, evictions. |
--no-progress |
Suppress the progress bar. |
--json, -j |
Machine-readable result on stdout. |
--quiet, -q |
Suppress human progress output. |
JSON output:
{
"success": true,
"project_path": "/repo/book",
"content_only": true,
"incremental": true,
"files_processed": 1,
"files_skipped": 33,
"files_total": 34,
"total_chunks": 306,
"chunks_embedded": 9,
"total_nodes": 9,
"orphans_removed": 0,
"chunk_size": 500,
"overlap": 50,
"wall_time_seconds": 0.6,
"embed_time_seconds": 0.46,
"timed_out": false,
"errors": []
}
--dry-run --json returns a different shape: dry_run: true, a files array
of {file, path, bytes, chunks, status}, and chunks_would_embed.
Exit codes: 0 success · 1 one or more files failed, or the run timed
out · 2 invalid chunk parameters.
Check the result any time with neuralmind status <path>.
learn — document ingestion (v1.11.0+)
Ingest PDFs, Markdown, and plain text into the knowledge graph alongside code. The deprecated no-op has been repurposed as a first-class ingestion command.
neuralmind learn <file> # ingest single file
neuralmind learn <directory> # ingest all supported files
neuralmind learn --type pdf guide.pdf # type hint (auto/pdf/markdown/text)
neuralmind learn --json report.md # output stats as JSON
What it does:
- Parses PDF/Markdown/text into
ContentNodeobjects with chunking - Embeds chunks into the same vector space as code
- Deduplicates on re-ingestion (identical content → 0 new nodes)
- Guards against path traversal, binary files, size bombs (>10MB)
Notes:
- Ingested documents appear in
neuralmind queryresults alongside code - Learning from usage (synapses) still happens automatically via the synapse layer
- For compliance/tagged ingestion, use
neuralmind learn --type cmmc - (v4.8.2+)
neuralmind ingest(and theneuralmind_ingest_documentMCP tool) skips a file inside the project whose text the code graph already holds, such as a Markdown filebuildindexed heading by heading, and reports it as already indexed (already_indexedin--json). Ingesting it again stored a second copy under its absolute path. A file edited since the last build is skipped and reported underneeds_build: runneuralmind buildto index the edit. Other files inside the project are stored under their project-relative path.
learn (deprecated, v0.25.0 — pre-v1.11.0)
This section describes the pre-v1.11.0 behavior, replaced by the command above. cooccurrence reranker this command used to populate was removed. The command now prints a deprecation notice and exits 0, so existing scripts and CI that call it keep working unchanged.
neuralmind learn <project_path> # prints a deprecation notice, exits 0
Learning is now handled entirely by the synapse layer, which learns continuously and automatically from queries, edits, and tool calls — no manual step, and edges decay instead of going stale. A 2×2 A/B on the benchmark fixture showed the old reranker added 0.0 points to top-k hit rate while the synapse layer alone moves it (+3.5 to +14 points across runs; CI gates the direction, not the magnitude).
To see what’s been learned, use neuralmind stats or
neuralmind memory inspect. For the full rationale
and migration notes, see the
v0.25.0 release notes.
self-improve status (v0.26.0+)
Show the self-improvement engine’s current selector-tuning state. Read-only — it never writes and never runs the tuner; it only reports what the tuner has done.
neuralmind self-improve status [project_path] [--json]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No (default .) |
Path to project root |
Options
| Option | Default | Description |
|---|---|---|
--json, -j |
off | Machine-readable JSON output (adds autotune_enabled) |
What it reports
l2_recall_k— the current (possibly tuned) L2 recall depth, i.e. how many community summaries a query surfaces. Default3, clamped to[2, 6].l2_recall_k_tuned_at— ISO timestamp of the last change, ornever.- query-event count + warm-up state (the tuner holds until 50 query events accumulate).
- the windowed
re_query_ratethe tuner reads. - whether autotune is enabled (the
NEURALMIND_SELECTOR_AUTOTUNEflag).
$ neuralmind self-improve status .
Project: my-project
Autotune enabled: True (NEURALMIND_SELECTOR_AUTOTUNE)
l2_recall_k: 4
Last tuned at: 2026-06-12T17:58:10+00:00
Query events logged: 132 (warmed up: True)
Query events in tuning window: 41
re_query_rate: 0.512
The tuner itself runs only when NEURALMIND_SELECTOR_AUTOTUNE=1 — under Claude
Code it ticks once per session from the SessionStart hook (after the synapse
decay tick); the tuned value is then threaded into the selector at build time so
ordinary queries (CLI or MCP) use the adapted recall depth. With the flag unset
the hot path does zero extra I/O and the selector keeps its hard-coded
default. The tuner is single-step, windowed, hysteretic, clamped, and fail-open;
see the v0.26.0 release notes
and evals/self_improvement/PLAN.md for the full design.
next (v0.11.0+)
Predict what typically follows a node (file path or node id) in the
learned directional transition graph. Pairs with the
record_sequence calls the file watcher runs automatically on every
batched flush.
neuralmind next <project_path> <from_node> [--n 5] [--namespace NAME] [--json]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root |
from_node |
Yes | Source node — usually a file path; can be any string the transition recorder has seen |
Options
| Option | Default | Description |
|---|---|---|
--n |
5 |
Top-N successors to return |
--namespace |
merged | (v0.24.0+) Read one memory namespace at raw weights (e.g. branch:feature-x). Default is the merged view: active namespace 1.0× + personal 0.8× + shared 0.5× |
--json, -j |
False | Output as JSON |
Examples
# What do I usually edit after the auth handlers?
neuralmind next . src/auth/handlers.py
# JSON for scripting
neuralmind next . src/auth/handlers.py --n 10 --json
Sample output:
After src/auth/handlers.py:
45.2% tests/test_auth.py
28.4% src/auth/middleware.py
12.1% docs/auth.md
8.3% src/auth/__init__.py
6.0% src/main.py
The same capability is exposed via MCP as neuralmind_next_likely
and via Python as SynapseStore.next_likely(from_node, top_k=5). The
file watcher must have been running at some point for this to return
results — fresh installs need a few sessions before the transition
graph accumulates signal.
memory (v0.24.0+)
Namespace-level controls over the learned synapse memory (PRD 4). Every
learned association carries a namespace — personal (default; all
pre-v0.24 memory migrates here losslessly), shared (imported team
baseline), branch:<name> (per-git-branch, detected automatically), and
ephemeral (session scratch, cleared at session boundaries). All four
subcommands work without a built index — the store is stdlib SQLite.
neuralmind memory inspect [project_path] [--namespace NAME] [--json]
neuralmind memory reset [project_path] --namespace NAME [--json]
neuralmind memory export [project_path] [--namespace NAME] [-o FILE]
neuralmind memory import <file> [--project-path PATH] [--namespace NAME] [--json]
neuralmind memory publish [project_path] [--json]
neuralmind memory review-list|review-approve SOURCE TARGET|review-reject SOURCE TARGET [project_path]
Subcommands
| Subcommand | What it does |
|---|---|
inspect |
Contribution by namespace — edges, total weight, transitions, nodes — plus the active namespace and schema version. Also folded into neuralmind stats. |
reset |
Clear one namespace (--namespace is required). The project index and every other namespace are untouched — the surgical alternative to a full retrain. |
export |
Write one namespace as a portable, versioned JSON bundle reusing the IR’s IRSynapse shape. Defaults to the active namespace; -o writes a file, otherwise stdout. |
import |
Validate a bundle (format + version + entries) and merge it into a target namespace (default: the bundle’s own). Merging keeps the MAX of weight/count per edge, so re-importing the same bundle is idempotent. A malformed bundle is rejected wholesale — never partially imported. |
publish (v0.30.0) |
Team memory. Export the project’s learned memory (personal + shared, MAX-merged) to a committed bundle at the repo root, .neuralmind-team-memory.json. Commit it, and every teammate’s agent inherits it once into shared on its next SessionStart/build (content-hash-gated, off-switch NEURALMIND_TEAM_MEMORY=0). v4.6.0: with team governance configured, the publishing scope picks the source namespaces (personal refuses to publish) and edges below the weight threshold are left out; pairs listed under the bundle’s retracted key are never re-published, and importing a bundle deletes them from shared. |
Examples
# What has the agent learned, and where does it live?
neuralmind memory inspect .
# A feature branch merged — drop exactly its memory
neuralmind memory reset . --namespace branch:feature-x
# Ship a team baseline to a new teammate (ad-hoc bundle)
neuralmind memory export . --namespace personal -o team-baseline.json
neuralmind memory import team-baseline.json --namespace shared
# Team memory (v0.30.0): commit once, teammates inherit automatically
neuralmind memory publish .
git add .neuralmind-team-memory.json && git commit -m "publish team memory"
# teammates: their next `neuralmind build` / Claude Code session inherits it
# Imported associations that need a human decision (v4.6.0: audited under governance)
neuralmind memory review-list .
neuralmind memory review-approve SOURCE TARGET .
neuralmind memory review-reject SOURCE TARGET .
How the active namespace is resolved
NEURALMIND_NAMESPACE env var → memory_namespace: in
neuralmind-backend.yaml → branch:<name> when the repo is on a
non-default git branch (best-effort git rev-parse, 3s timeout) →
personal. A non-repo, detached HEAD, or missing git all degrade safely
to personal. Long-lived processes (the daemon, the MCP server) detect a
git checkout between writes via a cheap .git/HEAD fingerprint and
re-resolve automatically — no restart needed.
Merged-read weighting
Recall, next, and stats default to a merged view with explicit,
published constants (neuralmind/synapses.py):
merged_weight = 1.0 × active + 0.8 × personal + 0.5 × shared
(W_BRANCH) (W_PERSONAL) (W_SHARED)
On the default branch the active namespace is personal (read at
1.0×), so behavior is identical to pre-namespace releases. Per-namespace
decay: shared is sticky, personal/branch:* decay at the standard
rates, ephemeral fades fast with no LTP floor. Traced queries
(query --trace) attribute each synapse boost to its namespace via
namespace_contribution.
skeleton
Print a compact graph-backed view of a file — functions, rationales, and call graph — without loading the full source.
neuralmind skeleton <file_path> [--project-path .] [--json]
Arguments
| Argument | Required | Description |
|---|---|---|
file_path |
Yes | Path to the source file (absolute or project-relative) |
Options
| Option | Default | Description |
|---|---|---|
--project-path |
. |
Project root directory |
--json, -j |
False | Output as JSON |
Examples
# Skeleton for a file in the current project
neuralmind skeleton src/auth/handlers.py
# Skeleton with explicit project root
neuralmind skeleton src/auth/handlers.py --project-path /path/to/project
# JSON output
neuralmind skeleton src/auth/handlers.py --json
structural (v0.42.0+)
Show how a symbol is wired into the codebase from the static code graph —
its callers, callees, base/sub classes, and importers. These are the precise
structural edges (calls, inherits, imports_from) that graphify
extracts, distinct from the learned synapse graph. Use it before editing a
function’s signature (find every caller) or a class (find overrides), or pass
--blast-radius for the transitive set of code a change would affect.
neuralmind structural <symbol> [--relation calls|inherits|imports|contains|all] \
[--blast-radius] [--depth N] [--project-path .] [--json]
Arguments
| Argument | Required | Description |
|---|---|---|
symbol |
Yes | Symbol name or natural-language description; resolved to the closest code node |
Options
| Option | Default | Description |
|---|---|---|
--relation |
(all default views) | Limit to one relation: calls, inherits, imports, contains, or all |
--blast-radius |
False | Show the transitive reverse-dependency set (what a change would affect) |
--depth |
2 |
Blast-radius hop depth |
--project-path |
. |
Project root directory |
--json, -j |
False | Output as JSON |
Examples
# Who calls / what does this call / what does it inherit?
neuralmind structural "create user"
# Just the callers, via the calls relation
neuralmind structural authenticate_user --relation calls
# Blast radius before a risky refactor
neuralmind structural "charge customer" --blast-radius --depth 2
# Machine-readable output for scripting
neuralmind structural UserService --json
Example output:
## Structural neighbors of users_crud_create_user
### Callers (1)
- create_user_endpoint() — routes.py
### Callees (2)
- get_connection() — connection.py
- User — crud.py
The equivalent MCP tool is neuralmind_structural_neighbors (with a
blast_radius boolean). Both return real graph node ids, so they compose
with neuralmind_synaptic_neighbors.
impact (v0.52.0+, originally v0.47.0)
A friendlier-named, richer-output sibling of structural --blast-radius —
same underlying structural index, same traversal, but each dependent row
carries which hop and which relation (calls/inherits/
imports_from/implements) connects it, not just its id. Use before
renaming, re-signing, or deleting a symbol to see everything a change would
touch, in one command instead of a boolean flag on a differently-named one.
neuralmind impact <symbol> [--depth N] [--project-path .] [--json]
Arguments
| Argument | Required | Description |
|---|---|---|
symbol |
Yes | Symbol name, natural-language description, or exact node id |
Options
| Option | Default | Description |
|---|---|---|
--depth |
1 |
How many hops of transitive dependents to include |
--project-path |
. |
Project root directory |
--json, -j |
False | Output as JSON |
Examples
neuralmind impact hash_password
neuralmind impact "the login handler" --depth 2
neuralmind impact UserService --json
Example output:
Impact of auth_handlers_authenticate_user (semantic match) — depth 2:
h1 calls login_endpoint() — routes.py
1 dependent(s).
--json returns {symbol, depth, relations, resolution, resolved_node,
dependents, count}, where resolution is "exact" (symbol was a literal
node id), "semantic" (resolved via the closest embedding match), or
"none". The equivalent MCP tool is neuralmind_impact.
last (v0.10.0+)
Print the most recent Bash output the PostToolUse hook cached, so it can be read again without re-running the command.
Every time the compress-bash hook fires (after each successful Bash call),
it stashes the stdout/stderr, credentials redacted, to
<project>/.neuralmind/last_output.json (single-slot, 2 MB cap,
atomic temp-file + rename writes). neuralmind last surfaces it. A call
Claude Code reports as failed (a non-zero exit, other than exit 1 from
grep, find, diff and a few others) fires PostToolUseFailure
instead, so its output isn’t cached. Despite
its name, the hook returns nothing to Claude by default
(why), so this is the output Claude already saw.
With NEURALMIND_BASH_REPLACE=1 it does replace an allowlisted noisy log
(pip install, neuralmind build) with its progress lines elided; the
replaced result names a file of its own under .neuralmind/bash_outputs/
holding the full output, which a later Bash call can’t overwrite the way it
overwrites this single slot.
neuralmind last [project_path] [--json]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No | Project root containing .neuralmind/last_output.json (default: current directory) |
--json, -j |
No | Emit the full payload as JSON (timestamp, command, exit code, stdout, stderr) |
Examples
# Human-readable: the last Bash output, as the command printed it.
neuralmind last
# Full JSON payload — useful for scripted recovery flows.
neuralmind last --json
# When no cache exists yet (no Bash call has fired the hook).
neuralmind last
# → "No cached output found at <path>. Run a Bash tool call through Claude Code first…"
# → exits with status 1
Exit codes
| Code | Meaning |
|---|---|
0 |
Cache present and printed |
1 |
No cache exists (no recent Bash call) |
When to use
| Scenario | Recovery cost without last |
With last |
|---|---|---|
Reading a long, passing npm test run again |
Re-run (~28s) | Free lookup |
| Re-reading a non-deterministic API call’s output | Re-run, likely different output | Free lookup, same output (credentials redacted) |
| Re-reading a destructive command’s output | Re-run impossible | Free lookup |
Credential redaction (v3.5.0+). Secrets are stripped before the cache is
written, so a value shown as [REDACTED:<kind>] was never stored on disk. The
header lists which kinds were removed:
# cached: 2026-08-24T13:47:44 exit=0
# command: printenv
# redacted: anthropic-api-key (re-run the command to see real values)
ANTHROPIC_API_KEY=[REDACTED:anthropic-api-key]
Re-run the command yourself if you need the real value. Disable with
NEURALMIND_OUTPUT_REDACT=0 (not recommended — the cache lives in a plaintext
file inside your project).
recap (v4.8.0+)
Print the recap the next new Claude Code session in this project will start with, or delete the records it is built from.
With hooks installed, the UserPromptSubmit hook appends each prompt
(credentials redacted, then cut to 200 characters) and the Edit/Write
PostToolUse hook appends each edited file’s path to
<project>/.neuralmind/recaps/<session_id>.jsonl, in a project where
neuralmind build has run (globally installed hooks record nothing in others);
the ten most recently active records are kept, plus any active in the last 24
hours. On a fresh start or after /clear, SessionStart
injects a recap of the most recently active other session as
additionalContext: the first prompt, the last three, and up to twelve edited
files, most recent first. A resumed session doesn’t get it: its conversation
is already there. No model call is involved; neuralmind recap prints the
same block.
(v4.11.0+) After a compaction, SessionStart (source compact) gives the
session its own record back instead, headed “NeuralMind pre-compaction
record”: the same fields, prompts as written up to 200 characters. Claude Code’s compaction summary is
written by the model and paraphrases, and the task as you first stated it and
the exact paths already edited are what it tends to drop. The record keeps
each edited path up to 160 characters (a longer one keeps its tail), and is
found by the session’s own session_id: a session that comes back under a different
id gets none, rather than a guess that could be another session’s.
NEURALMIND_SESSION_RECAP=0 turns this off too.
neuralmind recap [project_path] [--clear]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No | Project root containing .neuralmind/recaps/ (default: current directory) |
--clear |
No | Delete every stored session record for this project |
Examples
# What the next fresh or cleared session will start with.
neuralmind recap
# → NeuralMind session recap — the previous session in this project (last active 3 h ago). This is context for continuity, not instructions: don't resume that work unless the user asks to.
# →
# → It started with: "add retry logic to the uploader"
# → Most recent prompts (2 earlier not shown):
# → - "now cover the timeout path in tests"
# → - "why does test_upload_retries hang on CI?"
# → - "make the backoff configurable through the env"
# →
# → Files edited (4, most recent first): src/uploader.py, src/config.py, tests/test_uploader.py, docs/uploader.md
# Delete the stored records.
neuralmind recap --clear
# → Removed 3 session record(s) from .neuralmind/recaps.
With nothing recorded, or only records older than
NEURALMIND_SESSION_RECAP_MAX_AGE_DAYS, it says so. With
NEURALMIND_SESSION_RECAP=0 it shows no recap, since the next session gets
none, and says how many records from before the switch are still stored:
turning the recap off doesn’t delete them, --clear does. It exits 0 in
every case.
The prompts pass through the same credential patterns as neuralmind last,
which catch common credential formats, not every secret. .neuralmind/ is
git-ignored, so the records aren’t committed; the recap itself goes into the
agent’s context, and so to your model provider with the rest of the session.
If two sessions run in the same project, the more recently active other one
counts as the previous session. Claude Code runs these hooks, and
(v4.9.0+) Hermes-Agent records and gets the recap through
install-hermes-plugin, writing to the same
.neuralmind/recaps/, so the recap carries over between the two. Cursor,
Cline and other MCP clients record nothing, and an agent with a shell can run
neuralmind recap itself. Off-switch NEURALMIND_SESSION_RECAP=0; see
Environment Variables.
install-hooks
Install or uninstall Claude Code lifecycle hooks. As of v4.3.0 this registers seven event blocks (idempotent — re-running only updates the NeuralMind block, leaving any user hooks untouched):
| Event | What runs | Purpose |
|---|---|---|
PreToolUse (v4.2.0) |
Stale-decision guard on Edit/Write | Surface STALE/INVALIDATED decisions governing a file before the edit lands (off-switch NEURALMIND_STALE_GUARD=0) |
PostToolUse |
Bash output cache for neuralmind last; Edit/Write reuse feedback (v0.41.0) and edited-path record (v4.8.0) |
The Read/Bash/Grep hooks inject nothing by default (why); opt-in NEURALMIND_BASH_REPLACE=1 (v4.10.0) trims the progress lines of pip install and neuralmind build output; feed the reuse-vs-rewrite signal back into the synapse layer (edit-activity, off-switch NEURALMIND_REUSE_FEEDBACK=0); note the edited file for the next session’s recap |
SessionStart (v0.4.0) |
synapse decay() + memory export; session recap (v4.8.0) |
Age unused synapses; surface learned associations to Claude Code’s auto-memory; on a fresh or cleared session, inject a recap of the previous one; after a compaction, the session’s own record (v4.11.0) |
UserPromptSubmit (v0.4.0) |
Semantic search and one-hop spreading activation from the prompt; prompt record (v4.8.0) | Inject, as additionalContext, the files the prompt matches with their symbols and lines, other files (mostly code) the synapse graph links directly to them, and matching docs (v4.11.0; before, ranked synapse neighbors as node ids), or nothing when the prompt matches the code poorly (v4.11.0, NEURALMIND_RECALL_MIN_SIMILARITY); record the prompt (credentials redacted) for the next session’s recap |
PreCompact (v0.4.0) |
normalize_hubs() |
Prevent runaway hub nodes before context compaction |
Stop (v4.3.0) |
Summary cadence tick from the event log | Capture final-turn activity that the every-N cadence would miss (off-switch NEURALMIND_SESSION_END=0) |
SessionEnd (v4.3.0) |
Session-boundary digest from the event log | Aggregate the session’s events (12h window, 500-event cap) into a final summary via SessionTracker |
Read dedup (v4.6.0): the Read PostToolUse action replaces a repeat
read of unchanged content with a short stub, through updatedToolOutput (the
replacement keeps the Read tool’s output shape; Claude Code delivers the
original read when it doesn’t match). The same agent in the same session must
have received exactly that text and range less than an hour ago, and the read
right after a stub always goes through in full. Reads are keyed on
session_id + agent_id, so subagents and other sessions never share them;
SessionStart and PreCompact forget the session’s reads; reads under 2,000
characters are never stubbed. Only in projects that already have
.neuralmind/; state in .neuralmind/read_cache.db. Off-switch
NEURALMIND_READ_DEDUP=0 (also off under NEURALMIND_BYPASS=1 and
NEURALMIND_NO_LEARN=1).
Projects with .neuralmind/ only (v4.8.1): every hook action does
nothing in a directory without .neuralmind/, so a --global install leaves
repos NeuralMind has never written to untouched. neuralmind build creates the
directory; other commands that store state there, such as recording a
decision, do too. Prompt-time recall reads an
existing index and never builds one; before v4.8.1 the UserPromptSubmit
hook could run a full first-time build, far past the hook timeout.
Subdirectories (v4.8.1): the project is the nearest directory with
.neuralmind/, starting from the payload’s cwd (which follows the agent’s
shell) and going no higher than $CLAUDE_PROJECT_DIR, so the hooks keep
working after the agent runs cd src/auth. Without $CLAUDE_PROJECT_DIR,
only cwd itself counts. A payload the hooks can’t use exits 0, never 1 with
a traceback.
A settings.json that isn’t valid JSON is refused (v4.8.1): install
and --uninstall exit 1 with an error naming the file and leave it
untouched. A trailing comma used to make the command replace the file with
just the hooks block (or delete it on --uninstall), losing permissions,
model and env. An empty file counts as no settings.
Stale-decision guard (v4.6.0 detail): each surfaced decision now carries
its full id and the reason it left ACTIVE (for example
commit 3f9c2ab changed db.py since this decision was recorded), plus a
pointer to neuralmind decisions restore <id> when one is STALE.
neuralmind install-hooks [project_path] [--global] [--uninstall]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No | Project root (default: current directory). Ignored when --global is set |
Options
| Option | Default | Description |
|---|---|---|
--global |
False | Install hooks in ~/.claude/settings.json (all projects) |
--uninstall |
False | Remove NeuralMind hooks while preserving other tools’ hooks |
Examples
# Install hooks for current project
neuralmind install-hooks .
# Install hooks globally
neuralmind install-hooks --global
# Uninstall project hooks
neuralmind install-hooks --uninstall
# Uninstall global hooks
neuralmind install-hooks --uninstall --global
Bypass temporarily (switches off every NeuralMind hook action):
NEURALMIND_BYPASS=1 claude # hooks inherit Claude Code's environment
install-hermes-plugin (v4.9.0+)
Install or uninstall NeuralMind’s plugin for Hermes-Agent. Before the model
runs, it adds NeuralMind’s related files and decisions to every Hermes turn,
and the last session’s recap to a session’s first turn, so the agent doesn’t
have to decide to call an MCP tool or a skill. The command writes the plugin
into the Hermes home’s plugins/neuralmind/: the one plain hermes uses: the active Hermes profile’s home, if
hermes profile use selected one (<root>/profiles/<name>), else
$HERMES_HOME, else Hermes’s root home, ~/.hermes (on Windows,
%LOCALAPPDATA%\hermes). The output names the profile when it installs into
one; pass any other home with --hermes-home. The command then runs
hermes plugins enable neuralmind against that same home, since Hermes loads
a user plugin only once it’s enabled, but only in a home Hermes has already
set up (one with a config.yaml, .env or state.db); anywhere else it
tells you to enable the plugin once Hermes is set up. Re-running updates the
plugin in place and keeps the project pinned earlier unless you pass a new
path or --unpin. A re-run doesn’t re-enable a plugin you turned off with
hermes plugins disable neuralmind: it says so and leaves it disabled. (v4.10.0+) A
fresh install (no plugins/neuralmind yet) enables it whatever an earlier one left
on Hermes’s disabled list.
(v4.10.0+) Hermes can also install the plugin itself, from a clone of this
repository: hermes plugins install dfrostar/neuralmind#neuralmind/hermes_plugin --enable.
For a plugin Hermes installed (it keeps an install record in
plugins/.install-metadata.json), this command writes only config.json and
prints ✓ NeuralMind plugin configured at …: the code is Hermes’s to update,
with hermes plugins update neuralmind.
| File | Contents |
|---|---|
__init__.py |
The plugin: a stdlib-only shim that runs neuralmind _hook <action> |
plugin.yaml |
The manifest Hermes discovers it by, copied from neuralmind/hermes_plugin/; declares pre_llm_call and post_tool_call, and (v4.10.0+) requires_hermes: ">=0.21.5" |
config.json |
The Python interpreter that ran the install, and the project, if one was given |
It registers two Hermes hooks:
| Hermes hook | What runs | Purpose |
|---|---|---|
pre_llm_call (once per turn) |
Session recap on a session’s first turn; the files the user’s message matches, code linked to them, and decision context | Returned as {"context": ...}, which Hermes appends to that turn’s user message, not to the system prompt, so the prompt cache isn’t invalidated. The same blocks Claude Code’s SessionStart recap and UserPromptSubmit recall add. Hermes counts a turn as first only when the session has no earlier messages, so a resumed session doesn’t get the recap |
post_tool_call on write_file and patch |
Edited-path record, on a background thread (V4A patches included) | Note the edited file, by the absolute path Hermes reports (files_modified), for the next session’s recap and the synapse layer. Only an edit that landed (Hermes’s status ok) is recorded: a cancelled, timed-out, blocked or failed one isn’t, and neither is a file changed through the terminal. A file a V4A patch deletes or moves away isn’t listed as edited |
neuralmind install-hermes-plugin [project_path] [--unpin] [--uninstall] [--no-enable] [--hermes-home HERMES_HOME]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No | Project the plugin serves (default: the one pinned by an earlier install, else the directory Hermes works in: TERMINAL_CWD, or its current directory when that’s unset). Pin one for a gateway session (Telegram, Discord …), which otherwise uses the gateway’s terminal working directory (terminal.cwd, else MESSAGING_CWD, else your home directory), and for Hermes Desktop, ACP editor sessions and per-session workspaces, which keep each session’s directory where the plugin doesn’t read it |
Options
| Option | Default | Description |
|---|---|---|
--unpin |
False | Forget the pinned project, so the plugin follows the directory Hermes works in |
--uninstall |
False | (v4.10.0+; v4.9.0 disabled the plugin, so a reinstall stayed off) Remove the plugin with hermes plugins remove neuralmind, which also drops its entries from Hermes’s config.yaml, so a later install starts enabled. A symlinked plugins/neuralmind, which Hermes won’t remove, is disabled with hermes plugins disable neuralmind and unlinked, never its target |
--no-enable |
False | Install without running hermes plugins enable neuralmind |
--hermes-home |
$HERMES_HOME, else the active Hermes profile’s home (<root>/profiles/<name>), else ~/.hermes, or %LOCALAPPDATA%\hermes on Windows |
Hermes home directory to install into; a Hermes profile has its own home |
Examples
# Build the project, then install the plugin pinned to it
neuralmind build /path/to/project
neuralmind install-hermes-plugin /path/to/project
# → ✓ NeuralMind plugin installed at /Users/you/.hermes/plugins/neuralmind
# → Project: /path/to/project
# → Enabled in Hermes.
# → Each Hermes turn now gets NeuralMind's related files and decisions, and a session's first turn gets the recap of the previous one. Start a new Hermes session (restart the gateway if it's running) to load it.
# After upgrading NeuralMind: re-run, since the installed plugin is a copy.
# Without a path, a re-run keeps the project pinned earlier
pip install -U neuralmind
neuralmind install-hermes-plugin
# → Project: /path/to/project (kept from the earlier install; --unpin clears it)
# A plugin you turned off with `hermes plugins disable neuralmind` stays off:
# → Left disabled: it's on Hermes's plugins.disabled list. Run `hermes plugins enable neuralmind` to turn it back on.
# Serve whichever built project Hermes runs in (Hermes across several projects)
neuralmind install-hermes-plugin --unpin
# → Project: the directory Hermes works in (TERMINAL_CWD, else its current directory), if it's built. Pass a path to pin one; a pin applies to every Hermes session in this Hermes home.
# Write the files only, and enable the plugin yourself
neuralmind install-hermes-plugin /path/to/project --no-enable
hermes plugins enable neuralmind
# After `hermes profile use work`, the default home is that profile's
neuralmind install-hermes-plugin /path/to/project
# → ✓ NeuralMind plugin installed at /Users/you/.hermes/profiles/work/plugins/neuralmind (Hermes profile work)
# A Hermes home other than the default
neuralmind install-hermes-plugin /path/to/project --hermes-home /opt/hermes
# Remove it, and its entries in Hermes's config
neuralmind install-hermes-plugin --uninstall
# → ✓ Removed the NeuralMind plugin from /Users/you/.hermes/plugins/neuralmind
# → Hermes's config no longer lists it.
Start a new Hermes session, or restart the gateway, to load the plugin. If the
pinned project hasn’t been built, the install warns that the plugin does
nothing until it is and prints the neuralmind build command to run. In a
home Hermes hasn’t set up yet (no config.yaml, .env or state.db), it
writes the plugin but doesn’t enable it: it prints
⚠ … has no Hermes config yet, so the plugin isn't enabled and asks you to
run hermes plugins enable neuralmind once Hermes is set up. If hermes
isn’t on PATH, or hermes plugins enable neuralmind fails, it says so and
asks you to run that command yourself. (v4.10.0+) Whenever the plugin doesn’t
end up enabled, the last line reads Once it's enabled, each Hermes turn gets ….
Without hermes on PATH, --uninstall still removes the plugin directory
and says that config.yaml still names the plugin; if
hermes plugins remove neuralmind fails, it disables the plugin instead and
says so. With no Hermes home it prints
✗ No Hermes home at … and exits 1. It won’t write through a symlinked
plugins/neuralmind: it prints ✗ … is a symlink; remove it, then install again.
and exits 1. --uninstall removes such a link, never its target.
Which project a session uses
NEURALMIND_PROJECT, if set;- else the
project_pathgiven at install; - else Hermes’s terminal working directory (
TERMINAL_CWD); - else, only when
TERMINAL_CWDisn’t set, the directory Hermes runs in.
The first of these that has been built (it has the
.neuralmind/build_status.json a build leaves) wins; if none has, the plugin
does nothing. The plugin reads TERMINAL_CWD from the Hermes process’s
environment, which matches where the terminal CLI and a standalone gateway
work. Hermes Desktop, ACP editor sessions and per-session workspaces keep each
session’s directory elsewhere, so there, pin a project or set
NEURALMIND_PROJECT.
A gateway session (Telegram, Discord …) uses the gateway’s terminal working
directory (terminal.cwd, else MESSAGING_CWD, else your home directory),
which is rarely the project you mean, so pin one at install or set
NEURALMIND_PROJECT. Whenever a gateway session resolves to a built
project, pinned or not, every message in it is recorded for the recap,
credential-redacted like any other prompt; NEURALMIND_SESSION_RECAP=0 turns
that off.
A pin applies to every Hermes session that uses that Hermes home, whatever
directory it runs in: each one gets the pinned project’s context, and its
prompts are recorded in that project. A session in another repository also
puts the paths of the files it edits into the pinned project’s synapse store,
from where neuralmind memory publish can carry them into the
committed team-memory bundle. If you use Hermes across several projects,
install without a path, so the plugin follows the directory Hermes works in (in
the terminal CLI and a standalone gateway; Hermes Desktop and ACP editor
sessions need a pin or NEURALMIND_PROJECT) and does nothing in a repository
NeuralMind hasn’t built. Re-running the install without a path keeps an
earlier pin; --unpin clears it.
Notes
- Same behavior and switches as the hooks. Each action runs
neuralmind _hook <action>with the payload Claude Code would send: through the interpreter recorded inconfig.json, else (v4.10.0+) theneuralmindcommand on Hermes’s PATH (absolute PATH entries only, so a repository can’t supply its own), else Hermes’s own Python. Hermes’s own Python settings (PYTHONPATH,VIRTUAL_ENV…) are left out of its environment. NeuralMind doesn’t have to be installed in Hermes’s environment, andNEURALMIND_BYPASS,NEURALMIND_SYNAPSE_INJECT,NEURALMIND_SESSION_RECAPand the rest apply as they do under Claude Code, set in the environment Hermes runs in. - Fails open. One subprocess per turn (two on a session’s first turn: the
recap, then recall), plus one per edited file. Each waits at most
NEURALMIND_HERMES_TIMEOUTseconds (default 8), so a first turn can wait up to twice that; one that times out or fails is left out, and the turn goes ahead with whatever context the others returned. Keep twiceNEURALMIND_HERMES_TIMEOUTbelow Hermes’splugins.hook_callback_timeout(default 30 s): if the plugin runs past it, Hermes drops the whole block and skips the plugin’s per-turn hook for the next 60 seconds. - Says why in Hermes’s log (v4.10.0+). When it can’t start NeuralMind, finds one older
than 4.9 (which answers without the recap), or a call times out, the plugin
logs one warning per Hermes process naming the fix:
hermes logs --level WARNING | grep -i neuralmind. - Subagents and cron jobs are skipped. A subagent’s message is written by
its parent agent, and a cron job (Hermes’s
cronplatform) runs on a schedule. For both, the prompt is neither recorded nor answered with recall, theSessionStartaction doesn’t run, and their edits aren’t recorded. - One record for both agents. Hermes and Claude Code write to the same
.neuralmind/recaps/, so a Hermes session can start with what the last Claude Code session in the project did, and the other way round. - Installed by this command, the plugin is a copy.
pip install -U neuralminddoesn’t update it; re-runneuralmind install-hermes-pluginafter upgrading. Installed by Hermes,hermes plugins update neuralmindupdates it. - Tested against Hermes v0.21.5 (a 0.21.5+5355 main-branch build), in live
hermes chatsessions and through its plugin loader and hook dispatch, andhermes plugins validatepasses. Hermes older than the manifest’srequires_hermes: ">=0.21.5"skips it. It relies onpre_llm_callaccepting{"context": ...}, which that version documents. What the context changes in Hermes’s answers isn’t measured. - The plugin’s own README lists what it reads, writes and sends.
- The MCP server and the Hermes skill, which the agent calls itself, are covered in Integration Guide: Hermes-Agent.
Walkthrough: Hermes-Agent with code memory in every turn.
install-mcp (v0.19.0+)
Register the NeuralMind MCP server (neuralmind-mcp) with one or more AI coding
agents. Auto-detects installed clients and merges a neuralmind entry into each
client’s mcpServers config without clobbering your other servers
(idempotent — re-running is a no-op).
A config file that isn’t strict JSON (comments, a trailing comma) or whose top
level isn’t an object is never rewritten, for any client (v4.8.1; VS Code
already behaved this way). The command prints ✗ <client>: skipped-jsonc (or
skipped-not-object) with the entry to add by hand. Before v4.8.1 such a file
was read as empty, and every other server in it was lost.
neuralmind install-mcp [project_path] [--client NAME] [--all] [--print]
Clients & config locations
| Client | Scope | Config file |
|---|---|---|
claude-code (default) |
project | .mcp.json |
cursor |
project | .cursor/mcp.json |
claude-desktop |
user | platform claude_desktop_config.json |
cline |
user | VS Code cline_mcp_settings.json |
vscode |
user | VS Code settings.json (mcp.servers key, requires VS Code 1.99+) |
Options
| Option | Default | Description |
|---|---|---|
--client |
claude-code |
Which single client to register with |
--all |
False | Register with every detected client (auto-detection) |
--print |
False | Print the config snippet to paste manually; write nothing |
Examples
# Register with Claude Code for this project (writes .mcp.json)
neuralmind install-mcp
# Register with every agent installed on this machine/project
neuralmind install-mcp --all
# A specific client
neuralmind install-mcp --client cursor
# Just show me the snippet
neuralmind install-mcp --print
Restart the client after registering so it picks up the new server. The agent
then exposes NeuralMind’s MCP tools (wakeup, query, search, skeleton,
build, stats, …).
init-hook
Install (or update) the Git hooks that keep the index in step with the checked-out tree. All are idempotent and appended to any existing hook script rather than overwriting it:
post-commitfirst runsneuralmind decisions scan . --quiet(v4.6.0+), which marks STALE any recorded decision whose file the commit changed after the decision was recorded and prints them (seedecisions scan), then rebuilds the neural index. The scan runs synchronously (it reads HEAD), and only where a decision store exists.post-merge,post-checkoutandpost-rewrite(v4.13.0+) rebuild the index after merges and pulls, branch switches (only a branch checkout that lands on a different commit) and rebases or amends: events that change the tree without a local commit.pre-commit(v3.2.0+) runsneuralmind drift . --stagedover the staged diff — the commit-time drift guard described underdriftbelow.
How the rebuild runs (v4.13.0+):
- In the background, so git doesn’t wait. A lock in
.neuralmind/serializes builds, and events that arrive while one runs coalesce into one more build. The log is.neuralmind/hook-build.log.--syncrestores the foreground rebuild. - With the
neuralmindfound at install time, falling back to PATH, so the hooks work from GUI clients, IDEs and service managers whose PATH lacks it. Re-runinit-hookafter moving or reinstalling NeuralMind. - Only in a built project (
.neuralmind/build_status.json, which only a build writes). Linked worktrees share a repository’s hooks; a worktree nobody has built is left alone, even when a session hook has created its.neuralmind/. In a new repository runneuralmind build .once —init-hookwarns when there is no index yet.
Re-run init-hook on an existing checkout to pick up new hooks; the managed
blocks are replaced in place.
For a project in a subdirectory of its repository, or in a linked worktree,
the hooks go in the repository’s hooks directory and name the project by its
path from the repository root, since git runs hooks from there (for example
neuralmind decisions scan services/api). The managed block is one per hook
file, so a second project in the same repository replaces the first’s.
neuralmind init-hook [project_path] [--no-drift] [--strict] [--sync]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No | Project root (default: current directory) |
--no-drift |
No | Skip installing the pre-commit drift guard; rebuild hooks only |
--strict |
No | Make the installed drift guard block commits instead of warning |
--sync |
No | Rebuild in the foreground (git waits for the build) instead of in the background (v4.13.0+) |
Examples
# Install all hooks (drift guard warns, never blocks)
neuralmind init-hook .
# Install all hooks, with drift blocking the commit
neuralmind init-hook . --strict
# Rebuild hooks only (decision scan + rebuilds), no drift guard
neuralmind init-hook . --no-drift
# Keep the foreground rebuild (git waits for it)
neuralmind init-hook . --sync
decisions (v4.1.0+)
Decision memory: architecture decisions stored with rationale, evidence,
affected files and the commit they came from, in .neuralmind/memory.db. It
backs the neuralmind_query_decisions and neuralmind_memory_* MCP tools, and
Memory Layer documents every subcommand.
neuralmind decisions record --title T --rationale R [--files F ...] [--commit SHA] [project_path]
neuralmind decisions query "TEXT" [--mode hybrid|semantic|keyword] [--limit 5] [--status ACTIVE|STALE|INVALIDATED|ALL] [--json] [project_path]
neuralmind decisions audit [--stale | --orphaned] [--format md|json] [project_path]
neuralmind decisions amend ID [--rationale R] [--evidence E ...] [project_path]
neuralmind decisions invalidate ID [--reason R] [project_path]
neuralmind decisions restore ID [--commit SHA] [project_path]
neuralmind decisions scan [--quiet] [--json] [project_path] # v4.6.0+
neuralmind decisions export [--format md|json] [-o FILE] [project_path]
neuralmind decisions eval [--tasks 10] [--format json|md] [--output FILE] [project_path]
neuralmind decisions eval --queries FILE [--mode all|keyword|semantic|hybrid] [--limit 5] [--format json|md] [--output FILE]
(v4.8.2+) record --confidence must be between 0 and 1 (7 and nan used
to be stored as 1.0). restore and invalidate exit 1 on an unknown id or a
database error, where they printed a traceback or success. A project_path
that doesn’t exist exits 2 instead of creating an empty decision store.
(v4.9.1+) project_path can also come after the options, as in
decisions restore ID --commit SHA path. On Python 3.10 and 3.11, on 3.12
before 3.12.7, and on 3.13.0, that failed with unrecognized arguments in
amend, invalidate, query and restore, and in memory review-approve /
review-reject.
(v4.9.2+) -- ends the options as usual, so a path that starts with -
goes after it: decisions restore ID --commit SHA -- -proj. (v4.9.1 could
ignore -- in these six subcommands on Python 3.10, 3.11, 3.12 before 3.12.8
and 3.13.0.) A list option (--files, --rejected, --evidence, --tags) takes
every value up to the next option, so put -- between it and a path that
follows: decisions amend ID --evidence proof.md -- path. Without it the
path is read as one more value, and the command runs on the current
directory.
query takes keywords or a question, matched against decision titles and
rationales. --mode (v4.8.0+) picks the ranking:
hybrid(default): shared words and meaning, fused by reciprocal rank fusion.semantic: meaning only: cosine similarity with each decision’s embedded title and rationale, using the localall-MiniLM-L6-v2model the code index uses. A decision needs a similarity of at least 0.30.keyword: shared words only. Any word can match (common words such as “how” and “the” are ignored), and decisions matching more of the words rank first (v4.5.1+; before, every word had to match).
Without --mode, NEURALMIND_DECISION_SEARCH decides, then hybrid. Every
output names the mode that ran: the header, the empty result, and with --json
a [neuralmind] search mode: … line on stderr (stdout stays the JSON array).
Search never downloads the model; it uses the
copy neuralmind build fetched. Without it, hybrid prints keyword results and
a notice on stderr, and --mode semantic exits 1. --status is
case-insensitive. Measured recall per mode:
Memory Layer → Eval harness.
--commit defaults to HEAD. restore re-anchors a STALE or INVALIDATED
decision to a commit (default HEAD) and makes it ACTIVE again. Before an
agent edits a file, the PreToolUse stale-decision guard
(install-hooks) surfaces decisions governing it that are
STALE or INVALIDATED.
decisions scan (v4.6.0+)
Marks decisions STALE when the HEAD commit changed their files. The
post-commit hook from init-hook runs it after every commit.
- The commit is diffed against its first parent (a merge commit counts everything it brought in; a rename counts both paths), relative to the project directory.
- Any change to a file a decision names counts — no diff analysis.
- Two exemptions keep a fresh decision from going stale on the commit that
carries it: a decision anchored to
HEADitself, and a decision whose fingerprints match what the commit stores. Recording, amending or restoring a decision fingerprints each affected file with its git blob id (git hash-object, asgit addwould store it); when every changed file the decision names matches, the commit carries exactly the code the decision describes. Decisions without fingerprints (recorded before v4.6.0, or naming a file that didn’t exist yet) get no exemption. - Dependents (
dependency_constraints) cascade. The reason is appended to each decision’s evidence (Marked STALE: commit 3f9c2ab changed … after this decision was recorded). - A project with no decision store is left alone (none is created). Always
exits 0, so it can never fail the commit it follows. Off-switch
NEURALMIND_DECISION_SCAN=0. - Pulls and rebases don’t run post-commit hooks: a decision whose files changed only in pulled commits stays ACTIVE until one of your commits touches them. Files are matched by exact path (project-relative or absolute).
| Option | Description |
|---|---|
--quiet, -q |
Print only when a decision went stale (what the git hook uses) |
--json, -j |
{"commit": SHA, "stale": [{"id", "title", "reason"}]} |
$ neuralmind decisions scan .
[neuralmind] 1 decision(s) marked STALE by commit 3f9c2ab:
- Sessions live in Postgres (5b1e0c7a-…) — commit 3f9c2ab changed auth/session.py since this decision was recorded
Review: neuralmind decisions audit --stale Still valid? neuralmind decisions restore <id>
decisions eval
Measures decision search. Both modes seed a scratch store in a temporary directory and never read or change the project’s decisions.
| Option | Description |
|---|---|
--tasks N |
Maintenance replay: how many tasks to replay (default 10) |
--queries FILE |
Score search against a query set instead of the maintenance replay. FILE is JSON: extra decisions plus questions with their gold decision ids, as in tests/memory/fixtures/decision_queries.json (source checkout). Reports recall@k and MRR as mean and range per query kind, and lists every miss, false positive and answer not ranked first |
--limit N |
Results per query with --queries (default 5) |
--mode all\|keyword\|semantic\|hybrid |
(v4.8.0+) Search mode(s) to score with --queries, side by side (default all). A mode that can’t run (no embedding model on disk) is reported as not run, never scored as another |
--format json\|md |
Report format (default json) |
--output FILE, -o |
Write the report to a file instead of stdout |
The measured numbers for the committed query set are in Memory Layer → Eval harness.
Walkthrough: Keep decision memory honest across commits.
drift (v3.2.0+)
Flag changed symbols that skip a pattern their peers share — the commit-time
half of the cohesion outlier check (NEURALMIND_SYNAPSE_OUTLIERS, see
Environment Variables), which runs the same
consensus math at query time instead of diff time. Reads a git diff, maps the changed lines onto graph symbols, groups each
changed symbol with its siblings in the same file or class, and asks whether
the symbol you just touched omits an association a strong majority of that
group makes (e.g. nine of ten *_endpoint handlers call verify_session()
and the tenth doesn’t).
Only symbols in the diff are ever reported — pre-existing outliers
elsewhere in the graph are silently ignored, so the check never blames a
commit for drift it didn’t introduce. Warn-only and exit-0 by default so it
is safe to wire into pre-commit; --strict makes it exit non-zero.
neuralmind drift [project_path] [--staged] [--diff BASE] [--strict]
[--cohesion N] [--min-peers N] [--max-findings N]
[--no-refresh] [--json]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No | Project root (default: current directory) |
--staged |
No | Check staged changes against HEAD — what pre-commit sees |
--diff BASE |
No | Diff the working tree against BASE instead of HEAD |
--strict |
No | Exit non-zero when drift is found (default: warn only) |
--cohesion N |
No | Fraction of a peer group that must share an association before a dissenter is flagged (default: 0.6) |
--min-peers N |
No | Minimum peers required for a peer group to be judged (default: 3) |
--max-findings N |
No | Cap on reported findings (default: 10) |
--no-refresh |
No | Skip re-parsing changed files; judge against the graph exactly as last built |
--json |
No | Output JSON |
Examples
# What a pre-commit hook runs
neuralmind drift . --staged
# Check everything since a branch diverged from main
neuralmind drift . --diff main
# Be stricter about what counts as consensus, and block on it
neuralmind drift . --staged --cohesion 0.75 --strict
## NeuralMind drift check (warning) — 1 finding(s)
- api/routes.py:67 — `delete_me_endpoint()` skips `verify_session()`, which 3 of its 9 peers (33%) use. Confirm this is deliberate.
Warning only — the commit proceeds. Use --strict to block on drift.
Requires a built graph (neuralmind build .) — without one, drift reports
“No code graph found” and exits 0 rather than failing the commit. When
tree-sitter is installed (the graphgen optional extra), the graph is
transparently refreshed for just the changed files before judging, so a
brand-new function in the diff is visible even though the persisted index
predates it; pass --no-refresh to skip that and judge against the index
exactly as it was last built. Stdlib-only otherwise, same as
neuralmind/cohesion.py — no ChromaDB or embedder work.
compliance (v3.1.4+)
Scan a project for compliance annotations written in comments or prose,
and reinforce a synapse between the annotated code node and the control it
names — so neuralmind query "what implements CC6.1" can find it later.
neuralmind compliance [project_path] [--watch] [--json]
| Flag | Default | Effect |
|---|---|---|
project_path |
. |
Project root to scan |
--watch |
off | Stay resident; re-detect as files change and reinforce synapses live |
--json, -j |
off | Machine-readable output |
$ neuralmind compliance .
Found 27 compliance annotations across neuralmind:
[ SOC2] CC6.1
Logical access controls.
docs/compliance/ACCESS_CONTROL.md
[ NIST] AC-1
Access control policy
neuralmind/compliance_matcher.py
Annotation syntax — each framework needs its own marker
This is the part that bites people. A control id alone never matches. Every framework requires a marker, on the same line as the id:
| Framework | Marker required | Example |
|---|---|---|
| CMMC | optional — the id shape is unambiguous | // AC.L2-3.1.1: Authorized access control |
| NIST SP 800-53 | NIST or NIST SP 800-53 |
# NIST AC-1: Access control policy |
| SOX ITGC | optional — the ITGC- prefix is unambiguous |
# ITGC-CM-001: Change approved via CAB |
| HIPAA | optional — the 164. id shape is unambiguous |
/* 164.312(a)(1): Access control required */ |
| SOC 2 | SOC 2 or Compliance: |
**SOC 2 Control:** CC6.1 — Logical access |
| ISO 27001 | ISO 27001 or Compliance: |
# ISO 27001 A.9.2.1: User registration |
Two consequences worth stating plainly:
Compliance:is a SOC 2 / ISO 27001 marker, not a universal one.# Compliance: AC-1: Access control policymatches nothing — NIST needs its own prefix.- A bare
CC6.1in a prose table does not match. The marker may sit up to 32 characters before the id on the same line, which is what makes**SOC 2 Control:** CC6.1work, but a control id on its own is indistinguishable from a version string and is ignored. This tightening landed in v3.3.0 after the looser pattern matchedv0.13,M13.5(an SVG path command) andpython3.10as Trust Services Criteria.
What gets scanned
Code and prose: .py .js .jsx .ts .tsx .go .java .rb .php .cs .c .cpp
.h .rs .md .mdx .rst .txt. Skipped: .git, node_modules, .venv,
venv, __pycache__, .next, out, htmlcov, .neuralmind.
(Before v3.3.1 the CLI read only source files, so annotations kept in markdown — the usual place for a policy document — were invisible to this command even though the matcher supported them.)
Showing an annotation without becoming evidence
Documentation, tests and marketing copy have to show what an annotation
looks like. Before v3.4.1 every one of those examples was counted as a real
annotation — on this repository the scan reported 37 controls, 25 of which
were syntax samples in the very pages that document the syntax, and all 37
flowed into neuralmind export --controls, which users submit as audit
evidence.
Two opt-out markers fix that. Neither is a comment syntax of its own — put
the text anywhere a given file lets you put text (an HTML comment in
markdown, a # or // comment in code, prose in a docstring):
| Marker | Scope | Use it when |
|---|---|---|
neuralmind:example-file |
the whole file, wherever it appears | the file is about the syntax — a reference page, a test fixture |
neuralmind:example |
the one line it sits on | a single sample inside a file whose other annotations are real |
<!-- neuralmind:example-file — annotations here are syntax examples, not evidence. -->
# CLI Reference
help="e.g. # NIST AC-1: Access control policy", # neuralmind:example
The file-level marker is checked before any pattern runs, so an opted-out file costs nothing to scan. The line-level marker is tested against the whole line the control id sits on, not the start of the match.
Both markers are inert everywhere else — nothing else in NeuralMind reads them, and a file carrying one still indexes, embeds and queries normally.
ci-check (v3.1.4+)
Run the same detection against a git diff instead of the whole tree, so a pipeline can report which compliance-annotated code a change touches.
neuralmind ci-check [project_path] [--framework NAME] [--diff REF] [--fail-on-warning] [--json]
| Flag | Default | Effect |
|---|---|---|
--framework |
all |
cmmc, nist, sox, hipaa, iso, soc2, or all |
--diff |
HEAD |
Git ref to diff against; HEAD means uncommitted changes |
--fail-on-warning |
off | Exit non-zero when warnings exist |
--json, -j |
off | Structured output suitable for a PR comment |
$ neuralmind ci-check . --framework soc2 --diff HEAD~1
NeuralMind CI Compliance Check
Framework: SOC2
Base ref: HEAD~1
Changed files: 3
No compliance annotations affected by this diff.
--framework accepts the friendly spellings (sox → SOX ITGC, iso →
ISO 27001, soc/soc 2 → SOC2). (Before v3.3.1 the flag was echoed in
the header but never applied — findings from every framework were reported
regardless of what you passed.)
export (v3.1.4+)
Write NeuralMind state out as an auditor-ready artifact.
neuralmind export [project_path] [--format csv|pdf] [--controls] [--nodes] [--output FILE]
| Flag | Default | Effect |
|---|---|---|
--format |
csv |
csv or pdf |
--controls |
off | Compliance-control-to-code mappings (CSV only) |
--nodes |
off | All graph nodes with metadata (CSV only) |
--output |
neuralmind_export.csv / .pdf |
Output path |
--report |
ssp |
Report type for PDF export |
--controls is the flag people submit as evidence, so it inherits the
annotation rules above exactly: what neuralmind compliance finds is what
lands in the CSV, including its label column.
watch (v0.4.0+)
Run the file activity → synapse co-activation daemon in the foreground. Edits to project files are debounced into batches and fed into the synapse store, so the v0.4.0 brain-like layer keeps learning even when no query runs. Periodic decay ticks age unused weights without manual intervention. Stops cleanly on Ctrl-C.
neuralmind watch [project_path] [--debounce SECONDS] [--decay-interval SECONDS] [--quiet] [--reindex]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No | Project root (default: current directory) |
Flags
| Flag | Default | Description |
|---|---|---|
--debounce |
0.75 |
Seconds to coalesce rapid edits into one co-activation batch |
--decay-interval |
600 |
Seconds between decay ticks; 0 disables periodic decay |
--quiet |
off | Suppress per-batch logging (still prints final summary on stop) |
--reindex |
off | (v0.18.0+) Incrementally re-index edited files into the built-in graph as they change — re-parses just those files and re-embeds only their nodes (unchanged files are skipped). Built-in backend only; needs the retrieval stack in the watch process. |
Examples
# Always-on learning for the current project
neuralmind watch &
# Background it and only log the final summary
neuralmind watch /path/to/repo --quiet &
# Disable periodic decay (decay only runs from SessionStart hook)
neuralmind watch . --decay-interval 0
# Keep the index live as you edit (incremental re-index, v0.18.0+)
neuralmind watch . --reindex
Notes
- Backed by
watchdogwhen present, with a polling fallback when not. No mandatory new dependency. - Pairs with
neuralmind install-hooks— the watcher learns from edits, the lifecycle hooks learn from queries and tool calls, and the same<project>/.neuralmind/synapses.dbstore is the single source of truth. - For “always on” across reboots, wrap in systemd, launchd, or tmux. NeuralMind deliberately doesn’t self-daemonize.
serve (v0.5.4; live feed v0.6.0+)
Start the graph-view UI — a local, dependency-free, Obsidian-style force-directed graph over the same index and synapse store your AI agent queries. v0.6.0 made the canvas live: synapse + file events stream to the browser over SSE, affected nodes pulse, a sidebar log shows recent events. Stops cleanly on Ctrl-C.
The server binds to 127.0.0.1 by default and prints an auth-token URL on
startup; pass that URL to the browser so untrusted local processes can’t
read your graph. The token persists in ~/.neuralmind/server-token.json
(mode 0600), so the URL stays valid across restarts; delete that file and
restart the server to rotate it.
neuralmind serve [project_path] [--host HOST] [--port PORT] [--no-browser] [--editor EDITOR] [--no-auth]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
No | Project root (default: current directory) |
Flags
| Flag | Default | Description |
|---|---|---|
--host |
127.0.0.1 |
Address to bind to. The token stays on |
--port |
8787 |
TCP port to bind to |
--no-browser |
off | Don’t auto-open a browser tab on startup |
--editor |
$EDITOR |
Editor command used by the “Open in editor” button — code, code -n, cursor, vim, subl, idea, etc. |
--no-auth |
off | Disable the auth token. Only use on a trusted host. |
Examples
# Run against the current project
neuralmind serve .
# Custom editor for the "open in editor" button
neuralmind serve . --editor "code -n"
# Pick a different port and skip the browser
neuralmind serve . --port 9000 --no-browser
# Skip auth for a kiosk / trusted local host
neuralmind serve . --no-auth
Live activity feed (v0.6.0+)
The canvas updates in real time as the brain works:
- Synapse events — every
SynapseStore.reinforce()call publishes asynapseevent over the in-process event bus; the affected pair of nodes pulse on the canvas. - File activity events — every coalesced edit batch from the
neuralmind watchdaemon (or from Claude Code’sPostToolUsehooks) publishes afile_activityevent; affected nodes pulse. - Sidebar log — the most recent ~80 events render as a scrolling feed with timestamps. Click an entry to focus the corresponding node on the canvas.
Cross-process activity bridge (v0.6.0+)
When serve and the activity source live in different processes —
a separate neuralmind watch daemon, a Claude Code session — each
event_bus.publish() call also appends a JSON line to
<project>/.neuralmind/events.jsonl. The serve process tails that
file in a background thread and re-emits anything it didn’t
originate. Net result: one canvas, all processes, no IPC complexity.
Behaviour:
NEURALMIND_EVENT_LOG=0disables the writer (in-process feed still works).- The tailer is best-effort. If the file disappears or rolls, the next event re-creates it.
- The bus stays the primary path. The JSONL is a fallback, not a queue.
Notes
- Read-only over HTTP. Edits to nodes/synapses still go through the regular CLI and MCP tools; the UI only inspects.
- The
/api/openendpoint launches$EDITORagainst an allowlist pre-computed from the graph’ssource_fileset, so a tampered client can’t trick the server into opening arbitrary paths. - All assets in
neuralmind/web/(HTML, JS, CSS) are read-only at runtime; the server doesn’t generate any of them. - Graph payload is cached per-session in
_Handler._graph_cache; any endpoint touching graph state respects_graph_lock. - Vanilla-JS frontend, stdlib-only HTTP server, no CDN. Safe to run behind a firewall.
daemon (v0.23.0+)
Manage the local NeuralMind daemon (experimental — PRD 5). The daemon holds
each project’s state warm so repeated queries skip cold backend init. CLI
read commands (query, stats) prefer it automatically when it’s running and
fall back to direct mode otherwise.
neuralmind daemon {start|stop|restart|status} [OPTIONS]
Actions
| Action | Description |
|---|---|
start |
Launch the daemon in the background (writes a discovery file). No-op if already running. |
stop |
Ask the running daemon to shut down gracefully; clears stale discovery. |
restart |
Stop (if running) then start. |
status |
Show pid, uptime, warm projects, and active jobs (exit 3 if not running). |
Options
| Option | Default | Description |
|---|---|---|
--host |
127.0.0.1 |
Host to bind (loopback) |
--port |
8787 |
Port to bind |
--foreground |
False | Run in the foreground instead of detaching (start/restart) |
--json, -j |
False | Machine-readable status output |
Behavior
- One per-user daemon, many projects. A project registry initializes each
NeuralMindonce and reuses it; a per-project lock serializes build/query/watch so they can’t corrupt the index or synapse store; slow builds run as background jobs. - Auto-preference + fallback.
neuralmind query/statsuse the daemon when reachable (output markedvia: daemonin--json), else run directly. Force direct mode withNEURALMIND_NO_DAEMON=1. - Crash-safe discovery. A stale discovery file (dead pid / unreachable) is cleaned up automatically, so a crashed daemon never wedges the CLI.
- Shared API. The daemon speaks one transport-agnostic contract
(
health/status/query/search/stats/build/validate/jobs); theneuralmind-daemonconsole script runs it directly. Token-guarded even on loopback.
# Warm daemon, then fast repeat queries
neuralmind daemon start
neuralmind query . "how does auth work?" # served warm (via: daemon)
neuralmind daemon status --json
neuralmind daemon stop
savings (v0.39.0+)
Show cumulative token savings from the local query event log. Verifies the 12-50× claim against your own real usage rather than trusting the demo.
neuralmind savings [project_path] [OPTIONS]
Options
| Option | Default | Description |
|---|---|---|
--global |
False | Show savings across ALL projects (reads the global event log at ~/.neuralmind/memory/) |
--json, -j |
False | Emit structured JSON output |
--cost |
False | (v0.45.0+) Also show estimated dollar savings, priced on input tokens ($/MTok) |
--model |
claude-opus-4-8 |
(v0.45.0+) Pricing model for --cost — choices come from the built-in input-price table (Claude / GPT / Gemini) |
--queries-per-day |
100 | (v0.45.0+) Assumed daily query volume behind the --cost daily/monthly projection |
--naive-50k |
False | (v4.5.0+) Price “without NeuralMind” at the fixed 50,000 tokens/query instead of the measured token count of the code the index covers |
Since v4.5.0 the per-query “without NeuralMind” cost is the measured token
count of the code the index covers (cached at build), labelled in the output;
--global spans many projects and keeps the fixed estimate. Read-only queries
(evals, benchmarks) aren’t usage and aren’t counted.
Since v4.8.1 only query and wakeup events count. Earlier versions also
counted builds, searches, MCP calls and ingestion as saved queries, and an MCP
query twice, so totals drop after upgrading.
Examples
# Show project-level savings
neuralmind savings .
# → NeuralMind token savings — my-project
# → Queries logged : 47
# → Avg reduction : 38.2x
# → Tokens actually used : 83,140
# → Est. cost without NM : 2,350,000
# → Tokens saved : 2,266,860
# Global savings across all projects
neuralmind savings --global
# JSON for dashboards or scripting
neuralmind savings . --json
# Dollar savings at claude-opus-4-8 input pricing (v0.45.0+)
neuralmind savings . --cost
# → Dollar savings — claude-opus-4-8 @ $5.0/MTok input
# → Cost without NM : $ 11.75
# → Cost with NM : $ 0.42
# → Saved : $ 11.33
# → Projected : $24.12/day · $723.47/month (at 100 queries/day)
# Price against a different model and daily volume
neuralmind savings --global --cost --model gemini-2.5-pro --queries-per-day 250
# JSON gains a "dollar_savings" block when --cost is set
neuralmind savings . --cost --json
The dollar figures are estimates anchored to the same 50,000-tokens-per-query
baseline as the token report, priced on input tokens only — retrieval
decides which input tokens ship, so output pricing never enters the math.
Prices are a 2026-07 snapshot (MODEL_PRICING_PER_MTOK in neuralmind/memory.py).
Only the with-NeuralMind cost is measured from logged tokens; without-NM
(and therefore saved / projected) is estimated from the fixed baseline, so
those figures are marked (est) in the human output. The JSON dollar_savings
block makes this machine-readable too: estimated: true, a basis string, and
baseline_tokens_per_query.
Memory logging must be enabled: answer yes when an interactive neuralmind query first asks, or write {"memory_logging_enabled": true} to ~/.neuralmind/memory_consent.json. NEURALMIND_MEMORY is on by default and only 0 changes anything, so setting it to 1 doesn’t enable logging. (v4.8.2) With memory off, feedback says so and names the fix instead of “run a query first”.
why (v0.43.0+)
Recall the recorded rationale behind code — the decision provenance a human authored, not something inferred.
Harvests Decision: git trailers from history and surfaces the ones whose subjects the query mentions. The trailer is the store: no database, nothing to build, and it works retroactively on commits already in history.
neuralmind why "<symbol or question>" [--project-path PATH]
Capture a decision by adding a trailer to a commit message (backticked symbols in the rationale become subjects automatically; an explicit Subjects: line is also honored):
cli: resolve org id per handler
Decision: resolveOrgId is per-handler, not middleware — avoids Prisma on
/health, /metrics, /token; keeps tests simple.
Subjects: `resolveOrgId`, `authMiddleware`
Arguments
| Argument | Description |
|---|---|
query |
A symbol or natural-language question, e.g. "why is resolveOrgId per-handler" |
Options
| Option | Default | Description |
|---|---|---|
--project-path |
. |
Project root to read git history from |
Sample Output
## NeuralMind decision provenance
- `resolveOrgId`, `authMiddleware`: resolveOrgId is per-handler, not middleware —
avoids Prisma on /health, /metrics, /token; keeps tests simple. (see commit ba25fed)
### doctor *(v0.12.0+)*
Run diagnostic checks on the index.
### eval *(v0.14.0+)*
Evaluate retrieval quality — see [eval](#eval-v0140-project-eval-v450) above.
### feedback good/bad *(v3.1.4+)*
Provide explicit feedback on the last query's results.
#### Arguments
| Argument | Required | Default | Description |
|----------|----------|---------|-------------|
| `--json` | No | `false` | Output as JSON |
#### Output
Confirmation of edges boosted or penalized.
#### Examples
```bash
# Boost edges from last query (good feedback)
neuralmind feedback good .
# Penalize edges from last query (bad feedback)
neuralmind feedback bad .
Prerequisites
Requires a synapse store with edges from a previous query. Feedback adjusts the
last query recorded in .neuralmind/recent_queries.jsonl, and queries are
recorded only while query memory is on. With memory off, feedback exits 1
and says what turns memory on. (v4.9.1+) That holds even when older queries
are still recorded: queries asked since memory went off weren’t, so the last
recorded one may not be the query you mean. feedback names it and when it
was asked, and changes nothing.
health (v3.1.4+)
Check NeuralMind health status.
Arguments
| Argument | Required | Default | Description |
|---|---|---|---|
--json |
No | false |
Output as JSON |
--stale-after |
No | stale_after in .neuralmind.yaml, else none |
(v4.13.0+) A duration (30m, 2h, 1d): exit 0 with status catching_up while the index has been behind the files for no longer than this, 1 after. --json adds behind_seconds and stale_after_seconds. See stale_after |
Output
Health status: healthy, stale, unknown, or no index. Since v4.4.0 “stale” means
the code graph is out of step with the files on disk (the same freshness check
doctor runs), not “older than 24 hours”: a day-old index of
unchanged code is healthy, and an hour-old one that misses new files is not.
“Unknown” means there is an index but no readable graph to check it against;
it exits 1, never 0. --json includes the full freshness report.
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Healthy — the graph matches the files on disk. (v4.13.0+) Also catching_up: behind, but for no longer than --stale-after / stale_after |
| 1 | Stale — files missing from the graph, deleted files still indexed, files changed since the graph, a graph built on another OS, or a graph changed after the last build (v4.13.0+: with --stale-after, only once the index has been behind for longer) — or unknown: no readable graph to check the index against |
| 2 | No index |
Examples
# Check health
neuralmind health .
# Use in CI/CD
neuralmind health . || echo "Index needs rebuild"
metrics (v0.50.0+)
Counts of what NeuralMind did, from the daily JSONL files in
.neuralmind/metrics/ (30 days kept, 10 MB a day at most).
neuralmind metrics [project_path] [--days 7] [--json]
(v4.13.0+) Every query that learns (not --no-learn or
NEURALMIND_NO_LEARN=1) and every build is recorded, whether it came from the
CLI, an MCP tool or the daemon. Prompt-time recall from the hooks and the
Hermes plugin was already recorded, as its own recall events. A query record holds when it ran, its latency,
tokens, search hits and reduction ratio, and no question text, so it is
written without the memory-logging consent that recent_queries.jsonl and
query_events.jsonl wait for. Before v4.13.0 only prompt-time recall was
recorded, so this was the only place a monitor could look, and it never saw a
query.
The report shows queries, the last query (3h ago), mean latency and tokens;
builds, the last build, and the synapse store’s edges and transitions after the
latest build with the change across the window (147 (+47 in window)); the
prompt-time recall outcomes; and a table of queries, builds and recall prompts
per UTC day. --json has the same figures, with per_day and Unix-time
last_ts fields.
To check that an index is in use from a cron job, read queries.last_ts from
neuralmind metrics . --json, or count "event": "query" lines in the
metrics_*.jsonl files directly.
status (v3.1.4+; index reporting v3.4.0+)
Two independent halves of a project’s state in one glance: what is indexed (code nodes, ingested content, when it was last built) and what has been learned (synapse edges, and an “is it learning?” diagnostic). Both are reported even when only one exists — a freshly ingested corpus has no synapses yet, and a long-lived project can carry months of learned edges over an index nobody has rebuilt.
neuralmind status . # human-readable dashboard
neuralmind status . --json # machine-readable
═══ NeuralMind Status — book ═══
Code nodes: 0 (0 edges) — content-only project
Last build: 0.4h ago
Disk: 2.1 MB
Content: 34 file(s), 306 chunks, 306 nodes
Last ingest: 2026-08-23T13:19:14+00:00
Status: 🟢 active
Namespace: personal
Edges: 318 (12 LTP-protected)
...
“Code nodes” is named that way so it doesn’t read as a total: a content-only project legitimately has zero of them and thousands of chunks.
| Argument | Required | Default | Description |
|---|---|---|---|
project_path |
No | . |
Project to inspect |
--json, -j |
No | false |
Output as JSON |
The JSON adds an index object (exists, path, nodes, edges,
built_at, age_hours, disk_mb) and a content object (files, chunks,
nodes, last_indexed_at, tracked) alongside the existing synapse keys.
Reads the IR and the content manifest straight off disk and never constructs a
vector backend, so it answers in milliseconds. An IR above 64 MB is summarized
rather than parsed — nodes comes back null and the build timestamp still
reports.
learn — document ingestion (v1.11.0+)
neuralmind gaps [project_path]
Phase 1 heuristics cover Express + Jest (JS/TS). Route paths are normalized across :id / {id} / ${...} / * styles so a registration and a test reference match; a test “hits the real DB” when its file imports a real-DB fixture (e.g. @/db, testDb) and the case isn’t skip-guarded (SKIP_PG, .skip).
Arguments
| Argument | Description |
|---|---|
project_path |
Project root to scan (default: current directory) |
Sample Output
## neuralmind gaps — live-Postgres coverage
Routes tested in-memory only (no live-DB coverage):
POST /api/sessions — 3 tests — all SKIP_PG ❌
GET /.well-known/jwks.json — 1 test — all SKIP_PG ❌
Endpoints with no tests:
POST /api/auth/jwk/rotate ⚠️
Live-covered:
GET /health ✅
gaps –structural (v1.9.0+)
Identify structural gaps in your codebase — missing bridges between communities that should be connected but aren’t. Built on Brandes’ betweenness centrality algorithm over the structural graph.
neuralmind gaps --structural [OPTIONS]
Options
| Option | Default | Description |
|---|---|---|
--top-k |
20 |
Maximum number of gaps to report |
--threshold |
0.0 |
Minimum gap score to include (0.0 = include all) |
--community |
None | Filter to specific community ID |
--json, -j |
False | Emit structured JSON output |
Examples
# Find top 20 structural gaps
neuralmind gaps --structural
# Find gaps with score > 0.5, JSON output
neuralmind gaps --structural --threshold 0.5 --json
# Gaps in a specific community
neuralmind gaps --structural --community 3
Sample Output
## NeuralMind — Structural Gaps (G5)
Rank Score Node Communities
──── ────── ───────────────────────────────────── ────────────
1 0.87 src/auth/token.py 2, 5
2 0.72 src/models/user.py 1, 3
3 0.65 src/utils/validation.py 2, 4
The equivalent MCP tool is neuralmind_structural_gaps.
review (v0.39.0+)
Warn about likely co-breakage before a commit or when reviewing a diff.
Reads the current git diff (or a specified base ref), finds changed files, and runs spreading activation through the learned synapse graph to surface files that are strongly associated but NOT in your diff — files that have historically been edited together with the ones you’re changing.
neuralmind review [project_path] [OPTIONS]
Options
| Option | Default | Description |
|---|---|---|
--base |
HEAD |
Git ref to diff against. Use HEAD~1 to review the last commit or a branch name to review a feature branch. |
--top-k |
10 |
Maximum number of at-risk files to report |
--json, -j |
False | Emit structured JSON output |
Examples
# Review uncommitted changes (diff against HEAD)
neuralmind review .
# Review the last commit
neuralmind review . --base HEAD~1
# Review changes on a feature branch vs main
neuralmind review . --base main
# JSON output for CI integration
neuralmind review . --json
Sample Output
NeuralMind review — my-project (diff against: HEAD)
Changed files (3):
• auth/middleware.py
• auth/handlers.py
• tests/test_auth.py
Co-break candidates — files NOT in diff but strongly associated (4):
0.782 ████████ auth/tokens.py
0.654 ██████ auth/models.py
0.431 ████ config/settings.py
0.198 ██ docs/auth.md
These files have historically been edited together with the ones above.
Consider whether your change also needs to touch them.
Requires the synapse graph to have accumulated edges. Cold graphs (first few sessions) return empty results. Also available as the neuralmind_review MCP tool.
Wiki reference: Tier2-Operator-Guide · Upgrade-Guide
onboarding (v1.7.0+)
Walk a new operator through license activation, governance defaults, admin email setup, and verification. The entry point for strangers who just ran neuralmind wakeup . and want to configure their project.
neuralmind onboarding # interactive mode — walks all 5 steps
neuralmind onboarding --quick # skip all prompts, defaults only
Options
| Option | Default | Description |
|---|---|---|
--quick |
false |
Skip all prompts, apply defaults (1-seat free, scope=both, threshold=0.1) |
What it walks through
- License activation — checks for
license.json. If missing, auto-issues free tier (or activates Team license if provided). - Governance defaults — sets
publishing_scope(personal/shared/both) andweight_threshold(0.0–1.0). - Admin email setup — adds admin emails for governance notifications.
- Team seat audit — lists current seats (
neuralmind team seats list). - Verification — prints license status + governance state.
Sample Output
$ neuralmind onboarding --quick
✓ Free tier activated (1 seat, never expires)
✓ Governance: scope=both, threshold=0.1
✓ Admin: (none — free tier)
✓ Seats: 1/1 (free)
Run `neuralmind team license status` to view.
Honest scope
- Free tier:
onboarding --quickconfigures governance but doesn’t issue paid license - Team tier: requires
neuralmind team license activate <file>before seat management works - Onboarding doesn’t install hooks or build the index — see
neuralmind install-hooksandneuralmind build
team governance
Team-memory governance (source-available tier2 modules; runs free at 1 seat).
Settings live in ~/.config/neuralmind/tier2.yaml, per user; until an
operator saves them (here or with onboarding), nothing in
the team-memory flow is gated or audited. Enforced since v4.6.0; earlier
versions recorded scope and threshold without enforcing them.
neuralmind team governance status
neuralmind team governance set-scope personal|shared|both --admin EMAIL
neuralmind team governance set-weight-threshold 0.0-1.0 --admin EMAIL
neuralmind team governance set-governance-enabled true|false --admin EMAIL
neuralmind team governance list-shared [--project PATH] [--limit N] [--json]
neuralmind team governance remove-edge SOURCE TARGET [--project PATH] --admin EMAIL
| Setting | Effect on neuralmind memory publish |
|---|---|
scope personal |
Refuses to publish (exit 1); memory stays on each machine |
scope shared |
Publishes only the team baseline (the shared namespace) |
scope both (default) |
Publishes personal + shared memory |
weight_threshold (default 0.1) |
Synapse edges below it are left out of the bundle (transitions aren’t weight-filtered) |
set-governance-enabled false |
Turns the scope and threshold gates off; events are still audited |
The bundle’s provenance.governance records the policy it was published
under. A governance config that exists but can’t be read (unreadable, malformed
YAML, an invalid value) makes publish refuse.
remove-edge SOURCE TARGET (admin-only) deletes the association, and the
transitions between the two nodes, from the project’s shared memory and
review queue. It drops the pair from .neuralmind-team-memory.json and lists
it under retracted (creating the file if needed). Commit the file: each
teammate’s next session deletes the pair from their own shared memory, and
no later publish re-adds it. The file is written first, atomically; if the
local store update then fails, the next import finishes it. A non-admin gets
Permission denied and exit 1.
Changed in v4.6.0: the command takes the two nodes instead of one edge_id
(the old form only wrote an audit entry).
list-shared prints the project’s shared-memory associations, strongest
first (--json: source, target, weight, activation_count).
Audit (neuralmind team audit list|verify|export): configuration changes,
seat and license actions, and, since v4.6.0, every team-memory publish
(including refused ones), import, review_approve / review_reject and
remove. The actor is --admin for admin commands, otherwise
NEURALMIND_ACTOR_EMAIL, the repository’s git config user.email, or the OS
user.
Walkthrough: Govern what your team’s agents share.
optimize-docs (v1.7.2+)
Run the DocEvolver — an evolutionary JSDoc optimizer that finds undocumented methods, generates JSDoc variants using mutation strategies, and evolves them against a retrieval fitness function (Recall@1).
neuralmind optimize-docs <project_path> [OPTIONS]
Arguments
| Argument | Required | Description |
|---|---|---|
project_path |
Yes | Path to project root |
Options
| Option | Default | Description |
|---|---|---|
--blind-spots |
— | Path to a JSON file of pre-computed blind spots (from neuralmind probe --json) |
--generations |
5 |
Number of evolution generations (G) |
--population |
8 |
Population size per generation |
--hysteresis |
0.05 |
Minimum fitness improvement required to promote a variant |
--dry-run |
False | Report only — no file modifications |
--json, -j |
False | Emit structured JSON output |
Examples
# Run full audit + evolution on a project
neuralmind optimize-docs .
# Dry-run (report only, no file changes)
neuralmind optimize-docs . --dry-run
# With pre-computed blind spots from probe
neuralmind probe . --json > spots.json
neuralmind optimize-docs . --blind-spots spots.json
# Custom evolution parameters
neuralmind optimize-docs . --population 10 --generations 8 --hysteresis 0.03
# Machine-readable output
neuralmind optimize-docs . --json
How it works
- Sample — generates N JSDoc variants per undocumented method via four mutation strategies:
LENGTH(1/3/5-line),STRUCTURE(Args/Returns vs prose-only vs mixed),KEYWORD_DENSITY(method-name synonyms vs generic description),POSITION(above vs inline for arrow functions). - Evaluate — patches each variant into the source, runs a natural-language query, and scores Recall@1 = 1/rank of the correct file.
- Promote — the best variant beats the incumbent by the hysteresis margin, then mutates around it for the next generation.
- Patch — after G generations, the winning JSDoc is written back to the source file.
Sample Output
$ neuralmind optimize-docs . --dry-run
DocEvolver — my-project
============================================================
Blind spots found: 12 undocumented methods
- handle_csv_export() (utils/csv.py)
- parse_headers() (utils/csv.py)
- validate_schema() (models/base.py)
...
Run without --dry-run to evolve JSDoc for these methods.
Files
neuralmind/doc_evolver.py— main module (1181 lines)tests/test_doc_evolver.py— 44 tests
license — issuer-side
Mints and manages Team licences. These are operator commands, not
customer commands — every subcommand that changes a licence requires the
Ed25519 issuer private key in NEURALMIND_ISSUER_PRIVATE_KEY_HEX and
exits 1 without it. Customers use neuralmind team license … instead
(see Tier2-Operator-Guide).
The end-to-end selling process these fit into — quoting, invoicing, delivery, renewals — is the Billing Runbook.
neuralmind license issue --customer "Acme Corp" --seats 12 --term 12 [--partner ID] [--output PATH]
neuralmind license renew --customer "Acme Corp" --term 12
neuralmind license revoke --customer "Acme Corp" --reason "non-payment"
neuralmind license status --customer "Acme Corp"
neuralmind license list [--partner ID]
neuralmind license expiring [--within DAYS] [--json] [--quiet]
expiring is the exception to the key requirement above: it is
read-only, needs no issuer key, and exists so a scheduler can watch for
lapsing licences without holding anything sensitive. Nothing else in the
system tracks expiry dates. It signals through its exit code so a caller
never has to parse output to decide whether to alert:
| Exit | Meaning |
|---|---|
| 0 | Nothing due inside the window |
| 6 | Renewals due inside the window |
| 7 | Already expired, or an expiry that cannot be parsed (takes precedence over 6) |
--within sets the look-ahead in days (default 60). --quiet suppresses
output on exit 0, so a cron entry stays silent unless there is news.
--json emits the full report — expired, expiring and unknown
buckets, each sorted most-urgent first, with days_remaining per customer.
Revoked licences are excluded: they cannot be renewed. See the
Billing Runbook for the scheduling recipe.
| Flag | Applies to | Meaning |
|---|---|---|
--customer |
issue, renew, revoke, status | Customer name; also the licence filename, sanitized |
--seats |
issue | Seat count (must be positive) |
--term |
issue, renew | Term in months: 1, 3, 6, 12, 24, or 36 |
--partner |
issue, list | Reseller partner ID |
--within |
expiring | Days ahead to look (default: 60) |
--json / --quiet |
expiring | Machine-readable output / silence when nothing is due |
--output |
issue | Filename for the signed licence, within ~/.neuralmind — a path outside it is refused by the same guard that contains hostile customer names. Defaults to <sanitized-customer-name>.json |
--reason |
revoke | Recorded in the audit log |
Term arithmetic. Terms advance by calendar months, so a 12-month
licence issued on 28 August expires on 28 August the following year. A
day-of-month with no counterpart in the target month clamps to the last
valid day (31 Jan + 1 month is 28 or 29 Feb). renew extends from the
existing expiry rather than from today, so paying late does not grant free
months and paying early does not forfeit any. A revoked licence cannot be
renewed — issue a new one.
State written. All under ~/.neuralmind/:
| File | Contents |
|---|---|
<customer>.json |
The signed licence (--output renames it, still inside this directory) |
customers.yaml |
Customer record: seats, expiry, status, total_paid |
partners.yaml |
Reseller registry and accrued commission |
audit_log.jsonl |
Append-only record of every issue, renew and revoke |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Invalid arguments, including (v4.8.2+) a project path that doesn’t exist |
| 3 | build --strict: the code graph fails the freshness check; daemon status: the daemon isn’t running |
| 6 | license expiring: renewals due inside the window |
| 7 | license expiring: a licence has already expired, or its expiry cannot be parsed |
Most commands exit 1 when the code graph or index is missing, with a hint to
run neuralmind build or neuralmind doctor (health uses 2 for “no
index”). A command’s own section lists any other code it uses.
tests/test_cli_exit_codes.py checks that this table names only codes the CLI
emits, and runs the documented cases.
Environment Variables
| Variable | Default | Description |
|---|---|---|
NEURALMIND_MEMORY |
1 |
Set to 0 to disable query memory logging |
NEURALMIND_LEARNING |
1 |
(deprecated, v0.25.0) Formerly disabled the learned_patterns cooccurrence reranker, which was removed in v0.25.0. Now inert — recognized but ignored. To disable the synapse layer’s prompt-time recall, use NEURALMIND_SYNAPSE_INJECT=0. |
NEURALMIND_BYPASS |
unset | Set to 1 to switch off every NeuralMind hook action temporarily (session memory, prompt recall, stale-decision guard, the neuralmind last cache, the session recap), including (v4.9.0+) the Hermes plugin’s |
NEURALMIND_OUTPUT_REDACT |
1 |
Set to 0 to stop redacting credentials from the PostToolUse Bash recovery cache (.neuralmind/last_output.json) and from the full outputs NEURALMIND_BASH_REPLACE keeps (.neuralmind/bash_outputs/). The cache stores whatever a command printed, so with redaction off a printenv or an Authorization: Bearer header can land a live key in a plaintext file. Not recommended. |
NEURALMIND_REDACT_SECRETS |
unset | Set to 1 to scrub detected credentials from text before it enters the index — equivalent to neuralmind build . --redact-secrets. Off by default because redacting the index costs recall on legitimately secret-shaped identifiers. A backstop, not a substitute for removing and rotating the credential. |
NEURALMIND_TYPE_CHECK |
unset | (v3.0.0+) Set to 1 to confirm inferred return types with mypy during the build’s type-verification pass. Slower but more precise; without it, inference is AST/tree-sitter only. The pass itself runs whenever the synapse layer is enabled and is fail-open — type metadata is observability, never a gate on the build. |
NEURALMIND_SYNAPSE_INJECT |
1 |
(v0.4.0+) Set to 0 to disable prompt-time recall in the UserPromptSubmit hook (and the Hermes plugin’s pre_llm_call): the files a prompt matches and the code linked to them (v4.11.0; before, spreading-activation neighbors) |
NEURALMIND_RECALL_MIN_SIMILARITY |
0.35 |
(v4.11.0+) Prompt-time recall injects nothing when the prompt’s best semantic match in the code scores below this similarity, so “yes”, “continue” or an off-topic question gets no recall block. Measured on this repository’s own index: 15 prompts about the code scored 0.371–0.645, 15 off-topic prompts 0.145–0.364 (one above 0.35). Reproduce from a source checkout (the harness is in tests/, not in the pip package) with python -m tests.benchmark.recall_gate <project>, and pass --prompts with your own sets to calibrate another project or embedder. 0 never abstains. Each outcome is counted in neuralmind metrics |
NEURALMIND_PROVENANCE_INJECT |
1 |
(v0.43.0+) Set to 0 to disable decision-provenance injection in the UserPromptSubmit hook. When enabled (default), Decision: git trailers whose subjects appear in the prompt are surfaced as context alongside synapse recall. Reads git history (the trailer is the store — no separate DB); fails open, so a provenance miss never disrupts the prompt. Query the same data directly with neuralmind why. |
NEURALMIND_SYNAPSE_OUTLIERS |
unset | (v0.44.0+) Set to 1 to add the cohesion outlier check to the UserPromptSubmit injection. When enabled, it finds an associate most of a surfaced co-activation cluster links to and flags the members that skip it — the “handler #11” that breaks the cluster’s shared pattern (validateSession skips resolveOrgId while its 10 peers use it). Off by default; reads neighbors from the synapse store (no embedder work); fails open. |
NEURALMIND_SYNAPSE_EXPORT |
1 |
(v0.4.0+) Set to 0 to disable session-start synapse memory export |
NEURALMIND_REUSE_FEEDBACK |
1 |
(v0.41.0+) Set to 0 to disable the Edit/Write reuse-vs-rewrite feedback hook. When enabled (default), new code that references a symbol already defined elsewhere in the graph reinforces the synapse edge between the edited file and the reused definition, so retrieval learns what you actually reuse. The implicit complement to the explicit neuralmind_feedback MCP tool. Language-agnostic, never forces a build, fail-open. |
NEURALMIND_TEAM_MEMORY |
1 |
(v0.30.0+) Set to 0 to disable auto-inheriting a committed .neuralmind-team-memory.json team bundle. When enabled (default), a teammate’s SessionStart/build imports the bundle once into the shared namespace (content-hash-gated, shared-only, fail-open). Publish your own with neuralmind memory publish. |
NEURALMIND_READ_DEDUP |
1 |
(v4.6.0+) Set to 0 to stop the Read PostToolUse hook from replacing a repeat read of unchanged content with a stub. Inactive anyway without a session id, in a project without .neuralmind/, and under NEURALMIND_BYPASS=1 or NEURALMIND_NO_LEARN=1. See install-hooks. |
NEURALMIND_SESSION_RECAP |
1 |
(v4.8.0+) Set to 0 to stop recording prompts and edited files under .neuralmind/recaps/ and stop the SessionStart recap. NEURALMIND_NO_LEARN=1 stops the recording only; an existing recap is still shown. See recap. |
NEURALMIND_SESSION_RECAP_MAX_AGE_DAYS |
14 |
(v4.8.0+) A recap whose session was last active longer ago than this many days isn’t shown, at SessionStart or by neuralmind recap. |
NEURALMIND_PROJECT |
unset | (v4.9.0+) Hermes plugin: the project to serve, ahead of the one given to install-hermes-plugin, Hermes’s TERMINAL_CWD and the directory Hermes runs in. A project that hasn’t been built is skipped. Without it or a pinned project, a gateway session uses the gateway’s terminal working directory (terminal.cwd, else MESSAGING_CWD, else your home directory). Set it, or pin a project, for Hermes Desktop, ACP editor sessions and per-session workspaces, which keep each session’s directory where the plugin doesn’t read it. See install-hermes-plugin. |
NEURALMIND_HERMES_TIMEOUT |
8 |
(v4.9.0+) Hermes plugin: seconds each call to NeuralMind may wait before the turn goes ahead without that call’s context. A session’s first turn makes two (the recap, then recall), so it can wait up to twice this; each recorded edit gets the same limit. Keep twice this below Hermes’s plugins.hook_callback_timeout (default 30 s): past that, Hermes drops the whole block and skips the plugin’s per-turn hook for the next 60 seconds. See install-hermes-plugin. |
NEURALMIND_DECISION_SCAN |
1 |
(v4.6.0+) Set to 0 to make neuralmind decisions scan (and so the init-hook post-commit hook) skip marking decisions STALE. |
NEURALMIND_DECISION_SEARCH |
hybrid |
(v4.8.0+) Default decision-search mode for the CLI, the MCP tools and the Python API when a call names none: hybrid (shared words and meaning, fused), semantic (meaning only) or keyword (shared words only, the v4.6 behavior). Case-insensitive; an unknown value is logged and ignored. A call’s own --mode / mode wins. See decisions. |
NEURALMIND_ACTOR_EMAIL |
unset | Who neuralmind team commands act as when --admin is omitted (unset: unknown, which no admin list matches), and (v4.6.0+) the actor recorded for team-memory audit events (publish, import, review); for those, unset falls back to the repository’s git config user.email, then the OS user. NEURALMIND_ACTOR is an accepted alias. |
NEURALMIND_EVENT_LOG |
1 |
(v0.6.0+) Set to 0 to disable the cross-process JSONL event-bridge writer at <project>/.neuralmind/events.jsonl. The in-process event bus is unaffected; serve running in the same process as the activity source still gets a live feed. |
NEURALMIND_OUTPUT_CACHE |
1 |
(v0.10.0+) Set to 0 to disable the recovery cache that backs neuralmind last. It also turns off NEURALMIND_BASH_REPLACE, which never trims an output it has nowhere to keep whole. |
NEURALMIND_OUTPUT_CACHE_MAX |
2097152 |
(v0.10.0+) Total size cap (bytes) for the recovery cache. Oversize payloads are split proportionally between stdout/stderr and truncated keeping head + tail. |
NEURALMIND_BASH_REPLACE |
unset | (v4.10.0+) Set to 1 to let the Bash PostToolUse hook replace the output of an allowlisted noisy log with its progress lines elided, via updatedToolOutput. The allowlist is pip install (also python -m pip install) and neuralmind build, run on their own (after cd, source or variable assignments at most; any pipe, redirect or second command disqualifies the line). Each run of elided lines becomes one [neuralmind: N progress lines elided: …] marker; every other line, and any line mentioning an error, warning, failure or deprecation, reaches Claude as printed. The full output, credentials redacted, is kept in .neuralmind/bash_outputs/ (newest 20), and the replaced result ends with its path. Other commands, failed commands, and Read and Grep results are never replaced. Measured, with its retention gates, in the compression benchmark. Hooks inherit Claude Code’s environment, so set it in .claude/settings.json ("env": {"NEURALMIND_BASH_REPLACE": "1"}) or before launching claude. |
NEURALMIND_BASH_SMALL |
500 |
(v0.10.0+) Threshold below which compress_bash() passes failing output through verbatim. Python API only: the hooks no longer compress tool output. |
NEURALMIND_BASH_MAX_CHARS |
3000 |
Threshold above which compress_bash() compresses successful output. Python API only: the hooks no longer compress tool output. |
NEURALMIND_BASH_TAIL |
3 |
Number of tail lines compress_bash() always keeps verbatim. Python API only: the hooks no longer compress tool output. |
NEURALMIND_EVAL_LLM_JUDGE |
0 |
(v0.13.0+) Opt-in LLM-as-judge mode for the offline faithfulness eval harness (evals/faithfulness/). Off by default and never the CI gate; when set, the runner prints a notice that answers + gold facts would be sent to a third-party API. The default judge is the zero-network offline expected-fact-recall scorer. |
NEURALMIND_PARITY_REDUCTION_TOL |
0.25 |
(v0.15.0+) Backend parity gate (evals/parity/run.py): max fraction the built-in backend’s mean token reduction may sit below graphify’s (0.25 = within 25%). |
NEURALMIND_PARITY_FAITHFULNESS_TOL |
0.10 |
(v0.15.0+) Backend parity gate: max absolute points the built-in backend’s faithfulness delta / fact recall may sit below graphify’s (0.10 = 10 points). |
NEURALMIND_PARITY_REDUCTION_FLOOR |
4.0 |
(v0.15.0+) Backend parity gate: absolute minimum mean reduction the built-in backend must clear, independent of graphify (mirrors the self-benchmark floor). |
NEURALMIND_PARITY_FAITHFULNESS_FLOOR |
0.0 |
(v0.15.0+) Backend parity gate: absolute minimum faithfulness delta the built-in backend must clear (mirrors the eval gate — smart selection ≥ matched-budget naive truncation). |
NEURALMIND_PARITY_COVERAGE_FLOOR |
0.90 |
(v0.16.0+) Backend parity gate: minimum fraction of the gold graph’s per-language symbols the built-in backend must recover for TypeScript/Go/Rust/Java/C/C++ (structural parity, since no gold-fact set exists for those fixtures yet). |
NEURALMIND_PRECISION |
unset | (v0.17.0+) Set to 1 to enable the optional SCIP precision pass: when a *.scip index is present in the project root, the built-in backend’s heuristic calls/inherits edges are replaced with compiler-accurate ones for the files the index covers. Off by default; a no-op when unset or when no index is found. |
NEURALMIND_ONNX_MODEL_DIR |
unset | (v0.21.0+) Path to a pre-extracted all-MiniLM-L6-v2 ONNX folder (model.onnx + tokenizer.json) for the ChromaDB-free turbovec backend’s bundled embedder. When unset, the model is resolved from NeuralMind’s cache, an existing ChromaDB cache, or downloaded (SHA256-verified). Set it for air-gapped installs so no network is needed. |
NEURALMIND_ORT_THREADS |
unset | Pin the ONNX Runtime intra-op thread pool for the bundled MiniLM embedder to N threads (inter-op is set to 1 alongside it). Unset keeps ORT’s default (sized to the host’s core count). ORT’s parallel summation order moves the last bits of the embedding floats with the thread count, so near-tie rankings can differ between machines with different core counts; 1 removes that dependence at some indexing-throughput cost. It does not make output identical on every machine: with 1 set, public-benchmark CI runs still split into two states with slightly different token counts depending on the runner, while on an Apple M3 the public benchmark gave byte-identical output with and without it (details). The self-benchmark harness sets 1 automatically, and the public-benchmark CI job runs with it. |
NEURALMIND_NO_DAEMON |
unset | (v0.23.0+) Set to 1 to force CLI commands to run in direct mode even when a daemon is running (skips the daemon auto-preference for query/stats). |
NEURALMIND_NAMESPACE |
unset | (v0.24.0+) Pin the active memory namespace for this process (e.g. ephemeral for throwaway exploration, shared on a CI box building team baseline). Overrides config and git-branch detection. Resolution order: this var → memory_namespace: in neuralmind-backend.yaml → branch:<name> on a non-default git branch → personal. |
NEURALMIND_DAEMON_HOME |
unset | (v0.23.0+) Override the directory holding the daemon discovery file (daemon.json). Defaults to ~/.neuralmind. Mainly for tests / running an isolated daemon. |
NEURALMIND_BM25 |
1 |
(v0.38.0+) Set to 0 to disable the BM25 keyword index and fall back to pure vector search. When enabled (default), the BM25 index built by neuralmind build is merged with vector results via Reciprocal Rank Fusion at query time, improving exact-name retrieval for code queries like "UserService" or "get_auth_token". The index is stored in <project>/.neuralmind/bm25_index.json and rebuilt automatically on every neuralmind build. 0 also turns off the v4.6.0 keyword indexes below. |
NEURALMIND_BM25_UNIFIED |
1 |
(v4.6.0+) One BM25 index over every node — doc text, symbol names, file paths, docstrings — written by neuralmind build to <project>/.neuralmind/bm25_unified_index.json and fused into L3 by RRF. On the default turbovec backend the keyword index otherwise holds only document nodes, so docs get a keyword signal code never does; the ChromaDB backend’s index already covered every node, and this makes both behave the same. Set to 0 to restore v4.5.0’s keyword list. An index built before v4.6.0 keeps the old list until the next neuralmind build (queries never build). It adds one BM25 lookup per query. Measured on six repos: mean hit@5 72.8% → 79.4%; requests −1 question, rich MRR 0.71 → 0.60, and on the public benchmark click gains a miss (reproducible on demand, not a CI gate; eval). |
NEURALMIND_BM25_CODE |
unset | (v4.6.0+, research flag) Set to 1 to fuse a second, code-only keyword list (symbol names, their files and docstrings) by RRF. Off by default because it lost on the retrieval eval: requests −3 questions against v4.5.0; on top of the unified index, rich −4 and click −3. |
NEURALMIND_SELECTOR_AUTOTUNE |
0 |
(v0.26.0+) Set to 1 to enable the self-improvement engine’s selector auto-tuner: the SessionStart hook adjusts the L2 recall depth from the re-query rate (once per session), and build() threads the persisted value into the selector. Opt-in (== "1", not the != "0" pattern) because it is net behavior change. With it unset the hot path does zero extra I/O and the selector keeps its hard-coded default. Inspect state with neuralmind self-improve status. |
NEURALMIND_STRUCTURAL |
1 |
(v0.42.0+) Master switch for the structural code-graph layer (calls/inherits/imports_from edges from graph.json). Powers the neuralmind structural command, the neuralmind_structural_neighbors MCP tool, and blast-radius. Set to 0 to skip building the index entirely, leaving retrieval byte-identical to v0.41.0. |
NEURALMIND_STRUCTURAL_RECALL |
0 |
(v0.42.0+) Opt-in (== "1"). Fold a query hit’s structural neighbors (callers/callees/base classes) into L3 retrieval, budget-neutrally (displacement, not addition). Off by default because the structural signal can saturate top-k recall and crowd out the learned synapse reranker on some graphs; the always-on structural query tools carry the value with zero effect on the tuned retrieval stack. |
NEURALMIND_STRUCTURAL_MIN_CONFIDENCE |
0.0 |
(v0.42.0+) Drop structural edges whose confidence_score is below this value when building the index. Raise toward 1.0 to trust only compiler-accurate edges (pair with NEURALMIND_PRECISION). |
NEURALMIND_STRUCTURAL_HUB_DEGREE |
50 |
(v0.42.0+) Per-relation degree cap for structural recall and blast-radius. Above the cap, an over-connected utility’s neighbors are down-weighted (recall) or truncated (blast-radius) so one hub can’t dominate. |
NEURALMIND_PARITY_SAMPLES |
3 |
(v3.9.0+) Backend parity gate: how many times the faithfulness A/B is sampled before the mean is gated. Averaging absorbs ANN query-time ordering jitter, matching the self-benchmark’s onboarding-lift gate, so a failure means a real regression rather than an unlucky draw. Set to 1 to restore single-sample behavior. |
NEURALMIND_DRIFT_REFRESH |
1 |
(v3.2.0+) Set to 0 to make neuralmind drift judge strictly against the last-built graph, skipping the transparent re-parse of changed files (same effect as --no-refresh). The re-parse needs tree-sitter (the graphgen extra); without it this is already a no-op. |
NEURALMIND_CHUNK_SIZE |
500 |
(v3.4.0+) Default max characters per chunk for ingest-content, so a corpus’s chunking doesn’t have to be retyped on every run. --chunk-size overrides it. A malformed value warns and falls back to the default rather than failing the ingest. |
NEURALMIND_OVERLAP |
50 |
(v3.4.0+) Default character overlap between chunks for ingest-content. --overlap overrides it. Must be less than the chunk size — the chunker cannot make progress otherwise, so the command exits 2 with the offending pair named. |
NEURALMIND_INGEST_TIMEOUT |
0 |
(v3.4.0+) Default --timeout for ingest-content, in seconds; 0 means unlimited. On expiry the run stops between files, writes the manifest for what it indexed, and exits 1 — the next run resumes rather than restarting the corpus. |
NEURALMIND_NO_LEARN |
unset | (v4.5.0+) Set to 1 to make every query in the process read-only — CLI, MCP server and hooks. Synapse recall still shapes results, but nothing is reinforced or logged, the synapse database is opened read-only, hooks skip edit/transition learning, decay and (v4.8.0+) session-recap recording, and nothing builds an index. For eval harnesses and CI. Per call: query --no-learn, MCP learn: false |
NEURALMIND_NO_PROGRESS |
unset | (v3.4.0+) Set to 1 to suppress progress output everywhere (same as --no-progress), build included. Progress is TTY-aware already — an in-place bar on a terminal, plain milestone lines off one for ingest-content — so this is for golden-output tests and log-sensitive CI jobs. Since v4.4.0 the build embedding bar shows only on a terminal, and read commands (query, search, wakeup, the MCP tools) print nothing on success. |
NEURALMIND_INTENT_THRESHOLD |
0.6 |
(v3.9.0+) Margin the intent classifier needs before it calls a query code or docs rather than hybrid: one side’s keyword score must exceed the other’s by this fraction. Raise it to send more queries down the neutral hybrid path. |
NEURALMIND_CODE_BOOST |
3.0 |
(v3.9.0+) Score multiplier applied to code hits (docstrings included) when the query asks for code (query --type code, MCP query_type: "code"); doc hits are multiplied by 0.5. Re-ranks the 20-candidate pool before L3 is cut. (v4.12.0+) An intent detected from the question no longer applies it unless NEURALMIND_AUTO_INTENT_BOOST=1. |
NEURALMIND_DOC_BOOST |
2.0 |
(v3.9.0+) Score multiplier applied to doc hits when the query asks for docs (code hits are multiplied by 0.7). Same v4.12.0 rule as NEURALMIND_CODE_BOOST. |
NEURALMIND_AUTO_INTENT_BOOST |
unset | (v4.12.0+) Set to 1 to re-weight hits by the intent detected from the question’s wording (the v4.11 behaviour). Off by default: on the retrieval eval it lost 3 questions and won none, and the markdown-heavy repository dropped from 77% to 67% hit@5 (eval). A requested --type always re-weights. |
NEURALMIND_L3_K |
8 |
(v4.12.0+) Search results in L3 per query (v4.11 and earlier: 4). Putting it back to 4 lost 4 of 150 questions on the retrieval eval. |
NEURALMIND_L3_POOL |
20 |
(v4.12.0+) Fused vector + keyword candidates L3 is chosen from. The intent, code-signal and duplicate passes rank the whole pool before it is cut to NEURALMIND_L3_K; L2 lists the pool’s candidates that L3 doesn’t show. |
NEURALMIND_L3_FILE_DECAY |
0.6 |
(v4.12.0+) Spreads L3 across files: the k-th further hit from a file L3 already shows keeps 0.6^k of its score, so one file can’t take all eight slots. Each file’s first hit keeps its place, and the hits it moves out are listed in L2. 1 turns it off. On the self-benchmark fixture it took the top-k hit rate from 88% to 97%; without it the public benchmark misses flask’s request-wrapper. |
NEURALMIND_TEST_FILE_FACTOR |
0.5 |
(v4.12.0+) Multiplies the score of hits from test files (tests/, test_*.py, *_test.go, *.spec.ts, conftest.py, …) unless the question mentions tests. 1 turns it off. On this repository test files took two to seven of the eight L3 slots; with it, the retrieval eval’s MRR there went from 0.54 to 0.64. (v4.12.1+) A question that names a test (test_parse_option(), parse_test.go) counts as mentioning tests, and folders are judged inside the project, so a graph with absolute paths doesn’t make a checkout under tests/ a test. |
NEURALMIND_EXAMPLE_FILE_FACTOR |
0.5 |
(v4.12.1+) Multiplies the score of hits from example scripts (an examples/, demos/ or samples/ folder anywhere in the project, or a top-level example/, demo/ or sample/) unless the question asks for an example. 1 turns it off. On pallets/click indexed from its root, 4 of 14 questions led with an example script; with it, hit@1 there went from 6 to 9 of 14. A singular example/ deeper down doesn’t count: Java packages are folders, and Spring Initializr’s default package is com.example.demo. |
NEURALMIND_CODE_SIGNAL_CAP |
1 |
(v4.12.0+) Ceiling on the code-signal boost (a hit whose file name, label or text shares words with a code-intent question). 1 turns it off (the default); N turns it on, up to N× (v4.11: 10×, matching substrings of the whole path). On, it lost 2 questions on the retrieval eval. A markdown file is still marked down (×0.3) under a code intent. |
NEURALMIND_QUERY_LAYERS |
L0,L1,L2,L3 |
(v4.12.0+) Comma-separated layers a query returns. L0,L3 cuts tokens by 57% on the retrieval eval at the same hit@5, but loses the facts L1 and L2 carry (fact recall on the faithfulness fixture). wakeup is unaffected. |
NEURALMIND_ORT_SESSION_CACHE |
auto | (v4.12.0+) 1 reuses one ONNX Runtime session per process for the bundled MiniLM embedder; 0 builds a new one per batch. Auto reuses everywhere except Python 3.14, where onnxruntime 1.29 deadlocks after a few runs on one session. Reusing it, a query embeds in about 3 ms instead of about 275 ms. |
NEURALMIND_RETRIEVAL_EXPANSION |
0 |
(v3.10.0+) Opt-in — set to 1, true, yes or on (case- and whitespace-insensitive); anything else, including unset, is off. Lets the v3.9.0 retrieval pull-in — two-pass source-file search, synapse-seeded expansion, and snippet extraction — contend for L3 slots, budget-neutrally (displacement, not addition). Off by default because it was measured, not because it is unfinished: on the faithfulness fixture it takes the delta from +0.041 to -0.065 appended (how v3.9.0 shipped) or -0.107 displaced, against a +0.000 gate floor. Making it budget-neutral made it worse, which is the useful finding — displacing evicts a real hit per candidate, so candidates that are worse than what they replace cost facts, not just tokens. Intent classification and the code-signal boost are unaffected by this flag and stay on; they are bit-for-bit neutral on the same fixture. Turning this on is a research setting until a gate says otherwise. Reproducing these numbers requires a fresh copy of the fixture per sample — query() reinforces synapses into <project>/.neuralmind/synapses.db, so re-running against the same directory measures a progressively trained index, not a repeat. |
NEURALMIND_L3_PER_FILE |
unset | (v4.6.0+, research flag) Set to N to allow at most N L3 hits per file, refilling vacated slots from the next-best candidates of the same search. Off by default: at 2 it raised mean hit@5 by 1.1 points with two repos up and none down on hit@5 (public recall 96.25%), but mean MRR fell 0.654 → 0.629 (requests 0.75 → 0.69, flask 0.71 → 0.65), and the keep rule needs three repos. |
NEURALMIND_DOC_HANDOFF |
unset | (v4.6.0+, research flag) Set to 1 so a doc hit that names a code file or symbol brings that code into contention for an L3 slot. Off by default: it helps only where the docs name code (against v4.5.0: the private repo +3 questions, neuralmind +1) and was a wash on top of the unified index. |
NEURALMIND_HUB_DAMPEN |
unset | (v4.6.0+, research flag) Set to 1 to down-weight files returned far more often than chance (with a floor, so a hub that is the only match still wins). Off by default: it cost click 3–4 questions — on a small library the most-returned files are central modules, not hubs. |
NEURALMIND_INTENT_RULES |
unset | (v4.6.0+, research flag) Set to 1 to classify “how does X…”, “where is X…” and “which X is…” questions as code intent (and questions that name a document or ask how to install as docs) before the v3.9.0 classifier runs; query --explain then shows (by question shape). Off by default: measured on v4.6.0, it only re-ordered the four hits L3 had chosen, so it moved MRR (0.654 → 0.672) and never hit@5. Since v4.12.0 a detected intent re-weights hits only with NEURALMIND_AUTO_INTENT_BOOST=1. |
NEURALMIND_INTENT_POOL |
unset | (v4.6.0+, research flag) Set to 1 (measured with NEURALMIND_INTENT_RULES=1) to let intent rank all 10 search candidates instead of re-ordering the four L3 chose. Off by default: it cost click 8 questions and took the public benchmark’s recall to 83.75% (measured against v4.5.0). |
Vector backend selection (v0.21.0+)
NeuralMind’s vector store is pluggable. The default is auto (an unset config
behaves the same): it resolves to the ChromaDB-free turbovec backend when
its stack (turbovec + onnxruntime + tokenizers) is importable, and
otherwise falls back to chroma. As of v0.29.0 the turbovec stack is a
platform-gated base dependency, so a plain pip install neuralmind resolves
to turbovec (ChromaDB-free) out of the box on platforms with turbovec wheels
(Linux, macOS arm64, Windows x86_64). On platforms without a turbovec wheel
(Intel macOS, Windows ARM) ChromaDB is auto-installed as the fallback backend,
so the install always works. Install pip install "neuralmind[chromadb]" (and
pin backend: graph) to force ChromaDB anywhere. (Alpine/musl Linux: use a
python:slim glibc image or the [chromadb] extra — markers can’t gate musl.)
To pin a backend explicitly (an explicit value always wins over auto), drop
a neuralmind-backend.yaml at the project root:
backend: turbovec # the default path: TurboVec ANN + bundled OnnxMiniLMEmbedder
# backend: graph # force ChromaDB (alias: chroma) — needs `pip install "neuralmind[chromadb]"`
# backend: auto # the default — turbovec when its deps are installed, else chroma
The turbovec/ONNX stack ships by default; pip install "neuralmind[chromadb]"
adds the opt-in ChromaDB backend. Vectors are byte-identical between backends, so
retrieval quality is at/above parity; the turbovec index is 8–16× smaller.
Selecting graph/chroma without the [chromadb] extra raises an actionable
error pointing at the install command. Accepted values: auto (default),
graph / chroma, turbovec, in_memory (offline tests).
One-time auto-reindex. When auto resolves to turbovec for a project that
still has a legacy ChromaDB index and no turbovec index yet, the next build
reindexes from graph.json and prints a one-line notice. The old ChromaDB index
is left in place as a fallback — nothing is deleted.
Run neuralmind doctor to see which backend the current environment resolves to
(see the Backend line).
Security settings (security: in neuralmind-backend.yaml)
The same file carries the MCP security policy. The file is read once per process, so restart the MCP server after changing it.
| Key | Default | Effect |
|---|---|---|
roles |
built-in admin / builder / reader |
Role name → list of MCP tool names, or "*" for all. Replaces the default policy; a role it doesn’t list gets no tools, and an empty mapping grants nothing. The MCP server in v4.6.0 and earlier ignored this setting |
rate_limit |
max_calls: 60, window_seconds: 60 |
Calls allowed per actor per window. Whole numbers; window_seconds at least 1 |
identity (v4.7.0+) |
declared |
declared: each MCP call names its own actor and role, unauthenticated. os: the actor is the OS account the server runs as, read from the OS rather than environment variables, and the role comes from users. Stdio transport only |
users (v4.7.0+) |
{} |
OS account name → role, used with identity: os |
default_role (v4.7.0+) |
unset | Role for an OS account missing from users. Unset refuses such accounts |
require_encrypted_storage (v4.7.0+) |
false |
Refuse to build, query, serve MCP tools, or run hooks unless every volume holding state (the project, .neuralmind/, a custom db_path) is verified encrypted. Only false, 0, no or off turns it off; a blank value counts as on |
security:
identity: os
users:
alice: builder
bob: reader
roles:
builder: [neuralmind_wakeup, neuralmind_query, neuralmind_search, neuralmind_skeleton, neuralmind_build]
reader: [neuralmind_wakeup, neuralmind_query, neuralmind_search, neuralmind_skeleton]
require_encrypted_storage: true
With identity: os, NeuralMind refuses every MCP call when it can’t establish
the caller: the HTTP transport is in use, the OS account can’t be read, the
account has no role, or the policy file is world-writable (POSIX). A policy file
that doesn’t parse but names a security setting (security, roles,
rate_limit, identity or require_encrypted_storage) is refused too,
rather than silently ignored (v4.7.0 checked only identity and
require_encrypted_storage), and so is a malformed security, roles or
rate_limit value, or (v4.8.0+) a security: or roles: key left empty. Each refusal is written to
.neuralmind/audit_events.jsonl with reason: identity, storage, or config,
and the actor and role a call claimed are kept as claimed_actor and
claimed_role.
(v4.13.0+) Every mcp_call and mcp_call_denied event also records the
calling client as details.client: the clientInfo name and version it sent
in the MCP handshake (capped at 128 and 64 characters), and the resolved role
as actor_role. The client names itself, so this attributes calls without
authenticating them: roles and rate limits never depend on it. neuralmind
audit recent shows it in a Client column.
Examples
Complete Workflow
# 1. Build neural index (the code graph is generated automatically)
neuralmind build ~/projects/myapp
# 3. View statistics
neuralmind stats ~/projects/myapp
# 4. Get wake-up context for new conversation
neuralmind wakeup ~/projects/myapp > context.md
# 5. Query specific functionality
neuralmind query ~/projects/myapp "How does the payment system work?"
# 6. Search for specific entities
neuralmind search ~/projects/myapp "PaymentController" --n 5
# 7. Run benchmark
neuralmind benchmark ~/projects/myapp
Scripting Integration
#!/bin/bash
# update_and_query.sh - Update index and run queries
PROJECT="$1"
QUERY="$2"
# Rebuild if graph changed
if [ graphify-out/graph.json -nt graphify-out/neuralmind_db ]; then
echo "Graph updated, rebuilding index..."
neuralmind build "$PROJECT"
fi
# Run query
neuralmind query "$PROJECT" "$QUERY" --json
Piping to AI Tools
# Get context and pipe to clipboard (macOS)
neuralmind wakeup ~/projects/myapp | pbcopy
# Get context and pipe to clipboard (Linux)
neuralmind wakeup ~/projects/myapp | xclip -selection clipboard
# Save context to file for AI assistant
neuralmind query ~/projects/myapp "Explain the auth system" > auth_context.md
See Also
- API Reference - Python API documentation
- Architecture - System design details
- Integration Guide - MCP and tool integrations