raw-app
Scaffolds Windmill raw apps using the CLI by gathering path, summary, and framework configurations.
Install
mkdir -p .claude/skills/raw-app && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4168" && unzip -o skill.zip -d .claude/skills/raw-app && rm skill.zipInstalls to .claude/skills/raw-app
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
MUST use when creating raw apps.Key capabilities
- →Scaffold Windmill raw app file layout
- →Initialize frontend-backend connectivity
- →Configure app metadata
- →Create data table SDK configurations
How it works
It calls the Windmill CLI with non-interactive flags to ensure consistent directory and metadata scaffolding for platform-specific apps.
Inputs & outputs
When to use raw-app
- →Create a new internal dashboard
- →Scaffold a React-based Windmill app
- →Initialize a directory structure for a new tool
About this skill
Windmill Raw Apps — CLI workflow
This guide covers raw apps from the terminal: scaffolding via wmill app new, the on-disk layout, and the file-based conventions the CLI uses to represent backend runnables and data table configuration. The platform shape (how a raw app behaves at runtime — frontend bundling, runnable types, datatable SDK calls) is covered in the companion authoring guide.
Creating a Raw App
You — the AI agent — create the app yourself by running wmill app new with the right flags. Do NOT tell the user to "run wmill app new and follow the prompts" or wait for them to do it. The bare wmill app new is an interactive wizard that hangs waiting for stdin in any non-TTY context (which includes you). Always pass flags.
Step 1 — Gather the three required values by asking the user
You need three things to run the command:
- summary — a short description of the app
- path — the windmill path, e.g.
f/folder/my_apporu/username/my_app - framework — one of
react19(recommended),react18,svelte5,vue
If the user's request did not supply every one of these explicitly, ask. Do not guess values, do not invent paths, do not pick a framework on the user's behalf, do not "just use react19 because it's the default".
Use whichever interactive question facility your runtime provides — a structured multi-choice tool if available, otherwise plain chat — and group all missing fields into a single round-trip so the user answers them at once:
- For
framework— multiple-choice with the four allowed values; markreact19as(Recommended)and put it first. - For
summaryandpath— provide one or two example values as multiple-choice options (the user can pick "Other" to type a free-form answer).
Only proceed once you have concrete values for all three. If the user replies with something ambiguous, ask again rather than guessing.
Step 2 — Run the command yourself
Once you have summary + path + framework, run it:
wmill app new \
--summary "Customer dashboard" \
--path f/sales/dashboard \
--framework react19
That's the minimum. The datatable wizard and the "Open in Claude Desktop?" prompt are skipped silently because passing any of --summary/--path/--framework puts the command in non-interactive mode.
Optional flags
Layer these in only when the user asked for them:
| Flag | When to add it |
|---|---|
--datatable <name> | The user wants this app wired to a specific Windmill datatable. Without it, the app is created with no datatable. |
--schema <name> | Together with --datatable. Creates the schema with CREATE SCHEMA IF NOT EXISTS if it doesn't already exist. |
--overwrite | The target directory already exists and the user said it's OK to replace. Without it, non-interactive mode aborts with an error so you don't clobber existing work. |
--no-open-in-desktop | Already implied in non-interactive mode; only needed if you're somehow running interactively. |
Step 3 — Offer the visual preview
After wmill app new and any initial edits to App.tsx / index.tsx, offer to open the visual preview as a one-sentence next step (e.g. "Want me to open the visual preview?"). Don't auto-open — opening the dev page has side effects (browser window, possibly a launch.json entry when an embedded preview tool is in play) the user should consent to.
For apps the preview command runs from the app folder (cd <app_path>__raw_app && wmill app dev …); the preview skill picks the proxy vs direct branch based on whether the runtime exposes a tool that can embed a localhost URL. If the user already asked to see/preview/visualize the app in their original request, skip the offer and just invoke the skill.
Anti-patterns to avoid
- ❌ Running
wmill app newwith no flags (the prompt will hang). - ❌ Telling the user to "run
wmill app newand follow the prompts" — that's a step backwards from what you can do directly. - ❌ Inventing a path/summary/framework instead of asking the user.
- ❌ Defaulting to
react19because the user didn't say — even sensible defaults must be confirmed. - ❌ Passing
--overwriteautomatically when the directory exists — confirm with the user first.
Interactive (only when a human is at the terminal)
wmill app new
This is the wizard. It only works when run by a human in a real terminal. Don't call it this way from an agent.
On-disk app layout
my_app__raw_app/
├── AGENTS.md # AI agent instructions (auto-generated)
├── DATATABLES.md # Database schemas (run 'wmill app generate-agents' to refresh)
├── raw_app.yaml # App configuration (summary, path, data settings)
├── index.tsx # Frontend entry point
├── App.tsx # Main React/Svelte/Vue component
├── index.css # Styles
├── package.json # Frontend dependencies
├── wmill.ts # Auto-generated backend type definitions (DO NOT EDIT)
├── backend/ # Backend runnables (server-side scripts)
│ ├── <id>.<ext> # Code file (e.g., get_user.ts)
│ ├── <id>.yaml # Optional: config for fields, or to reference existing scripts
│ └── <id>.lock # Lock file (run 'wmill generate-metadata' to create/update)
└── sql_to_apply/ # SQL migrations (dev only, not synced)
└── *.sql # SQL files to apply via dev server
Backend runnables on disk
Add a code file to the backend/ folder:
backend/<id>.<ext>
The runnable ID is the filename without extension. For example, get_user.ts creates a runnable with ID get_user.
Supported languages (extension-driven)
| Language | Extension | Example |
|---|---|---|
| TypeScript | .ts | myFunc.ts |
| TypeScript (Bun) | .bun.ts | myFunc.bun.ts |
| TypeScript (Deno) | .deno.ts | myFunc.deno.ts |
| Python | .py | myFunc.py |
| Go | .go | myFunc.go |
| Bash | .sh | myFunc.sh |
| PowerShell | .ps1 | myFunc.ps1 |
| PostgreSQL | .pg.sql | myFunc.pg.sql |
| MySQL | .my.sql | myFunc.my.sql |
| BigQuery | .bq.sql | myFunc.bq.sql |
| Snowflake | .sf.sql | myFunc.sf.sql |
| MS SQL | .ms.sql | myFunc.ms.sql |
| GraphQL | .gql | myFunc.gql |
| PHP | .php | myFunc.php |
| Rust | .rs | myFunc.rs |
| C# | .cs | myFunc.cs |
| Java | .java | myFunc.java |
After creating or editing a backend runnable — especially when its imports or arguments changed — its local lock and wmill-lock.yaml go stale. Offer to run wmill generate-metadata and run it once the user agrees (or automatically if the project's AGENTS.md opts into that) — YOU run it, don't just name it and wait. It writes local files only (not a deploy), and keeping the lock current avoids noise in git-sync/CI:
wmill generate-metadata
After it runs, check the regenerated .lock diff and tell the user which dependency versions changed (e.g. requests 2.31.0 → 2.32.0), so they can catch an unwanted bump before deploying.
Optional YAML configuration
Add a <id>.yaml file alongside the code to configure fields or static values:
backend/get_user.yaml:
type: inline
fields:
user_id:
type: static
value: "default_user"
Referencing existing scripts
To use an existing Windmill script instead of inline code:
backend/existing_script.yaml:
type: script
path: f/my_folder/existing_script
For flows:
type: flow
path: f/my_folder/my_flow
Data tables — raw_app.yaml config
The data block in raw_app.yaml controls which tables the app can query.
data:
datatable: main # Default datatable
schema: app_schema # Default schema (optional)
tables:
- main/users # Table in public schema
- main/app_schema:items # Table in specific schema
Table reference formats:
<datatable>— All tables in the datatable<datatable>/<table>— Specific table in public schema<datatable>/<schema>:<table>— Table in specific schema
SQL Migrations (sql_to_apply/)
The sql_to_apply/ folder is for creating/modifying database tables during development.
Workflow
- Create
.sqlfiles insql_to_apply/ - Run
wmill app dev— the dev server watches this folder - When SQL files change, a modal appears in the browser to confirm execution
- After creating tables, add them to
data.tablesinraw_app.yaml
Example migration
sql_to_apply/001_create_users.sql:
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
After applying, add to raw_app.yaml:
data:
tables:
- main/users
Migration best practices
- Use idempotent SQL:
CREATE TABLE IF NOT EXISTS, etc. - Number files:
001_,002_for ordering - Always whitelist tables after creation
- This folder is NOT synced — it's for local development only
CLI Commands
Two commands you run yourself, not the user:
wmill app new— run it with flags, per the "Creating a Raw App" section above.wmill generate-metadata— (re)generates local lock files and refresheswmill-lock.yamlcontent hashes; writes local files only (not a deploy). After adding or editing a runnable, offer it and run it on agreement — or automatically if the project'sAGENTS.mdopts into that (see "After creating a runnable" above).
For the rest, tell the user which command fits their intent and let them run it — these deploy to the workspace, overwrite local file
Content truncated.
When not to use it
- →Creating traditional apps outside the Windmill platform
- →Projects not using Windmill backend runnables
Prerequisites
Limitations
- →Requires explicit framework choice from the platform supported list
- →Cannot be used without the wmill CLI tool
How it compares
It enforces a platform-specific project structure that is otherwise only accessible through interactive prompts.
Compared to similar skills
raw-app side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| raw-app (this skill) | 0 | 2mo | Review | Beginner |
| web-component-design | 5 | 5mo | No flags | Intermediate |
| uncodixfy | 0 | 3mo | No flags | Intermediate |
| formisch-usage | 0 | 5mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by windmill-labs
View all by windmill-labs →You might also like
web-component-design
wshobson
Master React, Vue, and Svelte component patterns including CSS-in-JS, composition strategies, and reusable component architecture. Use when building UI component libraries, designing component APIs, or implementing frontend design systems.
uncodixfy
BrammekeTV
Prevents generic AI/Codex UI patterns when generating frontend code. Use this skill whenever generating HTML, CSS, React, Vue, Svelte, or any frontend UI code to enforce clean, human-designed aesthetics inspired by Linear, Raycast, Stripe, and GitHub instead of typical AI-generated UI.
formisch-usage
sandros94
Form handling with Formisch, the type-safe form library for modern frameworks. Use when the user needs to create forms, handle form state, validate form inputs, or work with Formisch.
webf-quickstart
openwebf
Get started with WebF development - setup WebF Go, create a React/Vue/Svelte project with Vite, and load your first app. Use when starting a new WebF project, onboarding new developers, or setting up development environment.
app_runner
k4ilham
A skill to run the backend (Go Fiber) and frontend (React/Vite) applications. It includes pre-run checks to clear the required ports (8080 and 5173) if they are already in use.
screenshot-to-code
OneWave-AI
Convert UI screenshots into working HTML/CSS/React/Vue code. Detects design patterns, components, and generates responsive layouts. Use this when users provide screenshots of websites, apps, or UI designs and want code implementation.