@cerefox/memory 0.9.1 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENT_GUIDE.md CHANGED
@@ -359,7 +359,7 @@ If you're using Cerefox via the local CLI (Path C from `connect-agents.md`), the
359
359
  ## Governance
360
360
 
361
361
  - **Review status**: agent writes set `pending_review`; human edits set `approved`. Both are searchable.
362
- - **Soft delete**: deleted documents go to trash (recoverable). They are excluded from search. You can soft-delete via MCP (`cerefox_delete_document` if your client exposes it) or CLI (`cerefox document delete --yes --author <you> --author-type agent`).
362
+ - **Soft delete**: deleted documents go to trash (recoverable). They are excluded from search. There is no delete MCP tool soft-delete is done via the CLI (`cerefox document delete --yes --author <you> --author-type agent`) or the web UI.
363
363
  - **Permanent purge and restore-from-trash are web-UI-only**, by design. If you decide to delete something, **tell the user explicitly** that you soft-deleted it and that they can review or restore it via the Cerefox web UI. You cannot un-do your own soft-delete from agent code; only the human can. See [`docs/guides/access-paths.md` → Destructive operations and the trust model](docs/guides/access-paths.md#destructive-operations-and-the-trust-model).
364
364
  - **Versioning**: every update via `update_if_exists` creates an archived version. Old content is always recoverable.
365
365
  - **Audit log**: all write operations are recorded with author, timestamp, and size changes.
@@ -378,7 +378,7 @@ The Cerefox CLI is the TypeScript binary from `@cerefox/memory` (`npm install -g
378
378
 
379
379
  The legacy Python `uv run cerefox` is a frozen husk as of v0.9 — only `uv run cerefox mcp` (the standalone Python MCP fallback) still works; every other verb has moved to the TypeScript `cerefox` binary.
380
380
 
381
- > Full per-flag reference lives in [`docs/guides/cli.md`](docs/guides/cli.md). The mapping table below is the agent-facing summary. **CLI flag names match MCP parameter names exactly** (kebab-case); short forms like `--project`, `--filter`, `--count`, `--update`, `--version` are accepted as aliases.
381
+ > Full per-flag reference lives in [`docs/guides/cli.md`](docs/guides/cli.md). The mapping table below is the agent-facing summary. **CLI flag names match MCP parameter names exactly** (kebab-case), each with a single-letter short form (`-p`, `-f`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name or its short form — there are no long-form aliases like `--project` or `--count`.
382
382
 
383
383
  ### MCP tool ↔ CLI command mapping
384
384
 
@@ -407,7 +407,7 @@ Alternative: have your user set `CEREFOX_AUTHOR_NAME`, `CEREFOX_AUTHOR_TYPE`, `C
407
407
 
408
408
  ### Behavioural differences worth knowing
409
409
 
410
- 1. **CLI output is human-formatted by default.** `cerefox search` returns a numbered, indented text block with title, score, and a 300-char preview per result. To extract document IDs reliably, parse the `Doc: <title> (<source>)` lines or fall back to `cerefox document list` for a clean tabular listing. `cerefox document get <id>` prints raw Markdown to stdout. **For scripted access to audit data**, use `cerefox audit list --json` one JSON object per line, ideal for piping to `jq`.
410
+ 1. **CLI output is human-formatted by default.** In the default `docs` mode, `cerefox search` prints, per match, a header line `## <title> [id: <uuid>] · score · N chunks · M chars · partial|full` followed by the document body. Grab the document ID from the `[id: <uuid>]` tag, or use `cerefox document list` for a clean tabular listing. For structured output, `cerefox search --json` and `cerefox audit list --json` emit machine-readable JSON (the latter one object per line, ideal for `jq`). `cerefox document get <id>` prints raw Markdown to stdout.
411
411
 
412
412
  2. **Every invocation is independent.** With MCP, your tool framework can pass `requestor` once per session. With the CLI, every command is a separate process — pass `--requestor` / `--author` / `--author-type` on every relevant invocation, or set the env-var defaults once at the start.
413
413
 
@@ -53,7 +53,7 @@ metadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03
53
53
 
54
54
  If `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …). The legacy Python `uv run cerefox` is now a frozen husk as of v0.9 — only `uv run cerefox mcp` still works.
55
55
 
56
- Same operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); short forms (`--filter`, `--project`, `--count`, `--update`, `--version`) work as aliases.
56
+ Same operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.
57
57
 
58
58
  | MCP tool | CLI |
59
59
  |---|---|
package/README.md CHANGED
@@ -9,7 +9,7 @@ curated knowledge layer that multiple AI tools can read and write.
9
9
  > Supabase project** (Postgres + pgvector; free tier works). Installing this
10
10
  > npm package does **not** give you a working KB on its own; you also need a
11
11
  > Supabase project + an embedding API key, and a one-time server-side deploy
12
- > from the source repo. **See "Before you install" below.**
12
+ > (`cerefox server deploy` — no repo clone needed). **See "Before you install" below.**
13
13
 
14
14
  **Why cloud-backed?** Cerefox is designed as a *cloud-backed* memory layer so
15
15
  the same knowledge is reachable from every agent you run — Claude Code on
@@ -45,7 +45,7 @@ Cerefox is a self-hosted memory layer with two halves you set up independently:
45
45
  **Client side** (this npm package, runs on your machine):
46
46
  - `cerefox` CLI + `cerefox mcp` (local stdio MCP) + `cerefox web` (local UI at `http://localhost:8000`)
47
47
 
48
- The server side ships with the source repo, not this npm package. Both halves are required for a working install.
48
+ You deploy the server side with this package's CLI — `cerefox server deploy` stands up the schema, RPCs, and all 9 Edge Functions from bundled assets (no repo clone). Both halves are required for a working install.
49
49
 
50
50
  ### What you need
51
51
 
@@ -55,23 +55,20 @@ The server side ships with the source repo, not this npm package. Both halves ar
55
55
  | An **embedding API key** | OpenAI `text-embedding-3-small` (default) or Fireworks AI. Pennies/month for typical personal use (see [operational-cost.md](https://github.com/fstamatelopoulos/cerefox/blob/main/docs/guides/operational-cost.md)). | Get an [OpenAI API key](https://platform.openai.com/api-keys). |
56
56
  | **Node ≥ 20** or **Bun ≥ 1.0** | Runtime for the `cerefox` bin (and the bundled `cerefox mcp` server). | [nodejs.org](https://nodejs.org) · [bun.sh](https://bun.sh). The one-line installer below bootstraps Bun if neither is present. |
57
57
 
58
- ### One-time server-side setup (~10 min — clone required)
58
+ ### One-time server-side setup (~10 min — no clone needed)
59
59
 
60
- The schema, RPCs, and Edge Functions ship with the source repo, not this npm package. Clone once, configure, deploy:
60
+ The CLI stands up the whole server side schema, RPCs, and all 9 Edge
61
+ Functions — from bundled assets:
61
62
 
62
63
  ```bash
63
- git clone https://github.com/fstamatelopoulos/cerefox.git
64
- cd cerefox && cp .env.example .env # fill in CEREFOX_SUPABASE_* + OPENAI_API_KEY + CEREFOX_DATABASE_URL
65
- bun install # workspace deps for the deploy scripts
66
- bun scripts/db_deploy.ts # creates tables, indexes, RPCs in your Supabase
67
- npx supabase functions deploy cerefox-mcp cerefox-search cerefox-ingest \
68
- cerefox-metadata cerefox-get-document \
69
- cerefox-list-versions cerefox-get-audit-log \
70
- cerefox-metadata-search cerefox-list-projects
71
- npx supabase secrets set OPENAI_API_KEY=sk-...
64
+ cerefox init # enter your Supabase URL/keys + embedding key
65
+ cerefox server deploy # schema + RPCs + Edge Functions
72
66
  ```
73
67
 
74
- Full walkthrough (connection-pooling quirks, Supabase API-key flavors, troubleshooting): [setup-supabase.md](https://github.com/fstamatelopoulos/cerefox/blob/main/docs/guides/setup-supabase.md).
68
+ Details (Supabase login/linking, connection-pooling quirks, API-key flavors,
69
+ troubleshooting, and the contributor clone-and-deploy path) live in the
70
+ [quickstart](https://github.com/fstamatelopoulos/cerefox/blob/main/docs/guides/quickstart.md)
71
+ and [setup-supabase.md](https://github.com/fstamatelopoulos/cerefox/blob/main/docs/guides/setup-supabase.md).
75
72
 
76
73
  If you don't yet have Supabase + an OpenAI key, the [Cerefox
77
74
  quickstart](https://github.com/fstamatelopoulos/cerefox/blob/main/docs/guides/quickstart.md)
@@ -113,7 +110,7 @@ already provisioned (see "Before you install").
113
110
  > **Already ran the server-side setup above?** Then your schema is in place and
114
111
  > `cerefox init` only needs the URL + keys. If you skipped that step,
115
112
  > `cerefox doctor` will flag it and point you back to
116
- > `bun scripts/db_deploy.ts` from a repo clone.
113
+ > `cerefox server deploy`.
117
114
 
118
115
  > **Upgrading from the Python `cerefox` CLI?** If you have a working
119
116
  > `.env` in your repo clone, init detects it and offers to **copy** it to
@@ -160,17 +157,18 @@ the 10 MCP tools (`cerefox_search`, `cerefox_ingest`, `cerefox_get_document`,
160
157
  ## Common commands
161
158
 
162
159
  ```bash
163
- cerefox search "second brain" # hybrid (FTS + semantic)
164
- cerefox ingest notes.md --project "Personal" # add a doc
165
- cerefox list-projects # discover projects
166
- cerefox metadata-search --metadata-filter '{"type":"decision-log"}'
167
- cerefox get-audit-log --since 2026-05-01 # immutable history
168
- cerefox doctor # diagnose your install
169
- cerefox upgrade # alias for self-update
160
+ cerefox search "second brain" # hybrid (FTS + semantic)
161
+ cerefox document ingest notes.md --project "Personal" # add a doc
162
+ cerefox project list # discover projects
163
+ cerefox metadata search --metadata-filter '{"type":"decision-log"}'
164
+ cerefox audit list --since 2026-05-01 # immutable history
165
+ cerefox doctor # diagnose your install
166
+ cerefox upgrade # alias for self-update
170
167
  ```
171
168
 
172
- Run `cerefox --help` for the full command surface (28 subcommands grouped
173
- by category).
169
+ Run `cerefox --help` for the full command surface a resource-verb shape
170
+ (`document …`, `project …`, `metadata …`, `server …`) plus flat commands
171
+ like `search` and the lifecycle verbs.
174
172
 
175
173
  ---
176
174
 
@@ -181,8 +179,8 @@ MCP client) gives your AI agent full access to the knowledge base on
181
179
  its own. The rest of the `cerefox` CLI is useful for:
182
180
 
183
181
  - **One-off shell operations**: search, ingest, list, audit-log.
184
- - **Power-user workflows**: `cerefox ingest-dir ./meeting-notes`,
185
- `cerefox metadata-search --metadata-filter …`, `cerefox backup`.
182
+ - **Power-user workflows**: `cerefox document ingest-dir ./meeting-notes`,
183
+ `cerefox metadata search --metadata-filter …`, `cerefox backup create`.
186
184
  - **Setup + diagnostics**: `cerefox init`, `cerefox doctor`,
187
185
  `cerefox configure-agent`, `cerefox self-update`.
188
186
  - **Agents via local Bash tool**: some coding agents prefer running
@@ -196,7 +194,7 @@ its own. The rest of the `cerefox` CLI is useful for:
196
194
  - **Architecture overview**: [`CLAUDE.md`](https://github.com/fstamatelopoulos/cerefox/blob/main/CLAUDE.md)
197
195
  - **Setup guides**: [`docs/guides/`](https://github.com/fstamatelopoulos/cerefox/tree/main/docs/guides)
198
196
  - **Migration from v0.4.x**: [`docs/guides/migration-v0.5.md`](https://github.com/fstamatelopoulos/cerefox/blob/main/docs/guides/migration-v0.5.md)
199
- - **For AI agents using Cerefox**: [`AGENT_GUIDE.md`](https://github.com/fstamatelopoulos/cerefox/blob/main/AGENT_GUIDE.md), [`AGENT_QUICK_REFERENCE.md`](https://github.com/fstamatelopoulos/cerefox/blob/main/AGENT_QUICK_REFERENCE.md), or run `cerefox docs --list`.
197
+ - **For AI agents using Cerefox**: [`AGENT_GUIDE.md`](https://github.com/fstamatelopoulos/cerefox/blob/main/AGENT_GUIDE.md), [`AGENT_QUICK_REFERENCE.md`](https://github.com/fstamatelopoulos/cerefox/blob/main/AGENT_QUICK_REFERENCE.md), or run `cerefox guides list`.
200
198
  - **Changelog**: [`CHANGELOG.md`](https://github.com/fstamatelopoulos/cerefox/blob/main/CHANGELOG.md)
201
199
 
202
200
  ---
@@ -7184,7 +7184,7 @@ var exports_meta = {};
7184
7184
  __export(exports_meta, {
7185
7185
  PKG_VERSION: () => PKG_VERSION
7186
7186
  });
7187
- var PKG_VERSION = "0.9.1";
7187
+ var PKG_VERSION = "0.9.3";
7188
7188
  var init_meta = () => {};
7189
7189
 
7190
7190
  // ../../node_modules/.bun/tslib@2.8.1/node_modules/tslib/tslib.js
@@ -25748,7 +25748,7 @@ var init_get_document = __esm(() => {
25748
25748
  });
25749
25749
 
25750
25750
  // ../../_shared/mcp-tools/get-help-content.ts
25751
- var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **10 MCP tools** (9 of them have CLI equivalents — `cerefox_get_help` is MCP-only). For the full guide, search Cerefox for "How AI Agents Use Cerefox" or call `cerefox_get_help` to retrieve this content over MCP.\n\n## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata`, `author` |\n| `cerefox_get_document` | Get full document by ID | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find docs by metadata (no text query) | `metadata_filter` (required), `include_content`, `updated_since` |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc\'s project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.\n\n## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> modify ->\ningest(title="Same Title", content="...", document_id="abc123", author="my-agent")\n```\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true, author="my-agent")\n```\n\n## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```\n\n## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …). The legacy Python `uv run cerefox` is now a frozen husk as of v0.9 — only `uv run cerefox mcp` still works.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); short forms (`--filter`, `--project`, `--count`, `--update`, `--version`) work as aliases.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` |\n| `cerefox_set_document_projects` | _MCP-only; a CLI command will be added in a future release. Until then, run via MCP if available._ |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
25751
+ var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **10 MCP tools** (9 of them have CLI equivalents — `cerefox_get_help` is MCP-only). For the full guide, search Cerefox for "How AI Agents Use Cerefox" or call `cerefox_get_help` to retrieve this content over MCP.\n\n## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata`, `author` |\n| `cerefox_get_document` | Get full document by ID | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find docs by metadata (no text query) | `metadata_filter` (required), `include_content`, `updated_since` |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc\'s project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.\n\n## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> modify ->\ningest(title="Same Title", content="...", document_id="abc123", author="my-agent")\n```\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true, author="my-agent")\n```\n\n## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```\n\n## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …). The legacy Python `uv run cerefox` is now a frozen husk as of v0.9 — only `uv run cerefox mcp` still works.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` |\n| `cerefox_set_document_projects` | _MCP-only; a CLI command will be added in a future release. Until then, run via MCP if available._ |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
25752
25752
  var init_get_help_content = __esm(() => {
25753
25753
  HELP_SECTIONS = {
25754
25754
  Tools: "## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata`, `author` |\n| `cerefox_get_document` | Get full document by ID | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find docs by metadata (no text query) | `metadata_filter` (required), `include_content`, `updated_since` |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc's project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |",
@@ -25756,7 +25756,7 @@ var init_get_help_content = __esm(() => {
25756
25756
  "Update Workflow (ID-based -- preferred)": '## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> modify ->\ningest(title="Same Title", content="...", document_id="abc123", author="my-agent")\n```',
25757
25757
  "Update Workflow (title-based -- fallback)": '## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true, author="my-agent")\n```',
25758
25758
  "Catch-Up Workflow": '## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```',
25759
- "CLI fallback (when MCP is unavailable)": '## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …). The legacy Python `uv run cerefox` is now a frozen husk as of v0.9 — only `uv run cerefox mcp` still works.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); short forms (`--filter`, `--project`, `--count`, `--update`, `--version`) work as aliases.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` |\n| `cerefox_set_document_projects` | _MCP-only; a CLI command will be added in a future release. Until then, run via MCP if available._ |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.'
25759
+ "CLI fallback (when MCP is unavailable)": '## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …). The legacy Python `uv run cerefox` is now a frozen husk as of v0.9 — only `uv run cerefox mcp` still works.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` |\n| `cerefox_set_document_projects` | _MCP-only; a CLI command will be added in a future release. Until then, run via MCP if available._ |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.'
25760
25760
  };
25761
25761
  HELP_SECTION_HEADINGS = ["Tools", "Essential Rules", "Update Workflow (ID-based -- preferred)", "Update Workflow (title-based -- fallback)", "Catch-Up Workflow", "CLI fallback (when MCP is unavailable)"];
25762
25762
  });
@@ -40005,106 +40005,173 @@ init_cli_core();
40005
40005
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
40006
40006
  import { homedir as homedir3 } from "node:os";
40007
40007
  import { join as join3 } from "node:path";
40008
- function collectCompletionInfo() {
40008
+ function isHidden(cmd) {
40009
+ return Boolean(cmd._hidden);
40010
+ }
40011
+ function visibleSubs(cmd) {
40012
+ return cmd.commands.filter((c2) => c2.name() !== "help" && !isHidden(c2));
40013
+ }
40014
+ function longFlags(cmd) {
40015
+ const flags = [];
40016
+ for (const opt of cmd.options) {
40017
+ if (opt.long)
40018
+ flags.push(opt.long);
40019
+ }
40020
+ return flags;
40021
+ }
40022
+ function collectNodes() {
40009
40023
  const program2 = buildProgram();
40010
- const subcommands = [];
40011
- for (const cmd of program2.commands) {
40012
- if (cmd.name() === "help")
40013
- continue;
40014
- const flags = [];
40015
- for (const opt of cmd.options) {
40016
- const long = opt.long;
40017
- if (long)
40018
- flags.push(long);
40019
- }
40020
- flags.push("--help");
40021
- subcommands.push({ name: cmd.name(), flags });
40022
- }
40023
- subcommands.sort((a, b) => a.name.localeCompare(b.name));
40024
- return { subcommands };
40025
- }
40026
- function bashScript(info2) {
40027
- const cmdNames = info2.subcommands.map((s) => s.name).join(" ");
40028
- const cases = info2.subcommands.map((s) => ` ${s.name})
40029
- opts="${s.flags.join(" ")}"
40030
- ;;`).join(`
40024
+ const nodes = [];
40025
+ const walk = (cmd, path) => {
40026
+ const subs = visibleSubs(cmd);
40027
+ const candidates = [...subs.map((s) => s.name()), ...longFlags(cmd), "--help"];
40028
+ nodes.push({ path, candidates });
40029
+ for (const s of subs) {
40030
+ walk(s, path === "" ? s.name() : `${path} ${s.name()}`);
40031
+ }
40032
+ };
40033
+ walk(program2, "");
40034
+ nodes.sort((a, b) => a.path.localeCompare(b.path));
40035
+ return nodes;
40036
+ }
40037
+ function bashScript(nodes) {
40038
+ const candCases = nodes.map((n) => ` "${n.path}") echo "${n.candidates.join(" ")}" ;;`).join(`
40031
40039
  `);
40040
+ const pathPatterns = nodes.filter((n) => n.path !== "").map((n) => `"${n.path}"`).join("|");
40032
40041
  return `# Cerefox bash completion. Source from ~/.bashrc:
40033
40042
  # source <(cerefox completion bash)
40034
40043
  #
40044
+ _cerefox_candidates() {
40045
+ case "$1" in
40046
+ ${candCases}
40047
+ *) echo "--help" ;;
40048
+ esac
40049
+ }
40050
+ _cerefox_is_path() {
40051
+ case "$1" in
40052
+ ${pathPatterns}) return 0 ;;
40053
+ *) return 1 ;;
40054
+ esac
40055
+ }
40035
40056
  _cerefox_completion() {
40036
- local cur prev cmd opts
40057
+ local cur path trial w i
40037
40058
  COMPREPLY=()
40038
40059
  cur="\${COMP_WORDS[COMP_CWORD]}"
40039
- cmd="\${COMP_WORDS[1]}"
40040
- if [ "\${COMP_CWORD}" -eq 1 ]; then
40041
- opts="${cmdNames}"
40042
- COMPREPLY=( $(compgen -W "\${opts}" -- "\${cur}") )
40043
- return 0
40044
- fi
40045
- case "\${cmd}" in
40046
- ${cases}
40047
- *)
40048
- opts="--help"
40049
- ;;
40050
- esac
40051
- COMPREPLY=( $(compgen -W "\${opts}" -- "\${cur}") )
40060
+ path=""
40061
+ i=1
40062
+ while [ "$i" -lt "\${COMP_CWORD}" ]; do
40063
+ w="\${COMP_WORDS[$i]}"
40064
+ case "$w" in -*) break ;; esac
40065
+ if [ -z "$path" ]; then trial="$w"; else trial="$path $w"; fi
40066
+ if _cerefox_is_path "$trial"; then
40067
+ path="$trial"; i=$((i + 1))
40068
+ else
40069
+ break
40070
+ fi
40071
+ done
40072
+ COMPREPLY=( $(compgen -W "$(_cerefox_candidates "$path")" -- "$cur") )
40052
40073
  return 0
40053
40074
  }
40054
40075
  complete -F _cerefox_completion cerefox
40055
40076
  `;
40056
40077
  }
40057
- function zshScript(info2) {
40058
- const cmdNames = info2.subcommands.map((s) => `'${s.name}'`).join(" ");
40059
- const cases = info2.subcommands.map((s) => {
40060
- const flagList = s.flags.map((f) => `'${f}'`).join(" ");
40061
- return ` ${s.name}) flags=(${flagList}) ;;`;
40062
- }).join(`
40078
+ function zshScript(nodes) {
40079
+ const candCases = nodes.map((n) => ` "${n.path}") REPLY="${n.candidates.join(" ")}" ;;`).join(`
40063
40080
  `);
40081
+ const pathPatterns = nodes.filter((n) => n.path !== "").map((n) => `"${n.path}"`).join("|");
40064
40082
  return `#compdef cerefox
40065
40083
  # Cerefox zsh completion. Save and source from ~/.zshrc:
40066
40084
  # source <(cerefox completion zsh)
40067
40085
  #
40068
- _cerefox() {
40069
- local -a cmds flags
40070
- cmds=(${cmdNames})
40071
- if (( CURRENT == 2 )); then
40072
- _values 'cerefox subcommand' $cmds
40073
- return
40074
- fi
40075
- case "$words[2]" in
40076
- ${cases}
40077
- *) flags=() ;;
40086
+ _cerefox_candidates() {
40087
+ case "$1" in
40088
+ ${candCases}
40089
+ *) REPLY="--help" ;;
40090
+ esac
40091
+ }
40092
+ _cerefox_is_path() {
40093
+ case "$1" in
40094
+ ${pathPatterns}) return 0 ;;
40095
+ *) return 1 ;;
40078
40096
  esac
40079
- _values 'flag' $flags
40097
+ }
40098
+ _cerefox() {
40099
+ local path trial w i REPLY
40100
+ path=""
40101
+ i=2
40102
+ while (( i < CURRENT )); do
40103
+ w="\${words[i]}"
40104
+ case "$w" in -*) break ;; esac
40105
+ if [[ -z "$path" ]]; then trial="$w"; else trial="$path $w"; fi
40106
+ if _cerefox_is_path "$trial"; then
40107
+ path="$trial"; (( i++ ))
40108
+ else
40109
+ break
40110
+ fi
40111
+ done
40112
+ _cerefox_candidates "$path"
40113
+ compadd -- \${=REPLY}
40080
40114
  }
40081
40115
  compdef _cerefox cerefox
40082
40116
  `;
40083
40117
  }
40084
- function fishScript(info2) {
40085
- const lines = [
40086
- "# Cerefox fish completion. Save to ~/.config/fish/completions/cerefox.fish",
40087
- ""
40088
- ];
40089
- for (const sub of info2.subcommands) {
40090
- lines.push(`complete -c cerefox -n '__fish_use_subcommand' -a '${sub.name}'`);
40091
- for (const flag of sub.flags) {
40092
- lines.push(`complete -c cerefox -n '__fish_seen_subcommand_from ${sub.name}' -l '${flag.replace(/^--/, "")}'`);
40093
- }
40094
- }
40095
- return lines.join(`
40096
- `) + `
40118
+ function fishScript(nodes) {
40119
+ const candCases = nodes.map((n) => ` case "${n.path}"
40120
+ echo "${n.candidates.join(" ")}"`).join(`
40121
+ `);
40122
+ const pathList = nodes.filter((n) => n.path !== "").map((n) => `"${n.path}"`).join(" ");
40123
+ return `# Cerefox fish completion. Save to ~/.config/fish/completions/cerefox.fish
40124
+ function __cerefox_candidates
40125
+ switch "$argv[1]"
40126
+ ${candCases}
40127
+ case '*'
40128
+ echo "--help"
40129
+ end
40130
+ end
40131
+ function __cerefox_is_path
40132
+ for p in ${pathList}
40133
+ if test "$argv[1]" = "$p"
40134
+ return 0
40135
+ end
40136
+ end
40137
+ return 1
40138
+ end
40139
+ function __cerefox_complete
40140
+ set -l tokens (commandline -opc)
40141
+ set -l path ""
40142
+ set -l i 2
40143
+ while test $i -le (count $tokens)
40144
+ set -l w $tokens[$i]
40145
+ if string match -q -- '-*' $w
40146
+ break
40147
+ end
40148
+ set -l trial
40149
+ if test -z "$path"
40150
+ set trial $w
40151
+ else
40152
+ set trial "$path $w"
40153
+ end
40154
+ if __cerefox_is_path "$trial"
40155
+ set path "$trial"
40156
+ set i (math $i + 1)
40157
+ else
40158
+ break
40159
+ end
40160
+ end
40161
+ string split ' ' -- (__cerefox_candidates "$path")
40162
+ end
40163
+ complete -c cerefox -f -a '(__cerefox_complete)'
40097
40164
  `;
40098
40165
  }
40099
40166
  function scriptFor(shell) {
40100
- const info2 = collectCompletionInfo();
40167
+ const nodes = collectNodes();
40101
40168
  switch (shell) {
40102
40169
  case "bash":
40103
- return bashScript(info2);
40170
+ return bashScript(nodes);
40104
40171
  case "zsh":
40105
- return zshScript(info2);
40172
+ return zshScript(nodes);
40106
40173
  case "fish":
40107
- return fishScript(info2);
40174
+ return fishScript(nodes);
40108
40175
  }
40109
40176
  }
40110
40177
  function detectShell() {
@@ -43118,13 +43185,13 @@ var restoreCursor = terminal ? onetime_default(() => {
43118
43185
  var restore_cursor_default = restoreCursor;
43119
43186
 
43120
43187
  // ../../node_modules/.bun/cli-cursor@5.0.0/node_modules/cli-cursor/index.js
43121
- var isHidden = false;
43188
+ var isHidden2 = false;
43122
43189
  var cliCursor = {};
43123
43190
  cliCursor.show = (writableStream = process5.stderr) => {
43124
43191
  if (!writableStream.isTTY) {
43125
43192
  return;
43126
43193
  }
43127
- isHidden = false;
43194
+ isHidden2 = false;
43128
43195
  writableStream.write("\x1B[?25h");
43129
43196
  };
43130
43197
  cliCursor.hide = (writableStream = process5.stderr) => {
@@ -43132,14 +43199,14 @@ cliCursor.hide = (writableStream = process5.stderr) => {
43132
43199
  return;
43133
43200
  }
43134
43201
  restore_cursor_default();
43135
- isHidden = true;
43202
+ isHidden2 = true;
43136
43203
  writableStream.write("\x1B[?25l");
43137
43204
  };
43138
43205
  cliCursor.toggle = (force, writableStream) => {
43139
43206
  if (force !== undefined) {
43140
- isHidden = force;
43207
+ isHidden2 = force;
43141
43208
  }
43142
- if (isHidden) {
43209
+ if (isHidden2) {
43143
43210
  cliCursor.show(writableStream);
43144
43211
  } else {
43145
43212
  cliCursor.hide(writableStream);
@@ -45716,7 +45783,7 @@ import { homedir as homedir5 } from "node:os";
45716
45783
  import { join as join8 } from "node:path";
45717
45784
 
45718
45785
  // ../../_shared/ef-meta/index.ts
45719
- var EF_VERSION = "0.9.1";
45786
+ var EF_VERSION = "0.9.3";
45720
45787
 
45721
45788
  // src/cli/util/checks.ts
45722
45789
  init_config();