Pro Deck Builder
thatrebeccarae/claude-marketing
Create polished HTML slide decks and PDF-ready documents for consulting deliverables.
Build and edit small, personal visual tools and artifacts — dashboards, trackers, calculators, data visualizations, charts, simple landing pages, and slide decks the user wants for THEMSELVES.
The automated check flagged lines worth reading first. See the safety section below.
$ npx skills add vellum-ai/vellum-assistant --skill app-builder -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install vellum-ai/vellum-assistant app-builder --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/vellum-ai/vellum-assistant.git skills-src && mkdir -p .claude/skills && cp -r skills-src/assistant/src/config/bundled-skills/app-builder .claude/skills/app-builder && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "app-builder" agent skill from https://github.com/vellum-ai/vellum-assistant/tree/main/assistant/src/config/bundled-skills/app-builder into .claude/skills/app-builder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "app-builder", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/vellum-ai/vellum-assistant/tree/main/assistant/src/config/bundled-skills/app-builderType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add vellum-ai/vellum-assistant --skill app-builder -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install vellum-ai/vellum-assistant app-builder --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/vellum-ai/vellum-assistant.git skills-src && mkdir -p .agents/skills && cp -r skills-src/assistant/src/config/bundled-skills/app-builder .agents/skills/app-builder && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "app-builder" agent skill from https://github.com/vellum-ai/vellum-assistant/tree/main/assistant/src/config/bundled-skills/app-builder into .agents/skills/app-builder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "app-builder", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add vellum-ai/vellum-assistant --skill app-builder -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install vellum-ai/vellum-assistant app-builder --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/vellum-ai/vellum-assistant.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/assistant/src/config/bundled-skills/app-builder .cursor/skills/app-builder && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "app-builder" agent skill from https://github.com/vellum-ai/vellum-assistant/tree/main/assistant/src/config/bundled-skills/app-builder into .cursor/skills/app-builder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "app-builder", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/vellum-ai/vellum-assistant.git --path assistant/src/config/bundled-skills/app-builder--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add vellum-ai/vellum-assistant --skill app-builder -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install vellum-ai/vellum-assistant app-builder --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/vellum-ai/vellum-assistant.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/assistant/src/config/bundled-skills/app-builder .gemini/skills/app-builder && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "app-builder" agent skill from https://github.com/vellum-ai/vellum-assistant/tree/main/assistant/src/config/bundled-skills/app-builder into .gemini/skills/app-builder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "app-builder", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install vellum-ai/vellum-assistant app-builderInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add vellum-ai/vellum-assistant --skill app-builder -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/vellum-ai/vellum-assistant.git skills-src && mkdir -p .github/skills && cp -r skills-src/assistant/src/config/bundled-skills/app-builder .github/skills/app-builder && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "app-builder" agent skill from https://github.com/vellum-ai/vellum-assistant/tree/main/assistant/src/config/bundled-skills/app-builder into .github/skills/app-builder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "app-builder", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add vellum-ai/vellum-assistant --skill app-builder -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install vellum-ai/vellum-assistant app-builder --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/vellum-ai/vellum-assistant.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/assistant/src/config/bundled-skills/app-builder .opencode/skills/app-builder && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "app-builder" agent skill from https://github.com/vellum-ai/vellum-assistant/tree/main/assistant/src/config/bundled-skills/app-builder into .opencode/skills/app-builder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "app-builder", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
app-builderBuild and edit small, personal visual tools and artifacts — dashboards, trackers, calculators, data visualizations, charts, simple landing pages, and slide decks the user wants for THEMSELVES.
App Builder is an agent skill from vellum-ai/vellum-assistant. Build and edit small, personal visual tools and artifacts — dashboards, trackers, calculators, data visualizations, charts, simple landing pages, and slide decks the user wants for THEMSELVES. This is the right skill whenever the user asks to "visualize this," "make a chart," or "build an artifact" for their own use, or to edit an app they already built here. Do NOT reach for a uishow dynamicpage to fake an artifact — build a real persistent app here. A one-off diagram, chart, or explainer that just accompanies…
Its SKILL.md is about 7.1k tokens, which your agent loads only when the skill is triggered. The skill folder holds 22 other files, including reference files (for example `TOOLS.json`, `references/CUSTOM_ROUTES.md` and `references/DESIGN_SYSTEM.md`).
It sits in Frontend & Design, covering Data visualization, Landing pages and Slides and decks. The repository describes itself as: An AI Assistant that’s easy to setup, does your work 24/7, knows your preferences and gets better over time. The licence is MIT.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 844117a. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships script files (TypeScript, from the files we listed), which the agent can run.
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
App Builder loads about 7.1k tokens when it runs, and up to ~22k if it reads all its reference files. Until then it costs about 182 tokens; SKILL.md has 2,908 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found patterns that need a careful read before installing.
sign* below), and build it immediately. Don't ask permission to be creative — pick the colors, the layout, the atmospherAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from vellum-ai/vellum-assistant at commit 844117a, republished under its MIT licence (© vellum-ai). 2,908 words, ~7,074 tokens.
.claude/skills/app-builder/SKILL.md (or your agent's skills folder). This skill also uses 19 other files; get the full folder from GitHub.You build small, personal visual tools — dashboards, trackers, calculators, data visualizations, simple landing pages, and slide decks. These are quick, single-user tools the user wants for themselves, not products they ship to other people.
Move fast: think, plan in one pass, pick a striking visual direction following the frontend-design skill (included with this load — see Included Skill: Frontend Design below), and build it immediately. Don't ask permission to be creative — pick the colors, the layout, the atmosphere, the micro-interactions. Every tool gets its own identity: a plant tracker feels earthy and green, a finance dashboard precise and navy. They should feel designed, not generated.
Design quality is delegated to the frontend-design skill, and you MUST follow it completely. That skill owns the aesthetics (typography, color, motion); this skill owns the technical infrastructure (sandbox, data, widgets, lifecycle). Its instructions load automatically with this skill — if they are missing (listed under Suggested Included Skills (not loaded)), call skill_load("frontend-design") before building anything. Building without them gives generic, templated UI, which is a failed build.
Build here (the default — lean toward it): a tool the user wants for themselves. A dashboard, tracker, calculator, data viz, slide deck, or a simple landing page they'll use on their own. Personal and self-contained.
Does NOT belong here: anything complex, multi-user, or meant to be published, deployed, handed off, or shipped to other people. Sandbox apps are single-user, run only in this preview, and can't be exported or deployed. They're the wrong home for a real product.
When a request is for a shippable/complex app, don't build in the sandbox. Instead:
skill_load("acp") → acp_spawn({ task: "<what to build>", cwd: "<folder>" }) (agent defaults to claude), then follow the acp skill.Triage on intent, not artifact type. A simple landing page is a personal build by default — it only becomes a handoff when the user signals they want to publish or share it. When the signal is weak, lean personal and just build. If you genuinely can't tell, ask exactly one short question.
Editing an existing sandbox app? Skip scope entirely — that's iteration. Resolve the app (see below), open it, and go to Iteration.
app_open takes an app_id, not a name:
app_id is already in your context, use it.app_list(query: "<what they said>") returns matches with app_id + name. app_list() with no query lists everything.Apps live under /workspace/data/apps/:
/workspace/data/apps/
<slug>.json # App metadata
<slug>/
src/ # Source files (TSX) — what you write
dist/ # Compiled output, generated by app_refresh or assistant apps refresh
records/ # Data records (one JSON file per record)
<slug>.preview # Preview image (auto-generated)Metadata fields: id, name, description, icon, schemaJson, createdAt, updatedAt, formatVersion, dirName. Records: { "id", "appId", "data": {...}, "createdAt", "updatedAt" } — the system auto-adds everything but data.
Every app is multi-file TSX (formatVersion: 2). Never write a root-level index.html or pages/ — all source lives under src/.
⚠️ Correct source path is /workspace/data/apps/<slug>/src/. Never /workspace/apps/.
Every app works phone (~360px) to desktop (~1400px+). The <turn_context> block carries a client_os: field (the OS the user is on): ios/android → mobile-first (design narrow first, body 17px); macos/windows/web → desktop-first (multi-column, body 14px); absent → desktop-first unless the request implies phone use ("for my iPhone"). (The sibling interface: field is the transport surface, always web for the apps, so key layout off client_os, not interface.)
Universal baseline — every build, regardless of client_os:
width=device-width, initial-scale=1, viewport-fit=cover. Never user-scalable=no (blocks accessibility zoom).env(safe-area-inset-*) so content clears the notch: padding-top: max(var(--v-spacing-lg), env(safe-area-inset-top)), mirrored for the other sides.100dvh, not 100vh.input/textarea/select) must be font-size: 16px+ or iOS Safari zooms on focus. Add inputmode (numeric/decimal/email/tel/url)..v-button already complies; custom controls set min-height: 44px). Gate hover behind @media (hover: hover).%, fr, minmax, clamp(), never fixed px on containers. Size chart containers in vw/%. At narrow widths, collapse tables into stacked label-value cards.Mobile-first extras (client_os: ios / android): body --v-font-size-lg (17px); one column by default, multi-column only above @media (min-width: 720px); bottom-anchor the primary action (position: sticky; bottom: env(safe-area-inset-bottom)); bottom sheets instead of side modals.
Full detail when reachable: {baseDir}/references/RESPONSIVE.md.
A design-system CSS and CSS widget library are auto-injected (inside a @layer, so your own styles always win). Use the --v-* variables and .v-* classes below — they switch light/dark automatically, no manual dark-mode CSS needed. For charts, bundle chart.js (an allowed package), or hand-write inline SVG / CSS bars for tiny sparklines — sized to the container so they can't overflow.
Design tokens (use these, don't invent hex values):
| Category | Tokens |
|---|---|
| Backgrounds | --v-bg, --v-surface, --v-surface-border |
| Text | --v-text, --v-text-secondary, --v-text-muted |
| Accent | --v-accent, --v-accent-hover |
| Status | --v-success, --v-danger, --v-warning |
| Spacing | --v-spacing-xxs(2) -xs(4) -sm(8) -md(12) -lg(16) -xl(24) -xxl(32) -xxxl(48) |
| Radius | --v-radius-xs(2) -sm(4) -md(8) -lg(12) -xl(16) -pill(999) |
| Shadows | --v-shadow-sm/md/lg |
| Typography | --v-font-family, --v-font-mono, --v-font-size-xs(10) -sm(11) -base(14) -lg(17) -xl(22) -2xl(26) |
| Animation | --v-duration-fast(.15s) -standard(.25s) -slow(.4s) |
| Palettes | --v-slate/emerald/violet/indigo/rose/amber-{950..50} |
| Constant | --v-aux-white (always #FFF both modes — text on filled/accent backgrounds) |
Utility classes: .v-button (.secondary/.danger/.ghost), .v-card, .v-list/.v-list-item, .v-badge (.success/.warning/.danger), .v-input-row, .v-empty-state, .v-toggle.
Theme: the --v-* tokens switch light/dark on their own. For custom (non-token) colors that must follow the theme, use @media (prefers-color-scheme: dark) in CSS.
For a custom branded look, write complete CSS with hardcoded colors + @media (prefers-color-scheme: dark) — don't mix --v-* auto-switching vars with hardcoded colors in the same element.
⚠️ Never hardcode color: white / #fff — use var(--v-aux-white) on filled/accent backgrounds, var(--v-text) / var(--v-text-secondary) on surfaces. Hardcoded white goes invisible on light surfaces.
Full detail when reachable: {baseDir}/references/DESIGN_SYSTEM.md. Note: in local dev these reference files live outside the app's sandbox and may not be readable — the essentials here are self-contained, so you can build without them.
CSS classes for standard patterns: .v-metric-card/.v-metric-grid (big-number stats), .v-data-table (sortable, sticky header, th[data-sortable]), .v-tabs, .v-accordion, .v-search-bar, .v-timeline, .v-action-list (rows with per-item actions), .v-card-grid, .v-progress-bar, .v-status-badge (.success/.error/.warning/.info), .v-stat-row/.v-stat, .v-tag-group, .v-avatar-row. Landing-page components: .v-hero/.v-hero-badge/.v-hero-subtitle, .v-section-header/.v-section-label, .v-feature-grid/.v-feature-card, .v-pullquote, .v-comparison (.before/.after), .v-page, .v-gradient-text, .v-animate-in. Domain widgets: .v-weather-card, .v-stock-ticker, .v-receipt, .v-invoice, .v-itinerary, .v-boarding-pass.
Interactive behavior is your own JS: charts → the bundleable chart.js (or inline SVG for tiny sparklines); formatting → Intl.NumberFormat / Intl.DateTimeFormat; table sort/filter, tabs, accordions, toasts, countdowns → plain JS wired to the .v-* markup.
Use custom HTML for novel/creative UIs (games, art tools); the .v-* classes for standard patterns; mix freely. Full list: {baseDir}/references/WIDGETS.md.
App builds are multi-step and benefit from a stronger model. If the active model profile looks weak for this work, you may offer to switch profiles first. Use the ui_show tool to ask, with surface_type: "confirmation" and await_action: true, so the user explicitly opts in before anything changes. Do not call the shell command assistant ui confirm for this — it can block the build flow before app work starts. If the user declines, just proceed on the current profile.
Think (what's the tool, who's the single user), plan in one pass (visual direction, minimal schema, core layout), then build. No wireframes, no mockups, no color questions. Make the creative calls yourself. Only ask a question when the request is genuinely ambiguous about what to build — and even then, prefer building something strong from context clues.
A JSON Schema for a single record. The system auto-adds id, appId, createdAt, updatedAt — define only user-facing fields. Keep it flat (string, number, boolean); encode nested data as JSON strings.
{
"type": "object",
"properties": {
"title": { "type": "string" },
"status": { "type": "string", "enum": ["todo", "doing", "done"] }
},
"required": ["title"]
}Calculators, single-page tools, landing pages, and slide decks skip this — pass an empty schema_json or omit it.
⚠️ app_create is ONE-SHOT per build. Call it exactly once. After it returns an app_id, all further changes go through file_write / file_edit and a single compile (assistant apps refresh or app_refresh). To start over: app_delete(app_id) first, then a fresh app_create.
Apps are multi-file Preact + TSX projects; esbuild bundles when you compile. Structure:
src/
index.html # Minimal shell that loads the bundle
main.tsx # Renders <App /> into #app
components/App.tsx # Top-level component
styles.css # Global styles (import from TSX)import { render } from "preact";
import { App } from "./components/App";
import "./styles.css";
render(<App />, document.getElementById("app")!);Scaffold-then-expand is the pattern for every non-trivial app. Cramming all files into one app_create blows the response token budget mid-emit:
app_create with a 4-file scaffold: src/index.html, src/main.tsx, a placeholder src/components/App.tsx (<div>Loading...</div>), and an empty src/styles.css. The placeholders make the first compile clean — a 2-file scaffold leaves broken imports.file_write each real file, one per tool call, overwriting the placeholders and adding components.assistant apps inspect <name> to see whether source drifted, then assistant apps refresh <name> (or app_refresh).Actually write the files. Source code in reasoning/thinking is invisible to the app sandbox and is not a file write. Put every real file body in an app_create.source_files, file_write, file_edit, or app_update.source_files tool call, then verify the files exist before compiling when there is any doubt.
Allowed packages (esbuild-resolved, no CDN): date-fns, chart.js, lodash-es, zod, clsx. For icons, write inline <svg> markup directly — no icon package is bundled.
Constraints: Preact not React. No CDN imports. No external fonts/images (system fonts, inline CSS/SVG). Responsive only, no fixed-pixel widths. The WebView blocks navigation — href and form action don't work.
⚠️ compile_errors in the app_create response is NOT a retry signal — the response also has an app_id, so the app was created. Proceed. Calling app_create again makes a duplicate.
app_create accepts EXACTLY these 6 keys — nothing elsename (optional — defaults to the preview title, else "New App"), description, schema_json, source_files, preview, auto_open.
Anything else fails with Invalid input for tool "app_create": Unknown parameter "X". The retired keys models still reach for:
html — old single-file shortcut. Put your HTML inside source_files["src/index.html"].pages — retired. Multi-page apps use TSX components under src/components/.icon: NOT a top-level param. The icon goes in preview.icon as a Lucide icon name from the list below (e.g. preview: { title: "Bean Coffee", icon: "coffee" }). Pick the one that best says what the app is; it is drawn in the sidebar and the library, so an emoji or a URL is wrong here. For an AI-generated image icon, call app_generate_icon(app_id, description) after the app exists."src/components/Header.tsx") — these go inside source_files, or in a file_write after app_create.If a prior session in your context shows app_create({ html }) or app_create({ pages }), that example is outdated — ignore it.
// ❌ Wrong // ✅ Right
app_create({ app_create({
name: "Landing", name: "Landing",
html: "<!DOCTYPE...>" // INVALID source_files: {
}) "src/index.html": "<!DOCTYPE...>",
"src/main.tsx": "...",
"src/components/App.tsx": "...",
"src/styles.css": ""
}
})Key notes: preview — always include, title required (plus optional subtitle, description, icon, up to 3 metrics). auto_open — always pass false so you don't get a duplicate preview card (Step 5 owns surfacing).
App icon names (preview.icon, kebab-case, one of): calculator, calendar, list-todo, list-checks, square-check, timer, clock, alarm-clock, notebook-pen, sticky-note, pencil, file-text, clipboard-list, bookmark, book, book-open, chart-bar, chart-line, chart-pie, table, square-kanban, database, gauge, activity, target, flag, trophy, wallet, dollar-sign, piggy-bank, credit-card, receipt, percent, shopping-cart, package, gift, ticket, mail, inbox, message-square, phone, bell, users, contact, music, headphones, mic, video, film, tv, play, image, camera, gamepad-2, puzzle, party-popper, smile, map, map-pin, compass, globe, plane, car, bus, bike, ship, truck, house, bed, briefcase, graduation-cap, languages, brain, lightbulb, heart, heart-pulse, dumbbell, pill, stethoscope, baby, paw-print, utensils, coffee, wine, beer, cake, apple, carrot, salad, egg, fish, cloud, sun, moon, umbrella, snowflake, thermometer, droplets, flame, leaf, mountain, code, terminal, cpu, bot, wifi, lock, key, shield, settings, wrench, plug, battery, search, link, hash, layers, folder-open, palette, pen-tool, ruler, scale, scissors, shirt, newspaper, repeat, shuffle, volume-2, speaker, star, sparkles, zap, rocket, home.
File edits do not compile on their own. After ALL file writes, compile once:
assistant apps inspect <name>
assistant apps refresh <name>assistant apps inspect reports whether source changed since the last compile. assistant apps refresh compiles and refreshes open surfaces. app_refresh(app_id) also compiles. If compile fails, the response has error details; fix with file_edit, then compile again.
app_open(app_id, open_mode: "preview")⚠️ Don't skip this — without it the user has no Open button, just your text. It fires after all writes, so the card shows final content (this is why auto_open must be false). Don't use open_mode: "workspace" unless the user explicitly asks for the full panel.
Editing an existing app means reusing its app_id — never app_create. Resolve it from name if needed (see Resolving an app), open it so the live result is visible, then:
file_edit — targeted changes (styles, fixes, small features), full path /workspace/data/apps/<slug>/src/...file_write — new files or full rewritesapp_update — rename or change description/schema, and/or rewrite files via source_files, in one call. It recompiles for you, so no separate app_refresh is needed. Use this instead of editing <slug>.json by hand.Then compile ONCE (assistant apps refresh <name> or app_refresh(app_id)) unless you used app_update, which already recompiled. If the change is substantial, app_open(app_id, open_mode: "preview") for a fresh card; for small tweaks the existing card stays valid.
⚠️
skill_load("app-builder")is required before everyapp_*call (including the firstapp_create). The skill can auto-unload between turns; without the reload,app_refresh/app_openerror with "not currently active." It's idempotent — call it every time.
The point of these apps is to put the user's own data and the assistant's capabilities behind a real interface. Apps reach the assistant backend through custom routes.
Call routes with window.vellum.fetch("/v1/x/...") — never raw fetch(). Raw fetch fails in the sandboxed origin. This is how an app reads and writes persistent records, runs server-side logic, and touches files.
async function loadRecords() {
const res = await window.vellum.fetch("/v1/x/my-route");
if (!res.ok) { notifyError("Couldn't load"); return []; } // your own toast/inline error
return res.json();
}Always wrap calls in try/catch, check res.ok before parsing, and surface failures with a toast or inline error — never fail silently:
useEffect(() => {
window.vellum.fetch("/v1/x/items")
.then(res => res.ok ? res.json() : Promise.reject(res.status))
.then(setItems)
.catch(() => notifyError("Couldn't load")); // your own toast/inline error
}, []);Bundled media — window.vellum.asset(path). For binary assets too big to inline (images, audio, short video, custom fonts), bundle the file under the app directory with file_write and load it at runtime via window.vellum.asset("assets/intro.mp4"), which resolves to a blob: URL the sandbox can use directly. Don't reach for giant base64 data-URIs.
const [src, setSrc] = useState("");
useEffect(() => {
window.vellum.asset("assets/intro.mp4").then(setSrc).catch(() => notifyError("Couldn't load media"));
}, []);
return src ? <video src={src} controls /> : null;Paths are validated server-side (no traversal, no records/); the file is served only from this app's own directory.
Live updates — window.vellum.subscribe({ tags }, cb). Instead of polling on a timer, subscribe to your own invalidation tags and refresh when the data actually changes. After a route mutates data it publishes a sync_changed event (import { publishEvent } from "@vellumai/plugin-api", then publishEvent({ …, message: { type: "sync_changed", tags: ["my-app:items"] } })); the app subscribes to that tag and re-fetches when it fires. Returns an unsubscribe function — call it on cleanup. Only your own custom tags are delivered (host namespaces like conversation:/assistant: are never forwarded), and the payload is just the changed tags — re-fetch through window.vellum.fetch for the data.
useEffect(() => {
const off = window.vellum.subscribe({ tags: ["my-app:items"] }, () => {
void loadItems(); // re-fetch on change — no polling
});
return off;
}, []);Writing a route handler. Routes are .ts/.js files in {workspaceDir}/routes/, served at /v1/x/<filename> (routes/items.ts → /v1/x/items; routes/bar/index.ts → /v1/x/bar). Write them with file_write before app_refresh. Each exports named functions per HTTP method (GET/POST/PUT/PATCH/DELETE), receiving the Web Request. Full Node API access (fs, path, crypto), 30s timeout, hot-reloaded on change. No [id].ts dynamic segments — use query params.
// routes/items.ts
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
import { join } from "node:path";
export const description = "Item CRUD — JSON file storage"; // optional, for `assistant routes list`
const FILE = join(process.env.VELLUM_WORKSPACE_DIR!, "data", "items.json");
const load = () => existsSync(FILE) ? JSON.parse(readFileSync(FILE, "utf-8")) : [];
const save = (x:unknown[]) => { mkdirSync(join(process.env.VELLUM_WORKSPACE_DIR!,"data"),{recursive:true}); writeFileSync(FILE, JSON.stringify(x,null,2)); };
export function GET(): Response { return Response.json(load()); }
export async function POST(req: Request): Promise<Response> {
const item = { id: crypto.randomUUID(), ...(await req.json()), createdAt: new Date().toISOString() };
const items = load(); items.push(item); save(items);
return Response.json(item, { status: 201 });
}To reach daemon capabilities, import from @vellumai/plugin-api — publishEvent({...}) to push real-time events to connected clients (UI updates, navigation, notifications), or runConversationTurn({...}) to post an inbound event into a conversation as a real assistant turn. (An older context second argument still works in-process but is deprecated — prefer these imports.) Full guide + copyable examples (Focus Timer, Habit Tracker, Expense Tracker): {baseDir}/references/CUSTOM_ROUTES.md, {baseDir}/references/examples/.
Persistence options: localStorage for ephemeral UI state (filters, view modes, drafts); custom routes for persistent records and server-side logic.
.v-toast class with JS)..v-empty-state when there's no data.Wire window.vellum.sendAction() during the build so the app can pull the assistant in. The host handles two actions: relay_prompt ({ prompt, conversation }) sends a message to the assistant as if the user typed it — the way to get an explanation, summary, or follow-up from inside the app (conversation: "new" starts a fresh chat instead of the open one); set_view ({ view }) arranges the app and chat ("split" / "full" / "chat"). These two are the whole app→host surface — relay anything you want the assistant to act on as a self-contained prompt. Patterns in {baseDir}/references/INTERACTION_HOOKS.md.
For triage/bulk-action UIs: render selectable items + action buttons → the user selects and clicks → relay the choice as a prompt with window.vellum.sendAction("relay_prompt", { prompt: ... }), listing the selected items in the text, so the assistant can run the tools and report back. Render your own confirmation for destructive actions.
Slide decks are a different domain — skip app patterns (contextual headers, search/filter, toasts, form validation, custom routes). Build navigation and layouts with custom HTML/CSS. Templates and principles in {baseDir}/references/SLIDES.md.
app_create returned an app_id; all files written via file_write; compile ran ONCE clean (assistant apps refresh or app_refresh); app_open(open_mode: "preview") rendered the card; user told what was built (3-6 bullets); iterations reflected liveacp_spawn({ task, cwd }); user told work continues in the folderRead with file_read using the {baseDir}/references/... paths ({baseDir} resolves to this skill's directory):
RESPONSIVE.md — mobile vs desktop, universal baseline, safe areasDESIGN_SYSTEM.md — token table, utility classes, theme/dark modeWIDGETS.md — CSS widget classes (no JS chart/format runtime)CUSTOM_ROUTES.md — server-side persistence and custom API routesexamples/ — complete copyable example appsINTERACTION_HOOKS.md — relay_prompt / set_view app→host actionsSLIDES.md — presentation slide design© vellum-ai, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 19 other files (references) in assistant/src/config/bundled-skills/app-builder of vellum-ai/vellum-assistant.
Open the folder on GitHubat commit 844117a
App Builder next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| App Builder this skillvellum-ai/vellum-assistant | 1.4k | — | ~7.1k | Automated safety check: Warn | MIT | |
| Pro Deck Builderthatrebeccarae/claude-marketing | 162 | — | ~6.1k | Automated safety check: Pass | MIT | |
| Cc DesignZeroZ-lab/cc-design | 827 | — | ~2.2k | Automated safety check: Notes | None | |
| Claude Designjiji262/claude-design-skill | 203 | — | ~3.6k | Automated safety check: Pass | MIT | |
| Web Designdrewnekota/cetus | 146 | — | ~2.1k | Automated safety check: Pass | MIT | |
| Vetta UI Designopenvetta/open-vetta | 291 | — | ~7.3k | Automated safety check: Pass | Apache-2.0 |
thatrebeccarae/claude-marketing
Create polished HTML slide decks and PDF-ready documents for consulting deliverables.
ZeroZ-lab/cc-design
High-fidelity HTML design and prototype creation. An agent skill from ZeroZ-lab/cc-design.
jiji262/claude-design-skill
Produce thoughtful, high-fidelity design artifacts in HTML — landing pages, slide decks, interactive prototypes, animated videos, posters, wireframes, and visual explorations.
drewnekota/cetus
A skill your agent uses when building any web page, HTML artifact, landing page, dashboard, slide deck, report, email, or UI component the user will look at.
openvetta/open-vetta
Build and edit design documents (.vetd) on the Vetta design canvas — app screens, landing pages, slides, posters, infographics.
freshtechbro/claudedesignskills
Simple scroll-triggered reveal animations using AOS (Animate On Scroll).
vellum-ai/vellum-assistant
Create and configure a GitHub App so the assistant can push commits, open PRs, and comment under its own bot identity.
vellum-ai/vellum-assistant
Connect a Discord bot to the assistant via the Discord Gateway with guided application creation and intent configuration
vellum-ai/vellum-assistant
Create and configure a Sentry internal integration so the assistant can manage issues, alerts, and releases under its own identity
vellum-ai/vellum-assistant
Ingest a large dataset into memory as a skimmed map. An agent skill from vellum-ai/vellum-assistant.
vellum-ai/vellum-assistant
A skill your agent uses when the user wants to build, scaffold, ship, or edit a Vellum plugin that bundles multiple surfaces (hooks, tools, skills, and more) into one installable package.
vellum-ai/vellum-assistant
Connect a Slack app to the Vellum Assistant via Socket Mode.
Build and edit small, personal visual tools and artifacts — dashboards, trackers, calculators, data visualizations, charts, simple landing pages, and slide decks the user wants for THEMSELVES. App Builder is an agent skill from vellum-ai/vellum-assistant. Build and edit small, personal visual tools and artifacts — dashboards, trackers, calculators, data visualizations, charts, simple landing pages, and slide decks the user wants for THEMSELVES.
App Builder fits situations like: tasks that involve Data visualization; tasks that involve Landing pages; tasks that involve Slides and decks.
Run `npx skills add vellum-ai/vellum-assistant --skill app-builder -a claude-code`. Or copy the skill folder (assistant/src/config/bundled-skills/app-builder in vellum-ai/vellum-assistant) into .claude/skills/app-builder in your project. Claude Code loads it when a task matches its description.
Run `npx skills add vellum-ai/vellum-assistant --skill app-builder -a codex`. Or copy the skill folder (assistant/src/config/bundled-skills/app-builder in vellum-ai/vellum-assistant) into .agents/skills/app-builder in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add vellum-ai/vellum-assistant --skill app-builder -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/app-builder, .gemini/skills/app-builder, .github/skills/app-builder and .opencode/skills/app-builder in your project.
Going by SKILL.md and its folder, App Builder needs TypeScript for the scripts in its folder. Our summary lists: Node.js.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md flagged 1 warning(s): tells the agent its actions are pre-authorized / not to stop for confirmation. Read the flagged lines before installing; the check is not a guarantee either way.
App Builder is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 7.1k tokens (SKILL.md is roughly 28k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 15k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with App Builder: Pro Deck Builder (thatrebeccarae/claude-marketing, 162 stars), Cc Design (ZeroZ-lab/cc-design, 827 stars), Claude Design (jiji262/claude-design-skill, 203 stars) and Web Design (drewnekota/cetus, 146 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
vellum-ai (a GitHub organization) maintains it in vellum-ai/vellum-assistant, which has 1,400 GitHub stars. The repository holds 108 skills in this directory. The repository was last updated on October 9, 2026.
Source: vellum-ai/vellum-assistant on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.