Basic Cohub abilities. Most agents already have these mounted; listed here for reference.
Platform Published by the Neta team.
Official Made by the Neta team.
Verified Reviewed by the Neta team.
Community Shared by a Cohub user. Not reviewed — read SKILL.md before you use it.
memory-letters
Officialneta-search/memory-letters
Use it in Cohub
Paste this into your agent's chat — it fetches the skill and follows it.
Use the skill: memory-letters. Run `npx -y @neta-art/neta-search-cli@latest skills use neta-search/memory-letters --via site` and follow what it prints. Read its complete output, redirecting it to a temporary file first if necessary, and resolve relative paths from the skill directory it prints.
npx -y @neta-art/neta-search-cli@latest skills use neta-search/memory-letters
Summary
Turn your roleplay memories into handwritten letters the character sends you after each scene.
Each letter tears open with a paper strip, unfolds once, and turns pages both ways before folding back into the envelope.
The character writes from real memory: what happened, what they felt, what they noticed about you during your latest scenes.
New letters arrive automatically after memory digests, building a collection you can browse any time from one published page.
Works best when the character has a distinctive voice and the world has a clear visual style for stationery and setting.
Requires an existing roleplay and completed memory journal; if either is missing, create those first.
SKILL.md
# Memory Letters
Build a Letters web work: the character writes to the player from real memory; the page tears open, unfolds once, turns both ways, folds back, and picks up new letters after each digest.
**The deliverable is one published web Work — the Letters page.** The scene background and ornament images are its parts, not the delivery; never hand one over as the result.
**Four agent steps.** Everything mechanical is scripted — do not re-implement, re-read, or hand-run what `run.mjs` does. `SKILL_ROOT` = this file's directory. Do not read `create-web-work` or `generate` for this skill; the scripts already follow them (init → build → publish; model params checked, sync generation, transparency, references).
## 1. Inspect (one call)
```bash
node "$SKILL_ROOT/tools/inspect.mjs" --root /workspace --out /tmp/letters-ctx.json
# several companions → add --character-id <id> or --roleplay-id <id>
```
| Result | Do |
| --- | --- |
| `ok:false`, reason `memory-missing` / `roleplay-*` / `generated-memory-journal-missing` / `companion-is-player-identity` | **Stop.** Tell the creator what to make first (roleplay, or memory system + journal). Install nothing. |
| `ambiguous-companion` | Ask which character, once. |
| `action: patch-existing` | A Letters page exists. Visual change → step 2 + `run.mjs --retheme <existingLettersWork>` if it has `src/theme.ts`; otherwise patch that page's CSS directly. Never re-stage it. |
| any other `ok:true` | Continue. A missing journal entry is fine (honest waiting page); never invent memory or trigger a digest. |
The output already holds world, visual style, character facts, roleplay, locations and the latest memory. **Do not read more files** unless a design choice truly needs a fact that is absent.
## 2. Write the brief (one file, the only creative step)
Fill `briefTemplate` from the inspect output into `/tmp/letters-brief.json`:
- `palette`: 13 hex colours (ink, pen, muted, accent, paper, envelope, envelope2, highlight, tape, ui, scene, glow, mist). `ui` must read over the background.
- `returnAddress`, `postmark` (short, caps), `locationLabel`, `title`.
- `light` / `mist` / `ambience`: true only if the setting supports lamplight, haze, ambient sound.
- `images.background`: one sentence, the concrete surface/place in this setting where a letter lies. `images.ornaments`: one short phrase per slot (closure, corner, stamp1–3, keepsake1–3, keepsakeLast). Drop a slot rather than invent an off-setting object. Optional `backgroundRefs` / `ornamentRefs`: workspace image paths.
- `sources`: `{choice, fact, kind: "setting"|"inference"}` per main choice. Never present an inference as canon. No default forest / wax / honey / flowers unless the setting has them.
- Optional `extraCss`: cosmetic only. It must not touch the layout of `.strip/.fold/.fp-*/.sheet/.turn-layer`; the validator rejects that.
Don't ask design questions; derive. Ask once only if the sources genuinely conflict.
## 3. Run (one call, ≈ art time + ~30 s)
```bash
node "$SKILL_ROOT/tools/run.mjs" --brief /tmp/letters-brief.json [--character-id <id>] \
[--public-copy] # only if the creator asked that people outside the world can read the letters
[--retheme <workDir>] # existing template page: new look, same work id, letters untouched
```
`run.mjs` does not publish; publishing is step 4, once, after the last run.
It does: gate → `neta work init` → stage template + writer + hook (only if missing) → theme.json/theme.css from the brief → **in parallel**: background + one ornament sheet (sync generation, alpha-threshold slicing, WebP) and the first letter (one model call over the whole journal so far; skipped if it already exists) → writer idempotency check → tsc + bun build → browser QA, desktop + phone (synthetic letters only). One JSON line out, with `workRef`, `qa`, `visibilityBefore`.
**Reading the result**
- `ok:true` → step 4.
- `stage:"gate" | "reuse"` → relay `message`.
- `art.ok:false` → the page still ships with plain stationery. `art.blocked:"credits"` → no repair (it cannot succeed): go to step 4 and say the artwork is waiting on image credits. Otherwise repair only the failed part (`report[].kind`), once: `node "$SKILL_ROOT/tools/assets.mjs" --brief … --work <workRef> --only background|ornaments`, then `run.mjs --brief /tmp/letters-brief.json --retheme <workRef> --no-art` (without `--no-art` it regenerates both). Never regenerate both. If the repair fails too, go to step 4 with plain stationery and say which part is missing. No test / probe generations of your own — each one lands on the creator's canvas as a stray image Work.
- `letter.ok:false` (credits/rate-limit/model) → the page shows its waiting state; report it plainly, do not loop. Do not edit the installed writer to force it.
- Letter written but not what the creator hoped (a memory left out, a phrase you'd change) → it is the character's own letter: ship it as written and say so in the handover. Never patch the letter file, re-run the writer with `--force`, or rewrite an existing letter.
- `stage:"qa"` → read `qa.error`, fix that one thing in theme/brief, re-run with `--retheme`. Only read `references/implementation.md` if the engine itself is implicated.
## 4. Publish and hand over (one call, after the last successful run)
```bash
cd /workspace && node /mods/neta/neta work publish web <workRef>
```
- Its result carries `previewUrl` (a `https://neta.art/w/…` address): that is the Letters Work. No `previewUrl`, or `ok:false` → not delivered; relay the error (e.g. `world_slug_required` → set the slug as the error says, then publish again once).
- `slug already exists` → that address belongs to another Work already in this world (even if its folder is gone). It is not yours: never write its `cohubWorkId`/slug into your `meta.json`, never `cohub apps publish-version` / `update --slug` / delete it. `run.mjs` already picks a free folder name, so this means a slug was taken after the run: say the page is built but its address is taken, and stop.
- If `visibilityBefore` (from step 3) is set, read `cohub apps get <cohubWorkId> --json` (`cohubWorkId` is in `<workRef>/meta.json`); if `app.visibility` changed, restore it with `cohub apps update <cohubWorkId> --visibility <visibilityBefore>`.
- Hand over through `interaction-components`, pointing at **this published Work** (its `previewUrl`) — not the cohub.live public address, not the scene image. One sentence, the Work, the QA line (`desktop:ok, phone:ok`). No logs, no timings unless asked.
- The Link card is required even when the letter or art is still waiting (credits) — a bare URL in prose is not a handover. Target: `studio-route` (`route: "work"`, `worldId` = the `NETA_WORLD_ID` environment variable, `workId` = `<workRef>/meta.json` → `preview.workId`) when both ids exist; otherwise `{ "type": "external-url", "url": "<previewUrl>" }` with `presentation.name` = the page title. Never build an id by hand.
## Hard rules (the rest live in `references/rules.md`)
- Letters live in `letters/<characterId>/`, never in `memory/`. Never `--force` the writer. Never add a second cron; the digest hook writes future letters.
- Never hand-edit `memory/`, `letters/`, or the installed `scripts/letters/` writer, and never drop debug copies beside them. Scratch files (brief, debug scripts, sheets, logs) go in `/tmp` only.
- Don't swap player/companion, re-normalize the roleplay, or overwrite `character_context`.
- An existing writer/hook is never overwritten.
- Never carry another world's ids, letters, or art into this one.
- Keep the creator's visibility setting; `--public-copy` only on their explicit wish.
- Works that stood before this run (any page, app record or slug) are never republished, renamed, re-pointed or deleted — the Letters page is always a new Work or the one `inspect` reported as `existingLettersWork`.
## Package
```text
tools/run.mjs the pipeline tools/inspect.mjs gate + compact context + brief skeleton
tools/theme.mjs brief → theme files tools/assets.mjs parallel art + slicer (--slice for a sheet you already have)
tools/stage.mjs template copy tools/qa.mjs browser regression (auto-finds puppeteer/Chrome)
templates/page engine (TS), CSS, fonts, SDK templates/writer, templates/hooks
references/rules.md full policy references/implementation.md config/slots/engine internals
```
If `tools/` or `templates/` are missing, the fast path is unavailable: say so, and build from `references/implementation.md`.