create-web-work

Platformneta/create-web-work
Use it in Cohub

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

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

Turn an idea into a working web page or browser game, published with a link you can open and share.

  • Make web pages, interactive demos, clickable prototypes, small browser games and simple apps, such as a character quiz or a basic world map page.
  • Pages follow your world's visual style and reuse its existing characters, places and lore instead of inventing new facts.
  • When it's done you get a live link that opens in any browser, plus a card for the page on your board.
  • Works for changes too: restyle, fix, or update a page you already made when it isn't showing your edits.
  • Key game pictures are generated as real artwork, not rough placeholder shapes. Pages avoid scroll bars and use compressed images.
SKILL.md
# Web Work

Turn the user's request into a directory-based page-work that can be previewed in Studio and opened directly in a browser. The source project stays in `works/web/<slug>/`; the final browser preview is published as a Cohub Work (whose page URL is recorded in `meta.json`).

## When to use

Match any of these intents:

- "做网页" / "写网页" / "生成网页" / "做一个页面" / "HTML 页面" — make, write, or generate a web page / a page / an HTML page
- "互动网页" / "交互式页面" / "交互演示" / "可点击原型" / "browser demo" — interactive page, interactive demo, clickable prototype
- "网页游戏" / "浏览器小游戏" / "web game" / "mini game" / "web app"

If the user wants a playable story, story adventure, or adventure game with a screen of its own, use the `create-avg` skill.
If the user wants an ongoing in-chat roleplay with a character (which may carry memory), including tabletop-RPG-style play, use the `create-roleplay` skill.
If the user wants the world or a story written as readable novel chapter pages, use `novel-chapter-pages`.
If the user wants an interactive illustrated world map / living atlas with generated location art, detail maps, and POIs, use `world-map-experience`. If they already have a map image and want hotspots added, use `map-hotspot-annotator`. A plain 3D pointcloud world map, or any other simple map page, can be built here directly as a web work.

## Workflow

1. Read `world/WORLD.md` and run `neta work list` once first, to learn the world's config and text, the existing works, and whether this page should follow the world's theme.
2. If the page depends on existing world content, additionally read the minimum set of related atom / work files.
3. Give the work a kebab-case slug (for example `character-quiz`, `world-map-prototype`); it is both the work id and its directory name in the file tree. Run `ls works/web/` first to avoid duplicates.
4. **Declare the dir-work stub via CLI as the first step of the build**: `node /mods/neta/neta work init web <work-ref> --title "<work title>"`. The positional argument is the work-ref (a directory path relative to the workspace root, such as `works/web/character-quiz`, i.e. `works/web/` + slug), not the bare slug. Placeholder cards are handled fully automatically: if the user placed a placeholder card on the canvas first to pin the drop location, Studio injects the placeholder card id into this turn as the environment variable `NETA_PLACEHOLDER_ITEM_ID`, and the CLI reads and consumes it on its own — do not pass `--placeholder-id`, and do not dig through hidden context for the placeholder card id. Do not hand-write the initial `meta.json`; the CLI reads `sourceTurnId` from `COHUB_TURN_ID` and `sourceSessionId` from `COHUB_SESSION_ID`.
5. **Write the workspace source copy** to `works/web/<slug>/`. Writing only to the public-share directory is not allowed; keep the source files even for a simple page.
6. **Build according to the tech stack**:
   - No build step: the source files are themselves the static output; `works/web/<slug>/index.html` must open directly in a browser.
   - Vite / Next / other projects: run the matching build command to produce statically servable frontend files; the final publish entry must point to build output such as `dist/`, `out/`, or `build/`, not the source directory.
   - SSR / Express / projects that need a backend: the public share carries only the static frontend; the backend can stay exposed via sandbox server-mode.
7. **Complete the publish and the `meta.json` ready write in one CLI command**: do not hand-write `/public`, do not hand-assemble `preview.url`, do not hand-write `meta.json.preview`. The publish source must be the final, directly openable static directory; the entry file defaults to `index.html`, and when it is named differently, point at it with `--entry`:
   - No build step: `node /mods/neta/neta work publish web <work-ref>`
   - With a build step: `node /mods/neta/neta work publish web <work-ref> --from <work-ref>/dist` (substitute the actual output directory)
   - Entry not named index.html: append `--entry <relative-path>`, for example `--entry app/main.html`
   - If publish returns `"code":"world_slug_required"`, the world has no public address yet. Pick a short, readable kebab-case slug from the world's name or theme (lowercase letters, numbers, hyphens — e.g. `misty-harbor`; romanize a non-Latin name), set it once, then re-run the exact same publish:
     `node /mods/neta/neta world slug set misty-harbor`
     If the command reports the slug is taken, pick another. Never fall back to the world id or a random string: the slug is the address people will see.
8. If you have already run init but confirm the work cannot be completed, run `node /mods/neta/neta work fail web <work-ref> --error "<failure reason>"` to settle the state; do not leave the card building forever.
9. **Board.** The shipped page is a deliverable: put its work card on the Board per the **board-ops** skill (it reuses the card if a placeholder already pinned it). Images and assets used inside the page do not go on the board.
10. Local verification: at minimum confirm `works/web/<slug>/meta.json` exists with `status: "ready"` and a `preview.kind` (the publish command prints both). Content is served from Cohub's work asset CDN; curling the public URL is not required.

## Cohub runtime capability

If a web work involves chatting with an LLM, generating media, reading space files, or any other Cohub runtime capability, it will almost certainly use the `@neta-art/cohub` SDK — install a fresh copy and read its README before writing any SDK code.

Works that use the SDK must first run `neta work register web <work-ref>` — this persists the Cohub work id and token scopes to `meta.json`, which the Studio-as-Host bridge needs to authorize the runtime. `work publish web` registers the Cohub work record and publishes a version whenever `COHUB_SPACE_ID` / `COHUB_EXECUTION_TOKEN` are present, and registration failure is fatal (no `previewUrl`, no thumbnail, Studio falls back to SpaceFS). Invoke `register` explicitly only for SDK works that publish separately.

## Design and implementation preferences

- When building titles, measure the relationship between font size, text length, and container size to avoid broken layout.
- Keep components lean; avoid deep nesting.
- Do not use browser scroll bars.
- Compress asset resources; do not use raw full-size images.
- Key game assets must be produced with the image generation model; do not draw abstract SVG placeholders yourself.

## Publish / meta rules

Key rule: start by writing the `status=building` stub with `neta work init web`; the publish action and the `meta.json` ready write must land in the same CLI command, so `meta.json.preview` stays consistent with the Cohub Work that was actually published.

A published web work needs the world (Cohub space) to have a slug — the public URL is `/:owner/:worldSlug/:workSlug`. When publish returns `"code":"world_slug_required"`, set a readable slug with `neta world slug set <slug>` (see step 7) and retry; do not invent a default.

Forbidden:

- Do not hand-create or write into `/public/works/...`.
- Do not create or write into `/workspace/public`, `./public`, or `public/works/...`.
- Do not hand-assemble or write `meta.json.preview.url`.
- Do not treat a source directory that still needs a build as the final preview entry.

Use the CLI (adjust to the actual slug):

```bash
SLUG="character-quiz"
WORK_REF="works/web/$SLUG"
node /mods/neta/neta work init web "$WORK_REF" --title "Character Quiz"
# No manual placeholder handling: Studio injects the environment variable
# NETA_PLACEHOLDER_ITEM_ID automatically, and work init reads and consumes it.
# Do not pass --placeholder-id yourself.

# No build step: $WORK_REF/index.html is already the final static entry
node /mods/neta/neta work publish web "$WORK_REF"

# With a build step: build first, then publish the directly servable static output
# pnpm --dir "$WORK_REF" build
node /mods/neta/neta work publish web "$WORK_REF" --from "$WORK_REF/dist"

# When the entry file is not index.html, point --entry at it (relative to the published directory)
node /mods/neta/neta work publish web "$WORK_REF" --entry app/main.html
```

The CLI writes the building stub at init. At publish it registers (or updates) the Cohub work record, posts a new work version (this is what copies the directory to Cohub's work asset CDN), fetches the resulting Cohub Work page URL, writes it to `meta.json.preview` with `kind: "cohub-work"`, preserves `sourceSessionId` / `sourceTurnId`, marks `works/web/<slug>/meta.json` as `ready`, and schedules a screenshot thumbnail. Nothing is written to `/public`: the thumbnail PNG is uploaded to the space's public file area. If the specified entry cannot be found in the `--from` directory, that directory is not a publish directory a browser can open directly — fix the build output, `--from`, or `--entry`.

## meta.json format

```json
{
  "status": "ready",
  "sourceSessionId": "<session-id>",
  "sourceTurnId": "<turn-id>",
  "title": "Work title",
  "serve": { "kind": "static", "entry": "index.html" },
  "preview": {
    "kind": "cohub-work",
    "url": "https://cohub.run/:owner/:space/w/<slug>",
    "workId": "01J...",
    "publishedAt": "2026-06-08T00:00:00.000Z",
    "sourceVersion": "<slug>#sha256:..."
  }
}
```

`serve` describes the entry/type of the workspace source copy. `preview.url` is the Cohub Work page URL (returned by the API, never constructed locally); the underlying static-asset URL is resolved by Studio at render time and is never written to `meta.json`. `preview.sourceVersion` is a content-keyed hash (`<slug>#sha256:...`) used to skip redundant thumbnail regenerations. The legacy `kind: "public-share"` preview (with `publicPath`) is only written when Cohub registration is skipped — do not target it for new works.

This file is written by `neta work init web` and `neta work publish web`; the JSON above only documents the final shape. Do not hand-maintain the lifecycle or `preview` fields.

## Dev server / backend notes

When the project needs a backend or a dev server:

- The static frontend must still be published as a Cohub Work via `neta work publish web <work-ref> --from <static-output-dir>`; do not hand-write into `/public`, `/workspace/public`, or a relative `public/` path.
- The frontend cannot connect to `localhost`; it must connect to the real public backend URL exposed by the sandbox.
- The server must allow the Cohub Work page origin (a `cohub.run` host): handle CORS, allowed hosts, and WebSocket origin.
- `serve.kind: "server"` only marks the source project / dev / backend mode; when `preview.url` exists, Studio opens the Cohub Work preview first.
- **Vite** — set `server.allowedHosts: ['.cohub.run']` in `vite.config`.
- **Next.js / Webpack Dev Server** — `allowedHosts: ['.cohub.run']` or the equivalent setting.
- **Express and other self-listening servers** — skip Host validation or explicitly allow the cohub public origin.

## Output rules

- The directory `works/web/<slug>/` must exist and contain the source files plus the `meta.json` last written by the CLI.
- `works/web/<slug>/meta.json` must have `status: "ready"` and a `preview` block (kind `cohub-work`) after publish; `preview.url` is the Cohub Work page URL.
- Reference same-directory files with relative paths (`<script src="app.js">`, `<link rel="stylesheet" href="style.css">`), not workspace-absolute paths.
- Do not cram the whole page into one HTML file (unless there truly are no external scripts / styles). When the HTML runs long, or contains clearly separable JS / CSS, split it into multiple files.
- Do not build it as an image, a video, an AI Roleplay, or a plain text work.

## Details

- Page style follows the world's `visualStyle` first.
- When neither the creator nor the world's `visualStyle` settles the look, don't fall back on stock web defaults: a cream or off-white background, italic accent words in headlines, numbered "01/02/03" section labels, monospace labels, pill-shaped buttons.
- When the page needs existing world facts, reuse them; do not re-invent.
- `<slug>` cannot repeat within the world; check with `ls works/web/` before deciding.

## Final reply
Short. Create: work exists and is shipped, viewable in Works, with live URL. Ship: what changed and the live URL after this turn's publish. Close following the **next-steps** skill (REQUIRED).