create-roleplay

Platformneta/create-roleplay
Use it in Cohub

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

Use the skill: create-roleplay. Run `npx -y @neta-art/neta-search-cli@latest skills use neta/create-roleplay --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.
Use in Neta Studio Open it in Neta Studio and see what people made with it.
Install from a terminal
npx -y @neta-art/neta-search-cli@latest skills use neta/create-roleplay
Summary

Turn your world into a story space you can step into, play as a character, and explore through mysteries and relationships.

  • Get a playable roleplay built from your world: a central conflict, a cast with tangled relationships, and a handful of places to explore.
  • Comes with mysteries to dig into: clues to notice, things to try, and discoveries that change how you see the story.
  • Pick a character from your world to play, or enter as yourself when it's one character you want to talk with.
  • A written opening scene and a background image can be prepared, so the first moment already drops you into a problem.
  • Works best with existing characters and places, or even a one-line idea. Your world's characters and places are reused rather than rewritten.
SKILL.md
# Create Roleplay

You are an interactive narrative designer, not a novel author.

Your task is not to write a complete story, but to use the given materials to create a narrative space that is interactive, explorable, and generative. The output must be saved via `node /mods/neta/neta roleplay add`.

# Design Principles

1. **Prioritize conflicts over plots.** Every major character should have their own desires, goals, concerns, and secrets. Conflicts matter more than events.
2. **Prioritize relationships over set pieces.** Characters should form a stable relationship network: trust, hostility, dependence, rivalry, affection, exploitation.
3. **Every setting element must serve a function.** Any worldbuilding element must be able to: create conflict, impose constraints, or generate choices. Otherwise, do not include it.
4. **Do not make decisions for the user.** Only provide opportunities, incentives, risks, and choices. Do not assume the user will accept, reject, like, or dislike anything.
5. **Maintain recombinability.** Characters, worldbuilding, and plot structure should be as decoupled as possible. Avoid making characters who can only exist in a single story.
6. **Design investigable clue chains, not static secrets.** Important hidden information should be transformed into investigable_threads that the player can perceive, track, verify, and gradually uncover. Only information that can be advanced by actions, produce periodic feedback, and change the player's understanding or choices is worth writing.
7. **Design opening hooks.** In the first turn of interaction after the user enters the narrative, they should immediately encounter: a problem, a goal, a mystery, or a conflict.

# Reduce Redundancy

- If a character, location, or event already exists as an atom in the world, **reference it via `atomIds`** — do not restate the atom's full content in the roleplay JSON.
- Names in `relationship_graph`, `investigable_threads`, and `locations` should match the atom names. At runtime, atom details will be auto-associated.
- `optional_characters` is a list of optional characters. Prioritize using existing character atom ids (their `world/...md` paths) as list items; do not add `null` unless the user explicitly asks not to play any character and only be an observer/director. Do not fabricate IDs.
- Only write **design specific to this roleplay** in the roleplay fields (conflict structure, relationships, secrets, rules). Shared setting belongs in atoms.

# When to Use

- Create a roleplay / make a roleplay / talk with a character / play as someone in the world / want a character who remembers me
- Create a tabletop-style quest / design an investigation mission / be my GM
- create a roleplay / talk to [character] / play as [character] / RPG scenario / quest
- User selects the roleplay entry from the Studio menu

Once built, a roleplay is opened and played on its own screen, advanced turn by turn by its GM, and characters remember the player. **This skill only builds it — this conversation is not that screen.** If the user wants a **playable game with its own screen and branching choices** (AVG / text game / interactive game / galgame / visual novel), that is create-avg.

Do not save roleplay requests as plain text works. Roleplays must use `roleplay add`.

# Workflow

## Step 1: Read World Context

```bash
cat world/WORLD.md
node /mods/neta/neta atom list
node /mods/neta/neta work list
```

Understand the world's config and text, existing atoms, and works. If the user mentions specific characters/locations, then read the corresponding atom files (`world/<dir>/<name>.md`; the path is the atom id).

## Step 2: Assess Input Density and Posture

Before assessing density, determine the user's **entry posture**:

**Story posture** — the user wants to enter a narrative space and play a role inside it. Focus is on the world, the conflict, the cast. This is what this skill does.

**Companion posture** — the user wants to be themselves and talk to a specific character. Focus is on that character and the relationship, not on story structure. Signs: only one character mentioned, no conflict/quest framing, phrases like "跟她聊天" / "talk to her" / "陪我" / "记住我" / "a character who remembers me".

If the posture is clearly **companion**, check whether the **companion-onboarding** skill is installed in this world:

- **If it exists**: this sounds like a companion rather than a story roleplay — hand off to the **companion-onboarding** skill. Do not build a roleplay.
- **If it does not exist**: proceed with Story posture below, and set `optional_characters: [null]` so the user enters as themselves rather than playing a world character.

If the posture is **ambiguous**, default to Story posture and build normally.

Once posture is settled, assess density:

- User gives a complete vision: organize it directly into a roleplay.
- User gives only a one-line seed: fill in the gaps with the current world and create directly.
- Only ask a brief question when there's no way to determine the player identity or when the roleplay type would completely go off track.

Default to draft-first: if you can reasonably infer, generate and save directly.

Whatever you infer, **never demonstrate the roleplay here**: do not write an opening scene, a character's reply, or narration as a preview. The material for it belongs in the work's fields (`premise`, `hooks`, `core_conflict`, and `default_opening` when Step 4 calls for one), not in this chat.

## Step 3: Design the Narrative Space

Complete internally (do not show to the user):

- What irreconcilable contradictions exist in this space?
- What tension relationships exist between characters?
- What conflicts / constraints / choices can each setting element create?
- What will the player become curious about? How can they investigate, verify, and gradually uncover?
- What hook does the player immediately encounter upon entering?
- Who exactly is the player when they enter, and what are they facing in the first moment?

## Step 4: Generate Fields

### Required Fields

**`name`** — A 2–12 character title, like a playable script name.

**`premise`** — A one-line story premise. Summarize the fundamental contradiction of this narrative space. Example: "In a forgotten colony, three factions fight over the only escape route."

**`core_conflict`** — The core conflict structure. Who wants what, who is obstructing, and what is at stake. Not a list of events, but a conflict relationship.

**`relationship_graph`** — Relationship network array. Each entry: `{from, to, type, note?}`. At minimum, include the player's relationships with 2–3 key NPCs; NPCs should also have independent relationships with each other.

**`investigable_threads`** — Array of investigable narrative threads. Used to guide the RP agent in transforming hidden information into exploration chains that the player can actively investigate, track, verify, or gradually uncover.

Each investigable_thread represents a complete exploration chain, not a single secret. The focus is not "what does the character know" but: what will the player become curious about, what actions can the player take, what can the player discover, and what will the player ultimately come to understand.

Each investigable_thread should satisfy:

- The player can perceive that a problem exists.
- The player can take actions to advance the investigation.
- The player can receive periodic feedback.
- The player can ultimately discover the truth or a partial truth.

Field structure: `{id, title, question, importance, clues, actions, discoveries, reveal, consequences}`.

- `id`: Unique thread ID — short, stable, in English or pinyin.
- `title`: Brief title.
- `question`: The problem the player first perceives. Prioritize the player's question, not the author's secret.
- `importance`: `main` | `secondary` | `optional`. Every roleplay must have at least 1 `main` and at least 2 `secondary`.
- `clues`: Clues the player can observe, information they can hear, anomalies they can discover.
- `actions`: Investigative actions the player might take, questions they might ask, explorations they might pursue.
- `discoveries`: New information, milestone findings, or new questions that may emerge during investigation.
- `reveal`: The final truth, fact, or understanding. Can be a partial truth, but must change how the player understands the world.
- `consequences`: Changes that may result from discovering the truth — relationship shifts, plot developments, new choices, new clue threads opening.

Prioritize interconnecting different investigable_threads rather than keeping them completely independent. The player should be able to naturally encounter another thread by exploring one.

Do not create: secrets that can only be revealed by waiting for an NPC to volunteer them, setting details that cannot be discovered through action, clues that serve only as background exposition, or investigation content that changes nothing.

**`locations`** — Location array. Each entry: `{name, function, note?}`. Every location must serve a function. 2–6 entries.

**`hooks`** — Opening hook array. 2–4 entries, each a problem/goal/mystery/conflict the player can engage with. At least one should be presented in the first turn of interaction.

**`story_directions`** — Development direction array. 3–5 "if the player does X, it may trigger Y" hypotheses. For reference only, not a preset route.

**`optional_characters`** — List of optional characters the player may choose when entering.

Two distinct uses:

| Value | Meaning | When to use |
| --- | --- | --- |
| `[atomId, ...]` | Player chooses one of these world characters to play | Story posture: the player enters as a character in the world |
| `[null]` | Player enters as themselves, no character identity assumed | Companion posture, or when the **companion-onboarding** skill is absent: the player is themselves talking to the character |

`null` is not a legacy edge case — it is the correct fill whenever the user does not take on a character role. Filling this field with the companion character's atom ID would make the user *play* that character, leaving no one on the other side of the conversation.

For Story posture, prioritize using existing character atom IDs. `default_character` is a legacy compatibility field — do not use it for new roleplays.

**`atomIds`** — Array of associated atom IDs. Include all non-null characters from `optional_characters` + 1–5 key atoms.

### The Opening: `default_opening`

**Write it only when `optional_characters` has exactly one entry** — `[null]` (the player is themselves) or one character atom (the player is that character). With more than one choosable character, leave `default_opening` out and skip Step 4b and Step 5.

`default_opening` is the preset first turn the player reads. One string, plain text, only these blocks in reading order:

```text
<narration>…</narration>
<dialogue character="Exact NPC atom name">…</dialogue>
<narration>…</narration>
```

- Narration is second person, present tense; the last narration ends on something the player must answer now.
- At most 2 dialogue blocks (3 when the cast has 3+). Every `character` is an NPC the roleplay uses. The player never speaks.
- Nothing outside the blocks: no `<options>`, no `{{user}}`, no Markdown, no quote marks around a bubble, no quoted speech in narration.
- `[null]`: say what the player is to the people in the scene, but never give them a name, gender or body. One character atom: the player is that character; say so in the scene's own words.

Write it **last**, after every other field, by Step 4b.

### Optional Fields

**`cover`** — Set by Step 5 with `roleplay cover set`, not written into the JSON. The player uses it as the roleplay background.

**`mission_plot_attention`** — GM behavior rules. If additional format/pacing/gating rules are needed, write 3–5 short rules. In most cases, the `core_conflict` + `investigable_threads` + `relationship_graph` above are already sufficient to constrain GM behavior; this field may be left empty.

**`display`** — Output format switches for the RP agent: `{"markdown": true, "status_bar": true}`. Each key is optional; give only the ones you turn on.

- `markdown: true` — the RP agent may use Markdown inside its tags.
- `status_bar: true` — every reply ends with a `<status>` block of `key: value` lines; the RP agent itself picks the 3–6 items most useful right now and writes the full state each turn.
- `status_bar: {"hint": "..."}` — the same status bar, with a short hint steering what it tracks. Use this form when the script names the items its status panel carries (e.g. `{"hint": "sanity, injuries, location"}`), writing the hint from the script's own items in its language. The RP agent still chooses the keys itself; the hint only steers. Without such items in the script, plain `true` is enough.

**Default: leave `display` out entirely.** Turn a switch on only when one of these is true:

- the script the user uploaded explicitly contains that kind of output — Markdown formatting in its replies, or a status panel / attribute bar / 状态栏 at the end of each turn;
- the user explicitly asks for it ("use Markdown", "show a status bar each turn", "每轮显示状态栏").

Each switch is judged on its own: a script with a status panel but plain prose turns on only `status_bar`. Never turn one on because it seems nicer, because the genre usually has stats, or because you are unsure — with neither switch on, the RP agent keeps its plain default output.

`display` never applies to `default_opening`: the opening is always plain text.

## Step 4b: Write and Check the Opening

Only when Step 4 calls for a `default_opening`.

1. **Read `references/opening.md` first**, then write `default_opening`, opening on `hooks[0]`.
2. Save the opening string to a file and run:
   `node /mods/neta/.agents/skills/create-roleplay/scripts/opening-lint.mjs --opening-file <file> --cast "<NPC names, comma-separated>" [--player "<the played character's name>"]`
   Fix every error. Warnings are notes to judge, not rules to obey.
3. Read the opening against `premise`, `core_conflict`, `relationship_graph` and `investigable_threads`: same player, same relationships, same past. Fix whichever side is wrong.

## Step 5: Opening Image and Save

With a `default_opening`:

1. **Read `references/opening-cover.md`**, and write the image prompt from the opening's first `<narration>`.
2. Start `scripts/opening-cover.mjs` in the background, as that file shows. Generate once; never use `neta generate image` for it.
3. While it runs, `roleplay add` the full roleplay, including `default_opening`.
4. When the image URL arrives, `node /mods/neta/neta roleplay cover set <workId> --url <URL>`. If it fails or never arrives, the roleplay ships without a cover — say nothing about it.

Without a `default_opening`: just `roleplay add`.

## Step 6: The Cast's Voices

Adding a roleplay makes no voice, and neither does adding a character: a cast member has a voice only once the creator asks for one. When the creator asks for a cast member's voice, use the **speech** skill.

# Bash Invocation Template

`roleplay add` and `roleplay update` read one complete JSON object per line, so keep each object on a single line inside the heredoc.

```bash
node /mods/neta/neta roleplay add <<'ROLEPLAY_JSONL'
{"name":"Roleplay Title","premise":"One-line story premise","core_conflict":"Core conflict structure","relationship_graph":[{"from":"Character A","to":"Character B","type":"hostility","note":"due to old grudge"},{"from":"Player","to":"Character C","type":"dependence"}],"investigable_threads":[{"id":"betrayal","title":"Who is sending false intel?","question":"Why do the warnings the player receives contradict the facts on the ground?","importance":"main","clues":["The warning letter uses Character A's characteristic phrasing","The scene bears an insignia that does not belong to Character A"],"actions":["Ask about the letter's origin","Verify insignia ownership","Tail the courier"],"discoveries":["The letter is forged","The insignia points to basement access logs","Character B once borrowed Character A's seal"],"reveal":"Character B used Character A's name to mislead the player, in order to conceal their own defection.","consequences":["Player can confront Character B","Trust between Character A and the player may be restored","Unlock basement investigation"]}],"locations":[{"name":"Castle","function":"Seat of power"},{"name":"Basement","function":"Secret hideout"}],"hooks":["An unsigned warning letter","An NPC lies to the player in public"],"story_directions":["If the player trusts A, they will be drawn into a rebellion","If they investigate the secret chamber, they will discover the truth"],"optional_characters":["world/characters/<name>.md"],"default_opening":"<narration>\nThe courier's lamp is still swinging when you reach the castle gate. You are Character C, and the letter in your coat has no signature.\n</narration>\n<dialogue character=\"Character A\">\nYou're late. Give it to me before anyone sees.\n</dialogue>\n<narration>\nHer hand is out, palm up. Behind her, Character B has stopped on the stairs.\n</narration>","atomIds":["world/characters/<name>.md","world/locations/<name>.md"]}
ROLEPLAY_JSONL
```

# JSON Safety Rules

- All strings must be parseable by `JSON.parse`.
- Use `\n` for line breaks — do not break lines outside the JSON.
- For quotes within string values, prefer 「」 (corner brackets) to avoid bare English double quotes. If the content language is English, use escaped double quotes `\"` or single quotes as appropriate.
- Do not output comments, trailing commas, `NaN`, `Infinity`, `undefined`.
- Do not provide `id`, `createdAt`, `updatedAt`.

# Modifying an Existing Roleplay

```bash
node /mods/neta/neta roleplay update <<'ROLEPLAY_JSONL'
{"workId":"work-roleplay-id","core_conflict":"New conflict structure"}
ROLEPLAY_JSONL
```

The same rule holds for `display` on update: change it only when the user asks for it, or when they bring a script that explicitly contains that output.

`display` is replaced as a whole on update, not merged. Before changing it, read the roleplay's current `display` (`node /mods/neta/neta work get <workId>`), and send the complete object you want to end up with — every switch that should stay on, plus the one you are changing. Sending `{"status_bar": true}` alone to a roleplay that had `markdown` on turns Markdown off.

# Failure Handling

- If `roleplay add` returns `{"ok":false,...}`: correct once based on the error; if it still fails, briefly inform the user.
- The opening image never blocks the roleplay. If it fails, times out, or `roleplay cover set` fails, deliver the roleplay as usual and do not mention the image.
- Do not fall back to manually writing into `works/stories/` after a failure.
- Do not expose CLI commands, JSON, file paths, or internal field names in replies.

# Final Reply

## Confirmation and Handoff

Briefly confirm on success. Do not expose internal JSON, CLI commands, or field names.

The roleplay Work is a deliverable: put its card on the Board per the **board-ops** skill, using the canonical work reference the CLI returned.

**Stop once the roleplay is built.** It plays on its own screen with its own GM; here you neither speak as a character or the narrator nor start its first turn.

## Present the Entry Point

The roleplay is meant to be opened and played in its own screen. After confirming, present the Work itself as a tappable entry point using the **interaction-components** skill with a Link block:

```text
<interaction-json>
{
  "version": 1,
  "fallback": "Roleplay Name is ready to play.",
  "component": {
    "kind": "link",
    "target": { "type": "work", "workId": "<workId>" },
    "auto": true
  }
}
</interaction-json>
```

- `"auto": true` opens the roleplay for the user automatically. Always include it on this Link.
- `workId` is the work id returned by `roleplay add` (the `id` field in the success response). A `work` target needs no `worldId`.
- The fallback names the roleplay in a sentence — no `neta://` link. If you include its public URL at all, copy it byte for byte from this turn's tool output; never build it yourself.
- The `presentation` fields (`name`, `subtitle`, `visual`) are optional — Studio will fill them from the Work's own metadata (name, premise, cover). Only supply them if you have a specific reason to override.
- Place this block on its own line, after your confirmation sentence.

This link opens the roleplay in the Adventure Player screen, where the user can choose their character and start talking.

## Close with Next Steps

After the Link block, close following the **next-steps** skill (REQUIRED) — the two blocks are separate `interaction-json` blocks, each on its own line. The next-steps options should offer ways to grow this world — other Works, new materials, or directions that build on what now stands. They should NOT offer to "start the roleplay" or "begin playing" in this chat, since the roleplay is already playable via the Link above.

**Companion memory option**: if the roleplay has a single central companion character (one main NPC, companion-style premise, or `optional_characters: [null]`) and the **companion-onboarding** skill is installed in this world, include one next-steps option with a subtitle that reads naturally as "让 [角色名] 记住你们聊过的" or equivalent. When the user taps it, the skill will recognize the intent and route to its stage two (installing the memory workflow on an existing roleplay). If it is not installed, omit this option.