@cerefox/memory 1.13.1 → 1.14.0
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 +14 -14
- package/AGENT_QUICK_REFERENCE.md +11 -11
- package/dist/bin/cerefox.js +56 -29
- package/dist/frontend/assets/index-CsHgn4Tx.js +121 -0
- package/dist/frontend/assets/index-CsHgn4Tx.js.map +1 -0
- package/dist/frontend/index.html +1 -1
- package/dist/server-assets/_shared/ef-meta/index.ts +3 -3
- package/dist/server-assets/_shared/mcp-tools/audit-log.ts +7 -5
- package/dist/server-assets/_shared/mcp-tools/get-help-content.ts +3 -3
- package/dist/server-assets/_shared/mcp-tools/identity.ts +21 -0
- package/dist/server-assets/supabase/functions/cerefox-get-audit-log/index.ts +8 -5
- package/dist/server-assets/supabase/functions/cerefox-get-document/index.ts +5 -4
- package/dist/server-assets/supabase/functions/cerefox-ingest/index.ts +8 -3
- package/dist/server-assets/supabase/functions/cerefox-list-projects/index.ts +6 -5
- package/dist/server-assets/supabase/functions/cerefox-list-versions/index.ts +5 -4
- package/dist/server-assets/supabase/functions/cerefox-mcp/index.ts +4 -3
- package/dist/server-assets/supabase/functions/cerefox-metadata/index.ts +5 -4
- package/dist/server-assets/supabase/functions/cerefox-metadata-search/index.ts +5 -4
- package/dist/server-assets/supabase/functions/cerefox-search/index.ts +7 -4
- package/docs/guides/access-paths.md +6 -0
- package/docs/guides/cli.md +18 -18
- package/docs/guides/configuration.md +6 -2
- package/docs/guides/connect-agents.md +42 -26
- package/package.json +1 -1
- package/dist/frontend/assets/index-InRztcXr.js +0 -121
- package/dist/frontend/assets/index-InRztcXr.js.map +0 -1
package/AGENT_GUIDE.md
CHANGED
|
@@ -602,17 +602,17 @@ The Python implementation was fully removed at v1.0.0; every command is the Type
|
|
|
602
602
|
|
|
603
603
|
| MCP tool | CLI command |
|
|
604
604
|
|---|---|
|
|
605
|
-
| `cerefox_search(query, match_count, project_name, metadata_filter, author)` | `cerefox search "<query>" --match-count N --project-name <n> --metadata-filter '<json>' --
|
|
605
|
+
| `cerefox_search(query, match_count, project_name, metadata_filter, author)` | `cerefox search "<query>" --match-count N --project-name <n> --metadata-filter '<json>' --author <name>` (also `--mode`, `--alpha`, `--min-score`, `--only-metadata` — CLI-only) |
|
|
606
606
|
| `cerefox_ingest(title, content, project_name, metadata, update_if_exists, document_id, expected_content_hash, last_write_wins, source, author, author_type)` (file) | `cerefox document ingest <path> --title <t> --project-name <n> --metadata '<json>' --update-if-exists\|--document-id <uuid> --expected-content-hash <hash>\|--last-write-wins --source <s> --author <a> --author-type user\|agent` |
|
|
607
607
|
| `cerefox_ingest(...)` (paste) | `printf '%s' "<content>" \| cerefox document ingest --paste --title "<title>"` (same flags) |
|
|
608
|
-
| `cerefox_get_document(document_id, version_id, author)` | `cerefox document get <document-id> --version-id <vid> --
|
|
609
|
-
| `cerefox_list_versions(document_id, author)` | `cerefox document version list <document-id> --
|
|
610
|
-
| `cerefox_list_projects(author)` | `cerefox project list --
|
|
608
|
+
| `cerefox_get_document(document_id, version_id, author)` | `cerefox document get <document-id> --version-id <vid> --author <name>` |
|
|
609
|
+
| `cerefox_list_versions(document_id, author)` | `cerefox document version list <document-id> --author <name>` |
|
|
610
|
+
| `cerefox_list_projects(author)` | `cerefox project list --author <name>` |
|
|
611
611
|
| `cerefox_set_document_metadata(document_id, metadata, replace, author)` | `cerefox document set-metadata <document-id> --set key=value` (also `--remove key`, `--json '{...}'`, `--replace`) |
|
|
612
612
|
| `cerefox_set_document_projects(document_id, project_names, author)` | `cerefox document set-projects <document-id> <name...> --author <a> --author-type user\|agent` (or `--clear` to remove all) |
|
|
613
613
|
| `cerefox_list_metadata_keys()` | `cerefox metadata keys` |
|
|
614
|
-
| `cerefox_metadata_search(metadata_filter, project_name, updated_since, created_since, limit, include_content, author)` | `cerefox metadata search --metadata-filter '<json>' --project-name <n> --updated-since <iso> --created-since <iso> --limit N --include-content --
|
|
615
|
-
| `cerefox_get_audit_log(document_id, by_author, operation, since, until, limit, author)` | `cerefox audit list --document-id <id> --author <a> --operation <op> --since <iso> --until <iso> --limit N --json --
|
|
614
|
+
| `cerefox_metadata_search(metadata_filter, project_name, updated_since, created_since, limit, include_content, author)` | `cerefox metadata search --metadata-filter '<json>' --project-name <n> --updated-since <iso> --created-since <iso> --limit N --include-content --author <name>` |
|
|
615
|
+
| `cerefox_get_audit_log(document_id, by_author, operation, since, until, limit, author)` | `cerefox audit list --document-id <id> --by-author <a> --operation <op> --since <iso> --until <iso> --limit N --json --author <name>` |
|
|
616
616
|
| `cerefox_delete_document(document_id, expected_content_hash, reason, author)` | `cerefox document delete <document-id> --reason <text> --author <a> --author-type user\|agent --yes` (the CLI confirms interactively instead of requiring the hash) |
|
|
617
617
|
| `cerefox_restore_document(document_id, reason, author)` | `cerefox document restore <document-id> --reason <text> --author <a> --author-type user\|agent` |
|
|
618
618
|
|
|
@@ -623,7 +623,7 @@ The Python implementation was fully removed at v1.0.0; every command is the Type
|
|
|
623
623
|
You **MUST** identify yourself on every CLI invocation, exactly as you do via MCP:
|
|
624
624
|
|
|
625
625
|
- **Writes** (`document ingest`, `document ingest-dir`): set `--author "<your-agent-name>" --author-type "agent"`. The `author_type=agent` value marks the write `pending_review` (governance signal), matching the MCP path; the store's review-workflow flag only decides whether anyone sees that status. Attribution is recorded either way.
|
|
626
|
-
- **Reads** (`search`, `document get`, `document version list`, `project list`, `metadata search`, `audit list`): set `--
|
|
626
|
+
- **Reads** (`search`, `document get`, `document version list`, `project list`, `metadata search`, `audit list`): set `--author "<your-agent-name>"`, the same flag as on writes (v1.13.2; `--requestor` still works as a hidden alias).
|
|
627
627
|
|
|
628
628
|
Alternative: have your user set `CEREFOX_AUTHOR_NAME`, `CEREFOX_AUTHOR_TYPE`, `CEREFOX_REQUESTOR_NAME` in their `.env` once. The CLI picks them up automatically — see [`docs/guides/cli.md`](docs/guides/cli.md) for the precedence rules.
|
|
629
629
|
|
|
@@ -631,7 +631,7 @@ Alternative: have your user set `CEREFOX_AUTHOR_NAME`, `CEREFOX_AUTHOR_TYPE`, `C
|
|
|
631
631
|
|
|
632
632
|
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.
|
|
633
633
|
|
|
634
|
-
2. **Every invocation is independent.** With MCP, your tool framework can pass `
|
|
634
|
+
2. **Every invocation is independent.** With MCP, your tool framework can pass `author` once per session. With the CLI, every command is a separate process — pass `--author` / `--author-type` on every relevant invocation, or set the env-var defaults once at the start.
|
|
635
635
|
|
|
636
636
|
3. **Errors come back on stderr with a non-zero exit code.** Check both — a successful command prints results on stdout and exits 0; a failure prints to stderr and exits non-zero.
|
|
637
637
|
|
|
@@ -639,14 +639,14 @@ Alternative: have your user set `CEREFOX_AUTHOR_NAME`, `CEREFOX_AUTHOR_TYPE`, `C
|
|
|
639
639
|
|
|
640
640
|
**Search before answering:**
|
|
641
641
|
```bash
|
|
642
|
-
cerefox search "OAuth design notes" --match-count 5 --
|
|
642
|
+
cerefox search "OAuth design notes" --match-count 5 --author "claude-code"
|
|
643
643
|
```
|
|
644
644
|
|
|
645
645
|
**Search then read full content of a hit:**
|
|
646
646
|
```bash
|
|
647
|
-
cerefox search "OAuth design" --match-count 3 --
|
|
647
|
+
cerefox search "OAuth design" --match-count 3 --author "claude-code"
|
|
648
648
|
# Note the [n] entries. Pick one and grab the doc id from `cerefox document list` or the result preview.
|
|
649
|
-
cerefox document get <document-id> --
|
|
649
|
+
cerefox document get <document-id> --author "claude-code"
|
|
650
650
|
```
|
|
651
651
|
|
|
652
652
|
**Ingest a note (agent identity):**
|
|
@@ -662,10 +662,10 @@ printf '# Title\n\nBody markdown with H2s for chunking.\n' \
|
|
|
662
662
|
**ID-based update (preferred — deterministic):**
|
|
663
663
|
```bash
|
|
664
664
|
# Step 1: search and note the [id: abc12345-...] in the result
|
|
665
|
-
cerefox search "the exact doc" --match-count 1 --
|
|
665
|
+
cerefox search "the exact doc" --match-count 1 --author "claude-code"
|
|
666
666
|
|
|
667
667
|
# Step 2: read it — the header shows `content_hash:` (the concurrency token)
|
|
668
|
-
cerefox document get "abc12345-..." --
|
|
668
|
+
cerefox document get "abc12345-..." --author "claude-code"
|
|
669
669
|
|
|
670
670
|
# Step 3: update by ID, proving freshness with the hash from step 2
|
|
671
671
|
printf '...new content...' \
|
|
@@ -687,7 +687,7 @@ printf '...new content...' \
|
|
|
687
687
|
|
|
688
688
|
**Audit-log access (scripted, JSON):**
|
|
689
689
|
```bash
|
|
690
|
-
cerefox audit list --json --limit 1000 --
|
|
690
|
+
cerefox audit list --json --limit 1000 --author "claude-code" \
|
|
691
691
|
| jq 'select(.author_type == "agent")'
|
|
692
692
|
```
|
|
693
693
|
|
package/AGENT_QUICK_REFERENCE.md
CHANGED
|
@@ -74,7 +74,7 @@ recoverable answer, not a failure: retry with what it gave you.
|
|
|
74
74
|
|
|
75
75
|
1. **Search before ingesting** -- check if the document exists first.
|
|
76
76
|
2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.
|
|
77
|
-
3. **Set `author`** to your name on every call, reads and writes alike (e.g., "Claude Code", "archiver"). Same parameter on every tool. (`requestor` is still accepted everywhere as the pre-1.13.1 alias.) On CLI
|
|
77
|
+
3. **Set `author`** to your name on every call, reads and writes alike (e.g., "Claude Code", "archiver"). Same parameter on every tool. (`requestor` is still accepted everywhere as the pre-1.13.1 alias.) On the CLI it is `--author` on every command too (plus `--author-type` on writes); or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE` env vars set in the user's `.env`.
|
|
78
78
|
4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.
|
|
79
79
|
5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").
|
|
80
80
|
6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.
|
|
@@ -113,30 +113,30 @@ metadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03
|
|
|
113
113
|
|
|
114
114
|
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`, …).
|
|
115
115
|
|
|
116
|
-
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
|
|
116
|
+
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`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.
|
|
117
117
|
|
|
118
118
|
| MCP tool | CLI |
|
|
119
119
|
|---|---|
|
|
120
|
-
| `cerefox_search` | `cerefox search "<q>" --
|
|
120
|
+
| `cerefox_search` | `cerefox search "<q>" --author "<your-name>"` |
|
|
121
121
|
| `cerefox_ingest` (paste) | `printf '...' \| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |
|
|
122
122
|
| `cerefox_ingest` (update by ID) | `printf '...' \| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |
|
|
123
|
-
| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --
|
|
124
|
-
| `cerefox_list_versions` | `cerefox document version list <id> --
|
|
125
|
-
| `cerefox_list_projects` | `cerefox project list --
|
|
123
|
+
| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --author "<your-name>"` |
|
|
124
|
+
| `cerefox_list_versions` | `cerefox document version list <id> --author "<your-name>"` |
|
|
125
|
+
| `cerefox_list_projects` | `cerefox project list --author "<your-name>"` |
|
|
126
126
|
| `cerefox_list_metadata_keys` | `cerefox metadata keys` |
|
|
127
|
-
| `cerefox_insert` | `cerefox document insert <id> -t "<text>" -p <position> -a "<anchor-heading>" -e "<hash>" --
|
|
128
|
-
| `cerefox_edit` | `cerefox document edit-parts <id> --operations '<json>' -e "<hash>" --
|
|
127
|
+
| `cerefox_insert` | `cerefox document insert <id> -t "<text>" -p <position> -a "<anchor-heading>" -e "<hash>" --author "<your-name>" --author-type agent` |
|
|
128
|
+
| `cerefox_edit` | `cerefox document edit-parts <id> --operations '<json>' -e "<hash>" --author "<your-name>" --author-type agent` |
|
|
129
129
|
| `cerefox_delete_document` | `cerefox document delete <id> --reason "<why>" --author "<your-name>" --author-type agent --yes` (confirms interactively instead of requiring the hash) |
|
|
130
130
|
| `cerefox_restore_document` | `cerefox document restore <id> --reason "<why>" --author "<your-name>" --author-type agent` |
|
|
131
|
-
| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter '<json>' --
|
|
131
|
+
| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter '<json>' --author "<your-name>"` (list a project: `cerefox document list --project <name>`) |
|
|
132
132
|
| `cerefox_set_document_metadata` | `cerefox document set-metadata <id> --set key=value` (also `--remove key`, `--json '{...}'`, `--replace`) |
|
|
133
133
|
| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |
|
|
134
|
-
| `cerefox_get_audit_log` | `cerefox audit list --
|
|
134
|
+
| `cerefox_get_audit_log` | `cerefox audit list --author "<your-name>"` (add `--json` for scripted access) |
|
|
135
135
|
| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |
|
|
136
136
|
|
|
137
137
|
**Set identity on every call**, exactly as you would on MCP:
|
|
138
138
|
- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`
|
|
139
|
-
- Reads: `--
|
|
139
|
+
- Reads: `--author "<your-name>"` (the same flag; `--requestor` still works as a hidden alias)
|
|
140
140
|
|
|
141
141
|
Or have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.
|
|
142
142
|
|
package/dist/bin/cerefox.js
CHANGED
|
@@ -7400,7 +7400,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
7400
7400
|
});
|
|
7401
7401
|
|
|
7402
7402
|
// src/meta.ts
|
|
7403
|
-
var PKG_VERSION = "1.
|
|
7403
|
+
var PKG_VERSION = "1.14.0";
|
|
7404
7404
|
var init_meta = () => {};
|
|
7405
7405
|
|
|
7406
7406
|
// ../../_shared/config/paths.ts
|
|
@@ -25774,7 +25774,7 @@ var init_bundled_docs = __esm(() => {
|
|
|
25774
25774
|
});
|
|
25775
25775
|
|
|
25776
25776
|
// ../../_shared/ef-meta/index.ts
|
|
25777
|
-
var EF_VERSION = "1.
|
|
25777
|
+
var EF_VERSION = "1.14.0", CEREFOX_VERSION = "1.14.0", EF_LAST_CHANGED = "1.14.0";
|
|
25778
25778
|
var init_ef_meta = () => {};
|
|
25779
25779
|
|
|
25780
25780
|
// ../../_shared/compatibility/index.ts
|
|
@@ -54755,6 +54755,15 @@ function callerIdentity(args) {
|
|
|
54755
54755
|
}
|
|
54756
54756
|
return;
|
|
54757
54757
|
}
|
|
54758
|
+
function auditLogIdentity(args) {
|
|
54759
|
+
const str = (v) => typeof v === "string" && v.trim() !== "" ? v : undefined;
|
|
54760
|
+
const requestor = str(args.requestor);
|
|
54761
|
+
const byAuthor = str(args.by_author);
|
|
54762
|
+
if (requestor !== undefined && byAuthor === undefined) {
|
|
54763
|
+
return { identity: requestor, byAuthor: str(args.author) };
|
|
54764
|
+
}
|
|
54765
|
+
return { identity: callerIdentity(args), byAuthor };
|
|
54766
|
+
}
|
|
54758
54767
|
var DEFAULT_IDENTITY = "mcp-agent", EXAMPLE = 'e.g. "Claude Code", "archiver"', AUTHOR_PARAM_WRITE, AUTHOR_PARAM_READ;
|
|
54759
54768
|
var init_identity = __esm(() => {
|
|
54760
54769
|
AUTHOR_PARAM_WRITE = {
|
|
@@ -54773,11 +54782,12 @@ function utcStamp2(iso) {
|
|
|
54773
54782
|
return trimmed.includes("T") ? `${trimmed}Z` : `${trimmed} UTC`;
|
|
54774
54783
|
}
|
|
54775
54784
|
async function handler(supabase, args, ctx) {
|
|
54785
|
+
const { identity, byAuthor } = auditLogIdentity(args);
|
|
54776
54786
|
const params = {};
|
|
54777
54787
|
if (args.document_id)
|
|
54778
54788
|
params.p_document_id = args.document_id;
|
|
54779
|
-
if (
|
|
54780
|
-
params.p_author =
|
|
54789
|
+
if (byAuthor)
|
|
54790
|
+
params.p_author = byAuthor;
|
|
54781
54791
|
if (args.operation)
|
|
54782
54792
|
params.p_operation = args.operation;
|
|
54783
54793
|
if (args.since)
|
|
@@ -54793,7 +54803,7 @@ async function handler(supabase, args, ctx) {
|
|
|
54793
54803
|
logUsage(supabase, {
|
|
54794
54804
|
operation: "get_audit_log",
|
|
54795
54805
|
accessPath: ctx.accessPath,
|
|
54796
|
-
requestor:
|
|
54806
|
+
requestor: identity,
|
|
54797
54807
|
result_count: entries.length
|
|
54798
54808
|
});
|
|
54799
54809
|
if (!entries.length)
|
|
@@ -55751,12 +55761,12 @@ var init_partial_edits2 = __esm(() => {
|
|
|
55751
55761
|
});
|
|
55752
55762
|
|
|
55753
55763
|
// ../../_shared/mcp-tools/get-help-content.ts
|
|
55754
|
-
var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **15 core MCP tools** (14 with CLI equivalents — `cerefox_get_help` is MCP-only), plus 4 dormant relation tools that appear only when `relations_enabled` is on. 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`, `author` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `cerefox_insert` | **Add** to a document without resending it. Cannot destroy content. | `document_id`, `text`, `position` (`end_of_document`/`end_of_section`/`after_heading`/`before_heading`), `expected_content_hash` (required), `anchor_heading` (unless `end_of_document`), `section_part`, `author` |\n| `cerefox_edit` | **Change** parts of a document: 1..n operations applied atomically | `document_id`, `operations` (`insert`/`replace_section`/`delete_section`/`rename_section`), `expected_content_hash` (required), `author` |\n| `cerefox_delete_document` | **Soft**-delete a document (to trash; excluded from search; permanent purge is human-only) | `document_id`, `expected_content_hash` (**required** — a delete must follow a read), `reason` (recorded in the audit log — give one), `author` |\n| `cerefox_restore_document` | Restore a soft-deleted document from the trash (audited inverse of delete; no-op if not deleted) | `document_id` (required), `reason` (recorded in the audit log), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token), or with `outline: true` just its heading paths, sizes and hash, or with `section: "## Heading"` one section\'s text | `document_id` (required), `outline`, `section`, `section_part`, `author` |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required), `author` |\n| `cerefox_set_relation` ⚑ | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` ⚑ | Remove a relation | `source_id`, `target_id`, `rel_type`, `author` |\n| `cerefox_get_relations` ⚑ | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` ⚑ | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project\'s docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_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_metadata` | Change tags WITHOUT resending content. **Merges** by default; a `null` value removes a key | `document_id`, `metadata` (required), `replace` (rare: set exactly this object), `author` |\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), `author` |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `by_author` (filter), `operation`, `since`, `author` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n⚑ **Opt-in — usually absent.** The four relation tools are hidden unless the\noperator enables them (`relations_enabled`). **Trust your own tool list**: if\nthey are not in it, the feature is switched off for this deployment. That is\nnormal, not an error, and not something to work around.\n\n## Editing part of a document (prefer this over re-sending)\n\n**Re-sending a whole document to change part of it is the main way agents lose\ndata.** You have to reproduce the untouched remainder verbatim, and any drift\nsilently rewrites content nobody asked you to touch — which the caller cannot\ndiff. Use the partial-edit tools instead:\n\n1. **Learn the anchors** — `cerefox_get_document(document_id, outline: true)`.\n Returns heading paths, per-section sizes and the `content_hash`, without the\n body. The paths it returns are exactly what `anchor_heading` accepts.\n2. **Add** → `cerefox_insert`. `end_of_document` is a plain append;\n `end_of_section` adds inside a named section. It is structurally incapable of\n removing anything, so "I meant to append" cannot become "I replaced the file".\n3. **Look before you overwrite** — `cerefox_get_document(document_id,\n section: "## Heading")` returns exactly the text a `replace_section` on that\n anchor would destroy. The outline gives you a section\'s *size*, never its\n *text*, so on a document you did not write yourself this is the difference\n between a replace and a blind overwrite.\n4. **Change or remove** → `cerefox_edit`. Put changes that belong together in\n ONE call: they apply atomically, so a table row and the total it feeds cannot\n end up disagreeing. To change a single line, `replace_section` on its\n smallest enclosing heading — that is the intended granularity, not a\n workaround. To fix a stale heading (`## OPEN TODOs (as of ...)`), use\n `rename_section`: it changes the heading text and leaves the body and\n position alone.\n5. All of them require `expected_content_hash` and **have no last-write-wins**. A\n conflict means someone else changed the document; re-read and decide, do not\n force it.\n\n**A section runs to the next same-or-higher heading, or to the end of the\ndocument.** So `end_of_document` inserts land inside the *last* section, and\nreplacing or deleting that section removes them too. A large shrink in the\nresponse is your warning; `cerefox_list_versions` has the previous content.\n\n**When an anchor is ambiguous the tool refuses and hands you the options** — a\nrepeated heading returns the qualifying paths, and a section with both its own\ncontent and sub-sections returns both `section_part` choices. That is a\nrecoverable answer, not a failure: retry with what it gave you.\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`** to your name on every call, reads and writes alike (e.g., "Claude Code", "archiver"). Same parameter on every tool. (`requestor` is still accepted everywhere as the pre-1.13.1 alias.) On CLI, pass `--author`/`--author-type` on writes and `--requestor` on reads, 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); permanent purge is web-UI-only.** `cerefox_delete_document` requires the document\'s `content_hash` as you read it (read before you delete) and takes a `reason` — give one; it is what the human reviewing the trash sees. `cerefox_restore_document` undoes a mistaken delete (also audited, also takes a `reason`). Always surface deletes AND restores to the user. Once a human purges from the web UI, the document is gone for good.\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. **The server validates `](uuid)` links on every write** (v1.7.0): a link to a nonexistent id rejects the write, naming the offender — that means you mangled the UUID; re-read the source and correct it, do not retry unchanged. Example ids go in backticks (code is not validated). `[[Wikilinks]]` may dangle. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you last saw — every read shows one (`cerefox_get_document` incl. outline mode, `cerefox_search`, `cerefox_metadata_search`) and **every write returns the new one, including create** (v1.3.0, #189), so after writing you already hold the token for your next edit; no re-read needed. If it\'s stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer\'s work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.\n10. **Search: prefer a few distinctive terms; heed `below confidence`.** When nothing clears the relevance threshold, `cerefox_search` returns the closest candidates prefixed with a `below confidence` warning instead of an empty set — that flag means **weak signal, not absent knowledge**: check the candidates\' scores and titles before concluding the KB lacks the content. A truly empty response means nothing even weakly related exists.\n11. **Relations express how documents relate; lifecycle tells you if knowledge is still good.** Use `cerefox_set_relation` when one document supersedes, contradicts, references, or continues another. `supersedes` marks the target **superseded**; `contradicts` marks **both** stale; `related_to`/`duplicates`/`contradicts` are symmetric (both directions written). Any other type string is accepted without special behaviour. When a search result or `cerefox_get_relations` shows a neighbour marked `[superseded]` or `[stale]`, say so rather than presenting it as current.\n12. **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) -> note its content_hash -> modify ->\ningest(title="Same Title", content="...", document_id="abc123",\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\nOn a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", 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`, …).\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>" --expected-content-hash "<hash>" --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_insert` | `cerefox document insert <id> -t "<text>" -p <position> -a "<anchor-heading>" -e "<hash>" --requestor "<your-name>" --author-type agent` |\n| `cerefox_edit` | `cerefox document edit-parts <id> --operations \'<json>\' -e "<hash>" --requestor "<your-name>" --author-type agent` |\n| `cerefox_delete_document` | `cerefox document delete <id> --reason "<why>" --author "<your-name>" --author-type agent --yes` (confirms interactively instead of requiring the hash) |\n| `cerefox_restore_document` | `cerefox document restore <id> --reason "<why>" --author "<your-name>" --author-type agent` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_metadata` | `cerefox document set-metadata <id> --set key=value` (also `--remove key`, `--json \'{...}\'`, `--replace`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\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\n## Timestamps are UTC\n\nEvery timestamp Cerefox returns — `created_at` on audit entries, version\nhistory, document metadata — is **UTC**, and now carries its `Z` marker so it\ncannot be mistaken for local time.\n\n**When you write a date into a document\'s CONTENT, use your own clock, not a\nCerefox timestamp.** These are different things: a timestamp records when the\nserver stored something; a date in a log entry or a heading is authored content\nand belongs to your timezone. An agent working a Pacific afternoon read\n`2026-08-11` from version history, wrote "8/11" into its entries, and put a\nday\'s work in the future — the timestamp was correct, and copying it into\ncontent was not.\n\nCerefox deliberately does not convert to local time on the API or MCP paths.\n"Local" has no server-side meaning: the remote MCP server runs in a cloud\nfunction whose local time *is* UTC, while a local MCP server runs in yours, so\nthe same document would report two different times depending on transport. The\nweb UI converts because a browser knows the viewer\'s timezone; nothing\nserver-side does.\n\n## Mistakes that have actually happened\n\nEach of these comes from a real agent session, and each is easy to make.\n\n- **`cerefox_ingest` always replaces the ENTIRE document.** Never a section.\n Before sending, check that the tool name matches the intent: if the intent is\n "change one section", the call is `cerefox_edit` with `replace_section`. A\n section-sized edit sent as a full ingest truncated a 13,000-character index to\n a single word. It was recovered from version history within the minute, but\n only because it was noticed immediately.\n\n- **Do not include the anchor\'s own heading in your text.** `replace_section`\n keeps the heading and `insert` places your text inside the section, so\n including it produces two. This is now refused rather than silently applied,\n but the shape is worth knowing: it happened twice in one session, the second\n time while trying to repair the first. A *deeper* sub-heading inside your text\n is fine.\n\n- **Content between sections belongs to the section ABOVE it.** A section runs\n to the next heading of the same or higher level, so a `---` rule, a note, or\n any trailing text sitting just above the next heading is part of the section\n before it — even when it visually reads as belonging below. Replacing that\n section takes it too. An agent hit exactly this: a `---` that separated two\n major sections disappeared when the section above it was replaced. The write\n was correct by the addressing rules; the surprise is that "the end of this\n section" is further down the page than it looks. Note the loss warning will\n not catch it if your replacement text is longer than what it replaced, since\n there is then no net loss to report.\n\n- **To change only tags, use `cerefox_set_document_metadata`, never `cerefox_ingest`.**\n Ingest replaces the whole document, so re-sending it to set one tag carries the\n full transcription risk for no reason. The metadata tool merges: the keys you\n pass are set, everything else is left alone, so you do not need to read the\n document first and cannot drop a tag another agent set. Pass `null` as a value\n to remove a key.\n\n- **Never partial-edit to fix a partial edit.** If a write leaves unexpected\n structure, stop. Use `cerefox_list_versions`, retrieve the last good version,\n and re-ingest cleanly. Repairing edits with more edits compounds the damage.\n\n- **A rejected batch is safe.** Operations in one `cerefox_edit` are\n all-or-nothing: if any is invalid, nothing is written. A refusal costs you a\n retry, not data — so prefer one call for changes that belong together, and do\n not split a batch to "make it more likely to succeed".\n\n- **Read before replacing.** `cerefox_get_document(section: "## Heading")`\n returns exactly what a `replace_section` on that anchor would overwrite. Use it\n for any section you did not write in this session. The outline gives a\n section\'s *size*, never its *text*.\n\n- **Verify after writing** — read the result back before reporting success, and\n report what the read actually shows.\n\n- **Partial edits cannot change a document\'s stored TITLE.** `rename_section`\n changes a heading inside the content; the title is a separate field and still\n needs `cerefox_ingest`.\n\n- **If a capability seems missing from one server, suspect your client first.**\n Local and remote run the same code. **Every `cerefox_get_help()` response\n begins with the server\'s version and the operations it registers** — you do\n not need a special topic, and the *absence* of that block is itself an answer:\n a server that does not print it predates v1.5.0. If that\n disagrees with your tool list, the client is holding a list it fetched before\n an upgrade — clients cache it at connect time. Ask the user to restart the\n client. Do not record a capability difference between servers as a fact; every\n such report so far has been a stale client.\n- Long inline bodies can arrive with literal `\\n`/`\\"` (the author over-escaped; Cerefox stores bytes faithfully). For long or quote-dense content, ingest from a file or build incrementally with `cerefox_insert`; read back multi-line writes.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
|
|
55764
|
+
var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **15 core MCP tools** (14 with CLI equivalents — `cerefox_get_help` is MCP-only), plus 4 dormant relation tools that appear only when `relations_enabled` is on. 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`, `author` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `cerefox_insert` | **Add** to a document without resending it. Cannot destroy content. | `document_id`, `text`, `position` (`end_of_document`/`end_of_section`/`after_heading`/`before_heading`), `expected_content_hash` (required), `anchor_heading` (unless `end_of_document`), `section_part`, `author` |\n| `cerefox_edit` | **Change** parts of a document: 1..n operations applied atomically | `document_id`, `operations` (`insert`/`replace_section`/`delete_section`/`rename_section`), `expected_content_hash` (required), `author` |\n| `cerefox_delete_document` | **Soft**-delete a document (to trash; excluded from search; permanent purge is human-only) | `document_id`, `expected_content_hash` (**required** — a delete must follow a read), `reason` (recorded in the audit log — give one), `author` |\n| `cerefox_restore_document` | Restore a soft-deleted document from the trash (audited inverse of delete; no-op if not deleted) | `document_id` (required), `reason` (recorded in the audit log), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token), or with `outline: true` just its heading paths, sizes and hash, or with `section: "## Heading"` one section\'s text | `document_id` (required), `outline`, `section`, `section_part`, `author` |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required), `author` |\n| `cerefox_set_relation` ⚑ | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` ⚑ | Remove a relation | `source_id`, `target_id`, `rel_type`, `author` |\n| `cerefox_get_relations` ⚑ | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` ⚑ | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project\'s docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_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_metadata` | Change tags WITHOUT resending content. **Merges** by default; a `null` value removes a key | `document_id`, `metadata` (required), `replace` (rare: set exactly this object), `author` |\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), `author` |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `by_author` (filter), `operation`, `since`, `author` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n⚑ **Opt-in — usually absent.** The four relation tools are hidden unless the\noperator enables them (`relations_enabled`). **Trust your own tool list**: if\nthey are not in it, the feature is switched off for this deployment. That is\nnormal, not an error, and not something to work around.\n\n## Editing part of a document (prefer this over re-sending)\n\n**Re-sending a whole document to change part of it is the main way agents lose\ndata.** You have to reproduce the untouched remainder verbatim, and any drift\nsilently rewrites content nobody asked you to touch — which the caller cannot\ndiff. Use the partial-edit tools instead:\n\n1. **Learn the anchors** — `cerefox_get_document(document_id, outline: true)`.\n Returns heading paths, per-section sizes and the `content_hash`, without the\n body. The paths it returns are exactly what `anchor_heading` accepts.\n2. **Add** → `cerefox_insert`. `end_of_document` is a plain append;\n `end_of_section` adds inside a named section. It is structurally incapable of\n removing anything, so "I meant to append" cannot become "I replaced the file".\n3. **Look before you overwrite** — `cerefox_get_document(document_id,\n section: "## Heading")` returns exactly the text a `replace_section` on that\n anchor would destroy. The outline gives you a section\'s *size*, never its\n *text*, so on a document you did not write yourself this is the difference\n between a replace and a blind overwrite.\n4. **Change or remove** → `cerefox_edit`. Put changes that belong together in\n ONE call: they apply atomically, so a table row and the total it feeds cannot\n end up disagreeing. To change a single line, `replace_section` on its\n smallest enclosing heading — that is the intended granularity, not a\n workaround. To fix a stale heading (`## OPEN TODOs (as of ...)`), use\n `rename_section`: it changes the heading text and leaves the body and\n position alone.\n5. All of them require `expected_content_hash` and **have no last-write-wins**. A\n conflict means someone else changed the document; re-read and decide, do not\n force it.\n\n**A section runs to the next same-or-higher heading, or to the end of the\ndocument.** So `end_of_document` inserts land inside the *last* section, and\nreplacing or deleting that section removes them too. A large shrink in the\nresponse is your warning; `cerefox_list_versions` has the previous content.\n\n**When an anchor is ambiguous the tool refuses and hands you the options** — a\nrepeated heading returns the qualifying paths, and a section with both its own\ncontent and sub-sections returns both `section_part` choices. That is a\nrecoverable answer, not a failure: retry with what it gave you.\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`** to your name on every call, reads and writes alike (e.g., "Claude Code", "archiver"). Same parameter on every tool. (`requestor` is still accepted everywhere as the pre-1.13.1 alias.) On the CLI it is `--author` on every command too (plus `--author-type` on writes); or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE` 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); permanent purge is web-UI-only.** `cerefox_delete_document` requires the document\'s `content_hash` as you read it (read before you delete) and takes a `reason` — give one; it is what the human reviewing the trash sees. `cerefox_restore_document` undoes a mistaken delete (also audited, also takes a `reason`). Always surface deletes AND restores to the user. Once a human purges from the web UI, the document is gone for good.\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. **The server validates `](uuid)` links on every write** (v1.7.0): a link to a nonexistent id rejects the write, naming the offender — that means you mangled the UUID; re-read the source and correct it, do not retry unchanged. Example ids go in backticks (code is not validated). `[[Wikilinks]]` may dangle. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you last saw — every read shows one (`cerefox_get_document` incl. outline mode, `cerefox_search`, `cerefox_metadata_search`) and **every write returns the new one, including create** (v1.3.0, #189), so after writing you already hold the token for your next edit; no re-read needed. If it\'s stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer\'s work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.\n10. **Search: prefer a few distinctive terms; heed `below confidence`.** When nothing clears the relevance threshold, `cerefox_search` returns the closest candidates prefixed with a `below confidence` warning instead of an empty set — that flag means **weak signal, not absent knowledge**: check the candidates\' scores and titles before concluding the KB lacks the content. A truly empty response means nothing even weakly related exists.\n11. **Relations express how documents relate; lifecycle tells you if knowledge is still good.** Use `cerefox_set_relation` when one document supersedes, contradicts, references, or continues another. `supersedes` marks the target **superseded**; `contradicts` marks **both** stale; `related_to`/`duplicates`/`contradicts` are symmetric (both directions written). Any other type string is accepted without special behaviour. When a search result or `cerefox_get_relations` shows a neighbour marked `[superseded]` or `[stale]`, say so rather than presenting it as current.\n12. **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) -> note its content_hash -> modify ->\ningest(title="Same Title", content="...", document_id="abc123",\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\nOn a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", 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`, …).\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`). 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>" --author "<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>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --author "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --author "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --author "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_insert` | `cerefox document insert <id> -t "<text>" -p <position> -a "<anchor-heading>" -e "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_edit` | `cerefox document edit-parts <id> --operations \'<json>\' -e "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_delete_document` | `cerefox document delete <id> --reason "<why>" --author "<your-name>" --author-type agent --yes` (confirms interactively instead of requiring the hash) |\n| `cerefox_restore_document` | `cerefox document restore <id> --reason "<why>" --author "<your-name>" --author-type agent` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --author "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_metadata` | `cerefox document set-metadata <id> --set key=value` (also `--remove key`, `--json \'{...}\'`, `--replace`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --author "<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: `--author "<your-name>"` (the same flag; `--requestor` still works as a hidden alias)\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n\n## Timestamps are UTC\n\nEvery timestamp Cerefox returns — `created_at` on audit entries, version\nhistory, document metadata — is **UTC**, and now carries its `Z` marker so it\ncannot be mistaken for local time.\n\n**When you write a date into a document\'s CONTENT, use your own clock, not a\nCerefox timestamp.** These are different things: a timestamp records when the\nserver stored something; a date in a log entry or a heading is authored content\nand belongs to your timezone. An agent working a Pacific afternoon read\n`2026-08-11` from version history, wrote "8/11" into its entries, and put a\nday\'s work in the future — the timestamp was correct, and copying it into\ncontent was not.\n\nCerefox deliberately does not convert to local time on the API or MCP paths.\n"Local" has no server-side meaning: the remote MCP server runs in a cloud\nfunction whose local time *is* UTC, while a local MCP server runs in yours, so\nthe same document would report two different times depending on transport. The\nweb UI converts because a browser knows the viewer\'s timezone; nothing\nserver-side does.\n\n## Mistakes that have actually happened\n\nEach of these comes from a real agent session, and each is easy to make.\n\n- **`cerefox_ingest` always replaces the ENTIRE document.** Never a section.\n Before sending, check that the tool name matches the intent: if the intent is\n "change one section", the call is `cerefox_edit` with `replace_section`. A\n section-sized edit sent as a full ingest truncated a 13,000-character index to\n a single word. It was recovered from version history within the minute, but\n only because it was noticed immediately.\n\n- **Do not include the anchor\'s own heading in your text.** `replace_section`\n keeps the heading and `insert` places your text inside the section, so\n including it produces two. This is now refused rather than silently applied,\n but the shape is worth knowing: it happened twice in one session, the second\n time while trying to repair the first. A *deeper* sub-heading inside your text\n is fine.\n\n- **Content between sections belongs to the section ABOVE it.** A section runs\n to the next heading of the same or higher level, so a `---` rule, a note, or\n any trailing text sitting just above the next heading is part of the section\n before it — even when it visually reads as belonging below. Replacing that\n section takes it too. An agent hit exactly this: a `---` that separated two\n major sections disappeared when the section above it was replaced. The write\n was correct by the addressing rules; the surprise is that "the end of this\n section" is further down the page than it looks. Note the loss warning will\n not catch it if your replacement text is longer than what it replaced, since\n there is then no net loss to report.\n\n- **To change only tags, use `cerefox_set_document_metadata`, never `cerefox_ingest`.**\n Ingest replaces the whole document, so re-sending it to set one tag carries the\n full transcription risk for no reason. The metadata tool merges: the keys you\n pass are set, everything else is left alone, so you do not need to read the\n document first and cannot drop a tag another agent set. Pass `null` as a value\n to remove a key.\n\n- **Never partial-edit to fix a partial edit.** If a write leaves unexpected\n structure, stop. Use `cerefox_list_versions`, retrieve the last good version,\n and re-ingest cleanly. Repairing edits with more edits compounds the damage.\n\n- **A rejected batch is safe.** Operations in one `cerefox_edit` are\n all-or-nothing: if any is invalid, nothing is written. A refusal costs you a\n retry, not data — so prefer one call for changes that belong together, and do\n not split a batch to "make it more likely to succeed".\n\n- **Read before replacing.** `cerefox_get_document(section: "## Heading")`\n returns exactly what a `replace_section` on that anchor would overwrite. Use it\n for any section you did not write in this session. The outline gives a\n section\'s *size*, never its *text*.\n\n- **Verify after writing** — read the result back before reporting success, and\n report what the read actually shows.\n\n- **Partial edits cannot change a document\'s stored TITLE.** `rename_section`\n changes a heading inside the content; the title is a separate field and still\n needs `cerefox_ingest`.\n\n- **If a capability seems missing from one server, suspect your client first.**\n Local and remote run the same code. **Every `cerefox_get_help()` response\n begins with the server\'s version and the operations it registers** — you do\n not need a special topic, and the *absence* of that block is itself an answer:\n a server that does not print it predates v1.5.0. If that\n disagrees with your tool list, the client is holding a list it fetched before\n an upgrade — clients cache it at connect time. Ask the user to restart the\n client. Do not record a capability difference between servers as a fact; every\n such report so far has been a stale client.\n- Long inline bodies can arrive with literal `\\n`/`\\"` (the author over-escaped; Cerefox stores bytes faithfully). For long or quote-dense content, ingest from a file or build incrementally with `cerefox_insert`; read back multi-line writes.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
|
|
55755
55765
|
var init_get_help_content = __esm(() => {
|
|
55756
55766
|
HELP_SECTIONS = {
|
|
55757
55767
|
Tools: "## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `author` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `cerefox_insert` | **Add** to a document without resending it. Cannot destroy content. | `document_id`, `text`, `position` (`end_of_document`/`end_of_section`/`after_heading`/`before_heading`), `expected_content_hash` (required), `anchor_heading` (unless `end_of_document`), `section_part`, `author` |\n| `cerefox_edit` | **Change** parts of a document: 1..n operations applied atomically | `document_id`, `operations` (`insert`/`replace_section`/`delete_section`/`rename_section`), `expected_content_hash` (required), `author` |\n| `cerefox_delete_document` | **Soft**-delete a document (to trash; excluded from search; permanent purge is human-only) | `document_id`, `expected_content_hash` (**required** — a delete must follow a read), `reason` (recorded in the audit log — give one), `author` |\n| `cerefox_restore_document` | Restore a soft-deleted document from the trash (audited inverse of delete; no-op if not deleted) | `document_id` (required), `reason` (recorded in the audit log), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token), or with `outline: true` just its heading paths, sizes and hash, or with `section: \"## Heading\"` one section's text | `document_id` (required), `outline`, `section`, `section_part`, `author` |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required), `author` |\n| `cerefox_set_relation` ⚑ | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` ⚑ | Remove a relation | `source_id`, `target_id`, `rel_type`, `author` |\n| `cerefox_get_relations` ⚑ | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` ⚑ | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project's docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_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_metadata` | Change tags WITHOUT resending content. **Merges** by default; a `null` value removes a key | `document_id`, `metadata` (required), `replace` (rare: set exactly this object), `author` |\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), `author` |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `by_author` (filter), `operation`, `since`, `author` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n⚑ **Opt-in — usually absent.** The four relation tools are hidden unless the\noperator enables them (`relations_enabled`). **Trust your own tool list**: if\nthey are not in it, the feature is switched off for this deployment. That is\nnormal, not an error, and not something to work around.",
|
|
55758
55768
|
"Editing part of a document (prefer this over re-sending)": '## Editing part of a document (prefer this over re-sending)\n\n**Re-sending a whole document to change part of it is the main way agents lose\ndata.** You have to reproduce the untouched remainder verbatim, and any drift\nsilently rewrites content nobody asked you to touch — which the caller cannot\ndiff. Use the partial-edit tools instead:\n\n1. **Learn the anchors** — `cerefox_get_document(document_id, outline: true)`.\n Returns heading paths, per-section sizes and the `content_hash`, without the\n body. The paths it returns are exactly what `anchor_heading` accepts.\n2. **Add** → `cerefox_insert`. `end_of_document` is a plain append;\n `end_of_section` adds inside a named section. It is structurally incapable of\n removing anything, so "I meant to append" cannot become "I replaced the file".\n3. **Look before you overwrite** — `cerefox_get_document(document_id,\n section: "## Heading")` returns exactly the text a `replace_section` on that\n anchor would destroy. The outline gives you a section\'s *size*, never its\n *text*, so on a document you did not write yourself this is the difference\n between a replace and a blind overwrite.\n4. **Change or remove** → `cerefox_edit`. Put changes that belong together in\n ONE call: they apply atomically, so a table row and the total it feeds cannot\n end up disagreeing. To change a single line, `replace_section` on its\n smallest enclosing heading — that is the intended granularity, not a\n workaround. To fix a stale heading (`## OPEN TODOs (as of ...)`), use\n `rename_section`: it changes the heading text and leaves the body and\n position alone.\n5. All of them require `expected_content_hash` and **have no last-write-wins**. A\n conflict means someone else changed the document; re-read and decide, do not\n force it.\n\n**A section runs to the next same-or-higher heading, or to the end of the\ndocument.** So `end_of_document` inserts land inside the *last* section, and\nreplacing or deleting that section removes them too. A large shrink in the\nresponse is your warning; `cerefox_list_versions` has the previous content.\n\n**When an anchor is ambiguous the tool refuses and hands you the options** — a\nrepeated heading returns the qualifying paths, and a section with both its own\ncontent and sub-sections returns both `section_part` choices. That is a\nrecoverable answer, not a failure: retry with what it gave you.',
|
|
55759
|
-
"Essential Rules": '## 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`** to your name on every call, reads and writes alike (e.g., "Claude Code", "archiver"). Same parameter on every tool. (`requestor` is still accepted everywhere as the pre-1.13.1 alias.) On CLI
|
|
55769
|
+
"Essential Rules": '## 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`** to your name on every call, reads and writes alike (e.g., "Claude Code", "archiver"). Same parameter on every tool. (`requestor` is still accepted everywhere as the pre-1.13.1 alias.) On the CLI it is `--author` on every command too (plus `--author-type` on writes); or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE` 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); permanent purge is web-UI-only.** `cerefox_delete_document` requires the document\'s `content_hash` as you read it (read before you delete) and takes a `reason` — give one; it is what the human reviewing the trash sees. `cerefox_restore_document` undoes a mistaken delete (also audited, also takes a `reason`). Always surface deletes AND restores to the user. Once a human purges from the web UI, the document is gone for good.\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. **The server validates `](uuid)` links on every write** (v1.7.0): a link to a nonexistent id rejects the write, naming the offender — that means you mangled the UUID; re-read the source and correct it, do not retry unchanged. Example ids go in backticks (code is not validated). `[[Wikilinks]]` may dangle. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you last saw — every read shows one (`cerefox_get_document` incl. outline mode, `cerefox_search`, `cerefox_metadata_search`) and **every write returns the new one, including create** (v1.3.0, #189), so after writing you already hold the token for your next edit; no re-read needed. If it\'s stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer\'s work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.\n10. **Search: prefer a few distinctive terms; heed `below confidence`.** When nothing clears the relevance threshold, `cerefox_search` returns the closest candidates prefixed with a `below confidence` warning instead of an empty set — that flag means **weak signal, not absent knowledge**: check the candidates\' scores and titles before concluding the KB lacks the content. A truly empty response means nothing even weakly related exists.\n11. **Relations express how documents relate; lifecycle tells you if knowledge is still good.** Use `cerefox_set_relation` when one document supersedes, contradicts, references, or continues another. `supersedes` marks the target **superseded**; `contradicts` marks **both** stale; `related_to`/`duplicates`/`contradicts` are symmetric (both directions written). Any other type string is accepted without special behaviour. When a search result or `cerefox_get_relations` shows a neighbour marked `[superseded]` or `[stale]`, say so rather than presenting it as current.\n12. **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`.',
|
|
55760
55770
|
"Update Workflow (ID-based -- preferred)": `## Update Workflow (ID-based -- preferred)
|
|
55761
55771
|
|
|
55762
55772
|
\`\`\`
|
|
@@ -55768,7 +55778,7 @@ ingest(title="Same Title", content="...", document_id="abc123",
|
|
|
55768
55778
|
On a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.`,
|
|
55769
55779
|
"Update Workflow (title-based -- fallback)": '## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", author="my-agent")\n```',
|
|
55770
55780
|
"Catch-Up Workflow": '## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```',
|
|
55771
|
-
"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`, …).\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
|
|
55781
|
+
"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`, …).\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`). 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>" --author "<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>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --author "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --author "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --author "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_insert` | `cerefox document insert <id> -t "<text>" -p <position> -a "<anchor-heading>" -e "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_edit` | `cerefox document edit-parts <id> --operations \'<json>\' -e "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_delete_document` | `cerefox document delete <id> --reason "<why>" --author "<your-name>" --author-type agent --yes` (confirms interactively instead of requiring the hash) |\n| `cerefox_restore_document` | `cerefox document restore <id> --reason "<why>" --author "<your-name>" --author-type agent` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --author "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_metadata` | `cerefox document set-metadata <id> --set key=value` (also `--remove key`, `--json \'{...}\'`, `--replace`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --author "<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: `--author "<your-name>"` (the same flag; `--requestor` still works as a hidden alias)\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.',
|
|
55772
55782
|
"Timestamps are UTC": `## Timestamps are UTC
|
|
55773
55783
|
|
|
55774
55784
|
Every timestamp Cerefox returns — \`created_at\` on audit entries, version
|
|
@@ -73838,7 +73848,7 @@ async function action13(documentId, options) {
|
|
|
73838
73848
|
p_document_id: documentId,
|
|
73839
73849
|
p_metadata: patch,
|
|
73840
73850
|
p_replace: Boolean(options.replace),
|
|
73841
|
-
p_author: options.author
|
|
73851
|
+
p_author: resolveAuthor(options.author),
|
|
73842
73852
|
p_author_type: options.authorType === "agent" ? "agent" : "user"
|
|
73843
73853
|
});
|
|
73844
73854
|
} catch (err) {
|
|
@@ -78288,12 +78298,28 @@ function registerDoctor(program) {
|
|
|
78288
78298
|
init__utils();
|
|
78289
78299
|
init_cli_core();
|
|
78290
78300
|
init_client();
|
|
78301
|
+
|
|
78302
|
+
// src/cli/util/identity-flags.ts
|
|
78303
|
+
function authorOption(kind, opts = {}) {
|
|
78304
|
+
const flags = opts.short === false ? "--author <name>" : "-a, --author <name>";
|
|
78305
|
+
const where = kind === "write" ? "recorded in the audit log and the usage log" : "recorded in the usage log";
|
|
78306
|
+
return new Option2(flags, `Your name (agent or user); ${where}.`);
|
|
78307
|
+
}
|
|
78308
|
+
function requestorAliasOption() {
|
|
78309
|
+
return new Option2("-r, --requestor <name>").hideHelp();
|
|
78310
|
+
}
|
|
78311
|
+
|
|
78312
|
+
// src/cli/commands/get-audit-log.ts
|
|
78291
78313
|
async function action18(options) {
|
|
78314
|
+
if (options.author && !options.byAuthor) {
|
|
78315
|
+
process.stderr.write(`note: --author is your own name since v1.13.2; to list entries written by someone, use --by-author <name>.
|
|
78316
|
+
`);
|
|
78317
|
+
}
|
|
78292
78318
|
const limit = parsePositiveInt(options.limit, "--limit", 50);
|
|
78293
78319
|
const client = getClient();
|
|
78294
78320
|
const data = await client.rpc("cerefox_list_audit_entries", {
|
|
78295
78321
|
p_document_id: options.documentId ?? null,
|
|
78296
|
-
p_author: options.
|
|
78322
|
+
p_author: options.byAuthor ?? null,
|
|
78297
78323
|
p_operation: options.operation ?? null,
|
|
78298
78324
|
p_since: options.since ?? null,
|
|
78299
78325
|
p_until: options.until ?? null,
|
|
@@ -78302,7 +78328,7 @@ async function action18(options) {
|
|
|
78302
78328
|
if (data === null) {
|
|
78303
78329
|
throw systemError("Could not list audit entries: RPC returned no data.", "Verify cerefox_list_audit_entries is deployed.");
|
|
78304
78330
|
}
|
|
78305
|
-
const requestor = resolveRequestor(options.requestor);
|
|
78331
|
+
const requestor = resolveRequestor(options.author ?? options.requestor);
|
|
78306
78332
|
client.raw.rpc("cerefox_log_usage", {
|
|
78307
78333
|
p_operation: "get_audit_log",
|
|
78308
78334
|
p_access_path: "cli",
|
|
@@ -78326,7 +78352,7 @@ async function action18(options) {
|
|
|
78326
78352
|
})));
|
|
78327
78353
|
}
|
|
78328
78354
|
function registerGetAuditLog(program) {
|
|
78329
|
-
program.command("get-audit-log").description("Query the audit log with optional filters.").option("-d, --document-id <uuid>", "Filter by document.").option("-
|
|
78355
|
+
program.command("get-audit-log").description("Query the audit log with optional filters.").option("-d, --document-id <uuid>", "Filter by document.").option("--by-author <name>", "Filter: only entries written by this author name.").option("-o, --operation <type>", `Filter by operation: ${AUDIT_OPERATIONS.join(", ")}.`).option("--since <iso>", "Lower-bound ISO timestamp.").option("--until <iso>", "Upper-bound ISO timestamp.").option("-l, --limit <n>", "Maximum entries (max 200).", "50").addOption(authorOption("read", { short: false })).addOption(requestorAliasOption()).option("--json", "Emit machine-readable JSON.").action(action18);
|
|
78330
78356
|
}
|
|
78331
78357
|
|
|
78332
78358
|
// src/cli/commands/relation.ts
|
|
@@ -78482,7 +78508,7 @@ async function action19(documentId, options) {
|
|
|
78482
78508
|
throw notFound(options.versionId ? `Version ${options.versionId} of document ${documentId} not found.` : `Document ${documentId} not found.`);
|
|
78483
78509
|
}
|
|
78484
78510
|
const doc = rows[0];
|
|
78485
|
-
const requestor = resolveRequestor(options.requestor);
|
|
78511
|
+
const requestor = resolveRequestor(options.author ?? options.requestor);
|
|
78486
78512
|
client.raw.rpc("cerefox_log_usage", {
|
|
78487
78513
|
p_operation: "get_document",
|
|
78488
78514
|
p_access_path: "cli",
|
|
@@ -78559,7 +78585,7 @@ async function action19(documentId, options) {
|
|
|
78559
78585
|
println(doc.full_content);
|
|
78560
78586
|
}
|
|
78561
78587
|
function registerGetDoc(program) {
|
|
78562
|
-
program.command("get-doc").description("Retrieve the full content of a document by ID.").argument("<document-id>", "UUID of the document.").option("--version-id <uuid>", "Specific archived version (default: current).").
|
|
78588
|
+
program.command("get-doc").description("Retrieve the full content of a document by ID.").argument("<document-id>", "UUID of the document.").option("--version-id <uuid>", "Specific archived version (default: current).").addOption(authorOption("read")).addOption(requestorAliasOption()).option("--json", "Emit machine-readable JSON.").option("--outline", "Show the heading structure, per-section sizes and content_hash instead of the content. Cheap, and the paths are the anchors the edit commands take.").option("--section <anchor>", "Show ONE section's text instead of the whole document: exactly what a replace_section on this anchor would overwrite. Pass the bare heading line when it is unique, or the full ' > ' path from --outline when it repeats.").option("--section-part <part>", "own_body | subtree — only when the target section has child sections, where 'the end' means two different places. You are told (with both options) whenever it is needed.").action(action19);
|
|
78563
78589
|
}
|
|
78564
78590
|
|
|
78565
78591
|
// src/cli/commands/ingest.ts
|
|
@@ -79407,7 +79433,7 @@ async function runTool(toolName, args) {
|
|
|
79407
79433
|
}
|
|
79408
79434
|
}
|
|
79409
79435
|
function registerDocumentInsert(program) {
|
|
79410
|
-
program.command("insert <document-id>").description("Add text to a document without resending it (purely additive)").requiredOption("-t, --text <text>", "Markdown to insert. Use '-' for stdin or '@path' for a file.").option("-p, --position <position>", "end_of_document | end_of_section | after_heading | before_heading", "end_of_document").option("-a, --anchor-heading <heading>", "Heading line, or a ' > ' path. Required unless end_of_document.").option("--section-part <part>", "own_body | subtree — required when the target section has child sections").requiredOption("-e, --expected-content-hash <hash>", "content_hash you are basing this on (cerefox document get --outline shows it)").
|
|
79436
|
+
program.command("insert <document-id>").description("Add text to a document without resending it (purely additive)").requiredOption("-t, --text <text>", "Markdown to insert. Use '-' for stdin or '@path' for a file.").option("-p, --position <position>", "end_of_document | end_of_section | after_heading | before_heading", "end_of_document").option("-a, --anchor-heading <heading>", "Heading line, or a ' > ' path. Required unless end_of_document.").option("--section-part <part>", "own_body | subtree — required when the target section has child sections").requiredOption("-e, --expected-content-hash <hash>", "content_hash you are basing this on (cerefox document get --outline shows it)").addOption(authorOption("write", { short: false })).addOption(requestorAliasOption()).option("--author-type <type>", "user (default for the CLI) or agent, when scripting on an agent's behalf", "user").action(async (documentId, options) => {
|
|
79411
79437
|
await runTool("cerefox_insert", {
|
|
79412
79438
|
document_id: documentId,
|
|
79413
79439
|
text: resolveText(options.text, "--text"),
|
|
@@ -79415,13 +79441,13 @@ function registerDocumentInsert(program) {
|
|
|
79415
79441
|
...options.anchorHeading ? { anchor_heading: options.anchorHeading } : {},
|
|
79416
79442
|
...options.sectionPart ? { section_part: options.sectionPart } : {},
|
|
79417
79443
|
expected_content_hash: options.expectedContentHash,
|
|
79418
|
-
|
|
79444
|
+
author: resolveAuthor(options.author ?? options.requestor),
|
|
79419
79445
|
...options.authorType ? { author_type: options.authorType } : {}
|
|
79420
79446
|
});
|
|
79421
79447
|
});
|
|
79422
79448
|
}
|
|
79423
79449
|
function registerDocumentEditParts(program) {
|
|
79424
|
-
program.command("edit-parts <document-id>").description("Apply one or more section edits atomically (insert/replace/delete)").requiredOption("-o, --operations <json>", "JSON array of operations. Use '-' for stdin or '@path' for a file.").requiredOption("-e, --expected-content-hash <hash>", "content_hash you are basing these edits on").
|
|
79450
|
+
program.command("edit-parts <document-id>").description("Apply one or more section edits atomically (insert/replace/delete)").requiredOption("-o, --operations <json>", "JSON array of operations. Use '-' for stdin or '@path' for a file.").requiredOption("-e, --expected-content-hash <hash>", "content_hash you are basing these edits on").addOption(authorOption("write", { short: false })).addOption(requestorAliasOption()).option("--author-type <type>", "user (default for the CLI) or agent, when scripting on an agent's behalf", "user").action(async (documentId, options) => {
|
|
79425
79451
|
const raw = resolveText(options.operations, "--operations");
|
|
79426
79452
|
let operations;
|
|
79427
79453
|
try {
|
|
@@ -79434,7 +79460,7 @@ function registerDocumentEditParts(program) {
|
|
|
79434
79460
|
document_id: documentId,
|
|
79435
79461
|
operations,
|
|
79436
79462
|
expected_content_hash: options.expectedContentHash,
|
|
79437
|
-
|
|
79463
|
+
author: resolveAuthor(options.author ?? options.requestor),
|
|
79438
79464
|
...options.authorType ? { author_type: options.authorType } : {}
|
|
79439
79465
|
});
|
|
79440
79466
|
});
|
|
@@ -80067,7 +80093,7 @@ async function action25(options) {
|
|
|
80067
80093
|
if (data === null) {
|
|
80068
80094
|
throw systemError("Could not list metadata keys: RPC returned no data.", "Verify cerefox_list_metadata_keys is deployed (run `db_deploy.py`).");
|
|
80069
80095
|
}
|
|
80070
|
-
const requestor = resolveRequestor(options.requestor);
|
|
80096
|
+
const requestor = resolveRequestor(options.author ?? options.requestor);
|
|
80071
80097
|
client.raw.rpc("cerefox_log_usage", {
|
|
80072
80098
|
p_operation: "list_metadata_keys",
|
|
80073
80099
|
p_access_path: "cli",
|
|
@@ -80089,7 +80115,7 @@ async function action25(options) {
|
|
|
80089
80115
|
})));
|
|
80090
80116
|
}
|
|
80091
80117
|
function registerListMetadataKeys(program) {
|
|
80092
|
-
program.command("list-metadata-keys").description("List all metadata keys with document counts and example values.").
|
|
80118
|
+
program.command("list-metadata-keys").description("List all metadata keys with document counts and example values.").addOption(authorOption("read")).addOption(requestorAliasOption()).option("--json", "Emit machine-readable JSON.").action(action25);
|
|
80093
80119
|
}
|
|
80094
80120
|
|
|
80095
80121
|
// src/cli/commands/list-projects.ts
|
|
@@ -80101,7 +80127,7 @@ async function action26(options) {
|
|
|
80101
80127
|
if (error) {
|
|
80102
80128
|
throw systemError(`Could not list projects: ${error.message}`, "Verify CEREFOX_SUPABASE_KEY has read access to cerefox_projects.");
|
|
80103
80129
|
}
|
|
80104
|
-
const requestor = resolveRequestor(options.requestor);
|
|
80130
|
+
const requestor = resolveRequestor(options.author ?? options.requestor);
|
|
80105
80131
|
client.raw.rpc("cerefox_log_usage", {
|
|
80106
80132
|
p_operation: "list_projects",
|
|
80107
80133
|
p_access_path: "cli",
|
|
@@ -80124,7 +80150,7 @@ async function action26(options) {
|
|
|
80124
80150
|
})), "(no projects)");
|
|
80125
80151
|
}
|
|
80126
80152
|
function registerListProjects(program) {
|
|
80127
|
-
program.command("list-projects").description("List all projects in the knowledge base.").
|
|
80153
|
+
program.command("list-projects").description("List all projects in the knowledge base.").addOption(authorOption("read")).addOption(requestorAliasOption()).option("--json", "Emit machine-readable JSON.").action(action26);
|
|
80128
80154
|
}
|
|
80129
80155
|
|
|
80130
80156
|
// src/cli/commands/list-versions.ts
|
|
@@ -80144,7 +80170,7 @@ async function action27(documentId, options) {
|
|
|
80144
80170
|
throw notFound(`Document ${documentId} not found.`);
|
|
80145
80171
|
}
|
|
80146
80172
|
}
|
|
80147
|
-
const requestor = resolveRequestor(options.requestor);
|
|
80173
|
+
const requestor = resolveRequestor(options.author ?? options.requestor);
|
|
80148
80174
|
client.raw.rpc("cerefox_log_usage", {
|
|
80149
80175
|
p_operation: "list_versions",
|
|
80150
80176
|
p_access_path: "cli",
|
|
@@ -80171,7 +80197,7 @@ async function action27(documentId, options) {
|
|
|
80171
80197
|
})));
|
|
80172
80198
|
}
|
|
80173
80199
|
function registerListVersions(program) {
|
|
80174
|
-
program.command("list-versions").description("List archived versions of a document.").argument("<document-id>", "UUID of the document.").
|
|
80200
|
+
program.command("list-versions").description("List archived versions of a document.").argument("<document-id>", "UUID of the document.").addOption(authorOption("read")).addOption(requestorAliasOption()).option("--json", "Emit machine-readable JSON.").action(action27);
|
|
80175
80201
|
}
|
|
80176
80202
|
|
|
80177
80203
|
// src/cli/commands/mcp.ts
|
|
@@ -80235,7 +80261,7 @@ async function action28(options) {
|
|
|
80235
80261
|
if (rows === null) {
|
|
80236
80262
|
throw systemError("cerefox_metadata_search: RPC returned no data.");
|
|
80237
80263
|
}
|
|
80238
|
-
const requestor = resolveRequestor(options.requestor);
|
|
80264
|
+
const requestor = resolveRequestor(options.author ?? options.requestor);
|
|
80239
80265
|
client.raw.rpc("cerefox_log_usage", {
|
|
80240
80266
|
p_operation: "metadata_search",
|
|
80241
80267
|
p_access_path: "cli",
|
|
@@ -80271,7 +80297,7 @@ async function action28(options) {
|
|
|
80271
80297
|
}
|
|
80272
80298
|
}
|
|
80273
80299
|
function registerMetadataSearch(program) {
|
|
80274
|
-
program.command("metadata-search").description("Find or list documents by metadata, project, or time criteria (no text query).").option("-f, --metadata-filter <json>", "JSON object; only docs whose metadata contains ALL pairs are returned. Optional — omit to list by --project-name / time range alone (at least one criterion is required).").option("-p, --project-name <name>", "Filter to a specific project.").option("--updated-since <iso>", "Only docs updated on/after this ISO timestamp.").option("--created-since <iso>", "Only docs created on/after this ISO timestamp.").option("--include-content", "Include full document text in results.").option("-l, --limit <n>", "Maximum docs to return.", "10").option("--max-bytes <n>", "Response size budget in bytes (with --include-content).", "200000").
|
|
80300
|
+
program.command("metadata-search").description("Find or list documents by metadata, project, or time criteria (no text query).").option("-f, --metadata-filter <json>", "JSON object; only docs whose metadata contains ALL pairs are returned. Optional — omit to list by --project-name / time range alone (at least one criterion is required).").option("-p, --project-name <name>", "Filter to a specific project.").option("--updated-since <iso>", "Only docs updated on/after this ISO timestamp.").option("--created-since <iso>", "Only docs created on/after this ISO timestamp.").option("--include-content", "Include full document text in results.").option("-l, --limit <n>", "Maximum docs to return.", "10").option("--max-bytes <n>", "Response size budget in bytes (with --include-content).", "200000").addOption(authorOption("read")).addOption(requestorAliasOption()).option("--json", "Emit machine-readable JSON.").action(action28);
|
|
80275
80301
|
}
|
|
80276
80302
|
|
|
80277
80303
|
// src/cli/commands/reindex.ts
|
|
@@ -80799,7 +80825,7 @@ async function action32(query, options) {
|
|
|
80799
80825
|
accepted.push(row);
|
|
80800
80826
|
usedBytes += rowBytes;
|
|
80801
80827
|
}
|
|
80802
|
-
const requestor = resolveRequestor(options.requestor);
|
|
80828
|
+
const requestor = resolveRequestor(options.author ?? options.requestor);
|
|
80803
80829
|
client.raw.rpc("cerefox_log_usage", {
|
|
80804
80830
|
p_operation: "search",
|
|
80805
80831
|
p_access_path: "cli",
|
|
@@ -80879,7 +80905,7 @@ async function action32(query, options) {
|
|
|
80879
80905
|
}
|
|
80880
80906
|
}
|
|
80881
80907
|
function registerSearch(program) {
|
|
80882
|
-
program.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: CEREFOX_SEARCH_ALPHA; else 0.7).").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE; else 0.5, or 0.6 with the local embedder).").option("--min-term-coverage <float>", "OR-fallback keyword matches must cover at least this fraction of the query's meaningful terms to count as confident hits (default: CEREFOX_MIN_TERM_COVERAGE; else the server default 0.5; needs schema ≥ 0.9.1).").option("--max-bytes <n>", "Response size budget in bytes (default: CEREFOX_MAX_RESPONSE_BYTES or 200000).").
|
|
80908
|
+
program.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: CEREFOX_SEARCH_ALPHA; else 0.7).").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE; else 0.5, or 0.6 with the local embedder).").option("--min-term-coverage <float>", "OR-fallback keyword matches must cover at least this fraction of the query's meaningful terms to count as confident hits (default: CEREFOX_MIN_TERM_COVERAGE; else the server default 0.5; needs schema ≥ 0.9.1).").option("--max-bytes <n>", "Response size budget in bytes (default: CEREFOX_MAX_RESPONSE_BYTES or 200000).").addOption(authorOption("read")).addOption(requestorAliasOption()).option("--json", "Emit machine-readable JSON instead of the default text.").option("--only-metadata", "List matching docs (id, score, chunks, chars, partial/full) WITHOUT their content — like the web UI's collapsed result list. Grab a [id:…] then `cerefox document get <id>`.").action(action32);
|
|
80883
80909
|
}
|
|
80884
80910
|
|
|
80885
80911
|
// src/cli/commands/self-update.ts
|
|
@@ -85918,7 +85944,8 @@ function registerDocumentWriteRoutes(app, ctx) {
|
|
|
85918
85944
|
});
|
|
85919
85945
|
if (error)
|
|
85920
85946
|
return c.json({ detail: error.message }, 500);
|
|
85921
|
-
|
|
85947
|
+
const { data: still } = await ctx.supabase.from("cerefox_documents").select("id").eq("id", documentId).maybeSingle();
|
|
85948
|
+
return c.json({ success: true, purged: !still });
|
|
85922
85949
|
});
|
|
85923
85950
|
app.post("/api/v1/documents/:document_id/review-status", async (c) => {
|
|
85924
85951
|
if (!await reviewWorkflowEnabled(ctx.supabase)) {
|