Overview
The curator is the operational half of Lumen. It turns memory-source records into conservative Markdown wiki updates, and it lints the wiki for structural decay. It never calls providers, never deletes pages, and never overwrites operator-confirmed facts.
Pair it with lumen:wiki for methodology (OKF frontmatter, ingest/query/lint procedures). This skill is the CLI and adapter layer.
When to Use
Use this skill when the user wants to:
- Ingest recent memory into the wiki (
run) or preview that ingest (report) - Lint or validate wiki structure
- Search wiki pages by keyword, alias, or tag
- Configure which memory source the curator reads
Wiki Location
Resolve the wiki path in this order:
--wikiCLI overridepaths.wiki_pathin plugin config (shared/config/lumen.yamlor--config)$WIKI_PATHenvironment variable- Default
~/wiki
Plugin config (lumen.yaml)
Default file: <plugin>/shared/config/lumen.yaml. Override with --config or LUMEN_CONFIG.
paths:
wiki_path: ~/wiki # null → $WIKI_PATH → ~/wiki
curator:
timezone: Asia/Singapore
max_write_pages: 20 # 0 or negative disables the cap
memory_source: sessions # sessions | auto | json-file | mnemosyne | none
auto prefers sessions when a readable Hermes state.db exists (the conversation history is the ground truth; Mnemosyne holds derived notes), then Mnemosyne when hermes is on PATH, falling back to the json-file adapter if the export fails. none disables ingest (file-only lint/search still work).
Adapter layer
${HERMES_SKILL_DIR}/scripts/adapters.py is the read-only memory boundary. Adapters emit SourceItem records (source_id, source_kind, title, text, timestamp, tags, people, entities, projects, raw). Nothing writes back to a memory store.
| Adapter | Source | Notes |
|---|---|---|
sessions |
Hermes state.db (conversation history) |
Read-only SQLite; user messages paired with the first assistant reply; gateway-origin JSON envelopes stripped. Default source. |
json-file |
<wiki>/.knowledge/memory.json |
Tolerant record shapes (records map or list). Missing file → []. |
mnemosyne |
hermes mnemosyne export --output <tmp> |
Parses working_memory + episodic_memory. Subprocess failure → []. |
none |
empty | Explicit opt-out. |
Record fields. people / entities / projects take a list for several names; a plain string is one name ("Acme, Inc." stays one entity). tags may be a list or a comma-separated string. Records without an id get a content-derived id (memory_<digest> of timestamp and text), so reordering or inserting records does not re-ingest them. When a record names no people/entities/projects, capitalised words in its title/text are used as entity hints; a sentence-initial word ("Met with…", "Sent…") only counts if the same word also appears capitalised mid-sentence, and the Untitled placeholder title is never scanned.
Extension point (generic-json). Any provider that can emit the json-file memory.json shape (a records mapping or a list of record objects, with tolerant people/entities/projects keys) can be dropped in as <wiki>/.knowledge/memory.json and selected with memory_source: json-file. A dedicated generic-json adapter that takes an arbitrary export path is the documented next step; do not add provider-specific imports.
CLI
Script: ${HERMES_SKILL_DIR}/scripts/wiki_curator.py (Hermes substitutes ${HERMES_SKILL_DIR} with this skill's directory, so the path works from any cwd). Run with system python3 (stdlib + PyYAML).
CURATOR="${HERMES_SKILL_DIR}/scripts/wiki_curator.py"
python3 "$CURATOR" run --since 24h # ingest (writes wiki pages)
python3 "$CURATOR" run --since 24h --dry-run
python3 "$CURATOR" report --since 7d # planned changes, no writes
python3 "$CURATOR" validate # lint; exit 1 on ERROR
python3 "$CURATOR" lint # same checks
python3 "$CURATOR" lint --summary # counts only
python3 "$CURATOR" search "Acme" --format json --limit 10
python3 "$CURATOR" search "Acme" --format text --wiki ~/wiki
Global flags: --config <lumen.yaml>, --wiki <path> (also accepted after the subcommand).
--since accepts 90m, 24h, 7d, 2w; anything else is a usage error (exit 2). Runtime errors such as a wiki subdirectory that is a symlink out of the wiki print error: … and exit non-zero.
Read-only vs mutating: lint, validate, report, and search are read-only. run writes. run --dry-run previews. The curator never deletes pages.
Conservative write policy
- Create or update people / project / entity / daily pages from source items only
- Preserve operator-confirmed facts; append observations with
[source: id] - Refresh only the
<!-- lumen:auto:start -->…<!-- lumen:auto:end -->block ofindex.mdandoverview.md(hand-written content outside the markers is preserved; a file without markers gets a block appended); appendlog.md - Record text, titles and names are flattened to one line with
[source:/[[/ leading#neutralised, so memory content cannot forge sections, source markers or links - Never overwrite an existing page
titleorcreatedvalue - Skip an existing page whose YAML frontmatter does not parse: it is left byte-for-byte unchanged, a
skipline naming the page appears in the run report (and a warning on stderr), and the source is retried on the next run once the page is fixed - Rewritten files keep their existing permission bits; new files get
0666 & ~umask - Cap source items per run at
curator.max_write_pages(default 20; 0 or negative disables the cap) - Backup the wiki tree to
<wiki>/.lumen/backup/<timestamp>/before large batches (>5 items)..lumen/is excluded from index and overview regeneration.
OKF v0.2
Every curated .md page needs parseable YAML frontmatter with a non-empty type field. See lumen:wiki for the full schema. The curator assigns seq on create and preserves aliases / relations.
Lint checks
- Missing or malformed frontmatter (ERROR)
- Missing
type(ERROR) - Broken
[[wikilinks]](WARN) - Duplicate titles (WARN)
- Pages absent from
index.md(WARN) - Orphans with no inbound links (INFO)
- Stale pages (updated >90 days) (INFO)
File naming
Lowercase hyphenated ASCII slugs: acme-logistics.md. Accents are folded (Zoë → zoe.md); a name with no ASCII letters or digits (张伟) gets untitled-<first 8 hex of sha256(name)>.md, stable across runs and distinct per name. Wikilinks use the page title, so [[张伟]] resolves. Avoid generic names like notes.md.
Common pitfalls
- Skipping orientation (
purpose.md,SCHEMA.md,index.md, recentlog.md) - Creating duplicate entity pages without searching first
- Dumping raw notes as curated knowledge
- No frontmatter / missing
type - Unlinked pages that lint later reports as orphans
Verification checklist
- Wiki path resolved from plugin config, then
$WIKI_PATH, then~/wiki -
memory_sourcematches the host install (auto/json-file/mnemosyne/none) -
run --dry-runreviewed before a mutatingrun - Lint reports file paths and severity
- Query answers cite wiki pages