Skip to main content

CLI for managing a grimoire datastore

Project description

4lt7ab-grimoire-cli

The standalone CLI for grimoire — a single-file semantic datastore backed by SQLite and sqlite-vec. Operates on a mount directory holding one or more grimoire databases, plus an embedded MCP server for AI client integration.

For the Python library, see 4lt7ab-grimoire.

Install

uv tool install '4lt7ab-grimoire-cli[fastembed]'
# or: pipx install '4lt7ab-grimoire-cli[fastembed]'

Both install into an isolated venv — clean uninstall, no impact on system Python. The fastembed extra pulls the bundled embedder (ONNX-based, no service required).

The grimoire command is now on your PATH. Confirm the install with grimoire --version.

The first grimoire mount create (and the first time you reuse the mount on a new machine) fetches the default embedder weights (~30 MB) from HuggingFace into the mount's __models__/ cache. Subsequent runs reuse the cache and stay offline. On macOS, if the fetch stalls on TLS errors, set HF_HUB_DISABLE_XET=1 once to bypass the xet CDN.

Mount model

A mount is a directory containing one or more grimoire databases plus a shared embedder model cache:

<mount>/
├── grimoire.db            # the default DB
├── <name>/
│   └── grimoire.db        # a named DB
├── __models__/            # shared embedder cache
└── grimoire.toml          # registry file (reserved, currently inert)

The mount resolves in this order: --mount <dir> flag > GRIMOIRE_MOUNT env var > ~/.grimoire.

Set the env var once per shell to avoid passing --mount everywhere:

export GRIMOIRE_MOUNT=$PWD/.grimoire

A mount can hold one default database at <mount>/grimoire.db plus any number of named databases under <mount>/<name>/grimoire.db. Pick which one a command targets with --db <name> / -d <name>; omit --db to target the default. Names must match [a-z0-9_-]+ and cannot begin with __ (reserved).

Mental model

A grimoire entry has two layers:

  • The entry itself: uniq_id (library-assigned ULID) + data (JSON blob).
  • Three opt-in sidecars keyed by that uniq_id:
    • entry_idx — filterable columns: uniq_ref (external reference) and five symmetric ordinal_1..ordinal_5 slots (BLOB-affinity — store any scalar: numbers, strings, labels).
    • entry_fts — FTS5 keyword text.
    • entry_vec — semantic vector + source text.

entry add creates an entry and optionally writes any subset of the three sidecars in the same call. Sidecar writes are PUT — supplying any of --ref/--ord-* wholesale-replaces the entry_idx row; supplying --match replaces the FTS row; supplying --search replaces the vec row. Deleting an entry cascade-cleans every sidecar via a DB trigger.

Quickstart

export GRIMOIRE_MOUNT=$PWD/.grimoire

# Create the mount + default DB. Idempotent.
grimoire mount create

# Add an entry with data, idx metadata, keyword text, and semantic text.
# Each idx/match/search flag is independent — pick any subset.
grimoire entry add \
    --data '{"habitat": "volcano"}' \
    --ref phoenix-001 \
    --ord-1 creature \
    --ord-2 1.0 \
    --match "phoenix fire-bird ashes" \
    --search "A solar phoenix reborn from its own ashes at dawn"

# Search.
grimoire search "creatures that come back from the dead"
grimoire match "phoenix"

# Browse entry_idx rows with filters.
grimoire query --equals ordinal_1=creature --gte ordinal_2=0.5

# Look up entries by external reference.
grimoire fetch phoenix-001

# Inspect the database — model, dimension, schema version, per-table counts, file size.
grimoire info

Every command prints pretty-indented JSON. Pipe through jq for filtering and extraction.

Commands

Global options

Option Behavior
--mount <dir> Override the mount path for this invocation. Precedence: --mount > $GRIMOIRE_MOUNT > ~/.grimoire.
--version Print the CLI version and exit.
--help Print help for the command (or subcommand) and exit.

Mount administration

grimoire mount create

Create the mount directory, shared __models__/ cache, and the default database. Idempotent — safe to re-run. Loads the default embedder on first create to write the embedder lock.

grimoire mount destroy --yes

Wipe the entire mount: every database, the model cache, the registry. There is no undo. --yes is required.

grimoire mount add <name>

Create a named database in the mount. The mount itself must already exist (run mount create first).

grimoire mount ls

List databases in the mount as a JSON array of {"db": <str|null>, "path": <str>}. The default DB appears first with db: null; named DBs follow alphabetically.

grimoire mount remove <name> --yes

Delete a single named database file from the mount. The model cache and other databases are preserved. --yes is required.

Database inspection

grimoire info [--db <name>]

Show metadata for a database: embedder lock (model, dimension), schema_version, per-table row counts (entry_count, entry_idx_count, entry_fts_count, entry_vec_count), file path, file size. Does not load the embedder.

grimoire analyze [--db <name>]

Run SQLite's ANALYZE to refresh sqlite_stat1. The rotation composite indexes on entry_idx rely on accurate selectivity stats for the planner to pick among them — run after bulk loads or whenever the data distribution shifts. Prints {"db": <name|null>, "analyzed": true} on success.

Entry CRUD

grimoire entry add [options]

Create an entry and optionally PUT-index its sidecars in one call.

Option Behavior
--db, -d Target a named DB. Omit for the default.
--data JSON value stored in entry.data (object, array, scalar, or null).
--ref entry_idx.uniq_ref value.
--ord-1 .. --ord-5 entry_idx.ordinal_N values. Each value is coerced int → float → string, so a numeric literal stores as a number and anything else stores as text.
--match Text written to the FTS5 row. PUT-replaces the entry's entry_fts row.
--search Text embedded via the bundled embedder. PUT-replaces the entry's entry_vec row.

Supplying any of --ref or --ord-* PUT-replaces the entry's entry_idx row; omitted columns become NULL. Omit them all to leave entry_idx untouched.

grimoire entry update <uniq_id> [options]

Update entry data and/or PUT-index sidecars. Omit --data to leave the data column untouched. Sidecar flags follow the same PUT semantics as entry add.

grimoire entry get <uniq_id> [<uniq_id>...]

Fetch one or more entries by uniq_id. Returns a JSON array of entry objects.

grimoire entry remove <uniq_id> --yes

Remove an entry. Sidecar rows are cascade-cleaned by DB trigger. --yes is required. Returns {"uniq_id": <id>, "removed": <bool>}removed=false if the id did not exist.

Reads

All four read commands return paired results: each match is {"entry": {...}, "<key>": ...}, where <key> is "index", "score", or "distance" depending on the command.

grimoire query [filters] [--cursor <id>] [--limit <n>]

Browse entry_idx rows ordered by uniq_id ASC, joined to entry for the data side. Returns [{"entry": {...}, "index": {...}}, ...].

Option Behavior
--db, -d Target a named DB.
--equals KEY=VALUE Filter entry_idx.<KEY> IN (...). Repeatable. Valid keys: any entry_idx column. Value is coerced int → float → string.
--gte KEY=VALUE Filter entry_idx.<KEY> >= VALUE. Repeatable. Valid keys: ordinal_1..ordinal_5. Same coercion as --equals.
--lte KEY=VALUE Filter entry_idx.<KEY> <= VALUE. Repeatable.
--cursor Return rows with uniq_id > <cursor>. Pass the last id of the previous page.
--limit Maximum rows (default 100).
LAST=$(grimoire query --limit 100 | jq -r '.[-1].entry.uniq_id')
grimoire query --limit 100 --cursor "$LAST"

grimoire fetch <uniq_ref> [<uniq_ref>...]

Fetch entries whose entry_idx.uniq_ref is in the given list. Returns [{"entry": {...}, "index": {...}}, ...]. uniq_ref is sparse-unique (UNIQUE partial index over the non-NULL rows), so each ref maps to at most one entry; entries without an entry_idx row are invisible to fetch.

grimoire match <query> [filters] [--limit <n>]

FTS5 BM25 keyword search. Free-form prose is tokenized into safe FTS5 syntax automatically: each word token is quoted and joined with OR, so apostrophes, punctuation, and bareword FTS5 operators (AND, OR, NOT, NEAR, *) in the query can't reach the parser.

Option Behavior
--db, -d Target a named DB.
--equals KEY=VAL, --gte KEY=NUMBER, --lte KEY=NUMBER Apply entry_idx filters via JOIN. Same shape as query.
--limit Maximum hits (default 10).

Returns [{"entry": {...}, "score": <bm25>}, ...]. score is positive (higher = better).

grimoire search <query> [--limit <n>]

vec0 KNN semantic search. Embeds the query via the bundled embedder, then ranks by vector distance.

Option Behavior
--db, -d Target a named DB.
--limit Maximum hits (default 10).

Returns [{"entry": {...}, "distance": <float>}, ...]. distance is the raw vec0 distance (lower = better, non-negative).

MCP server

grimoire mcp serve

Run a FastMCP server over stdio, scoped to this mount. Wires the library's read+write surface as MCP tools an AI client can call directly. Mount administration (mount create/destroy/add/remove) stays CLI-only.

Tools exposed: info, add, update, get, remove, query, fetch, match, search. add and update both accept the data + idx + match + search kwargs (mirroring the CLI's entry add/entry update). For MCP update, passing data: null (or omitting it) leaves the data column alone — there's no way to MCP-update an entry's data to NULL because JSON can't distinguish "passed null" from "not passed."

Output format

Every command prints pretty-indented JSON to stdout. There is no table mode, no --raw, no auto-detect — JSON in, JSON out, pipe to jq to slice. Errors go to stderr via Typer's standard messaging.

Environment variables

Variable Default Behavior
GRIMOIRE_MOUNT ~/.grimoire Mount directory. Overridden by --mount.
GRIMOIRE_TELEMETRY off Telemetry sink wired into every Grimoire.open(), including the in-process MCP server. off drops everything; logging enables stdlib logging (INFO records per span/event with structured fields under extra={"grimoire": {...}}). Records go to stderr so they don't interfere with JSON-on-stdout.

Schema notes

Pre-v1, schema changes are not migrated in place. The library checks PRAGMA user_version against its expected SCHEMA_VERSION on every open; mismatches raise SchemaVersionError. Recreate the file (and re-index its contents) when this happens. Migration ergonomics get designed once v1 is on the table.

Uninstall

uv tool uninstall 4lt7ab-grimoire-cli
# or: pipx uninstall 4lt7ab-grimoire-cli

The mount directory at ~/.grimoire (or wherever you pointed GRIMOIRE_MOUNT) is left intact. Remove it manually with grimoire mount destroy --yes before uninstalling, or rm -rf after.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

4lt7ab_grimoire_cli-0.0.21.tar.gz (17.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

4lt7ab_grimoire_cli-0.0.21-py3-none-any.whl (16.5 kB view details)

Uploaded Python 3

File details

Details for the file 4lt7ab_grimoire_cli-0.0.21.tar.gz.

File metadata

  • Download URL: 4lt7ab_grimoire_cli-0.0.21.tar.gz
  • Upload date:
  • Size: 17.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for 4lt7ab_grimoire_cli-0.0.21.tar.gz
Algorithm Hash digest
SHA256 61dc25b187f230be4c1a96e5f71d2dd214fd8408bddc7e2f05087e9049ab4835
MD5 ddff37c8ca5e3ad24f03604a7ed55d1c
BLAKE2b-256 e5fb3730b08a871a440280f92e94e648dea56d6efcda1f1814ac9ef5cd8a4bf8

See more details on using hashes here.

File details

Details for the file 4lt7ab_grimoire_cli-0.0.21-py3-none-any.whl.

File metadata

  • Download URL: 4lt7ab_grimoire_cli-0.0.21-py3-none-any.whl
  • Upload date:
  • Size: 16.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for 4lt7ab_grimoire_cli-0.0.21-py3-none-any.whl
Algorithm Hash digest
SHA256 4214c9a2e38e39234dc3ec6ff6dd528e6bda327befa758a04862d551b2c9fcf0
MD5 4f4580ef17ec83f09c3de92bcc5416d7
BLAKE2b-256 7dc7fe9951f0e39531c728dc12b94b64d2ac1e7bd4995d809afcf5f7251e6c8a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page