wicked-interactive
v0.12.0
Published
The wicked family's document engine: document storage with write-once version lineage and fork, HTML/PDF/PPTX/video export, and demo recording behind one local API. wicked-crew spawns and proxies it; the builder UI lives in wicked-studio.
Readme
_ _ _ _ _ _ _
__ _(_) ___| | _____ __| | (_)_ __ | |_ ___ _ __ __ _ ___| |_(_)_ _____
\ \ /\ / / |/ __| |/ / _ \/ _` |_____| | '_ \| __/ _ \ '__/ _` |/ __| __| \ \ / / _ \
\ V V /| | (__| < __/ (_| |_____| | | | | || __/ | | (_| | (__| |_| |\ V / __/
\_/\_/ |_|\___|_|\_\___|\__,_| |_|_| |_|\__\___|_| \__,_|\___|\__|_| \_/ \___|The wicked family's document engine. One local service owns document storage with write-once version lineage and forking, renders and exports HTML / PDF / native PowerPoint / video, records narrated demo walkthroughs, and learns themes from your existing brand pages. wicked-crew spawns it as a local bridge and reverse-proxies its API; wicked-studio is where you point, edit, rewind, fork, and export. You depend on it — you don't visit it.
What lives here
The builder UI moved to wicked-studio (see Moving? below). The engine did not move — this repo is its sole implementation:
- 📦 Documents & lineage — every change is a write-once saved version; rewind to any of them, or fork a version and chase two ideas at once without losing either.
- 📤 Exports — single-file HTML with the document's local assets embedded, PDF, native editable PowerPoint (
vendor/pptx/html_to_pptx.py; needspython-pptxon the exporting machine), and video. The recipient needs no wicked install, but remote fonts, scripts, stylesheets and images stay links to the network unless you export in strict offline mode — every export tells you which ones (see Exports). - 🎬 Demo recording — narrated walkthroughs of a live app with chapter thumbnails (
src/service/demo.js): mp4, poster, GIF. - 🎨 Theme learning — an SSRF-hardened theme-grab over pages you already ship, plus the learned-theme readback (
GET /d/:docId/api/theme/learned, v0.8.1) that studio's brand tooling polls, and its write side (PUT/DELETEon the same path): every token is checked against a per-field grammar before it can reach the CSS. - 🔌 One HTTP API + one bus vocabulary — the
/api/*surface and thewicked.interactive.*event types (ADR-0019) that the studio UI and supervising agents both speak.
The loop in the demo — describe, point, fix, rewind — is powered by this engine; the surface you drive it from is wicked-studio.
How it runs
You normally never start this service yourself:
- Through crew — the supported path.
npx wicked-crew serve(studio's UI is bundled) spawns this package from npm as a local bridge (wicked-interactive@^0.9.3— crew'sINTERACTIVE_DEFAULT_RANGE, overridable withWICKED_INTERACTIVE_SPEC; one bridge per project docs root) and reverse-proxies its entire HTTP surface at/api/v1/projects/:projectId/interactive/*on crew's own origin — pure transport, SSE streamed unbuffered both ways. The studio client only ever talks to crew. - Directly, as an API.
npx wicked-interactive serve --root ~/wicked-interactive/docsruns the API-only service on a dynamic port (ADR-0022/0025).GET /redirects to the recorded studio origin; there is no bundled UI on the supported path. - The dev escape hatch.
wicked-interactive serve --standalone(orWI_STANDALONE=1) serves the retired SPA shell for local development of the engine itself — not the supported UI.
Recording demos needs a browser that npm install does not provide. The recorder drives Playwright's bundled headless Chromium (+ ffmpeg for video); on a fresh machine it is provisioned automatically on the first recording (a one-time ~100 MB download, progress on the document thread; opt out with WI_RECORDER_AUTO_INSTALL=0). To check or provision it yourself: npx wicked-interactive doctor (exit 1 while it cannot record) / npx wicked-interactive doctor --install. A recording that cannot run answers with a typed error (recorder_browser_missing, recording_step_failed, …) carrying the one-line remedy — it is never retried into a dead letter (ADR-0028).
The package also ships a CLI for artifact work — wicked-interactive create | publish | validate | adopt (+ doctor) — and Claude Code skills (serve, assist) that put an agent session into the supervising loop the feedback cycle needs (ADR-0010). The plugin install is two steps:
/plugin marketplace add mikeparcewski/wicked-interactive
/plugin install wicked-interactiveExports
POST /api/export {"version": N, "format": "html" | "pdf" | "pptx"} writes the file under the
document's exports/ folder and answers with a download URL. What it embeds, and what it does not:
- Local assets are embedded, from the document only. Stylesheets, scripts, images and media
(
src,srcset,poster,<object data>, SVG<image href>, icon/preload links), CSSurl()and@importbecome inline text ordata:URIs. A reference is read only when its real path, after symlinks are resolved, is inside the document's own folder or inside a folder the operator approved withWI_EXPORT_ASSET_ROOTS(absolute paths, separated likePATH). A../escape, an absolute path elsewhere, a symlink pointing out, a missing file or afile:URL is not read: it is dropped from the output and reported. - Remote resources are never fetched.
http(s):and protocol-relative references stay as authored, so the exported file still needs the network for them. - Every export returns a dependency receipt in
dependencies:{ self_contained, embedded: [{ref, kind}], remote: [{ref, kind, remedy}], unresolved: [{ref, kind, reason, remedy}] }.self_containedis true only when nothing is remote or unresolved. PPTX is built natively from the markup and carries no linked resources, so its receipt is always empty. - Strict offline mode: add
"offline": true. The export succeeds only when every resource is embedded; otherwise it answers422 {code: "export_not_offline", dependencies}listing each remote or unresolved resource, and writes nothing. - The PDF renderer opens its print copy from a one-file loopback
http://127.0.0.1origin, never fromfile://, so neither a resource nor a navigation in the page can reach local files.
The receipt covers what the markup and CSS reference. It cannot see a URL that a script builds at
runtime, a CSS image-set() string, or markup inside an <iframe srcdoc>.
What Studio exposes
Every engine capability is reachable over this API. Not every one has a Studio action yet.
Checked against wicked-studio main (2026-10-10):
| Capability | Engine API | Studio action |
|---|---|---|
| Create a document | POST /api/docs | Yes |
| Browse documents and versions | GET /api/docs, GET /d/:doc/api/versions | Yes |
| Point-and-edit / feedback | wicked.interactive.feedback.submitted | Yes |
| Chat on the document thread | wicked.interactive.chat.posted | Yes |
| Fork / rewind a version | POST /d/:doc/api/fork | Yes |
| Retire a document | DELETE /api/docs/:doc | Yes |
| Export HTML / PDF / PPTX | POST /d/:doc/api/export | Yes |
| List and download exports | GET /d/:doc/api/export, GET /d/:doc/api/export/file/:name | Yes |
| Export dependency receipt + strict offline | dependencies, "offline": true on export | No (API only) |
| Learn a theme from a page | wicked.interactive.theme.requested | Yes |
| Read the learned theme | GET /d/:doc/api/theme/learned | Yes |
| Edit / clear the learned theme | PUT / DELETE /d/:doc/api/theme/learned | No (API only) |
| Attach / list reference sources | POST / GET /d/:doc/api/sources | No (client wrapper, no UI action) |
| Record a demo through this bridge | wicked.interactive.demo.requested, /api/demo/* | No (Studio records demos through crew's governed demo run instead) |
| Analyze / review a document | wicked.interactive.review.requested | No (API only; Studio shows review.completed verdicts on the thread) |
Moving? The builder UI now lives in wicked-studio
If you used the standalone builder UI — the page this service served at http://localhost:<port>/ —
here's what changed and where things went.
The UI moved, the documents didn't. Everything you've made stays exactly where it was (your docs root, versions and all). The builder is now a mode inside the merged wicked-studio app, which is where you point, comment, rewind, fork, export and record from. Nothing was dropped in the move: the merge design's parity ledger had to be green before this landed. That ledger covers the old shell's actions; engine APIs that never had a button still have none (see What Studio exposes).
This service is API-only now. It still runs, still owns your documents, and still answers every
/api/* route — it just doesn't serve a UI:
GET /redirects to the studio origin recorded in<docs-root>/.wi-serve.json. wicked-crew records that origin when it starts or adopts this bridge; you can also record it yourself withPOST /api/studio-origin {"origin":"http://localhost:4200"}or start the service with--studio-origin <url>.- With no origin recorded,
GET /returns a short page saying so — never a blank 404. - A
?doc=<name>bookmark still works: a document that belongs to a project lands on that document in studio; anything else lands on the board.
Want the old shell back? It's still in the box, for local development:
wicked-interactive serve --root ~/wicked-interactive/docs --standalone # or WI_STANDALONE=1That serves the retired SPA exactly as before. It is a development escape hatch, not the supported path — the merged app is where the UI is maintained.
Some capabilities have no button in studio yet (see What Studio exposes),
notably analyze / review (the reviewer pass over a document). It remains fully reachable over the API — POST /api/events with
wicked.interactive.review.requested — and the affordance for it belongs to the studio side.
Why this repo stays live
Asked and answered (twice — 2026-08-24 parity audit, re-verified 2026-08-29; the full record is
ADR-0027 in docs/architecture-decisions.md): the UI merge did
not move the engine, so this repo cannot be archived. crew resolves wicked-interactive@^0.9.3
from the public npm registry at runtime — archiving would strand a live dependency, deprecating
would announce a migration that never happened, and unpublishing would kill every route under
crew's interactive proxy. The repo stays live, reworded to the engine story it now is.
Requirements
- Node.js ≥ 20.0.0 (for the wicked-bus peer)
- wicked-bus ≥ 2.3.0 — the event backbone the service, studio UI, and agents all share (
npm i -g wicked-bus); auto-installed on first run if not already present - macOS, Linux, or Windows
- For the agent-supervised loop: Claude Code ≥ 1.0 (the plugin surface)
MIT licensed — see LICENSE. Part of the wicked-* family of local-first, AI-native developer tools — the foundation document engine beneath wicked-crew and wicked-studio.
