juanmarchetto/agent-skills/demo-pipeline
- community
Checking that the SKILL.md file is still in the repository.
Record automated demos for mobile and web apps. Converts natural language demo scripts into Maestro flows (mobile) or webreel configs (web), executes them to capture screenshots and video, and generates a QA report. Use when: record demo, demo recording, record app demo, grab demo, automated demo.
Install
npx vskill@latest install juanmarchetto/agent-skills/demo-pipelineSKILL.md
Demo Pipeline
Record polished app demos from a natural language script. Works with Expo mobile (Maestro) and web apps (webreel/Playwright).
Pipeline: DISCOVER → DETECT → STATE SETUP → PARSE → EXECUTE → COMPILE
Quick Start
User says: "record demo — show the login, navigate to rewards, highlight featured items, end on profile"
You:
- Discover all screens, states, and flows via static analysis
- Detect the project type and running state
- Set up demo state injection (if demo_mode is enabled)
- Generate a unified YAML script from the description
- Present YAML for user approval
- Execute with the right tool (Maestro or webreel)
- Collect screenshots, video, and generate report
Phase 0: DISCOVER — Flow & State Analysis
Analyze the codebase to map all screens, states, flows, and native dependencies.
Step 1: Discover screens
Read the file-based routing structure:
For each screen file, extract the route path from its filesystem location.
Step 2: Identify states per screen
For each screen file, search for UI conditionals that reveal possible states:
Common patterns to look for:
{loading ? <Skeleton/> : ...}→loadingstate{isLoggedIn && ...}or{user ? ... : ...}→guestvslogged-in{items.length === 0 ? <EmptyState/> : ...}→emptystate{balance > 0 ? ... : ...}→empty-balancevshas-balance{error ? <Error/> : ...}→errorstate
Step 3: Map navigation flows
Search for navigation calls to build a flow graph:
Group into named flows (e.g., "Full reward claim": home → rewards → [id] → scan → confirmation).
Step 4: Identify native dependencies
Search for native module usage per screen:
Mark each screen's native dependencies. These will be flagged as native: real | mock in the YAML.
Step 5: Present flow map
Generate a flow map in YAML format (see templates/flow-map.yaml) and present to user:
"I discovered N screens with M possible states and K flows. Here's the map:[flow map YAML]Which flows do you want in the demo?"
Then proceed to Phase 1.
Phase 1: DETECT — Project Type & State
Before anything, determine what kind of project this is and whether it's running.
Step 1: Identify project type
Check in order (stop at first match):
- Read `app.json` — if it has an
"expo"key → Expo project
- Check if
app.jsonhas"web"in platforms → Expo universal - Extract
expo.slugorexpo.namefor app ID - Default app ID: from
expo.ios.bundleIdentifierorexpo.android.package
- Glob for `next.config.*` — if found → Next.js web project
- Read `package.json` scripts — if has
"dev"or"start"→ Generic web project
- If none match → ask user
Step 2: Check running state
For mobile (Expo):
If no device/emulator → tell user: "No running device detected. Start your emulator/device and the Expo dev server, then try again."
For web:
If no server → tell user: "No dev server detected. Start your dev server and try again."
Step 3: Announce detection
Tell the user what you detected, e.g.:
"Detected: Expo mobile project (com.example.myapp) with Android emulator running. I'll use Maestro for recording."
Then proceed to Phase 2.
Phase 1.5: STATE SETUP — Demo State Injection
Only runs when the unified YAML has `demo_mode: true`. Skip this phase entirely for regular demos without state emulation.
Step 1: Check if demo mode is needed
If any step in the YAML has a state: block or native: mock, demo mode is required.
Step 2: Inject state via adb
For each step that has a state: block, inject the mock data before executing that step's actions.
Injection mechanism:
Alternative — use deep links to navigate to specific states:
Step 3: Handle native mocks
For steps with native: mock:
- Inject the mock result into AsyncStorage before the step executes
- The app's demo bootstrap reads this and skips the native module, showing the result directly
For steps with native: real (or no native field):
- No injection — let the real native flow execute
Step 4: Cleanup after recording
After all steps complete:
Important: App-side requirement
The project needs a demo bootstrap file. If it doesn't exist, tell the user:
"Demo mode requires a bootstrap file. I'll createsrc/demo/bootstrap.ts— a dev-only file that reads mock state from AsyncStorage. This file has zero impact on production builds since it's gated behind__DEV__."
See state-injection.md for the full bootstrap implementation.
Phase 2: PARSE — Natural Language → Unified YAML
Convert the user's natural language demo description into a structured YAML script.
Step 1: Analyze the description
Extract:
- Screens/sections to show (each becomes a step)
- Interactions (taps, scrolls, typing)
- Key elements to highlight (get screenshot actions)
- Flow order (navigation sequence)
Step 2: Identify testIDs or selectors
For mobile (Expo/React Native):
- Search codebase for testID props:
grep -r 'testID=' src/ - Match testIDs to screens/elements in the description
- If no testID exists, use text-based selection
For web:
- Search for
id=,data-testid=, or distinctive text - Use CSS selectors
Step 3: Generate unified YAML
Supported actions (see reference.md for full details):
| Action | Example | |
|---|---|---|
tap | tap: "tab-rewards" or tap: "Button Text" | |
type | type: { target: "search-input", text: "Espresso" } | |
scroll | scroll: down | |
swipe | swipe: left | |
wait | wait: 2s | |
wait_for | wait_for: "loading-done" | |
screenshot | screenshot: "03-rewards" | |
navigate | navigate: "http://localhost:3000/dashboard" | |
key | key: "Enter" |
Auto-number screenshots with NN- prefix (01, 02, 03...).
Step 4: Present for approval
Show the generated YAML to the user. Ask:
"Here's the demo script I generated. Review it and let me know if you'd like to change anything, or approve to proceed with recording."
HARD GATE: Do NOT proceed to Phase 3 until the user explicitly approves the YAML.
Phase 3: EXECUTE — Run the Recording
Translate approved unified YAML → tool-specific format → execute.
Mobile Path: Maestro
Step 1: Generate Maestro flow
Action mapping:
| Unified | Maestro | |
|---|---|---|
tap: "id" | - tapOn:\n id: "id" | |
tap: "Text" | - tapOn: "Text" | |
type: { target, text } | - tapOn:\n id: "target"\n- inputText: "text" | |
scroll: down | - scroll | |
swipe: left | - swipe:\n direction: LEFT\n duration: 400 | |
wait: 2s | (no-op — Maestro auto-waits; used for pacing only) | |
wait_for: "id" | - extendedWaitUntil:\n visible:\n id: "id"\n timeout: 10000 | |
screenshot: "name" | - takeScreenshot: "name" |
Step 2: Save to ./maestro/demo-recording.yaml
Step 3: Execute:
Fallback (no video): maestro test maestro/demo-recording.yaml
Copy screenshots:
Video Mode: device-only
When output.video_mode is device-only, record only the device screen (no terminal/Maestro UI):
Limitation: adb screenrecord max 3 minutes per recording. For longer demos, chain multiple recordings and concatenate:
When video_mode is full (default), use maestro record as before.
Web Path: webreel
Step 1: Generate webreel config
Action mapping:
| Unified | webreel | |
|---|---|---|
tap: "id" | { "action": "click", "selector": "#id" } | |
tap: "Text" | { "action": "click", "text": "Text" } | |
type: { target, text } | { "action": "type", "selector": "#target", "text": "text" } | |
scroll: down | { "action": "scroll", "y": 500 } | |
wait: 2s | { "action": "pause", "ms": 2000 } | |
wait_for: "id" | { "action": "wait", "selector": "#id" } | |
screenshot: "name" | { "action": "screenshot", "output": "name.png" } | |
navigate: "url" | { "action": "navigate", "url": "url" } | |
key: "Enter" | { "action": "key", "key": "Enter" } |
Step 2: Save to ./webreel.config.json
Step 3: Execute: npx webreel record
Web Fallback: Playwright
If webreel not installed:
- Generate Playwright TypeScript script with
recordVideocontext - Execute:
npx tsx scripts/demo-recording.ts - Convert:
ffmpeg -i recordings/demo.webm -c:v libx264 -preset fast videos/demo.mp4
Error Handling
If execution fails:
- Show error output to user
- Suggest fixes (missing testIDs, wrong selectors, app not running)
- Let user edit YAML and retry from Phase 3
Phase 4: COMPILE — Generate Outputs
Step 1: Organize screenshots
Verify all expected screenshots exist. Report any missing.
Step 2: Verify video
Report file size. If missing, explain (e.g., maestro test doesn't record video).
Step 3: Generate report
Create ./docs/demo-report.md using the report template:
- Project name and type (from Phase 1)
- Tool used
- Date
- Step table with screenshot paths and status
- Output file paths and sizes
- Errors encountered
Step 4: Present summary
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/demo-pipeline/SKILL.md
Popularity
- Repository stars
- 5
- Repository forks
- 1
- 7-day trend score
- 9
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/juanmarchetto/agent-skills/demo-pipeline)