This document covers CLI commands, options, configuration, and developer facing details. It does not cover MCP behavior or server side implementation beyond public API references.
Provide a complete CLI command reference and developer context for Neotoma CLI behavior.
- Commands and options MUST match the CLI implementation.
- Output formatting MUST remain deterministic and stable.
- Configuration details MUST remain consistent with the CLI config module.
- Operation ID: OpenAPI operation identifier used by
neotoma request. - Config path: The file path where CLI stores connection settings.
- PKCE: OAuth Proof Key for Code Exchange used for login.
Run via npm scripts: npm run cli or npm run cli:dev (dev mode with immediate source changes). For global neotoma command: npm run setup:cli (build and link in one step), or manually npm run build:server then npm install -g . or npm link. If neotoma is not found, add $(npm config get prefix)/bin to PATH. See CLI setup and CLI overview for full installation and troubleshooting details.
The global neotoma runs the built files in dist/, not the TypeScript source. After you change CLI code, either build once (npm run build:server) or run a watcher so the global install stays current:
- One-off:
npm run build:serverafter CLI changes. - Ongoing (terminal): Run
npm run dev:typesin a terminal. It runstsc --watchso every save recompilesdist/; the nextneotomainvocation uses the new code. Withnpm link, no re-link is needed. - Ongoing (after reboot): Run
npm run setup:launchd-cli-synconce (alias:setup:launchd-watch-build). This installs a macOS LaunchAgent that re-links the globalneotomacommand to the current checkout, runstsc --watchat login, and keeps it running so the global CLI stays in sync even after restart. The agent captures the currentnode/npmpaths during setup; re-run the setup command after switching Node manager or Node version. Logs:data/logs/launchd-watch-build.log. Unload withlaunchctl unload ~/Library/LaunchAgents/com.neotoma.watch-build.plist.
- Dev API:
npm run setup:launchd-dev(aliassetup:launchd-dev-server) installscom.neotoma.dev-server, which runsnpm run dev:server. Seedocs/developer/launchd_dev_servers.md. - Built production-mode API:
npm run setup:launchd-prod-serverinstallscom.neotoma.prod-server, which runsnpm run start:server:prod. Seedocs/developer/launchd_prod_server.md.
Run npm run setup:launchd-issues-sync once to install com.neotoma.issues-sync: it runs neotoma issues sync every 5 minutes (and once at login). Logs: data/logs/launchd-issues-sync.log. Optional env for launchd (not in your shell profile): copy scripts/launchd-issues-sync.env.example to data/local/launchd-issues-sync.env. Unload with launchctl unload ~/Library/LaunchAgents/com.neotoma.issues-sync.plist.
To target a specific API environment, pass dev or prod as the first argument or use --env.
neotoma dev— development API (port 3080).neotoma prod— production API (port 3180).
Examples: neotoma prod, neotoma dev, neotoma prod storage info. Equivalent to using --env.
When you run neotoma with no arguments, source checkout selection follows this precedence:
- explicit CLI flags (for commands that accept root/path flags)
NEOTOMA_REPO_ROOT- directory-local checkout (walk up from current directory)
- saved config
project_root(legacyrepo_root) in~/.config/neotoma/config.json - user-level env at
~/.config/neotoma/.envwhen no checkout is detected
With a source checkout, the CLI:
- Uses use-existing policy only (no automatic server start).
- Discovers running API instances from session ports, default ports (
3080,3180), remembered ports, and optional extra configured ports. - Applies
--envas a preference when selecting among multiple detected instances. - If multiple candidates remain, prompts you to choose the instance.
- If none respond, shows fallback startup guidance and the command menu.
- Enters an interactive session with a
neotoma>prompt.
Non-interactive (e.g. agents): When stdout is not a TTY, the CLI does not prompt. It uses use-existing (connect only). If no command is given and stdout is not a TTY (e.g. an agent runs neotoma with no args), the CLI prints: "No command given. Run neotoma (e.g. neotoma entities list). Use neotoma --help for options." and exits with code 1.
Direct invocation and session parity: Every action available at the interactive neotoma> prompt is available as a direct CLI call: neotoma <top-level> [subcommand] [options] [args]. Examples: neotoma entities list, neotoma relationships list <entityId>, neotoma storage info. Agents and scripts should always use direct invocation; do not depend on entering the interactive session. See CLI overview and the command sections below for the full command tree.
To show the intro and then a command menu (prompt > and ? for shortcuts; no servers), use:
neotoma --no-sessionYou get the same intro panel, then an interactive prompt. Type a command, or?/helpto list commands. Typeexitorquit, or press Ctrl+D, to exit.
To enter a session without starting any servers (connect to an already-running API):
neotoma --no-serversorneotoma --servers=use-existingConnect-only startup. Detects all available local API instances (not only3080/3180), prefers--envmatches when available, and prompts when multiple candidates remain.
To start the API in the background:
neotoma api start --background --env dev(or--env prod) Starts the selected environment only and writes env-specific PID/log files.neotoma api start --background --env dev --tunnel(or--env prod --tunnel) Starts the API with an HTTPS tunnel (ngrok/cloudflared) for remote MCP access. Tunnel URL is written to/tmp/ngrok-mcp-url.txtwhen ready. Use--tunnel-provider ngrokor--tunnel-provider cloudflareto force a provider; otherwise the script auto-detects from installed tools.neotoma api start --env dev --tunnel(or--env prod --tunnel, no--background) Runs the API and tunnel in the foreground in the current terminal (same asnpm run dev:server:tunnel). Logs stream to the terminal; Ctrl+C stops both. Tunnel URL:cat /tmp/ngrok-mcp-url.txt.
--watch flag (source checkouts). The product CLI command remains neotoma api start, but source-checkout npm spawn targets now use the dev:server* taxonomy. Production-env source checkouts route to dev:server:prod; installed-package production starts route to start:server:prod.
neotoma api start --env prod --watch— keeps source-checkout watch behavior explicit.neotoma api start --env prod --tunnel— routes todev:server:prod:tunnel.
The flag is a no-op on --env dev, on installed-package checkouts without source watch scripts, and on the --tunnel path (the tunnel flow always wants the watcher). The deprecation line is suppressed under --output json.
For a real headless / systemd production deployment, install the published npm package (npm install -g neotoma) and use the recipe in install.md § Production deployment (headless / systemd) rather than neotoma api start on a source checkout.
All npm run <script> commands in one place. Scripts follow the taxonomy documented in docs/developer/npm_scripts.md: dev:* for local development and watch processes, build:* for one-shot builds, start:* for compiled-dist runners, and info:* for one-shot informational output. Old watch:*, start:api*, and pages-site names remain as one-release compatibility aliases.
| Script | Description |
|---|---|
build:server |
Compile server TypeScript (tsc) → MCP, API, CLI, services |
build:ui |
Build frontend (Vite) |
start:mcp |
Run built MCP server (stdio) |
start:server |
Run built HTTP Actions API |
start:api |
Deprecated alias for start:server |
start:server:prod |
Run build:server, pick HTTP port from 3180, set NEOTOMA_ENV=production, then run start:server (same node dist/actions.js as start:server). Does not build UI or run MCP. |
start:api:prod |
Deprecated alias for start:server:prod |
start:ws |
Run MCP WebSocket bridge |
| Script | Port | Description |
|---|---|---|
dev:mcp, watch |
— | MCP stdio watch |
dev:server, watch:server |
3080 | HTTP Actions API + watch |
dev:ui, watch:ui |
Vite | Frontend dev server |
dev:server:tunnel |
3080 | API + HTTPS tunnel (Cloudflare/ngrok) |
dev:server:tunnel:types |
3080 | API + tunnel + tsc --watch |
dev, dev:full |
3080, Vite, WS | API + UI + build + resource banner + Inspector live build into dist/inspector (NEOTOMA_INSPECTOR_LIVE_BUILD=1: no long cache + full page reload when build output mtimes change — index.html plus assets/*, so lazy chunks trigger reload). With out-of-package dist/inspector, Vite watch uses chokidar polling by default so LaunchAgents pick up saves (NEOTOMA_INSPECTOR_BUILD_WATCH_POLL=0 disables). |
dev:full:prod |
3180, Vite, WS | Same as dev with NEOTOMA_ENV=production and prod-scoped Inspector watch (dev:inspector:prod) |
dev:inspector, dev:inspector:prod |
— | Inspector only: vite build --watch → repo dist/inspector (use with API already running, or rely on dev / dev:full:prod; polling applies when output is ../dist/inspector per inspector/vite.config.ts) |
watch:mcp:dev-shim, dev:mcp:dev-shim |
— | Stable stdio dev shim that restarts its MCP worker behind the client connection |
dev:mcp |
— | MCP stdio watch |
dev:mcp:prod |
— | MCP stdio, production env |
dev:server:mcp |
3080, 8280 | API + MCP HTTP + build |
dev:ws |
3080, 8280 | API + WebSocket bridge watch |
dev:server:prod |
3180 | API + build, production env |
dev:server:prod:tunnel |
3180 | API + tunnel + build, production env |
Scripts that start servers use the port(s) above when free; if a port is in use, they bind to the next available port (no process killing). See scripts/pick-port.js.
| Script | Description |
|---|---|
tunnel:https |
Start HTTPS tunnel only (Cloudflare or ngrok) |
setup:cli |
Build and link neotoma globally |
setup:env-hook |
Install git hook to copy .env into worktrees |
copy:env |
Copy env file into current worktree |
setup:worktree |
Cursor worktree init |
setup:data |
Data directory symlink |
setup:foundation |
Foundation submodule symlink |
setup:storage |
Create storage buckets |
| Script | Description |
|---|---|
test |
Run Vitest |
test:unit |
Vitest, unit/services/agent/contract/security/cli only (no integration dir) |
test:remote |
Vitest with remote DB tests (RUN_REMOTE_TESTS=1) |
test:frontend |
Vitest for frontend |
test:integration |
Integration tests |
test:agent-mcp |
Agent MCP tests |
test:coverage |
Vitest with coverage |
test:coverage:critical |
Coverage for critical paths |
test:e2e |
Playwright E2E |
test:e2e:headed |
Playwright E2E, headed browser |
lint |
ESLint (src) |
type-check |
tsc --noEmit |
validate:coverage |
Validate coverage map |
validate:doc-deps |
Validate doc dependencies |
validate:mdx-site |
Validate docs/site/pages/**/*.meta.json + MDX siblings and ROUTE_METADATA keys |
validate:routes |
Site route parity vs seo_metadata / MainApp |
doctor |
Project health check (env, local SQLite hints) |
| Script | Description |
|---|---|
migrate |
Run migrations |
migrate:dry-run |
Migrations dry run |
migrate:plans |
Migrate plans |
migrate:plans:dry-run |
Migrate plans dry run |
schema:export |
Export schema snapshots |
schema:init |
Initialize schemas |
schema:init:dry-run |
Schema init dry run |
schema:icons:generate |
Generate schema icons |
schema:cleanup:test |
Cleanup test schemas |
recover:db |
Check dev SQLite integrity / recover copy |
recover:db:prod |
Check prod SQLite integrity / recover copy |
wipe:dev |
Wipe dev database |
wipe:prod |
Wipe prod database |
wipe:local |
Wipe local database |
wipe:*:storage |
Wipe + storage (e.g. wipe:prod:storage) |
| Script | Description |
|---|---|
cli, cli:dev |
Run Neotoma CLI (built / dev) |
docs:generate |
Generate docs |
build:docs, dev:docs, dev:docs:serve |
Docs site build/dev/serve |
openapi:generate |
Generate OpenAPI types |
branches:prune |
Prune merged branches |
parquet:samples |
Create sample parquet files |
sync:env |
Sync env from 1Password |
For environment and ports, see Getting started.
--base-url <url>: Override API base URL.--env <env>: Environment selector (devorprod). Required for server commands such asapi start,api stop,api logs, andwatch.--offline: Force in-process local transport for data commands (no external API process required).--api-only: Force API-only mode; fail when API is unreachable (use when you want to avoid loading the local DB).--json: Output machine readable JSON.
-
neotoma issues create --title <title> --body <body> [--visibility public|private] [--labels a,b]: submit an issue through the same orchestration as MCPsubmit_issue. Reporter environment is required (v0.12+): the underlying API requires at least one ofreporter_git_shaorreporter_app_version; pass them via the HTTP body when scripting against/issues/submit, or set--reporter-git-sha/--reporter-app-versiononce the CLI exposes those flags. JSON output includesguest_access_tokenwhen the configured operator instance accepts the issue; treat that value as a credential for later token-scoped status or message calls. Cross-repo mirror: the/issues/submitbody also accepts an optionaltarget_repo(owner/repo) field that redirects the GitHub mirror destination for that one call — use it to file an issue about a repo other than the one Neotoma is globally configured for (NEOTOMA_ISSUES_REPO). It overrides only the GitHub mirror; the Neotoma authoring home (operator instance) is unchanged, and the storedissue/conversationidentity is namespaced totarget_repo. A malformed value is rejected with a structuredowner/repovalidation error. Deprecated hidden alias:--advisorymaps to--visibility privatefor one minor release and printsvisibility 'advisory' is deprecated; use 'private' instead.to stderr outside JSON output. -
neotoma issues message [number] --body <body> [--entity-id <id>] [--guest-access-token <token>]: append to an issue thread. Use the guest token when the local issue snapshot does not already carry the operator token. On public issue threads, supply at least one ofreporter_git_sha/reporter_app_versionto record the build the message was authored against (soft requirement; the server emits a warning when both are missing). -
neotoma issues status [--entity-id <id> | --issue-number <n>] [--skip-sync] [--guest-access-token <token>]: print issue metadata and thread messages. At least one of--entity-idor--issue-numberis required. The guest token is only needed for remote operator read-through when it is not already stored on the local issue row. -
neotoma issues import --from-jsonl <path> [--since <iso8601>] [--until <iso8601>] [--reporter-channel <label>] [--reporter-git-sha <sha>] [--reporter-app-version <version>] [--mode proactive|consent] [--dry-run] [--limit <n>]: ingest an observer JSONL log and file or fold issues for detected anomalies. Each line is scanned against a fixed set of filter predicates (see below); matching lines are redacted with the samerunRedactionGuardPII backstop used byissues create, then either filed as new issues or folded as thread messages on existing open issues with the same dedup key(anomaly_class, command_prefix, reporter_channel). Options:--from-jsonl <path>(required) path to the JSONL file;--since/--untilfilter lines by ISO 8601 timestamp;--reporter-channeloverrides the channel label stamped on every filed issue;--reporter-git-sha <sha>/--reporter-app-version <version>supply the reporter environment when the JSONL lines lack per-linereporter_git_sha/reporter_app_versionfields and the CLI binary version is unavailable (dev builds); the import fails fast with an actionable error if neither the resolved version nor any per-line field is available;--mode proactivefiles immediately,--mode consent(default) prompts before each filing action;--dry-runemits a structured JSON sweep report and commits nothing;--limit <n>stops after extracting n anomalies. Sweep report (emitted at end, or as JSON with--dry-runor--json) includes:lines_scanned,lines_unparseable,lines_skipped_by_filter,anomalies_extracted,issues_filed,issues_folded,issues_skipped, and per-issueoutcomes.Filter predicates (hard-coded in
src/services/issues/observer_import.ts):exit_code != 0ANDERR_class instderr→hard_error(transport / execution failure)unknown_fields_count > 0→schema_drift(unknown response fields indicate schema projection drift)HEURISTIC_MERGEinstderr→heuristic_merge(identity resolution warning)duration_ms > 5000on store/retrieve commands →perf_regressionERR_STORE_RESOLUTION_FAILEDin any field →resolver_bugdatabase disk image is malformedin any field →sqlite_corruptionERR_REPORTER_ENVIRONMENT_REQUIREDin any field →reporter_env_requiredstatus_code == 503on/mcppath →stale_mcp_session
Clean lines (
exit_code == 0, no warnings matching any predicate) are skipped silently. PII redaction runs in scan mode: UUIDs, email addresses, auth tokens, and home-directory paths are replaced with<LABEL:hash>placeholders before the title and body are filed; lines are never dropped for containing PII. Redaction can only fail if an unexpected exception is thrown by the guard itself (which would indicate a programming error); in that case the line is skipped with askippedoutcome and an error reason in the sweep report. Deduplication stamps a deterministicobserver-dedup:<hash>label (derived from(anomaly_class, command_prefix, reporter_channel)) on every filed issue, loads the existing open GitHub issues (best-effort; proceeds without dedup if GitHub is unavailable), and folds viaissues add_messagewhen an existing issue carrying the exact same dedup label is found. -
neotoma plans capture <file> | --all: persist a harness.plan.mdfile (raw markdown source + structuredplanrow + EMBEDS) into Neotoma via the canonical combinedstorepath.--allwalks.cursor/plans/,.claude/plans/,.codex/plans/,.openclaw/plans/under the current repo (or--repository-root <path>). Optional--source-message-entity-id,--source-entity-id,--source-entity-typelink the plan to the prompting message and a source entity (e.g. anissue). Seedocs/subsystems/plans.md. -
neotoma plans list [--source-entity-id <id>] [--status <s>] [--harness <h>] [--limit <n>]: listplanentities filtered by source / status / harness. WrapsPOST /retrieve_entities. -
--pretty: Output formatted JSON. -
--no-session: With no arguments, show intro then command menu (prompt>,? for shortcuts). No servers started. -
--no-servers: With no arguments, enter session with connect-only behavior. -
--tunnel: Start HTTPS tunnel (ngrok/cloudflared) with server start commands. -
--tunnel-provider <provider>: Force tunnel provider tongrokorcloudflarewhen using--tunnel; default is auto-detect from installed tools. -
--no-update-check: Disable the update availability check. When enabled (default), the CLI checks the npm registry for a newer version and, if available, prints a one-line notice to stderr. The notice is never shown when--jsonis used.
neotoma access list: Show effective non-default guest access policies. Resolution follows env var > SchemaMetadata > deprecated config fallback > default; text output includes the winning source.neotoma access set <entity_type> <mode>: Set a schema metadata guest policy when the schema exists, falling back to the deprecated config file only when SchemaMetadata is unavailable.neotoma access reset <entity_type>: Clear any deprecated config fallback for the entity type and set schema metadata to the defaultclosedpolicy when the schema exists. If an environment override still wins, output reports the remaining effective mode/source.neotoma access enable-issues: Setissue,conversation, andconversation_messagetosubmitter_scoped.neotoma access disable-issues: Resetissue,conversation, andconversation_messageto effectiveclosedpolicy, subject to any env override.
CLI behavior can be pinned per invocation via flags or across invocations via environment variables. Every runtime override follows the same precedence: explicit flag > environment variable > default.
| Flag | Environment variable | Purpose |
|---|---|---|
--api-only |
NEOTOMA_API_ONLY |
Force API-only transport; fail if API is unreachable. |
--offline |
NEOTOMA_OFFLINE |
Force in-process local transport; do not contact a remote API. |
--env <env> |
NEOTOMA_ENV (development / production) |
Environment selector for server lifecycle commands. |
--root <path> |
NEOTOMA_REPO_ROOT |
Source-checkout root when neotoma is run with no args. |
--user-id <id> |
NEOTOMA_USER_ID |
Pin the user scope for read-verb requests (entities, observations, relationships, timeline, schemas, sources, stats, list-recent-changes (alias recent), memory-export). The flag wins per call; the env var acts as a session-wide pin; the server falls back to the authenticated user when both are unset. |
--offline and --api-only are mutually exclusive; setting both (via flag or env) raises an error at startup.
NEOTOMA_USER_ID must be non-empty when set — an empty or whitespace-only value causes the CLI to fail fast with a clear error rather than silently falling back to the authenticated user. Resolution is performed by resolveEffectiveUserId() in src/cli/index.ts.
Any new runtime override must follow this precedence model: add the env var read in the preAction hook in src/cli/index.ts alongside the existing transport variables, use a NEOTOMA_-prefixed name, let an explicit flag override it, and document it in the table above. See docs/architecture/change_guardrails_rules.mdc for the cross-cutting guardrails.
These are read by the Neotoma HTTP server (not the CLI preAction hook) for outbound peer sync and optional release-note enrichment:
| Environment variable | Purpose |
|---|---|
NEOTOMA_PUBLIC_BASE_URL |
Public base URL of this API (no trailing slash). Required for POST /peers/{id}/sync outbound /sync/webhook as sender_peer_url. |
NEOTOMA_LOCAL_PEER_ID |
Stable id this instance sends as sender_peer_id; must match the peer_id your counterparty stored for you. |
GITHUB_TOKEN |
Optional bearer for GitHub API when npm_check_update runs with include_release_notes: true. |
GET /peers/{peer_id} returns remote_health from probing {peer_url}/health and semver compat vs the local package version (same rules as neotoma compat). See docs/subsystems/peer_sync.md.
Read by the Neotoma HTTP server when running the public-sandbox profile; no paired CLI flag. Full runbook: docs/subsystems/sandbox_deployment.md.
| Environment variable | Default | Purpose |
|---|---|---|
NEOTOMA_SANDBOX_MODE |
unset | Truthy (1/true/yes) enables all sandbox-only behaviors (ephemeral sessions, anon writes, tighter limits). Also auto-enables tenant-scoped entity ids. |
NEOTOMA_TENANT_SCOPED_ENTITY_IDS |
follows NEOTOMA_SANDBOX_MODE |
Truthy forces tenant-scoped deterministic entity ids (salt generateEntityId with user_id) independent of sandbox mode. Without it (and outside sandbox mode) entity ids are global hash(entity_type, canonical_name), so per-tenant same-named entities collide on one row. |
NEOTOMA_SANDBOX_BASE_URL |
http://127.0.0.1:$HTTP_PORT |
Base URL the per-session pack seeder calls back into (/sandbox/session/new seeds over HTTP as the new bearer). Defaults to loopback on the server's own port. |
These are read by the Neotoma HTTP server when it serves the MCP StreamableHTTP transport at /mcp. They keep the long-lived SSE stream alive behind reverse proxies (Cloudflare Tunnel, nginx, ngrok) and clients with idle timeouts. This is a reliability/transport concern; see issue #1483 and the v0.15.1 release supplement.
| Environment variable | Default | Purpose |
|---|---|---|
NEOTOMA_MCP_SSE_KEEPALIVE_MS |
25000 |
Interval (ms) between SSE comment heartbeat frames (: hb\n\n) on the MCP GET SSE stream. The frames keep proxies and clients from treating the stream as idle and silently closing it. Set to 0 (or any value <= 0) to disable the heartbeat (restores prior behavior); an unset, empty, or non-numeric value uses the default. TCP keepalive on the socket stays enabled regardless. |
NEOTOMA_KEEPALIVE_TIMEOUT_MS |
120000 |
Node HTTP server.keepAliveTimeout (ms). Extended from Node's 5 s default so idle keep-alive sockets are not closed before typical proxy idle windows (60–300 s), which otherwise surfaces as a mid-stream RST / 502 reconnect cycle. |
NEOTOMA_HEADERS_TIMEOUT_MS |
125000 |
Node HTTP server.headersTimeout (ms). Must exceed NEOTOMA_KEEPALIVE_TIMEOUT_MS to avoid a Node bug where the socket is closed before the headers timeout fires. |
The MCP transport tuning vars are server-process knobs read in src/actions.ts, not CLI preAction overrides; they have no paired flag.
Cursor reads these from the env block for run_neotoma_mcp_signed_stdio_dev_shim.sh (they are not parsed by neotoma CLI preAction; document them here for discoverability).
| Environment variable | Values | Purpose |
| ------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| NEOTOMA_MCP_USE_LOCAL_PORT_FILE | 1 / true | Shim: read <repo>/.dev-serve/local*http_port*<dev | prod>(and legacylocal_http_portfor dev) after HTTP bind, then setMCP_PROXY_DOWNSTREAM_URLafter a successful TCP probe. CLI:resolveBaseUrl()uses the same rules and probe (after session port env, beforeconfig.json base_url). Repo root order: NEOTOMA_PROJECT_ROOT, then project_root/repo_rootin~/.config/neotoma/config.json, then cwd. |
| NEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE | dev / prod | Which port file the shim and CLI prefer when NEOTOMA_MCP_USE_LOCAL_PORT_FILE is on; preset A sets this per MCP slot for parallel dev + prod APIs. If unset, CLI infers from NEOTOMA_ENV; shim reads legacy local_http_port only. |
| NEOTOMA_MCP_PORT_PROBE_MS | integer ms (200–5000, default 1200) | TCP probe timeout for the port file in both the shim and the CLI. |
| MCP_PROXY_DOWNSTREAM_URL | URL | Explicit downstream /mcp; used when port-file mode is off, or as fallback when the file is missing / probe fails. |
Details, defaults, and verification steps: docs/developer/mcp/proxy.md.
When the CLI runs in an interactive context (TTY, not --json), it may check the npm registry for a newer version of the package. If an update is available, it writes one or two lines to stderr only (e.g. "Update available: neotoma 0.2.15 → 0.2.16" and "Run: npm i -g neotoma@latest"). The check is fire-and-forget and does not block startup. To disable: set NO_UPDATE_NOTIFIER=1 or pass --no-update-check. The check is also skipped when CI is set. Cache: ~/.config/neotoma/update_check.json with a 24-hour TTL so the registry is not queried on every run.
neotoma session: Start an interactive session with a persistentneotoma>prompt. Run subcommands (e.g.storage info,api status) without restarting the CLI. Similar to a native shell or IDE command prompt. Does not start servers automatically.- Type
helpto list commands. - Type
/to open the live command list (command + description per line), and keep typing to filter matches. - Type
exitorquit, or press Ctrl+D, to end the session. - Arguments with spaces can be quoted:
request --operation "GET /api/entities". - Maintenance invariant (TTY redraw): slash suggestions are rendered on the next line with a leading newline, so cursor-up must move
rendered_lines + 1. SeegetSlashSuggestionCursorUpLines()andtests/cli/cli_session_startup_ux.test.ts(regression guard).
- Type
- Data commands (entities, relationships, sources, observations, timeline, store, schemas, stats, corrections, snapshots): offline-first in-process local transport by default (no API server required).
- Explicit local path: pass
--offline. - Strict remote path: pass
--api-only. - Server lifecycle commands (
api start|stop|status|logs): server-management behavior is unchanged and not redirected to local fallback. - Local OS inspection (
processes list|kill): usespsonly; does not use the Neotoma API or offline SQLite transport. - Watch/storage/logs/backup: already local backend commands and continue to run without a running API.
Transport default rationale: The CLI defaults to offline-first so that data commands work without a running API (e.g. neotoma entities list works right after neotoma init). The native SQLite addon is lazy-loaded only when a data command actually uses the local DB, so entry points that never touch the DB (e.g. neotoma --help, neotoma api start) do not trigger macOS permission prompts. The first time a user runs a data command, the local DB may be opened and the OS may show a one-time permission prompt for the native addon; this is an accepted tradeoff for out-of-the-box usability. Use --api-only if you want to avoid loading the local DB and require the API instead.
Examples:
# Session only (no servers; use when API is already running)
neotoma session
# neotoma> storage info
# neotoma> api status
# neotoma> exit
# Session with dev + prod servers (same as running `neotoma` with no args)
neotoma session --serversneotoma init: Initialize Neotoma for first-time use. Creates data directories, initializes the SQLite database, and can prompt to create encryption keys for privacy-first mode. Default flow is auto-detection-first: if a source checkout is detected, init targets project.env; otherwise it configures user.envat~/.config/neotoma/.envwithout prompting for a source path. Source-path prompts only appear in personalization mode. In interactive mode (TTY), init can prompt to setOPENAI_API_KEYfor LLM extraction. Init can also delegate harness setup through the same MCP and CLI instruction path used byneotoma setup.--data-dir <path>: Custom data directory path. Default:./data(if in repo) or~/neotoma/data(if installed globally).--force: Overwrite existing configuration.--skip-db: Skip database initialization.--skip-env: Skip interactive.envcreation and variable prompts (e.g. for CI or non-interactive use).--project-local: Store the Neotoma config in.neotoma/config.jsonin the current directory (project-scoped) instead of the user-level~/.config/neotoma/config.json. The CLI reads this project-local config automatically for all subsequent commands run from that directory viareadEffectiveConfig, which checks for.neotoma/config.jsonbefore falling back to the user-level config. Trust boundary: project-local config is only loaded when the containing directory is owned by the current user (prevents untrusted directories from silently overriding user settings). Use this when you want per-project Neotoma configuration independent of the user-level setup.--safe: Dry-run mode. Reports whatinitwould do (create directories, write config, run migrations) without writing any files or making any changes. Output lists each planned action with a check mark or blocker reason. Exit code is 0 if all planned actions would succeed; exit code 1 if any blocker is detected (e.g. config already exists without--force, parent directory not writable). Combine with--jsonto get machine-readable output. Note:--safechecks the filesystem layout planned byinit; it does not simulate authentication or preflight steps.
Examples:
# Basic initialization
neotoma init
# Initialize with custom data directory
neotoma init --data-dir /path/to/data
# Store config in current project directory instead of user home
neotoma init --project-local
# Preview what init would do without making any changes
neotoma init --safe
# Dry-run with machine-readable output
neotoma init --safe --json
# Combine: dry-run scoped to current project
neotoma init --safe --project-localWhat it creates:
- Data directories:
<data-dir>/,<data-dir>/sources/,<data-dir>/logs/(event log is<data-dir>/logs/events.log) - SQLite database:
<data-dir>/neotoma.db(with WAL mode enabled) - Encryption key (if user chooses key-derived auth when prompted):
~/.config/neotoma/keys/neotoma.key(mode 0600). - Environment file target: project
<checkout>/.envwhen checkout is detected, otherwise~/.config/neotoma/.env - Config file:
~/.config/neotoma/config.json(default) or.neotoma/config.jsonin the current directory when--project-localis given.
Runtime overrides for neotoma init:
| Precedence | Source | Description |
|---|---|---|
| 1 (highest) | --data-dir flag |
Explicit data directory path |
| 2 | NEOTOMA_DATA_DIR env var |
Environment variable override |
| 3 (default) | Auto-detected or ~/neotoma/data |
Resolved at startup |
Config scope for neotoma init is a binary switch: --project-local writes to .neotoma/config.json in cwd; without the flag, init writes to ~/.config/neotoma/config.json. All subsequent commands that call readEffectiveConfig will read the project-local file when present (and owned by the current user).
neotoma setup: One-shot, idempotent harness setup. It runs init when needed, configures MCP entries, applies agent CLI instruction files, installs lifecycle hooks or the Claude Code plugin for the selected harness, then patches permission allowlists where the harness supports them.--tool <claude-code|cursor|codex|openclaw|claude-desktop|windsurf|continue|vscode>: Target harness. If omitted,neotoma statussupplies the current tool hint when available.windsurf,continue, andvscodeare MCP-only targets (no skills or hook installation; setup writes the MCP config entry only).--install-scope <project|user|both>: Scope for MCP entries and agent CLI instruction files.--scope <project|user|both>: Permission-file scope only. This is intentionally separate from--install-scope.--mcp-transport <a|b|c|d|e>: Same transport presets asneotoma mcp config.--rewrite-neotoma-mcp: Rewrite existing Neotoma MCP entries in the selected install scope.--skip-hooks: Skip lifecycle hook/plugin installation.--all-harnesses: Infer hook-capable harnesses from MCP configs and install hooks for all of them. The default remains one harness via--toolor theneotoma statushint.--dry-run: Plan the setup without writing files.--yes: Suppress prompts in init, MCP config, CLI instruction config, and hook install paths.--skip-permissions: Skip permission-file writes.
neotoma setup writes a structured report when the global --output json flag is active. The report includes steps[], permission_patches, doctor_before, doctor_after, overall_ok, verify_line, and privacy_transport_summary, which lets agent-led installs show exactly what changed.
After the JSON report, neotoma setup always emits two plain-text lines to stdout (regardless of --json / --pretty mode):
- Install-verification line — a single grep-able line confirming the binary path, version manager, version, data directory, and MCP transport. Format:
Neotoma installed at <path> (resolved via <manager>; v<version>; data_dir=<dir>; mcp=<transport>). Agent harnesses grep this line to confirm install succeeded without parsing JSON. - Privacy/transport summary —
Transport: local stdio MCP (no network egress). Override with --mcp-transport=http for signed HTTP /mcp proxy.Surfaces the data-egress model for privacy-conscious evaluators.
Use neotoma setup --tool <harness> --yes for the normal greenfield path. Use neotoma mcp config or neotoma cli config when only one layer needs repair.
One-shot onboarding for friction reporters (testers or user agents who file issues via Neotoma):
neotoma reporter setup: Orchestrate the full reporter setup in one command.--tool <tool>: Target harness (claude-code|cursor|codex|openclaw|claude-desktop). Auto-detected from cwd when omitted.--git-sha <sha>: Defaultreporter_git_shaembedded in every submitted issue.--app-version <version>: Defaultreporter_app_versionembedded in every submitted issue.--default-visibility <public|private>: Default issue visibility (default:public).--dry-run: Plan the setup without writing files or applying changes.--print-block: Print a copy-pastable env-var block instead of writing the project config file.
The command runs four steps in sequence: (1) version check — warns when the installed CLI is older than the minimum required version; (2) preflight — applies harness permission-file allowlist entries via neotoma preflight --apply; (3) config write — persists the reporter defaults to .neotoma/reporter.json in the project root so subsequent neotoma issues create calls pick them up automatically; (4) smoke-test hint — prints a ready-to-run dry-run command. The structured JSON report includes tool, dry_run, installed_version, version_ok, steps[], reporter_config, reporter_config_path, smoke_test_command, overall_ok, and summary.
neotoma preflight: Check or apply harness permission-file entries for Neotoma.--tool <tool>: Target harness. Accepted values:claude-code,cursor,codex,openclaw,claude-desktop,windsurf,continue,vscode.--apply: Write the allowlist file(s) directly instead of printing a copy-paste block. Only has effect forclaude-code,cursor, andcodex; other harnesses do not expose a writable allowlist file.--scope <project|user|both>: Permission scope forclaude-code(default:bothwhen--applyis set).--dry-run: Plan the write without modifying files.
Allowlist-capable harnesses (claude-code, cursor, codex): without --apply, prints a copy-paste block; with --apply, writes the allowlist file(s) directly. claude-code writes both project (.claude/settings.local.json) and user (~/.claude/settings.json) scopes by default. neotoma setup calls the same write path, so running preflight before setup is only necessary when you need the allowlist entry in place before npm install -g neotoma runs.
MCP-only harnesses (claude-desktop, openclaw, windsurf, continue, vscode): these harnesses have no writable command allowlist. Passing --apply is accepted but produces no file write; preflight prints a message directing you to neotoma setup --tool <tool> to configure the MCP server entry instead.
The structured report includes tool, apply, dry_run, patches[], already_ok, copy_paste_block, and overall_ok.
neotoma reset: Reset local Neotoma state to a clean slate.- Backs up and removes local Neotoma state (config/env/keys/instructions and known local data directories).
- Always backs up a data directory before removing it.
- If
.envsetsNEOTOMA_DATA_DIR, that configured data directory is protected: reset does not back it up or remove it. - Cleans Neotoma MCP entries from user/project MCP configs.
- Attempts global uninstall via
npm uninstall -g neotoma(best effort; failures are reported but do not roll back completed local cleanup). -y, --yes: Skip confirmation prompt.
CLI uses the same auth patterns as MCP and REST API. Local CLI commands can run without login in development, but MCP OAuth now requires key-auth preflight.
-
neotoma auth status: Show auth mode (none, dev-token, key-derived) and user details. Works without prior login. -
neotoma auth login: OAuth PKCE flow for MCP Connect (Cursor) setup. Browser flow requires key authentication (/mcp/oauth/key-auth) first. Use--tunnelto read the API base URL from/tmp/ngrok-mcp-url.txt(for testing OAuth when the API is behind a tunnel). -
neotoma auth logout: Clear stored OAuth credentials (MCP Connect only). -
neotoma auth mcp-token: Print MCP auth token derived from private key (when encryption is enabled). Add to mcp.json headers. -
If key-authenticated OAuth is unavailable, configure
Authorization: Bearer <NEOTOMA_BEARER_TOKEN>for MCP instead of OAuth. -
neotoma inspector admin unlock: Redeem a feedback-admin challenge via the same CLI AAuth signer used for signed API traffic; prints an Inspector URL to/feedback/admin-unlock?challenge=…(and optional--open). Omit--challengeto mint one from the API. When the Inspector dev server runs on another origin, setNEOTOMA_INSPECTOR_BASE_URLor pass--inspector-base. Use--skip-browser-urlto suppress the printed link in text mode. When the HTTP API binds a non-default port, setNEOTOMA_MCP_USE_LOCAL_PORT_FILE=1andNEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILEwhen needed (same rules as the MCP signed shim) so--base-urlresolution reads the matching.dev-serve/local_http_port_*file after a successful TCP probe; setNEOTOMA_PROJECT_ROOTif the file lives outsidecwd.
neotoma access list: Show effective non-default guest access policies. Resolution follows env var > SchemaMetadata > deprecated config fallback > default; text output includes the winning source.neotoma access set <entity_type> <mode>: Set a schema metadata guest policy when the schema exists, falling back to the deprecated config file only when SchemaMetadata is unavailable.neotoma access reset <entity_type>: Clear any deprecated config fallback for the entity type and set schema metadata to the defaultclosedpolicy when the schema exists. If an environment override still wins, output reports the remaining effective mode/source.neotoma access enable-issues: Setissue,conversation, andconversation_messagetosubmitter_scoped.neotoma access disable-issues: Resetissue,conversation, andconversation_messageto effectiveclosedpolicy, subject to any env override.
Commands for managing MCP server configuration files (Cursor, Claude Code, Windsurf, etc.). The CLI uses symmetric verbs: guide is read-only guidance; config mutates files.
-
neotoma mcp guide: Show MCP configuration guidance for Cursor and other clients.--no-check: Skip checking for existing MCP config files (default: check and suggestmcp configif servers are missing).- Prints example JSON config for Cursor with URL or stdio server entries.
- After printing config, scans current directory for MCP config files and suggests running
neotoma mcp configif dev or prod servers are missing.
-
neotoma mcp config: Scan for MCP config files in current directory and subdirectories, detect whether dev and prod Neotoma servers are configured, and offer to install missing servers.--rewrite-neotoma-mcp: After you pick install scope and transport, rewrite existingneotoma-dev/neotoma(or Claude Desktopmcpsrv_*) entries in that scope to match the preset. Without this flag, configs that already have both slots are skipped so transport changes only apply where something was still missing.--user-level: Include user-level MCP config paths (e.g.~/.cursor/mcp.json, Claude, Windsurf) in scan (default: project-local only).--install-hooks/--no-hooks: Install or skip matching Neotoma lifecycle hooks when a configured MCP path maps to a hook-capable harness. Hook installation is enabled by default for non-JSON runs; use--no-hooksto limit the command to MCP config only.--yes: Skip hook installation confirmation prompts. MCP server config prompts still follow the normal interactive flow.--mcp-transport <a|b|c|d|e>: Transport preset. TTY installs prompt when omitted; non-TTY defaults tob.
MCP transport presets:
| Preset | Use when | Notes |
|---|---|---|
a |
You want signed HTTP /mcp proxy entries with AAuth attribution. |
Requires a reachable Neotoma API for each configured slot (neotoma-dev → dev, neotoma → prod). |
b |
You want the lowest-friction local MCP setup. | Default. Packaged npm installs launch Neotoma directly over stdio; source checkouts use the unsigned dev shim. |
c |
You explicitly want direct stdio entries. | Best for simple local clients that do not need HTTP proxy parity. |
d |
You want both MCP slots to point at prod HTTP /mcp. |
Signed proxy path; requires prod API reachability. |
e |
You want both MCP slots to point at prod HTTP /mcp using the local port file. |
Signed proxy using NEOTOMA_MCP_USE_LOCAL_PORT_FILE=1 + NEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE=prod. Requires prod API reachable on the port recorded by the local port file. |
- Scans for known config file patterns:
- Cursor:
.cursor/mcp.json,.mcp.json(project),~/.cursor/mcp.json(user-level with--user-level) - Claude Code:
claude_desktop_config.json(project or user-level with--user-level):- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
- macOS:
- Windsurf:
mcp_config.json(project or user-level with--user-level):- macOS/Linux:
~/.codeium/windsurf/mcp_config.json - Windows:
%APPDATA%\Codeium\Windsurf\mcp_config.json
- macOS/Linux:
- Continue:
config.json(user-level with--user-level):- All platforms:
~/.continue/config.json
- All platforms:
- VS Code (Copilot Chat MCP):
.vscode/mcp.json(project-level, picked up by directory scan)
- Cursor:
- For each found config, checks for
neotoma-devandneotomaserver entries (based oncommandscript names orurlpatterns). In Claude Desktop'sclaude_desktop_config.json, new entries usemcpsrv_neotoma_devandmcpsrv_neotoma, and legacyneotoma-dev/neotomakeys are reported for repair because Claude Desktop validates server IDs as UUIDs ormcpsrv_*tags. - If any config is missing dev or prod servers, prompts to add them with absolute script paths.
- If no config files found, offers to create
.cursor/mcp.jsonin current directory. - Uses Neotoma source root (from
findRepoRoot, config, orNEOTOMA_REPO_ROOT) to resolve absolute script paths for the selected transport. Presetaemits signed shim /mcp proxy --aauthwith dev+prod downstreams as above;bemits unsigned dev shim;cemits direct stdio scripts;demits signed shim with prod downstream for both MCP server entries. - After install, verifies or installs lifecycle hooks for hook-capable harnesses inferred from the configured MCP paths (for example
.cursor/mcp.json→ Cursor hooks,.codex/config.toml→ Codex hooks). Harnesses without an auto-installer print the matching manual instruction. - After install, shows a reminder to run
neotoma cli config; when MCP servers are installed interactively, it can also prompt to add CLI instructions if missing. neotoma mcp checkis retained as a deprecated alias forneotoma mcp configduring the migration window.
MCP transport presets:
- A: signed shim + AAuth. Installs both
neotoma-devandneotoma: dev slot → dev APIhttp://127.0.0.1:3080/mcp(default whenneotoma-devomitsMCP_PROXY_DOWNSTREAM_URL, matchingrun_neotoma_mcp_signed_stdio_dev_shim.sh), prod slot → prod APIhttp://127.0.0.1:3180/mcp(viascripts/run_neotoma_mcp_signed_stdio_dev_shim.shor packagedneotoma mcp proxy --aauth). Requires matching API processes to be reachable. - B (default): unsigned/local stdio. Packaged npm installs launch
dist/index.jsdirectly over stdio so local MCP does not require a separate API process; source checkouts usescripts/run_neotoma_mcp_stdio_dev_shim.sh, and prod entries addNEOTOMA_ENV=production. - C: direct stdio. Uses
scripts/run_neotoma_mcp_stdio.shfor dev andscripts/run_neotoma_mcp_stdio_prod.shfor prod. Reconnect the MCP client after code changes. - D: signed prod parity. Signed shim with
MCP_PROXY_DOWNSTREAM_URL=http://127.0.0.1:3180/mcpfor both slots (includingneotoma-dev), so both MCP entries hit the prod HTTP/mcpwhen the prod API is on the default port.
Dev vs Prod detection patterns:
- Dev:
commandcontainsrun_neotoma_mcp_stdio.sh,run_neotoma_mcp_stdio_dev_watch.sh,run_neotoma_mcp_stdio_dev_shim.sh,run_neotoma_mcp_signed_stdio_dev_shim.sh,run_neotoma_mcp_unsigned_stdio_dev_shim.sh, or the legacy forwarderrun_neotoma_mcp_unsigned_stdio_proxy.shunder a dev server id, orurlcontainslocalhost:3080/mcpor127.0.0.1:3080/mcp. During connect-only sessions, the currently selected instance port is also considered for dev if the active env is dev. - Prod:
commandcontainsrun_neotoma_mcp_stdio_prod.sh,run_neotoma_mcp_stdio_prod_watch.sh,run_neotoma_mcp_signed_stdio_dev_shim.sh,run_neotoma_mcp_unsigned_stdio_dev_shim.sh, or the legacy forwarderrun_neotoma_mcp_unsigned_stdio_proxy.shunder a prod server id / prod downstream URL, orurlcontainslocalhost:3180/mcp,127.0.0.1:3180/mcp, orneotoma.fly.dev/mcp. During connect-only sessions, the currently selected instance port is also considered for prod if the active env is prod.
Example workflow:
# Show config guidance and check if servers are configured
neotoma mcp guide
# Scan project-local MCP configs and add missing servers
neotoma mcp config
# Scan including user-level configs (Cursor, Claude, Windsurf)
neotoma mcp config --user-level
# Deterministically install direct stdio entries without the transport prompt
neotoma mcp config --user-level --mcp-transport c
# Configure MCP and install matching hooks without hook prompts
neotoma mcp config --user-level --yes
# Interactive: refresh user-level Cursor/Claude JSON entries to transport D even when dev+prod already exist
neotoma mcp config --rewrite-neotoma-mcpCommands to ensure agent instructions tell agents to use the Neotoma CLI when running locally and MCP when remote. Like MCP, guide is read-only and config mutates files:
neotoma cli guide: Show guidance for adding the "prefer MCP when available, CLI as backup" rule to Cursor, Claude, and Codex (project:.cursor/rules/,.claude/rules/,.codex/; user:~/.cursor/rules/,~/.claude/rules/,~/.codex/). Does not modify files.neotoma cli config: Scan only paths that each IDE actually loads (applied paths). Reports Cursor, Claude, and Codex separately and, with--yes, writes missing or stale files to the selected scope.--scope <project|user|both>: Choose where to apply instruction files. This is separate fromneotoma setup --scope, which controls permission-file patches.--yes: Apply without prompting. Without--yes, the command reports what would change.
neotoma cli-instructions configandneotoma cli-instructions checkare deprecated aliases forneotoma cli guideandneotoma cli config.neotoma instructions print: Print the first fenced code block fromdocs/developer/mcp/instructions.mdbundled with this package (same text MCP sends to clients). Use--format mdto wrap output in a markdown fence, or--jsonwith global JSON output mode for{ path, body }.--user-level/--no-user-level: Include or exclude user-level paths in scan (default: include).
See docs/developer/agent_cli_configuration.md for the rule text and strategy.
neotoma entities list:--type <entityType>--search <query>--limit <n>--offset <n>--include-merged--status <value>: Server-side exact-match filter on the snapshotstatusfield. Returns only entities whosesnapshot.statusequals the given value (e.g.--status active). Applied before pagination. Each returned entity includesstatusin its snapshot projection even without--include-snapshots. Passing--status ""is not meaningful; omit the flag to return all statuses.--updated-since <iso>/--since <iso>: Return only entities whoseupdated_atis >= this ISO 8601 timestamp.--created-since <iso>: Return only entities whosecreated_atis >= this ISO 8601 timestamp.
neotoma entities get <id>neotoma entities search [identifier]:- Preferred: positional
identifieror--identifier <id> - Compatibility alias:
--query <id>(equivalent to--identifier) - If both
--identifierand--queryare provided, they must match
- Preferred: positional
neotoma entities import <file>: bulk-import entities from a JSONL file (one entity JSON object per line) by chunking them into batchedPOST /storecalls.--batch-size <n>: entities per store call (default200). Lower it if a request exceeds the server body limit.--idempotency-prefix <prefix>: prefix for the per-chunk idempotency key (default derived from the file name). Re-running with the same prefix and file is a safe no-op, so a large backfill can be replayed without duplicating data.--user-id <userId>: store under a specific user.--commit: actually write. Without it the command runs in dry-run/plan mode.- The
/storeendpoint already writes each batch transactionally; this command owns file reading, chunking, and per-chunk idempotency. For very large backfills, this is the supported "replay from an external source" path.
neotoma entities export: the inverse ofentities import— pages through all entities and emits import-compatible JSONL (one object per line,entity_typeplus the snapshot fields at the top level), so an instance can be exported and rebuilt with a matched command pair. Seeexit_rebuild_test.mdfor the leave-and-rebuild protocol this supports.--type <entityType>: only export this entity type.--out <path>: write JSONL here (default: stdout).--with-relationships: also write a companion<out>.relationships.jsonof typed edges thatrelationships create --filere-imports, for a full entities-plus-edges round-trip.--page-size <n>: entities fetched per page (default500).--include-merged: include merged entities (default: exclude).--user-id <userId>: export under a specific user scope.- Export reflects only what the snapshot captured: fields an entity's registered schema does not declare are not in the snapshot and do not survive this path. Declare every field that matters before relying on export for a rebuild.
neotoma sources list:--search <query>--mime-type <mimeType>--limit <n>--offset <n>
neotoma observations list:--entity-id <id>--entity-type <type>--limit <n>--offset <n>
neotoma relationships create --source-entity-id <id> --target-entity-id <id> --relationship-type <type>: Create one relationship.--metadata <json>: attach relationship metadata.--file <path>: create a batch from a JSON array, or an object withrelationships: [...]. Each entry usesrelationship_type,source_entity_id,target_entity_id, and optionalmetadata.
neotoma relationships list <entityId>:--direction <direction>: inbound, outbound, or both
neotoma relationships get-snapshot <relationshipType> <sourceEntityId> <targetEntityId>: Get relationship snapshot with provenance (observations). Relationship type is one of: PART_OF, CORRECTS, REFERS_TO, SETTLES, DUPLICATE_OF, DEPENDS_ON, SUPERSEDES, EMBEDS.neotoma relationships restore <relationshipType> <sourceEntityId> <targetEntityId>: Restore a deleted relationship (creates restoration observation). Optional:--reason <reason>.
neotoma timeline list:--start-date <date>--end-date <date>--event-type <type>--entity-id <id>: Filter by entity ID--user-id <userId>: Filter by user ID (default: authenticated user)--limit <n>--offset <n>--order-by <column>:event_timestamp(default, document dates) orcreated_at(when the timeline row was written)
neotoma schemas listneotoma schemas get <entityType>neotoma schemas audit-fragments [entityType]: Report undeclaredraw_fragmentsawaiting schema declaration (fields stored on observations but excluded from the entity snapshot because no active schema declares them). Read-only. Outputs, per type, the undeclaredfragment_keys with occurrence / affected-entity counts and aschema_missingflag, plustotal_entity_types/total_undeclared_fieldsrollups. Optional positionalentityType(or--entity-type <type>) scopes the audit to one type;--user-id <id>overrides the resolved user. Maps toPOST /audit_undeclared_fragments/ MCPaudit_undeclared_fragments.
Cross-instance peer sync. Backed by src/services/sync/ and the HTTP /peers surface; see docs/subsystems/peer_sync.md for the underlying model and the env vars (NEOTOMA_PUBLIC_BASE_URL, NEOTOMA_LOCAL_PEER_ID) required on the API process.
neotoma peers add --peer-id <id> --name <name> --url <url> --types <csv>: Register a peer.--direction <push|pull|bidirectional>(defaultbidirectional).--sync-scope <all|tagged>(defaultall). Withtagged, only entities whose snapshot lists this peer insync_peersare eligible.--auth-method <shared_secret|aauth>(defaultshared_secret).--shared-secret <secret>: HMAC peer secret whenauth-method=shared_secret.--peer-public-key-thumbprint <thumbprint>: Expected AAuth thumbprint whenauth-method=aauth.--conflict-strategy <last_write_wins|source_priority|manual>(defaultlast_write_wins).--target-user-id <id>: Receiveruser_idon the peer instance.
neotoma peers list: List configured peers.neotoma peers status <peer_id>: Show peer config plusremote_health(probed{peer_url}/health) and semver compatibility vs the local package version.neotoma peers sync <peer_id>: Run a bounded bilateral catch-up against the peer (push local changed observations, pull remote snapshots through/sync/entities).--limit <n>: Cap observations / snapshots per direction.
neotoma peers remove <peer_id>: Deactivate the peer (does not delete priorobservation_source: "sync"rows).
neotoma interpretations list: List interpretation runs.--source-id <id>: Filter by source.--limit <n>/--offset <n>: Pagination.
neotoma interpretations create --source-id <id> --entities <path>: Create an interpretation row for an existing source from agent-extracted flat entities.--interpretation-config <json>: Audit configuration such as extractor type/version, model, prompt hash, schema version, and agent notes.--relationships <path>: Optional JSON array of relationship refs to create after entity observations.--idempotency-key <key>: Optional replay-safe operation key.
neotoma store:- Preferred structured input:
--entities <json>or--file <path> - Compatibility alias:
--json=<json>maps to structured--entitiesfor backward compatibility.- Use
--json=(equals, no space) so the payload is parsed as entities input. - Bare
--json(without=) remains the global output-format flag.
- Use
--file <path>: Path to JSON file containing entity array. Use for long payloads.--interpretation-source-id <id>/--interpretation-source-ref <structured|unstructured>plus--interpretation-config <json>: Opt into Source -> Interpretation -> Observation provenance for source-derived extraction. Omit for ordinary already-structured/chat-native facts.- Silent-failure guard (v0.5.1+): when a commit-mode store returns
entities_created=0, no resolved entities, and is not an idempotency replay (replayed: true), the CLI emits a non-fatal stderr warning so agents/humans don't mistake an empty result for success. Typical root cause: fields nested underattributes(see v0.5.0 breaking change) or mismatcheduser_idscope. - Idempotency replays:
POST /storereturnsreplayed: truewhen a request is a deterministic re-commit of a previously committed(user_id, idempotency_key)tuple. Use this to distinguish "same call, no new work" from "call produced zero entities."
- Preferred structured input:
neotoma ingest: Atomic structured-entities + source-file ingest (composes/store).--entities <path>(required): JSON file containing the entity array extracted by the caller.--source-file <path>(required): Raw source artifact (PDF, transcript, CSV, etc.) attached as provenance.--user-id <id>/--idempotency-key <key>/--file-idempotency-key <key>: same semantics asneotoma store.--plan/--dry-run: Preview planned actions without committing.--strict: Refuse silent merges (schemacanonical_name_fieldsmust match, or--target-idmust be supplied).--source-upload(v0.5.1+): Force base64 upload of the source file viafile_content. Use this when the CLI and API run on different machines so the server can't read the CLI's local filesystem.--source-content(v0.5.1+): Alias for--source-upload.- Auto-upload (v0.5.1+): when the resolved base URL is non-localhost (anything other than
localhost,127.0.0.1,::1,0.0.0.0), the CLI automatically switches fromfile_pathtofile_contentso remote deployments work without any flag. Localhost base URLs continue to sendfile_pathso the server reads the artifact directly from disk. - Upload size limit: the server caps JSON bodies at 10 MB (
express.json({ limit: "10mb" })). Accounting for base64 overhead (~1.37×), the CLI refuses to upload source files larger than ~7.5 MB with a clear error pointing operators at a localhost API as the alternative.
For chat persistence recipes, MCP and CLI use the same underlying store contract. In CLI examples:
- entity lookup supports positional identifier,
--identifier, and compatibility alias--query - structured store supports preferred
--entities/--fileand compatibility alias--json=<json>
neotoma upload <path>: Store an unstructured file (raw upload with optional AI interpretation).--no-interpret: Skip AI interpretation after store.--idempotency-key <key>: Idempotency key (default: content hash).--mime-type <type>: MIME type (default: inferred from extension).--local: Run store and interpretation in-process without starting or connecting to the API server. Uses the same.env, database, and storage backend as the server. Requiresneotoma initto have been run. Encryption-off only (local dev user) for MVP. Example:neotoma upload --local invoice.pdf.
neotoma analyze <filePath>
neotoma stats
neotoma watch: Stream record changes (entities, observations, sources, etc.) as they happen. Uses local dev user when encryption is off (no login needed).--interval <ms>: Polling interval in ms (default: 400).--json: Output NDJSON (one JSON object per line).--human: Output one sentence per change (e.g. "Created person "George"", "Updated relationship "wife" for person "Alice" with person "Bob""). No timestamps, emoji, or IDs.--tail: Only show changes from now (skip existing records).- When encryption is on, set
NEOTOMA_KEY_FILE_PATHorNEOTOMA_MNEMONIC.
neotoma processes list(default forneotoma processes): Scanpsfor processes whose argv matches Neotoma checkout paths,tsx watch src/actions.ts,run-neotoma-api-node-watch.sh/node --watch-path+src/actions.ts,run_neotoma_mcp*.sh,run_watch_build_launchd.sh,mcp proxy, orchestrator (pick-port/concurrently),tsc --watch,esbuild, Cursor hooks under the repo, etc. TCP listen ports per PID come from a singlelsof -nP -iTCP -sTCP:LISTEN -F pnpass (comma-separated in the PORTS column,-when none); iflsofis missing or fails, PORTS stays-. Text mode adds ENV (dev|prod|mix|?) and CAT (comma-separated tags:server,mcp,build,orchestrator,tunnel,tooling,other) inferred heuristically from argv and listen ports; a footer line explains the encoding. The fullpscommand line is word-wrapped within the COMMAND column to the current terminal width (continuation lines are blank under the fixed columns so COMMAND stays aligned).--jsonprints{ "processes": [...] }withports(number array),env_hint,categories(string array), and each fullcommandon one line.neotoma processes servers: Sameps+lsofscan aslist, but only rows with categoryserver(not limited to default HTTP ports). Rows are deduplicated by server stack: all processes in a connected parent-child chain that share the same stack context are collapsed into a single row showing the PID that actually holds the TCP listener (or the deepest child when no listener is detected). Prints a compact table: PID (the representative listener PID for the stack), ENV (resolvedNEOTOMA_ENVwhen readable, otherwise inherited from the nearest connected server/orchestrator stack when the stack is unambiguous, else the same heuristic aslist), PORT (all TCP listen ports visible anywhere in that connected server stack, comma-separated, with3080/3180listed first when present;-when none of the connected rows expose a listener), LAUNCHAGENT (macOS LaunchAgent label inferred from the process ancestry, plist path, or known launchd wrapper /npm run ...entrypoint such ascom.neotoma.dev-server;-when not inferable), DATA_DIR (NEOTOMA_DATA_DIRfrom procfs or argv on the PID itself, otherwise inherited from the connected server stack when available, else-), LOGS (best-effort deduped paths: default event log persrc/config.tswhen data dir /NEOTOMA_EVENT_LOG_*overrides are known, appNEOTOMA_LOGS_DIRor data-relativelogs/logs_prodwhen distinct from the parent directory of the event log,~/.config/neotoma/logs/api.log(dev) orlogs_prod/api.log(prod) forneotoma api start --background, optional repodata/logs/session.log/session.prod.logwhenNEOTOMA_PROJECT_ROOTis set).--watchkeeps the table running and refreshes it every few seconds until interrupted; use--interval <ms>to override the default 3000 ms cadence. In--json --watchmode it emits one JSON object per refresh line withrefreshed_at. Plain--jsonprints{ "servers": [ { "pid", "neotoma_env", "port", "launchagent", "data_dir", "log_paths" } ] }(log_pathsis a string array;portis comma-separated).neotoma processes kill <pids...>: One or more PIDs as separate tokens (e.g.neotoma processes kill 92450 18852) or a single comma/space string in quotes. Re-runs the same classifier; only PIDs that appear in a fresh scan are signalled (prevents typos from killing unrelated processes). Default signal is SIGTERM; use--signal SIGKILLwhen needed.--dry-runprints the plan. On a TTY, prompts once unless--force. Non-TTY requires--force.
neotoma mirror enable: Enable the markdown mirror under<NEOTOMA_DATA_DIR>/mirror/and run an initial rebuild.--path <dir>: Override mirror root.--git: Enable git-backed history (git initon the mirror path, one commit per write batch). Requires the optionalsimple-gitdependency (installed by default).--kinds <list>: Comma-separated kinds to mirror (entities,relationships,sources,timeline,schemas, default all).--gitignore: After enabling, append the resolved mirror path to the enclosing git repo's.gitignore(idempotent). No-op when the mirror path is not inside a repo. Equivalent to runningneotoma mirror gitignoreafterenable.--no-gitignore: Explicit form for--yesscripts that want to leave.gitignorealone (default).
neotoma mirror disable: Disable mirror write-through. Does not delete existing files.neotoma mirror rebuild: Regenerate the mirror from SQLite.--kind <entities|relationships|sources|timeline|schemas|all>(defaultall).--entity-type <type>,--entity-id <id>: Scope rebuild to a type or single record.--clean: Remove stale mirror files within the targeted scope that this rebuild did not produce.
neotoma mirror push [profile]: Write on-disk mirror edits BACK into Neotoma as corrections — the inverse ofrebuild. Opt-in and gated: only runs for profiles withallow_disk_writeback: true, and is a no-op otherwise. Pull-based (explicit invocation, no background watcher): for each mirrored file it 3-way compares the editable fields (last-synced base / on-disk / current canonical) and applies changed fields ascorrect()calls; managed/generated regions (frontmatter, "do not edit" headers) are never written back. When both the file and canonical changed since the base, it surfaces a conflict instead of overwriting.--check/--dry-run: Show the corrections that would be applied, without writing.--target <path>: Restrict the push to a specific mirror file or directory.--verbose: Print per-field diffs and per-file outcomes.- Example (dry-run):
neotoma mirror push --dry-runlists files with pending edits and the field-level corrections each would produce. - See
docs/subsystems/markdown_mirror.mdfor 3-way-diff semantics.
neotoma mirror status: Print current mirror config, file counts per kind, and whether git is enabled.neotoma mirror gitignore: Idempotently append the resolved mirror path to the enclosing git repo's.gitignore, under a# Neotoma markdown mirrorcomment. The helper walks up from the mirror path to find the enclosing.gitdirectory; it never prompts for a path and never writes to a repo it did not detect. When the mirror path is not inside a repo, it exits 0 with a "not inside a git repo" message. Use--jsonto get the structured result (repo_root,gitignore_path,entry,added,already_present).
The mirror is a derived artifact: SQLite is the only source of truth. Mirror files carry a header warning that manual edits are overwritten on the next write — unless the profile opts into write-back (allow_disk_writeback: true), in which case neotoma mirror push (above) is the supported path to flow on-disk edits back as corrections. To edit an entity directly, use neotoma edit <id> or the Inspector (see below). The mirror defaults to disabled; activation offers it as an opt-in (see install.md). See docs/subsystems/markdown_mirror.md for layout, determinism, and git semantics.
neotoma skills sync: Mirror the published skills (skills/shipped with the package) into every installed harness's skills directory. This is the same reconcilerneotoma setupuses for skill installation, run across all harnesses at once.--scope <user|project>(defaultuser): Mirror into the home-directory harness dirs (~/.claude/skills,~/.cursor/skills,~/.codex/skills,~/.openclaw/skills) or into the project's.<harness>/skills.--json: Emit the machine-readable report (source,source_present,scope, per-harnessresults,changed,has_errors) instead of the human summary.- Targets a harness only when its base directory exists (e.g.
~/.cursor), and creates theskillssubdirectory if missing — so a harness installed later is picked up on the next sync. Cursor uses.cursor/skills(not Cursor's separate built-in.cursor/skills-cursorroot). - Reconciliation: a single whole-directory symlink when the target is absent or already ours; a per-skill symlink fallback that preserves any foreign content when the target holds non-Neotoma skills. Foreign content is never overwritten or deleted.
- Exit code
1when the source is missing or any per-skill link fails (errors are surfaced per harness in both pretty and--jsonoutput); otherwise0.
For continuous mirroring, install the com.neotoma.skills-sync LaunchAgent with npm run setup:launchd-skills-sync (macOS; watches skills/ and re-runs neotoma skills sync on change). See npm_scripts.md.
neotoma edit <id>: Open the entity snapshot as YAML in$EDITOR, diff on save, and submit changed fields as one batch correction (onecorrect()observation per changed field, applied atomically).--set field=value(repeatable): Non-interactive edit. No editor invoked. Same batch backend, same concurrency prompt.--force: On optimistic-concurrency conflict, overwrite without prompting (non-interactive use).- Editor fallback order:
$EDITOR, then$VISUAL, thenvi. - Optimistic concurrency: captures
last_observation_aton load. If a newer observation exists at save time, the CLI prints conflicting fields and prompts[r]ebase / [o]verwrite / [a]bort. - On validation failure, the draft file is preserved under
~/.config/neotoma/edit-drafts/<entity_id>-<ts>.yamland the path is printed so the user can rerunneotoma edit <id>.
The CLI and the Inspector share one applyBatchCorrection backend (src/services/batch_correction.ts), so both surfaces have identical semantics.
neotoma memory-export: Write a singleMEMORY.mdfile with bounded, deterministic contents for agent harnesses that consume file-based memory.--path <path>(defaultMEMORY.md): Output path.--limit-lines <n>(default 200): Hard line cap. When exceeded, output is truncated with a<!-- fold: N entities not shown -->marker. Set to0to include every entity with no line cap.--include-types <list>: Comma-separated entity types to include. Omit to include all types (minus bookkeeping).--exclude-types <list>: Comma-separated entity types to exclude. Applied on top of the default bookkeeping filter.--include-bookkeeping: Include chat bookkeeping (conversation,agent_message). Excluded by default because they are noise in a memory manifest and already live in conversation history.--max-field-chars <n>(default 400): Per-field character cap for long string values. Long bodies (posts, notes, transcripts) are truncated with a deterministic… (<N> chars truncated)suffix so one long entity cannot consume the entire line budget. Set to0to emit full values.--order <importance|recency>(defaultimportance): Sort strategy.importance: type-weighted signal scoretypeWeight × log2(observation_count + 2) × recency_decay(half-life 30 days). Tier-1 types (task,contact,event,transaction,business_opportunity,issue,user_persona_insight,life_tenets,architectural_decision,agent_decision,release,feature_unit) rank above Tier-2 durable artifacts (note,post,meeting_transcription,email_message,location,file_asset, etc.). Bookkeeping types score 0 when included explicitly.recency:last_observation_at desc, ties broken byentity_id asc.
--user-id <id>: Scope to a specific user. Defaults to the active local dev user when encryption is off.
MEMORY.md is regenerated automatically after each mirror batch when mirror.memory_export.enabled is set in ~/.config/neotoma/config.json.
neotoma storage info: Show where CLI config and server data are stored (file paths and backend).- Local backend (only supported backend): prints
data_dir,sqlite_db(defaultdata/neotoma.dbin development,data/neotoma.prod.dbin production),raw_sources(e.g.data/sources),event_log(e.g.data/logs/events.log),logs(e.g.data/logs). Paths are resolved from current directory when run from a Neotoma source checkout, or fromNEOTOMA_PROJECT_ROOT/NEOTOMA_DATA_DIRand other env overrides.
- Local backend (only supported backend): prints
neotoma storage set-data-dir <dir>: Update repo.envwithNEOTOMA_DATA_DIR=<dir>, and optionally copy SQLite DB files (neotoma.db,neotoma.prod.db, and-wal/-shmsidecars) from the old data directory.- For durable SQLite storage on macOS, prefer a local-only path such as
~/Library/Application Support/neotoma/dataover iCloud-syncedDocuments,Desktop, oriCloud Drivefolders. - Interactive mode asks whether to copy DB files.
- When DB files exist in both old and new directories, conflict handling is:
merge: back up target DB files, then insert missing rows from old DBs into target DBs.overwrite: back up target DB files, then replace target DB files with old DB files.use-new: keep target DB files unchanged and only switchNEOTOMA_DATA_DIR.
- Old directory files are always preserved (copy/merge only, never delete old DB files).
- Options:
--move-db-files/--no-move-db-files--on-conflict <merge|overwrite|use-new>--yes(skip prompts)
--jsonoutput includes backup paths, copied files, conflict strategy, and merge stats.
- For durable SQLite storage on macOS, prefer a local-only path such as
neotoma storage merge-db: Merge one SQLite DB file into another.- Required:
--source <path>: Source DB file.--target <path>: Target DB file.
- Options:
--mode <safe|keep-target|keep-source>:safe(default): fail when matching primary keys contain differing row values.keep-target: insert missing rows; keep target rows on conflicts (INSERT OR IGNOREbehavior).keep-source: insert/replace source rows into target (INSERT OR REPLACEbehavior).
--dry-run: Analyze conflicts and merge impact without writing the target DB.--no-recompute-snapshots: Skip post-merge snapshot recomputation.
- Behavior notes:
- Default mode is safety-first (
safe) to avoid silent row loss on conflicts. - Post-merge recomputation rebuilds
entity_snapshotsandrelationship_snapshotswhen those tables exist. - This command merges DB rows only; it does not copy
sources/files or log directories. Use backup/restore or storage migration workflows when file assets must move with DB content.
- Default mode is safety-first (
- Required:
neotoma storage recover-db: Check SQLite integrity and optionally write a recovered copy viasqlite3 .recover.- Default: check only. Exits non-zero when corruption is detected.
--recover: write a new sibling file such asneotoma.prod.recovered-<timestamp>.db.--output <path>: explicit recovered DB path.- Behavior notes:
- Requires
sqlite3onPATH. - Does not replace the live DB automatically.
- Stop Neotoma (MCP/API) before running
--recover. - After a successful recover, manually archive the live
.db/-wal/-shmand then copy the recovered file into place. - If corruption recurs, run
neotoma status --json; thedata.risksblock flags cloud-synced data directories and prior repair artifacts.
- Requires
-
neotoma status(alias:neotoma doctor): Check Neotoma configuration and connectivity. Verifies that the global CLI is onPATH, thatNEOTOMA_DATA_DIRand the SQLite db are usable, whether a local API process is running, which MCP server entries the configured harnesses expose, and whether the data directory is on a high-risk filesystem.- The
doctorname is a deprecated alias retained for backward compatibility; preferneotoma status. The--jsonpayload shape is unchanged. --json: Emit a structured report instead of the human summary. Designed for agent-led installs (neotoma setup --output jsonconsumes it) and for CI/operator scripts.--tool <claude-code|cursor|codex|openclaw|claude-desktop|windsurf|continue|vscode>: Override the current-tool hint thatstatusships in the report and thatneotoma setupreads.
--jsonshape (v0.12+):data.risks[]entry shape:{ "code": "icloud_drive" | "macos_synced_desktop_or_documents" | "prior_sqlite_repair_artifacts", "severity": "warn", "message": "<human-readable description>", "suggested_action": "<single concrete next command>" }The risk detector (
detectDataDirRisksinsrc/cli/doctor.ts) covers three classes:icloud_drive—NEOTOMA_DATA_DIRresolves inside~/Library/Mobile Documents/. iCloud Drive can re-uploadneotoma.db,neotoma.db-wal, andneotoma.db-shmwhile Neotoma is writing, which is the corruption shape behind the SQLiterecover-dbflow.suggested_actionpoints at the suggested local-only path returned bysuggested_safe_data_dir.macos_synced_desktop_or_documents— data dir sits under~/Documentsor~/Desktop, both of which macOS enables for iCloud Drive sync by default.suggested_actionis the matchingneotoma storage set-data-dir "<safe path>" --move-db-filesinvocation.prior_sqlite_repair_artifacts— the data directory contains marker files (repair_backups/,repair_swaps/,recover-errors.log, ordb_corrupt_before_swap_*) left behind by a previousneotoma storage recover-dbrun. A recurrence is a signal to move the data dir to local-only storage.
suggested_safe_data_diris computed bysuggestSafeDataDir(os.homedir()). On macOS it is~/Library/Application Support/neotoma/data; on other platforms it returnsnulland the report only containsriskswithout a generated suggestion. The field isnullwheneverdata.risksis empty.Migration command: when
data.risksis non-empty, an operator (orneotoma setupacting on their behalf) can move the data dir with:neotoma storage set-data-dir "<suggested_safe_data_dir>" --move-db-filesCross-references:
docs/foundation/local_install.md§§ data-dir hygiene,docs/security/threat_model.md## Operator hardening knobs, and therecover:dbscript in### Database and schemaabove. - The
Fleet-general write-integrity tooling built on the canonical snapshot layer. snapshots check / snapshots request operate on the runtime snapshot tables; snapshots export, snapshots diff, and snapshots parsers power the fleet-neutral drift workflow described in docs/releases/in_progress/v0.6.0/ (item 3: snapshot export + drift comparison).
neotoma snapshots check: Check for stale entity snapshots.--auto-fix: Also recompute stale snapshots in place.
neotoma snapshots request: Request snapshot recomputation for stale snapshots.--dry-run: Check only; do not recompute.
neotoma snapshots export: Export entity snapshots as fleet-neutral JSON (schema_version0.1.0) with per-field provenance, anobservation_sourcehistogram, and anattribution_fingerprintroll-up per entity.--entity-types <csv>: Restrict to comma-separated entity types (e.g.agent_task,agent_attempt).--agent-sub <sub>: Restrict to a single AAuthagent_sub.--attribution-tier <tier>: Restrict to one tier —hardware | software | unverified_client | anonymous.--observation-source <kind>: Restrict to one write kind —sensor | llm_summary | workflow_state | human | import.--since <iso>: ISO-8601 lower bound onlast_observation_at.--limit <n>: Cap on returned entities (default: 500).--out <path>: Write the export to this file instead of stdout.--user-id <id>: Override user scope (flag >NEOTOMA_USER_ID> authenticated user).- Output shape:
{ schema_version, exported_at, filter, total_entities, entities[] }.
neotoma snapshots diff: Compare a Neotoma export against an external-state snapshot via a pluggableExternalParser. Defaults to the identityjsonparser; fleet-specific parsers register viaregisterExternalParserinsrc/services/drift_comparison.ts.--neotoma <path>(required): Path to asnapshots exportJSON file.--external <path>(required): Path to the external-state snapshot file.--parser <name>: External parser name (default:json; list viasnapshots parsers).--out <path>: Write the drift report to this file instead of stdout.- Report shape:
{ summary, missing_in_neotoma[], missing_in_external[], field_diffs[], provenance_gaps[] }.provenance_gapsflags entities with unclassifiedobservation_sourceor anonymous / unverified_client attribution even when field values agree.
neotoma snapshots parsers: List registered external parsers available tosnapshots diff.
Fleet round-trip example (AIBTC Lumen, LangGraph, custom adapters all follow the same shape):
neotoma snapshots export --entity-types agent_task,agent_attempt --out ./neotoma.json
# produce a NormalizedExternalSnapshot from whatever your fleet keeps on disk
neotoma snapshots diff --neotoma ./neotoma.json --external ./fleet.json --parser json-
neotoma db migrate-encryption [--direction encrypt|decrypt] [--dry-run]: Bulk-encrypt or bulk-decrypt all sensitive columns in the local SQLite database. Operates on the closed database file directly (server must not be running). Safe to re-run — idempotent.--direction encrypt(default): Encrypt all plaintext values in covered columns acrossobservations,entity_snapshots,relationship_snapshots,raw_fragments,schema_recommendations, andauto_enhancement_queue.--direction decrypt: Reverse the encryption on all covered columns.--dry-run: Report how many rows would be processed without writing any changes.- Requires
NEOTOMA_KEY_FILE_PATHorNEOTOMA_MNEMONICto be set.
-
neotoma db repair-schema-lag [--dry-run] [--types <entity_types>] [--rollback <run_id>]: Audit and repairraw_fragmentsrows that were misrouted due to the schema-projection-lag bug (issue #142). Rows inraw_fragmentswhosefragment_keynow matches a declared field in the active schema for their entity type are promoted to observations, and affected entity snapshots are recomputed. Safe to re-run — uses deterministic observation IDs.--dry-run: Report affected entity types and fragment counts without writing any rows.--types <entity_types>: Comma-separated list of entity types to limit the repair scope (default: all).--rollback <run_id>: Roll back a prior repair run by itsrun_id. Deletes the observations inserted by that run and recomputes affected snapshots.- Every observation inserted by
repair-schema-lagcarries a_migration_run_idfield in itsfieldsJSON, making runs fully rollback-safe. - After a repair, the command prints the
run_idwith a copy-paste rollback hint.
neotoma backup create: Create a backup of the local database, sources, and logs.--output <dir>: Output directory (default:./backups).--tar: After the directory backup is verified, also create<backup-folder-name>.tar.gzin the same parent directory as the backup folder (GNU/BSDtaronPATHrequired). The timestamped directory is kept; the archive is an additional artifact for transport.- Creates a timestamped subdirectory with
neotoma.db, WAL file,sources/,logs/(includesevents.log), and amanifest.jsonwith checksums. - Human output prints backup size first (total bytes on disk for the backup directory, file count, and raw byte count; with
--tar, the same line also includes the.tar.gzsize). Then Backed up from, Contents, Sizes (per-component breakdown; the directory total is labeled total (directory)), db_checksum when recorded. With--tar, also an Archive line for the.tar.gzpath and size. - JSON output (
--json) includesbackup_size:{ bytes, files, human }for the backup directory walk (same totals assizes.total_backup_bytes/total_backup_files, plus a singlehumansummary string). With--tar,backup_sizealso hasarchive_bytesandhumanincludes the archive. Other fields:manifest_path,backed_up_from,sizes, and with--tararchive_path,archive_bytes,sizes.archive_bytes. - Exits non-zero if the database copy is missing or under 1 KiB after write, or if
--tarwas set and creating the archive failed. - If encryption is enabled, data in the backup remains encrypted. Preserve the key file or mnemonic for restore.
neotoma backup verify <dir>: Verify a backup directory (manifest, DB size, optional SHA-256 vs manifest). Exits non-zero if invalid. Human and--jsonboth includebackup_size(bytes,files,human) for the full directory tree on disk.neotoma backup restore: Restore a backup into the data directory.--from <dir>: Backup directory to restore from (required).--target <dir>: Target data directory (default:NEOTOMA_DATA_DIRor./data).
neotoma logs tail: Read persistent log files.--decrypt: Decrypt encrypted log lines usingNEOTOMA_KEY_FILE_PATHorNEOTOMA_MNEMONIC.--lines <n>: Number of lines to show (default: 50).--file <path>: Specific log file (default: latest in env-specificdata/logs; prod usesdata/logs_prod).
neotoma dev list: List available npm scripts frompackage.json.neotoma dev <script>: Run a script frompackage.json(equivalent tonpm run <script>).neotoma dev run <script>: Run a script by name (same asneotoma dev <script>).- Use
-- <args>to pass through extra arguments to the npm script. - The source checkout is found by: explicit CLI inputs (where supported), then
NEOTOMA_REPO_ROOT, then walking up from the current directory, then saved configproject_root(legacyrepo_rootis still read).
- When debugging CLI-related issues, always check the CLI log first. In dev the default path is
~/.config/neotoma/cli.log. If the user passed--log-file, check that path. For session server output, check<repo>/data/logs/session.log(dev) orsession.prod.log(prod). The same file is used whether the API was started by the CLI or by the MCP server (both append); look for "started by MCP" in the log to see who started a given run. For background API, checkneotoma api logsor~/.config/neotoma/logs/api.log. See Where to look for what (logs) indocs/operations/troubleshooting.mdfor a per-component log map. - Dev (default): CLI appends stdout and stderr to
~/.config/neotoma/cli.log. Use--no-log-fileto disable. - Prod (
NEOTOMA_ENV=production): No log file by default; use--log-file <path>to enable. --log-file <path>: Append CLI output to this path (overrides env default).--no-log-file: Do not write to the log file (dev only; prod has no default log file).--debug: Emit detailed initialization logs to stderr when starting a session.
neotoma request --operation <id>:--params <json>: JSON object with{ path, query, body }.--body <json>: JSON body override.--query <json>: JSON query override.--path <json>: JSON path override.--skip-auth: Skip auth token for public endpoints.
The CLI stores configuration in:
~/.config/neotoma/config.json
To see where server data is stored (SQLite path, raw sources dir, etc.), run:
neotoma storage info
Fields:
{
"base_url": "http://localhost:3080",
"access_token": "redacted",
"token_type": "bearer",
"expires_at": "2026-02-01T12:00:00Z",
"connection_id": "redacted"
}
The CLI sorts object keys before outputting JSON. This keeps output stable between runs.
flowchart TD
cli[CLI] -->|"Read config"| config[ConfigFile]
cli -->|"Call API"| api[HttpApi]
api -->|"JSON response"| cli
neotoma request --operation listEntities --params '{"query":{"limit":5}}' --pretty
neotoma store --file ./fixtures/tasks.json
neotoma watch --tail --json # NDJSON, only new changes
neotoma watch --tail --human # Plain one-line-per-change, only new
neotoma watch --tail # Human-readable, only new changes
neotoma watch --human # Plain one-line-per-change, no technical formatting
neotoma watch # Human-readable, includes existing records on startup
- Verify each command prints deterministic JSON output.
- Validate auth login flow against local server.
Load this document when updating CLI commands, options, or configuration behavior.
docs/NEOTOMA_MANIFEST.mddocs/conventions/documentation_standards.mddocs/api/rest_api.md
- Commands and flags MUST match the CLI implementation.
- Config path and fields MUST be accurate.
- Examples MUST use synthetic data.
- Listing commands that do not exist
- Using real tokens or private values
- Omitting required sections
- Purpose, Scope, Invariants, Definitions present
- Commands and options match CLI implementation
- Config path and fields are accurate
- Agent Instructions included
{ "version": "0.12.0", // installed neotoma version "cwd": "/Users/.../repos/neotoma", "global_cli": { "node_on_path": true, "resolved_via": "global-bin" | "fallback" | "missing", "which_neotoma": "/usr/local/bin/neotoma" | null, "global_bin": "/usr/local/bin", "global_package_dir": "/usr/local/lib/node_modules/neotoma", "npm_global_root": "/usr/local/lib/node_modules", "path_fix_hint": null | "Add /usr/local/bin to your PATH" }, "data": { "config_dir": "/Users/.../.neotoma", "data_dir": "/Users/.../Library/Application Support/neotoma/data", "db_exists": true, "initialized": true, "risks": [ // 0..N entries — see "data.risks codes" below ], "suggested_safe_data_dir": "/Users/.../Library/Application Support/neotoma/data" | null }, "api": { "running": true, "env": "dev" | "prod" | null, "port": 3080, "pid": 12345, "base_url": "http://127.0.0.1:3080" }, "mcp_servers_detected": { "cursor": { "path": "~/.cursor/mcp.json", "has_neotoma": true, "has_neotoma_dev": false } // …one entry per harness probed (cursor, claude_code, codex, claude_desktop, openclaw) }, "cli_instructions": { "project": { "cursor": true, "claude": false, "codex": false }, "user": { "cursor": true, "claude": false, "codex": false } }, "permission_files": { /* per-harness file existence + allow-list match */ }, "current_tool_hint": "cursor" | null, "hooks": { /* HooksReport — installed hooks by harness */ }, "mirror": { /* MirrorReport — mirror clone state */ }, "suggested_next_step": "install" | "init" | "configure-mcp" | "configure-cli-instructions" | "configure-permissions" | "activate" | "offer-hooks" | "offer-mirror" | "ready" }