character-emotes

Verified@baize/character-emotes
Use it in Cohub

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

Use the skill: character-emotes. Run `npx -y @neta-art/neta-search-cli@latest skills use @baize/character-emotes --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 @baize/character-emotes
Summary

Turn one of your characters into a set of chat stickers, each with a caption in their own voice.

  • Get 6 to 9 stickers of the same character, each showing a different feeling: hello, thanks, no, angry, sleepy, and more.
  • Every caption is short and written the way this character would talk, using their habits, favorite props and personality from their card.
  • Pick a look: the character's own art style, a cute chibi version, or one of 123 hand-drawn styles like ink wash, crayon or knitted toy.
  • You get a sticker page: tap to save one sticker, or download a whole set or everything at once. Make new sets later, and ask to redo any sticker by its number.
  • Works best with a character who has a picture and a distinct personality, catchphrases or a signature item.
SKILL.md
# Character sticker pack

One generated sheet, cut into a set of chat stickers of **one** character: same person in every
sticker, each a different emotion, each with a short caption that only this character would say.
The creator gets a sticker-pack page (tap to save one, or save all as a zip) — not a poster, not a
character sheet, not one picture of nine.

## Requirements

This skill decides *what* the stickers are; the neta platform's `generate` skill does the
generating.

- Step 1 (character and look, maybe one questionnaire round) comes first. Only once both are
  settled, read `/mods/neta/.agents/skills/generate/SKILL.md` and
  `/mods/neta/.agents/skills/generate/playbooks/image.md` and follow their core loop (declaration,
  character references, submit, look). Steps 2–6 run inside that loop.
- If `/mods/neta` does not exist in this world, tell the user image generation is not available here
  and stop.
- `<skill-dir>` below = the folder this SKILL.md sits in. `scripts/split-sheet.py` needs `python3`
  with `numpy` and ImageMagick (`magick` or `convert`) — both are in the Studio sandbox.

## Inputs

| Input | Required | Counts as given when |
|---|---|---|
| Character | yes | the message names a character of this world, or carries a selected character card. "My character", a description or a guess from recent works does not count → ask (step 1). |
| Number of stickers | no (9) | the user says a number; 6–9 per sheet |
| Theme / mood of the set | no (everyday chat) | the user says what the set is for ("for hyping up friends", "上班摸鱼用"); asked only as part of a round that happens anyway |
| Caption language | no | the user's own language; the world's language if the user wrote none |
| Look | asked when not given | the user names a look or a style — see **Looks** |

Ask at most **one** round, with the platform questionnaire, and only for what is open: the look
(and the character, when missing) — see step 1. Everything else has a default. When the message
already gives the character and the look, build to delivery without asking anything.

## Looks

| Look | When | How it is made |
|---|---|---|
| **original** | asked for, picked in the questionnaire, or the questionnaire skipped | the character's own image is the reference; the stickers keep its art style (medium, line, colouring, proportions) and only add the sticker format |
| **chibi** | "chibi", "Q版", "cute / 萌一点", "ちびキャラ" | same reference; drawn 2–2.5 heads tall, simplified |
| **hand-drawn** | the user names one of the styles below, in any language, or picks one by its id | style first: the style's own sample image is the only image, the character is described in words, the white die-cut outline is added by the cutting script |

Hand-drawn styles: 123 in `references/handdrawn-styles.json`, each tested as a full sheet. Do not
read that file whole (140 KB); use `python3 <skill-dir>/scripts/styles.py`:
`find "<words>"` (a name in any language, a keyword, an id like `FE-061`, an old handraw number like
"232号"), `get <id>` (name, `prompt`, `note`, `sample` = the style reference image URL, `preview`),
`tiles --kind …` (the questionnaire's choices). Add `--lang <user language>` for names.

- The user asks for a style that is **not** in the list (e.g. "watercolour" that matches nothing,
  "pixel art"): pick the closest `find` result and say which one you used; if nothing is close,
  use the original or chibi template with their words in the style line and say it was not one of
  the tested styles. "Which styles are there?" → name the families (ink, doodle, comic, crayon,
  picture book, flat graphic, print, retro, 3D craft, pixel) with two or three names each in the
  user's language, give the style gallery link (`gallery` in the json plus `?lang=<user language
  code>`: every style with a preview and its id, tap to copy a request), and stop; no generation.
- Style names shown to the user (questionnaire, reply, `pack.js` `style`) use `name.<lang>` for
  en / zh / ja / ko / es / de, `name.en` for any other language.
- The hand-drawn styles are adapted from **handraw-style** by yang0 — credited in this skill's
  `CREDITS.md` and description, not on the creator's pack. The pack page and the reply carry no
  third-party credit or link: the pack is the creator's work.

## Deliver

**One collection page per character**: a published web Work, `works/web/<character-slug>-stickers`
(the CLI may write `works/webs/…`). Every pack made for that character is a **set** on that page,
newest on top: 6–9 transparent PNGs under `stickers/set-NN/`, a filter row to show one set, tap to
save one sticker, save a set or everything as a zip, page UI in the user's language. A new set
re-publishes the same Work, so the Board keeps one card per character. The creator gets a separate
page only when they ask for one ("单独一页", "a separate page"). Handed over with an
interaction-components work card and a numbered list of the new set as it actually came out, then
next-steps. The raw sheet stays an ingredient image work, not placed on the Board.

## Steps

1. **Pick the character and the look.** Read `manifest.json` first.
   - The character is *given* only when the message names it, or carries a selected character
     material. A description, "the selected one" with nothing attached, or a guess from recent works
     is **not given**. Once given, read the atom (`node /mods/neta/neta atom get
     "world/characters/<Name>.md"`).
   - The look is given when the message names one (see **Looks**). "用 X 的样子", "a pack of X",
     "X's face" name the character, not a look.
   - Anything open → **one questionnaire round** following
     [`references/look-questionnaire.md`](references/look-questionnaire.md) (tiles with preview
     images; the `use` question rides along). Nothing is generated before the answers are back.
     Both given → no questionnaire.

2. **Find the reference** exactly as `generate` says: `node /mods/neta/neta find images "<Name>"`,
   existing character / three-view images first, the atom cover otherwise. A cover that is a busy
   scene is still the right reference — the prompt pulls the character out of it. If the character
   has **no image at all**, go on without one, describe the look from the atom, and say in the reply
   that a sticker pack keeps the character steadier once the card has a picture.
   **Hand-drawn look:** still find and open the image — you need it to write the character in words
   (step 4) — but it is not sent to the image model.

3. **Write the set** — 9 stickers by default; the number the user asked for if they did (6–9 per
   sheet; more than 9 → make 9 now and offer the rest as a second sheet). For each sticker write
   `caption — expression + action`:
   - **Cover what people actually send in chat**: hello / on my way, OK / got it, thanks, yes, no /
     refuse, happy, sad / crying, angry, shocked, embarrassed, sleepy / good night, waiting, "?". Pick
     the ones that fit this character; no two stickers with the same emotion.
   - **Make each one this character's**: the caption is in their voice (catchphrases, how they
     address people, how much or little they talk); the action uses their habits, signature prop,
     power, pet or companion from the card. A calm character can be funny by staying calm.
   - **Only what the card or the image says.** Do not invent relationships, items or traits. For a
     well-known IP character, famous canon details (a catchphrase, a signature object) may be used
     when the card does not contradict them; where the card differs from canon, the card wins.
   - Captions follow the user's language (the world's language when the user wrote none). Short:
     ≤ 6 CJK characters or ≤ 3 English words, so they stay readable at chat size.
   - **The `use` answer** (or a theme in the message) steers the mix: hype → cheering, praise,
     "you got this", applause; work → on it, in a meeting, overtime, done, coffee; sass → polite
     refusals, "noted", side-eye, "sure, Jan". Still in this character's voice.
   - Do not stop to confirm the list — the set is cheap to remake. Show it in the reply instead.
   - **This character already has stickers here** (`python3 <skill-dir>/scripts/pack.py find --name
     "<Name>"` prints their page and its sets): the user still asked for a new one — make a new set
     with reactions and captions the earlier sets do not have; it goes on top of that page (step 5).
     Never re-publish or hand over an old set as the result.

4. **Build and submit the sheet.** Original and chibi: fill
   [`references/sheet-prompt.md`](references/sheet-prompt.md). Hand-drawn: fill
   [`references/handdrawn-prompt.md`](references/handdrawn-prompt.md). Keep each template's
   Layout / Format and Constraints lines as written — the cutting script depends on them (flat
   light-grey background, wide gaps, no grid lines).
   - Read the declaration (`node /mods/neta/neta generate image params`). Square output: a
     `size` parameter → `1024x1024`; an `aspect_ratio` parameter → `1:1`. For a 6-sticker 2×3
     sheet use `1536x1024` / `3:2`. Never pass a transparent-background parameter (slow, often times
     out; the script makes the background transparent).
   - Original / chibi: pass the reference with `--image <path>` **and** start the prompt with
     `@<Name>,`. Hand-drawn: pass only the style's `sample` URL with `--image`, no `@<Name>`.
   - **Hand-drawn uses GPT Image 2.5 for this call**: prefix the command with
     `NETA_DEFAULT_IMAGE_MODEL=gpt-image-2.5` (and read `params` with the same prefix). Other models
     draw every hand-drawn style as the same soft pencil look. If `params` with that prefix fails,
     use the creator's model and say in the reply that the style may come out generic. It is
     slower (1–2 minutes): say so in one short line before submitting.
   - Submit **synchronously** with a work title in the user's language:

     ```bash
     node /mods/neta/neta generate image --prompt "$(cat <prompt file>)" \
       --work-title "<Name> · sticker sheet" --image "<reference path>" --param <size or aspect_ratio>
     # a rework: same command plus --target-work <work.id>, and --work-title again
     ```

     If the provider blocks it, follow **If generation is blocked** below.

5. **Look, then cut.**
   - Open the sheet (`mediaPath`) and check: the look is the one asked for (hand-drawn: does it
     look like the style sample, not a generic cartoon?), the same character in every sticker (hair, eyes,
     outfit, signature item), every caption spelled exactly as written and appearing **once**
     (models often repeat a caption under its sticker), the right number of stickers, gaps
     between them. A miss in what is drawn (wrong or missing character, wrong count, a caption that is
     wrong or appears twice, stickers merged) → **one** rework with `--target-work <work.id>` and the same
     `--image`; then go on with the better one and name what is still off. Punctuation slips (a
     final "!" or "?" drawn as a comma, a stray quote mark) are not a miss: do not rework for them,
     do not mention them; `pack.js` captions use the intended text.
   - Find or create the character's page, then cut into the next set folder:

     ```bash
     P=<skill-dir>/scripts/pack.py
     python3 $P find --name "<Name>"            # {"work": "works/webs/juno-reyes-stickers", ...} or {"work": null}
     # no page yet (or the creator asked for a separate one):
     WORK="works/web/<character-slug>-stickers"  # kebab-case ASCII, e.g. dobby-stickers
     node /mods/neta/neta work init web "$WORK" --title "<Name> · <stickers in the user's language>"
     # use the work dir init printed if it differs, then:
     python3 $P init --work "$WORK" --title "<Name> · 表情包 / Sticker Pack" --subtitle "<world name>" \
       --lang <user language code> --file-base <character-slug>
     # either way:
     SET=$(python3 $P next --work "$WORK")      # e.g. stickers/set-03
     python3 <skill-dir>/scripts/split-sheet.py "<mediaPath of the sheet>" "$WORK/$SET" --grid 3x3
     # hand-drawn: add the white die-cut outline the drawing does not have
     python3 <skill-dir>/scripts/split-sheet.py "<mediaPath of the sheet>" "$WORK/$SET" --grid 3x3 --outline 12
     ```

     `--outline` is in sheet pixels: 12 for a 1024 px sheet (about 1.2% of the sheet width).

     `--grid` is ROWSxCOLS (`2x3` for six). Exit 0 = every cell ok. Exit 2 = open
     `$WORK/$SET/preview.png` and `report.json`: `empty` / `merged` (two stickers touching, or a
     piece of a neighbour) → that is the one rework from above if it is still unspent; `tiny` is
     usually a caption-only cell — look before deciding. Always look at `preview.png` once before
     building the page: a sticker with a grey halo means the background was not flat — rework.

6. **Add the set, publish and place.**

   ```bash
   # captions.json = the set's captions in sticker order (the text you intended, not slips in the drawing)
   python3 $P add --work "$WORK" --dir "$SET" --style "<look name in the user's language>" --captions-file captions.json
   # first set of a new page, or an old one-look page becoming a collection: also --title "<Name> · …"
   cp <skill-dir>/templates/index.html "$WORK/index.html"     # always the current page; it reads pack.js
   cp -r <skill-dir>/templates/neta "$WORK/"                  # NETA tokens, Figtree, line icons
   T=$(python3 -c "import json,re;t=open('$WORK/pack.js').read();print(json.loads(t[t.index('{'):t.rindex('}')+1])['title'])")
   sed -i "s|{{PACK_TITLE}}|$T|" "$WORK/index.html"           # the published title is read from the HTML
   node /mods/neta/neta work publish web "$WORK"
   node /mods/neta/.agents/skills/board-ops/scripts/place.mjs --title "$T" --work "$WORK"   # already on the Board → kept
   ```

   - `pack.py` writes `pack.js`; never edit it by hand. An old page from before collections (one
     look, `stickers/NN.png`) becomes `set-01` of the collection the first time a set is added.
   - The look name: original / chibi in the user's language ("原画风", "Q 版", "Chibi"…), or the
     style's `name` from `styles.py`.
   - `world_slug_required` from publish → pick a readable kebab-case slug from the world's name,
     `node /mods/neta/neta world slug set <slug>`, publish again.
   - If publishing still fails, do not fake a page: place the sheet's image work instead
     (`--work <outputs[].work.path>`), say plainly that the separate stickers are in
     `$WORK/$SET/` and the page did not publish.
   - The sheet's own image work is an ingredient: do not place it next to the pack.

7. **Reply and close.** Hand the pack over with an **interaction-components** link card whose target
   is the pack work (`{ "type": "work", "workId": "<cohubWorkId>" }` — print it with
   `jq -r .cohubWorkId "$WORK/meta.json"` and paste that output; never retype an id),
   written as a real `<interaction-json>` block, not in a code fence. Under it, list the stickers
   as `caption — what they are doing`, one line each, so the creator can ask to change one by
   number. Describe what you **saw** in `preview.png`, not what the prompt asked for — when a
   sticker came out different from its line (a different pose, a missing prop), the list says what
   is really there. Hand-drawn: name the style. No links to the style gallery here: links leave
   Studio; another look is one tap away through next-steps.
   Then close with **next-steps**: the first option is **the same character in another look** — its
   subtitle asks for another look without naming one (e.g. "给 Juno 换个画风再做一套"), so the
   questionnaire comes up with fresh styles; then one named look that suits this character
   (original ↔ chibi, or a hand-drawn style — name it); also remake one sticker, a second sheet,
   or a pack for another character in this world.

## Board menu entries

The board menu can send either kind of message: a generic one ("Make a sticker pack of {card}")
that leads to the questionnaire, or a specific one ("…in ink-wash style", "…a chibi pack for
cheering friends on") that names the look and goes straight to the build. Both are normal input;
treat them by the rules above.

## Changing one sticker later

"Change number 4", "make the crying one funnier": rework the **sheet** with `--target-work`, the
same reference (hand-drawn: the same `sample` and model prefix) and the same prompt with only
that line changed, then cut again into that set's own folder (`stickers/set-NN`, from `pack.js`),
run `pack.py replace --work "$WORK" --dir stickers/set-NN` and publish the same `$WORK` again (the
page and its card update in place). "Number 4 of the ink set" → the set by its look name. Do not
hand-edit a single PNG.

## If generation is blocked

**Budget: at most 4 sheet submits for one request** — the first, the one rework, and the two rungs
below. Every submit is a full sheet. Never send single test stickers, probes, or a prompt cut into
pieces to find which line trips the filter: each try costs the creator credits and minutes, and
the filter does not explain itself.

Read the error first:

- **Copyright** ("copyright restrictions", the *generated image* was rejected): the model will not
  draw this well-known character. Rewording does not help, because a faithful sticker *is* the
  copyrighted look. Do rung 2 once at most; if it is blocked too, stop.
- **Prompt rejected** (HTTP 451, "rejected the prompt"): try the rungs in order.

1. Remove anything that reads as a minor (ages, "kid", "student", school uniform words) and add
   "adult character, cute chibi proportions"; replace violent or suggestive actions (weapons,
   blood, injury) with other emotions.
2. Drop the `@<Name>` token **and** the reference image; describe the look in plain words from the
   atom, without franchise or character names, also in the captions.

When the budget is spent or the error is copyright again, stop: say plainly that the image model
refused this character, what was tried, and offer what can work — a pack for an original
character of this world, or for the creator's own version of the character (their own name and
design). Do not leave half-made works behind as if they were results.

## Out of scope

- No changes to world settings, character cards or their covers; the pack only adds its own works.
- No deleting or overwriting files that existed before the run (a reworked pack overwrites only its
  own `stickers/` folder).
- No animated stickers, no uploading to WeChat / LINE / Telegram sticker stores, no packs of real
  people.

## Not this skill

- One picture of the character → `generate`. A design sheet / turnaround → `character-design-sheet`.
- Icons or a symbol set without the character's face → an icon-sheet skill.
- An animated sticker / GIF → not covered yet; offer a still pack, or an animation via `generate`
  video from one sticker.