world-ops

Platformneta/world-ops
Use it in Cohub

Paste this into your agent's chat — it fetches the skill and follows it.

Use the skill: world-ops. Run `npx -y @neta-art/neta-search-cli@latest skills use neta/world-ops --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.
Install from a terminal
npx -y @neta-art/neta-search-cli@latest skills use neta/world-ops
Summary

Make quick, precise changes to a world you've already built: add, edit, move or delete one thing, or fix its cover.

  • Look up what's in your world, find a character or place by name, and check whether a card's cover is ready yet.
  • Add a single character or location, rewrite its details, rename it, move it into another folder, or delete it for good.
  • New characters and locations get a cover picture drawn automatically. You can also ask for covers on the ones that have none yet.
  • Use an image you already have as a card's cover, or redraw a cover after you've changed that character's details.
  • Change one world setting such as name, tone or visual style, set or clear the world's cover image, or save an uploaded picture as a work.
SKILL.md
# World Ops

Surgical, single-item edits to a world that already exists. Every branch is one class of `neta` CLI call against the current world's atoms, works, or config. This skill never builds a world from scratch and never runs a batch reveal — that is create-world / world-reveal.

## Route

Pick the one branch that matches the ask. If the ask is a whole world, a batch, a new generation, a canvas layout (that is board-ops), a web work, or a roleplay, it is not world-ops — hand it to the skill that owns it.

| User intent | Branch |
|---|---|
| See what's in the world; find an atom/work | [Inspect](#inspect) |
| Add one atom to the current world | [Add atom](#add-atom) |
| Change an atom's name, aliases or text | [Edit atom](#edit-atom) |
| Move atoms into another folder | [Move atom](#move-atom) |
| Permanently delete an atom | [Remove atom](#remove-atom) |
| Set / requeue an atom's cover from an image already in hand | [Atom cover](#atom-cover) |
| Draw covers for atoms that have none ("generate the rest") | [Missing covers](#missing-covers) |
| Change one worldConfig field (name, tone, visual style, …) | [World config](#world-config) |
| Set the world's public address (slug) | [World slug](#world-slug) |
| Set / clear the owner's world cover image | [World cover](#world-cover) |
| Import an uploaded image file as a work | [Import work](#import-work) |

## Shared rules

- **Read before write.** Run `atom list` (and `work list` when works matter) first to see what exists. Never recreate an atom whose name already exists.
- **Ids.** An atom's id is its file path, e.g. `world/characters/林恩.md`; work ids are CLI-generated. Use the ids the CLI returns or lists; never invent one.
- **Covers arrive on their own.** `atom add` auto-queues a cover for character/location atoms; other types stay text-only until the user asks. Do not use the **generate** skill to make a card's cover appear. A finished cover is the first image in the atom's `media`; a cover still drawing shows only through `atom cover status` (see [Inspect](#inspect)).
- **JSON shape is in the CLI.** For the exact stdin fields of any write command, read `node /mods/neta/neta <resource> <action> --help`. This skill states only world-ops judgment, not the schema.
- **NDJSON on stdin.** `add` / `update` / `mv` take one complete JSON object per line via heredoc — never pretty-print a single object. Use `「」` for quotes inside Chinese values to avoid bare `"`.
- **Write atoms in the world's language**, matching the language of existing atoms; do not mix.
- **Board.** A new atom is world material and an imported work is a deliverable — both go on the Board per the **board-ops** skill (it dedupes against cards that already landed). Edits, moves, and cover changes keep the same card reference; leave the board alone.
- Close every delivery turn following the **next-steps** skill (REQUIRED). Do not expose CLI commands, JSON, or field names in the reply.

## Inspect

Read-only. No backup, no confirmation.

```bash
node /mods/neta/neta config get                 # whole worldConfig, or pass keys
node /mods/neta/neta atom list --type character # optionally --query <text>
node /mods/neta/neta atom get <id...>
node /mods/neta/neta atom cover status <atomId>  # cover task state; a finished cover is media[0]
node /mods/neta/neta work list                  # optionally --type <type>
node /mods/neta/neta find atoms "<text>"
```

Done when: the requested state is read and reported in the user's language.

## Add atom

Adding one atom to a world that already has a shape. Match that shape rather than a fixed template.

1. `atom list --type <type>`, then `atom get` one existing atom of the same type to mirror how its body is written (this world's convention wins over any default layout).
2. Add it:

   ```bash
   node /mods/neta/neta atom add <<'ATOM'
   {"type":"character","name":"...","body":"Free Markdown text","aliases":["..."]}
   ATOM
   ```

   `body` is free Markdown; no headings are required. Keep the CLI's default cover flow — do not pass `--no-cover` unless the user asked for no image.
3. Read the returned `{"ok":true,"added":N,"ids":[...]}`. On `{"ok":false,...}`, fix once from the error; if it partially succeeded, report what landed and stop.
4. Adding a character makes no voice, and none is made later by itself: a character gets one only when the creator asks, in the chat (the **speech** skill) or with Edit on its card. Go on with the next step, and do not say a voice is on its way.

Done when: the atom exists (`ids` returned) and the reply names it in the world's terms. The cover appears on its own; the card goes on the Board per [Shared rules](#shared-rules).

## Edit atom

```bash
node /mods/neta/neta atom update <<'PATCH'
{"id":"world/characters/林恩.md","name":"...","body":"..."}
PATCH
```

Each line carries the atom id plus only the fields to change: `name`, `type` and `aliases` replace those frontmatter fields, `body` replaces the whole body. Read the current body with `atom get` first and send the full edited text. Renaming does not move the file; to give the file a new name, move it yourself and fix references as in [Move atom](#move-atom).

Done when: `{"ok":true,"updated":N,...}`. If the edit changed facts that a character/location cover should reflect, offer a cover requeue via [Atom cover](#atom-cover) — do not requeue unasked.

## Move atom

Move atom files into `world/<folder>/`. The path is the id, so the returned `ids` are the new ids. The moved file's own `media` paths are rebased; type and text do not change.

```bash
node /mods/neta/neta atom mv <id...> --folder <folder>
# or NDJSON: {"id":"world/characters/林恩.md","folder":"main-quest"}
```

Links in other files that point at the old path are not rewritten. Search for the old path (`find`, or grep under `world/` and `works/`) and update each link yourself.

A character's voice (its pack, which holds the card line, and earlier versions) moves with the card.

Done when: `{"ok":true,"moved":N,...}` and no file still links the old path.

## Remove atom

`atom rm` permanently deletes the atom file — different from canvas removal, which only takes a card off the board. Confirm the user means to delete the atom itself, not just hide the card (that is **board-ops**). Deleting the atom leaves its card on the board showing "content deleted" — ask whether to remove the card too, and do that via board-ops.

```bash
node /mods/neta/neta atom rm <id...>
```

Done when: `{"ok":true,"removed":N,...}` and the reply states the atom is gone.

## Atom cover

Only for an image the user already has, or requeueing after a facts edit. Generating a new image from a prompt is the **generate** skill; restyling every cover in the world is its batch-restyle reference.

```bash
node /mods/neta/neta atom cover set <atomId> --work <workId>   # or --url <url>
node /mods/neta/neta atom cover retry <id...>                  # requeue after editing atom facts
node /mods/neta/neta atom cover status <atomId>                # check state without blocking
```

Done when: `set` returns the cover's `path`, or `retry` returns `{"ok":true,"retried":N,...}` and you tell the user each cover regenerates on its own — never call a `generating` cover finished.

## Missing covers

Atoms without a cover get their first one through the same cover pipeline as automatic covers — never `neta generate image`, which skips the world's cover style and reference images. Each cover spends one image from the user's quota, so draw only the atoms the user named or asked for.

```bash
node /mods/neta/neta atom list --missing-cover     # optionally --type character
node /mods/neta/neta atom cover retry <id...>      # draws a first cover when none exists
```

If the user asks for "all" and more than 10 are missing, state the count and confirm once before drawing.

Done when: `retry` returns `{"ok":true,"retried":N,...}` and you tell the user the covers appear on their own — never call a `generating` cover finished.

## World config

Edit one or a few world config fields on an existing world. `name`, `genre`, `tone`, `era`, `visualStyle` and `language` go to the `world/WORLD.md` frontmatter; `body` replaces the WORLD.md body.

```bash
echo '{"name":"...","tone":"..."}' | node /mods/neta/neta config set
```

`config set` accepts only real worldConfig fields; an unknown key (a typo, or
`worldSlug`) fails that input instead of being silently dropped. The world slug
is not a worldConfig field — see [World slug](#world-slug). Changing `name` here
does not change the public slug.

Changing `visualStyle` only sets the default for images generated afterward; it does not refresh existing covers. If the user wants the whole world restyled, that is the **generate** skill's batch-restyle reference, not this branch.

Done when: `{"ok":true,"updated":N,...}` and the reply names what changed.

## World cover

The owner-authored world cover is a single image that overrides the auto-collage on every world surface. It is the first image in the `media` of `world/WORLD.md`; `set` downloads it there, and clearing it falls back to the collage.

```bash
node /mods/neta/neta world cover get
node /mods/neta/neta world cover set --work <workId>   # or --url <url>
node /mods/neta/neta world cover clear
```

Done when: the command returns the new `worldCover` value (or `null` for get/clear).

## World slug

The world's public address segment — the Cohub space slug, the `:worldSlug` in
`/:owner/:worldSlug/:workSlug`. It is not a worldConfig field, so `config set`
cannot carry it; use the dedicated command. A web work cannot publish until the
world has a slug.

```bash
node /mods/neta/neta world slug get
node /mods/neta/neta world slug set <slug>
```

`<slug>` is lowercase letters, numbers, hyphens and underscores, 1-80 chars,
starting and ending with a letter or number. Choose a short, readable slug from
the world's name or theme (romanize a non-Latin name — 雾港 → `misty-harbor`),
never the world id or a UUID. Setting it changes where existing shared links
point, so treat it like a rename.

Done when: `{"ok":true,"data":{"worldSlug":"..."}}` and the reply names the new
address.

## Import work

Save an image the user already uploaded as an image work, without generating anything. The CLI imports image files only (png/jpg/jpeg/gif/webp).

Chat uploads arrive as a URL, so download the file first:

```bash
curl -fsSL -o uploads/<name> '<url>'
node /mods/neta/neta work import image --file uploads/<name> --prompt "short description of the image"
```

Optional: `--work-title`, `--work-description`.

Done when: `{"ok":true,"data":{"workId":"..."}}` and the reply names the new work. The work card goes on the Board per [Shared rules](#shared-rules).