Robin is a digital commonplace book. The user gives content to the AI agent, and the agent uses Robin to store it in topic-organized Markdown files and resurface it later for review.
robin-config.jsonrobin-review-index.jsontopics/ for topic-organized Markdown filesmedia/ for copied image assetsRecommended default state directory:
/data/robin/ Typical host examples:
~/.hermes/data/robin/~/.openclaw/workspace/data/robin/Robin does not guess host layout. The caller must provide the Robin state directory explicitly.
The host agent must be able to:
The host agent is responsible for first-run setup.
On setup, the agent should:
/data/robin/ robin-config.json in that directory if it does not existtopics/ and media/robin-review-index.json; if it is missing, Robin starts with an empty index and writes the file when review state is savedpython3 scripts/doctor.py --state-dir --json python3 scripts/selftest.pyExample robin-config.json:
{
"topics_dir": "topics",
"media_dir": "media",
"min_items_before_review": 30,
"review_cooldown_days": 60
}
Robin does not need a separate content-root path. Topic files and copied media live inside the state directory under topics/ and media/.
robin-config.json itself is required, but it may be an empty JSON object ({}). All fields inside it are optional. Robin defaults to:
topics_dir: topicsmedia_dir: mediamin_items_before_review: 30review_cooldown_days: 60Optional example robin-review-index.json:
{
"items": {}
}
Robin accepts both:
--state-dir /path/to/data/robinROBIN_STATE_DIR=/path/to/data/robinPrecedence:
--state-dirROBIN_STATE_DIRIf neither is provided, Robin exits with:
Robin state directory is not configured. Pass --state-dir or set ROBIN_STATE_DIR.
Default runnable path:
python3 scripts/add_entry.pypython3 scripts/doctor.pypython3 scripts/entries.pypython3 scripts/review.pypython3 scripts/reindex.pypython3 scripts/search.pypython3 scripts/selftest.pypython3 scripts/topics.pyNo pip install -e . or manual path setup is required for this repo-local script path.
Optional installed path for advanced users:
pip install -e ., Robin also exposes:robin-addrobin-doctorrobin-entriesrobin-reviewrobin-reindexrobin-searchrobin-topicsFor advanced manual setup details and shell examples, see docs/guide.md.
After setup or after a Robin upgrade, the agent can run:
python3 scripts/selftest.py
By default, this uses a temporary state directory and does not touch the user's real Robin library. It verifies setup, doctor, entries, add, search, review, rating, duplicate rejection, override, and reindex behavior against the documented JSON contracts.
For a non-destructive check of a real state directory, run:
python3 scripts/selftest.py --state-dir <state-dir>
That mode checks only that robin-config.json, topics/, media/, topics.py --json, and doctor.py --json work for the supplied state directory.
When using Robin, the agent is responsible for:
text, image, or videodescription for every entrycreator, published_at, and summary for every media entryhttp(s) URLs for videos--state-dir or ROBIN_STATE_DIR on every Robin invocationRobin is responsible for:
Robin is a storage and retrieval skill, not a code-editing skill.
Normal Robin use may:
Normal Robin use must not:
If the agent notices a Robin bug while using the skill, it should report the issue to the user instead of patching Robin unless the user explicitly asks for a fix.
python3 scripts/topics.py --state-dir --json .add_entry.py checks deterministic duplicates before saving; the agent uses --allow-duplicate only when the user intentionally wants another copy.image or video entries, the agent must also supply creator, published_at, and summary. If any are missing, Robin rejects the entry.python3 scripts/topics.py --state-dir --json before filing.quotes, wisdom, poetry, or talks; avoid one-off topics named after a single entry.add_entry.py blocks deterministic duplicates by default when an existing entry has the same source URL, same media reference, or same normalized body text.--allow-duplicate.description.description is required context for every entry: why this item matters and how to recognize it later.summary is required only for media entries: what the media itself contains.note is optional agent commentary for extra curation, reminders, or connections to other entries.--tags "writing,clarity". Robin stores them as a frontmatter list.texttopic, content, description--media-path, source, note, tags--media-path is provided, Robin copies the image into media// , sets media_source, keeps entry_type as text, and does not require creator, published_at, or summary--media-urlimagetopic, local image file path, description, creator, published_at, summarycontent, source, note, tagsmedia// under the state directory and creates that subdirectory automaticallyvideotopic, video URL, description, creator, published_at, summarycontent, source, note, tagshttp(s) URL, save a normal text entry with the local path/context and tell the user Robin did not store the video file itselfRobin rejects the add operation if:
contentdescription, creator, published_at, or summaryhttp(s) URL* line, which Robin reserves as its internal entry separatorWhen the add operation fails, the agent should pass the returned error back to the user instead of pretending the item was stored.
Review is host-triggered. Robin itself does not run a scheduler.
During setup, the agent should ask the user what recall cadence they want. If the host supports scheduling, the agent should usually configure a daily or weekly recall trigger at the user's preferred time. Otherwise, the agent should expose active review as an on-demand action.
If the host scheduler supports skill metadata, scheduled recall jobs should load the robin skill. If it does not, the cron prompt must explicitly follow this Review Mode section.
Scheduled recall means Robin resurfaces an item for learning. It is not an active review session, and cron or scheduled recall messages must not ask the user to reply with a bare 1-5 rating because the agent may not know that a later numeric reply refers to Robin.
Default recall text output:
📚 Robin Recall
Topic: <topic>
Type: <entry_type>
Source: <source if present, else media_source if present, else "Not provided">
Creator: <creator if present, else "Not provided">
Saved on: <date_added>
Description:
<description if present, else "Not provided">
Body:
<body if present, else "Not provided">
Review behavior:
len(items) >= min_items_before_reviewreview_cooldown_dayspython3 scripts/review.py --state-dir increments times_surfaced, sets last_surfaced, and keeps _awaiting_rating as falsepython3 scripts/review.py --state-dir --active-review increments times_surfaced, sets last_surfaced, and marks _awaiting_rating as true_awaiting_rating is true, rating only overwrites the rating and clears _awaiting_rating back to falseIf the agent calls --rate directly on an item that was not surfaced first, Robin still sets last_surfaced, increments times_surfaced, and leaves _awaiting_rating as false.
Preferred rating flow:
python3 scripts/review.py --state-dir --active-review --json to surface an item in a live active-review session.python3 scripts/review.py --state-dir --rate --json .--rate without a prior surface only for manual corrections or when the user explicitly names an existing entry id.When the host supports file indexing, Robin topic files should be part of the host agent's searchable corpus.
Use host/global search for:
Use robin-search for:
robin-search can combine filters. If both --topic and --tags are provided, Robin first narrows to the topic and then applies the tag filter within that topic.
Default repo-local commands for agents:
python3 scripts/add_entry.pypython3 scripts/doctor.pypython3 scripts/entries.pypython3 scripts/review.pypython3 scripts/reindex.pypython3 scripts/search.pypython3 scripts/selftest.pypython3 scripts/topics.pyOptional installed entry points for advanced users:
robin-addrobin-doctorrobin-entriesrobin-reviewrobin-reindexrobin-searchrobin-topicsAll Robin commands accept:
--state-dirAgents should use --json whenever they need to parse command output. Without --json, Robin prints human-readable text for interactive use; that text is not a stable machine contract.
CLI flags by command:
add_entry.py: --state-dir, --topic, --entry-type text|image|video, --content, --description, --source, --media-path, --media-url, --creator, --published-at, --summary, --note, --tags, --allow-duplicate, --jsondoctor.py: --state-dir, --jsonentries.py: --state-dir, --delete ID, --move ID --topic TOPIC, --jsonreview.py: --state-dir, --status, --active-review, --rate ID RATING, --jsonsearch.py: --state-dir, optional positional query string, --topic, --tags, --jsonselftest.py: optional --state-dir for non-destructive setup checks, --keep-temptopics.py: --state-dir, --jsonreindex.py: --state-dir, --jsonExamples:
The examples below use the repo-local python3 scripts/.py path. The installed robin- commands are equivalent aliases if the package has been installed.
python3 scripts/add_entry.py --state-dir /path/to/data/robin --topic "reasoning" --content "..." --description "..."python3 scripts/add_entry.py --state-dir /path/to/data/robin --topic "writing" --content "..." --description "..." --note "Useful for later review."python3 scripts/add_entry.py --state-dir /path/to/data/robin --topic "writing" --content "Write as if speaking to a smart friend." --description "A reminder to keep prose conversational and clear." --source "https://example.com/article" --jsonpython3 scripts/add_entry.py --state-dir /path/to/data/robin --topic "reasoning" --content "The map is not the territory." --description "A reminder that abstractions are not reality itself." --tags "thinking,quotes"python3 scripts/add_entry.py --state-dir /path/to/data/robin --topic "wisdom" --content "Filed this screenshot to wisdom." --description "A text note with a local screenshot attached for later context." --media-path ~/Downloads/screenshot.png --jsonpython3 scripts/review.py --state-dir /path/to/data/robinpython3 scripts/review.py --state-dir /path/to/data/robin --jsonpython3 scripts/review.py --state-dir /path/to/data/robin --active-review --jsonpython3 scripts/review.py --state-dir /path/to/data/robin --status --jsonpython3 scripts/review.py --state-dir /path/to/data/robin --rate 20260408-a1f3c9 5python3 scripts/review.py --state-dir /path/to/data/robin --rate 20260408-a1f3c9 5 --jsonpython3 scripts/search.py --state-dir /path/to/data/robin "clear thinking" --jsonpython3 scripts/search.py --state-dir /path/to/data/robin --topic "AI Reasoning" --jsonpython3 scripts/search.py --state-dir /path/to/data/robin --tags "writing,clarity" --jsonpython3 scripts/search.py --state-dir /path/to/data/robin --topic "AI Reasoning" --tags "clarity" --jsonpython3 scripts/topics.py --state-dir /path/to/data/robin --jsonpython3 scripts/selftest.pypython3 scripts/selftest.py --state-dir /path/to/data/robinpython3 scripts/reindex.py --state-dir /path/to/data/robinpython3 scripts/reindex.py --state-dir /path/to/data/robin --jsonUse python3 scripts/reindex.py --state-dir after manual edits to topic files, when rebuilding review state from existing markdown, or when importing legacy entries and wanting the review index rebuilt from disk.
Add success shape includes:
idtopicfilename (topic file basename only, for example quotes.md)entry_typemedia_sourcedescriptionReview success payload:
{"status": "ok", "id": "...", "topic": "...", "date_added": "YYYY-MM-DD", "entry_type": "text|image|video", "media_kind": "image|video|\"\"", "media_source": "...|\"\"", "source": "...|\"\"", "description": "...", "creator": "...|\"\"", "published_at": "...|\"\"", "summary": "...|\"\"", "tags": [...], "body": "...", "rating": 1|2|3|4|5|null, "times_surfaced": N}Review rate success payload:
{"id": "...", "topic": "...", "date": "YYYY-MM-DD", "rating": 1|2|3|4|5, "last_surfaced": "ISO-8601 timestamp", "times_surfaced": N, "_awaiting_rating": false}Search success payload:
{"count": N, "entries": [{"id": "...", "topic": "...", "date_added": "YYYY-MM-DD", "entry_type": "text|image|video", "media_kind": "image|video|\"\"", "media_source": "...|\"\"", "source": "...|\"\"", "description": "...", "creator": "...|\"\"", "published_at": "...|\"\"", "summary": "...|\"\"", "tags": [...], "body": "...", "rating": 1|2|3|4|5|null}]}Topics success payload:
[{"topic": "...", "filename": "...", "entries": N, "rated": N, "unrated": N}]Reindex success payload:
{"entries_found": N, "items_indexed": N, "rated": N, "unrated": N}Review skip payloads:
{"status": "skip", "reason": "not_enough_items", "total_items": N, "min_items_before_review": N}{"status": "skip", "reason": "no_eligible_items"}Review status payload:
{"total_items": N, "rated": N, "unrated": N, "min_items_before_review": N, "ready": true|false}Failure shape:
{"error": "..."}Exit status on failure:
1Entries are separated by *. Each entry is a frontmatter block followed by a blank line and then the body text.
Text entries may omit entry_type; omitted entry_type is parsed as text.
id: 20260408-a1f3c9
date_added: 2026-04-08
entry_type: image
media_kind: image
media_source: media/poetry/20260408-a1f3c9.png
description: A photographed poem excerpt worth revisiting.
creator: Mary Oliver
published_at: 1986
summary: An excerpt about attention and observation.
tags: [poetry]
Opening lines from the photographed page.
Topic filename: lowercase topic slug with non-alphanumeric characters normalized to dashes.
Frontmatter parsing rules:
{
"items": {
"20260408-a1f3c9": {
"id": "20260408-a1f3c9",
"topic": "quotes",
"date": "2026-04-08",
"rating": null,
"last_surfaced": null,
"times_surfaced": 0,
"_awaiting_rating": false
}
}
}
Review settings such as min_items_before_review and review_cooldown_days live only in robin-config.json.
_awaiting_rating is an internal review-state flag. It is true only after Robin surfaces an item with --active-review and becomes false again after that surface is rated. Scheduled recall leaves it false.
--- as an entry separator. Use *.* line inside entry body content. Robin reserves that exact line as its internal entry separator and rejects it on add.http(s) video URL, or save a normal text entry that records the local path/context without storing the video file.id stable when manually editing entries.robin-config.json contains invalid JSON, Robin exits with an error; recreate it as {} or with the supported config fields.robin-review-index.json is missing, Robin can rebuild review state as entries are used. If it is corrupted, back it up or recreate it as {"items": {}}, then run python3 scripts/reindex.py --state-dir --json to rebuild from topic files.python3 scripts/doctor.py --state-dir --json for a read-only diagnostic report.python3 scripts/entries.py --state-dir --move --topic to move entries and python3 scripts/entries.py --state-dir --delete to delete entries. Delete keeps copied media files.* lines in body content. Numbered lists are safe after the blank line.MEDIA:/path/file.gif converts them to static photos. To preserve animation, GIFs must be sent as documents, not photos. The gateway may require a document wrapper or alternate approach for animated GIF delivery.When a user sends an image to file to Robin via a messaging platform:
~/.hermes/image_cache/ with a name like img_.jpg --entry-type image and pass --media-path; this requires creator, published_at, and summary--entry-type text and pass --media-path; Robin copies the image and sets media_source without requiring media metadataadd_entry.py returns, verify media_source is populated in the JSON outputImportant: Saying "Filed!" after running add_entry.py is premature if an expected image attachment is missing. Always check the returned JSON media_source field.
When filing many items at once (e.g. a batch of quotes):
add_entry.py calls together when practical; each call performs deterministic duplicate checks before saving.--allow-duplicate only for entries the user intentionally wants saved as another copy.* line.add_entry.py and search.py can fail globally until the file is fixed.共 1 个版本