Skip to main content

crewship memory

Access memory FTS5 indexes on the local filesystem without a running server — useful for development, debugging, and scripting — plus the server-side reads that work from any machine: inventory, health, hybrid and versions.

Subcommands

FTS5 search across memory with scope control.
Flags:
memory search used to own a local --format flag with the -F shorthand. Because it took the name format, it shadowed the global persistent flag on this command — and a shadowed persistent flag takes its shorthand with it, so crewship memory search … -f json did not fall back to human output, it failed with unknown shorthand flag: 'f'.The global --format/-f now reaches this command like every other, with the full table|json|yaml|ndjson|quiet vocabulary instead of table|json. -F survives as a deprecated alias under the name --output-format so existing scripts keep working; it prints a deprecation notice and will be removed.
Examples:

status

Show memory index statistics.
Example:
Exit codes. A scope whose index cannot be read is reported on stderr, naming the resolved path (which --path and --scope decide together) and the cause — the directory does not exist, is not a directory, cannot be entered, or holds an index.sqlite that cannot be opened. When every scope fails the command exits 3; a partial failure exits 0 with the successful scopes on stdout. SQLite’s own unable to open database file (14) is never what you see: that string is identical for all four causes and names none of them. When the base path itself is missing, that is a mistyped --path and the message says so:
When the base path is real and has simply never been indexed, reindex is the fix and it creates the directory itself:
A directory you cannot enter is called out as such, because inside an agent container the memory tree belongs to uid 1001:

health

Print the 5-metric memory health score for the caller’s workspace. Hits the running server (requires crewship login and a workspace context) — unlike search/status/reindex which read local FTS indexes, health calls GET /api/v1/memory/health, which recomputes fresh on every request from five aggregate SQL queries. The persisted memory_health_snapshots table is for trend graphs, not for this command.
Flags: The global --format flag is honoured (table, json, yaml, ndjson). Example:
The 77/100 is colour-coded — red below 50, yellow 50–74, green 75 and above. The colour is applied via terminal escape codes; in pipes (| cat or --format json) it disappears. See Episodic memory — health scoring for what each metric measures and the weighted-composite formula.

reindex

Rebuild FTS5 index from markdown files. Signal-aware (responds to SIGINT/SIGTERM).
Example:
It creates the index directory it is asked to build in, so the .memory a crew has never had is one reindex away rather than a mkdir you have to work out. It will not create the base path: a --path whose own directory is absent is a typo, and inventing the tree would hide it. Creating .memory needs the base path to exist and be writable by you. When it is not, reindex says so rather than reporting the directory as merely missing — that wording advises running reindex, which is the command that just failed:
Returns a non-zero exit code if all reindex operations fail — 3 when the reason is that no scope had a directory to index, which is the code status uses for the same condition, and 1 otherwise. Per-scope failures go to stderr with the same actionable wording status uses.

hybrid

Search workspace memory through the server’s hybrid engine — full-text chunks plus episodic journal recall, merged and ranked. Unlike search (local filesystem FTS), this requires a login token and works from any machine.

inventory (server API)

List the current knowledge documents of one agent or one crew, as the server reads them right now — the same read the crew Memory tab makes, over GET /api/v1/agents/{agentId}/memory or GET /api/v1/crews/{crewId}/memory. Requires a login token and a workspace; works from any machine. It never starts or provisions a container.
Exactly one of the two is required. Both add the workspace-wide documents when the server has a workspace memory root configured. What is listed: AGENT.md, CREW.md, BRIEF.md, LEAD.md, pins.md, lessons.md, learned-*.md and daily/*.md (workspace scope: every .md except PERSONA.md). Personal users/ and peers/ files are never included — read your own through crewship privacy; personas have crewship persona. Human output leads with one line of per-scope states and then one row per document:
A document row’s STATE is available or unavailable: an unreadable or oversized file (over 64 KiB, or past the 512 KiB per-scope budget) is listed with no content, which is not the same as empty content. REVISION is the first 12 hex characters of the content’s SHA-256; the full digest, the content itself and the history_path to pass to memory versions list are in --format json. --format quiet prints the bare document ids. Exit codes: 3 when the agent or crew does not resolve in the workspace; non-zero with the server’s sentence on 503 memory storage unavailable.

versions (server API)

The memory versions group mirrors log/show/restore over the running server’s API — use these from any machine; the direct-DB commands below only work on the server host. The workspace comes from the auth context.
restore requires OWNER/ADMIN, prompts for confirmation (--yes skips), and the server confines <canonical-path> to its configured memory root. list columns: SHA256, WRITTEN, BYTES, WRITTEN BY. --limit defaults to 20 and the server clamps it to 1–1000.

Projection state — “nothing here” vs “we could not look”

list leads with a projection state whenever it changes how the list should be read. An empty version history has two completely different meanings and they used to print the same empty table: Recorded paths are agent:<slug>/… or crew:<crew_id>/… ending in AGENT.md, CREW.md, pins.md, learned-*.md, or daily/*.md. Everything else — lessons.md, PERSONA.md, peers/* — is unrecorded and has its own history surface; the reason text names it. The state and its reason are printed above the table in human output and carried verbatim in --format json:
A server predating this field answers without it. The CLI reads that as recorded — the state every path had when the only writers were recording ones — so an older server is never reported as having versioning switched off.

log

List the memory_versions audit chain for a path, newest-first. Reads the server the CLI targets, via GET /api/v1/admin/memory/versions (ADMIN+).
Until #2086 this command opened ~/.crewship/crewship.db directly and ignored --server / CREWSHIP_SERVER / --profile — while the route above already existed and had no CLI command at all. On any host where crewshipd runs with its own DATABASE_URL (every development clone uses file:./crewship.db) it therefore reported an audit chain that was not the one being audited.--local is the old behaviour, asked for explicitly: it reads the database file on this host, works with the server down, and prints the path it opened. It is also the only path that reports payload_ref — the API deliberately does not hand out on-disk blob locations.
<path> is the audit-trail identifier the consolidator + approve flow record under. For canonical learned files this is crew:<crew_id>/learned-YYYY-MM-DD.md (see internal/consolidate.canonicalAuditPath); for pins it’s crew:<crew_id>/pins.md. Empty result prints no versions for <ws> @ <path> to stderr and exits 0.
Partial results on a very busy workspace. The route filters by path prefix while this command wants one exact path, and rows come back ordered by (written_at, id) across the whole workspace rather than per path. So a rarely-written file can sit behind a long run of unrelated versions. The CLI walks at most 200 pages of 500 rows; if it stops there it prints the matches it found and warns on stderr:
Exit code stays 0 and stdout still carries only rows, so --format json | jq is unaffected — check stderr before treating a short list as the whole chain. --local has no ceiling: it queries the file directly.

show

Print the raw content of one historical version to stdout. The canonical file on disk is not touched — this is the “preview without restoring” path.
Reads the server the CLI targets: the row is resolved by (path, sha) through GET /api/v1/admin/memory/versions, then its bytes are streamed from GET /api/v1/admin/memory/versions/{id}/content. Two calls because the content route is keyed by row id while the arguments here are the audit chain’s own coordinates — which is what memory log puts in front of you. --local reads the database file and blob store on this host instead. Stdout = raw blob bytes (pipe-friendly). Stderr carries status/errors. Exit codes:
  • 0 — blob found and streamed
  • 1 — version not found or read error
  • 2 — invalid usage
A sha that is not in the chain always runs the resolving walk to its end, so on a very busy workspace it can hit the 200-page ceiling described under log. That is still 1 / version not found — the ceiling’s stderr warning prints first, so a not-found that might be a truncation artefact says so rather than masquerading as a different error.

restore

Atomically replace the canonical memory file at <canonical-path> with the content of version <sha>, then write a fresh memory_versions row so the chain stays forward-only — history is never rewritten.
This one has no server-side route and is not getting one: it writes a canonical file onto this machine’s disk from a blob in this machine’s content-addressed store, so there is nothing a remote server could do with the request. When --server / CREWSHIP_SERVER / --profile names a server it refuses rather than restore from another instance’s history; --local is how you say you mean this host. Stop the server first: the command writes the database file, and a running server owns the only writer. The path guard rejects empty strings, any path containing .., and any target whose absolute form does not start with {blob-root}/.. (the data dir, one level above versions).

export

Write memory out as a portable OKF bundle — markdown with YAML frontmatter, readable and git-friendly. Goes through the server, so it works from any machine the CLI can reach.
OWNER/ADMIN only — the bundle contains every private note the agent holds. The same export is available in the dashboard: an agent’s Memory tab has an Export button that downloads the bundle. Import has no UI equivalent — see Memory Portability.

import

Read a memory directory produced by another harness, map it onto Crewship’s tiers, and write it in. Recognises crewship, okf, nanoclaw and openclaw layouts.
--apply requires OWNER/ADMIN, the same as export — it writes into the context an agent reasons from. Imported documents pass through the same guards an agent’s own writes do: a closed allowlist of paths, per-file size ceilings, secret and prompt-injection scanning, and symlink-safe confinement. Refusals are listed per document and the command exits non-zero; a failed document does not stop the rest. Full mapping table and limits: Memory Portability.

Scope and Path Resolution

The meaning of --path depends on the selected scope: