Install
$ agentstack add skill-juanmarchetto-agent-skills-demo-pipeline Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Pipes remote content directly into a shell (remote code execution).
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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:
find src/app -name "*.tsx" -not -name "_layout.tsx" -not -name "+not-found.tsx" | sort
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:
grep -n "loading\|isLogged\|isEmpty\|error\|balance.*>\|\.length.*==.*0" src/app/**/*.tsx
Common patterns to look for:
{loading ? : ...}→loadingstate{isLoggedIn && ...}or{user ? ... : ...}→guestvslogged-in{items.length === 0 ? : ...}→emptystate{balance > 0 ? ... : ...}→empty-balancevshas-balance{error ? : ...}→errorstate
Step 3: Map navigation flows
Search for navigation calls to build a flow graph:
grep -rn "router\.push\|router\.replace\|href=" src/app/ src/components/
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:
grep -rn "expo-camera\|CameraView\|useMobileWallet\|SeedVault\|Linking\|expo-local-authentication" src/
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](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.jsonscripts — if has"dev"or"start"→ Generic web project
- If none match → ask user
Step 2: Check running state
For mobile (Expo):
# Android
adb devices 2>/dev/null | grep -v "List"
# iOS (macOS only)
xcrun simctl list booted 2>/dev/null
If no device/emulator → tell user: "No running device detected. Start your emulator/device and the Expo dev server, then try again."
For web:
curl -s -o /dev/null -w "%{http_code}" http://localhost:8081 2>/dev/null
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null
curl -s -o /dev/null -w "%{http_code}" http://localhost:5173 2>/dev/null
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:
# Write mock state to AsyncStorage via adb
adb shell "run-as sh -c 'cat > /data/data//files/demo-state.json'" ://demo?screen=&state="
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:
adb shell "run-as sh -c 'rm -f /data/data//files/demo-state.json'"
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 create src/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](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
target:
app_id: # mobile only
base_url: # web only
demo_mode: false # true to enable state injection
env: # environment variables (optional)
DEMO_MODE: "true"
steps:
- name: ""
state: # optional — mock state to inject
key: value
native: real # optional — real | mock (default: real)
actions:
- :
- screenshot: ""
output:
screenshots: ./screenshots/
video: ./videos/demo.mp4
video_mode: device-only # device-only | full
report: ./docs/demo-report.md
Supported actions (see [reference.md](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:
maestro record maestro/demo-recording.yaml --output videos/demo.mp4
Fallback (no video): maestro test maestro/demo-recording.yaml
Copy screenshots:
mkdir -p screenshots
cp ~/.maestro/tests/*/screenshots/* screenshots/ 2>/dev/null || true
Video Mode: device-only
When output.video_mode is device-only, record only the device screen (no terminal/Maestro UI):
# Start device screen recording in parallel
adb shell screenrecord /sdcard/demo-recording.mp4 &
RECORD_PID=$!
# Run Maestro flow in test-only mode
maestro test maestro/demo-recording.yaml
# Stop recording and extract
kill $RECORD_PID 2>/dev/null
adb shell "kill $(adb shell ps | grep screenrecord | awk '{print $2}')" 2>/dev/null
sleep 1
adb pull /sdcard/demo-recording.mp4 videos/demo.mp4
adb shell rm /sdcard/demo-recording.mp4
Limitation: adb screenrecord max 3 minutes per recording. For longer demos, chain multiple recordings and concatenate:
ffmpeg -f concat -safe 0 -i segments.txt -c copy videos/demo.mp4
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
mkdir -p screenshots
ls -la screenshots/
Verify all expected screenshots exist. Report any missing.
Step 2: Verify video
ls -la videos/demo.mp4
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](templates/demo-report.md):
- 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
> Demo recording complete! > - Screenshots: N captured in ./screenshots/ > - Video: ./videos/demo.mp4 (X MB) > - Report: ./docs/demo-report.md
Example Output
Demo recording complete!
- Screenshots: 5 captured in ./screenshots/
01-login.png (1290x2796)
02-home-feed.png (1290x2796)
03-rewards-list.png (1290x2796)
04-coupon-detail.png (1290x2796)
05-profile.png (1290x2796)
- Video: ./videos/demo.mp4 (12.3 MB, 45s)
- Report: ./docs/demo-report.md
Error Handling
- No device/emulator detected: "Start your emulator/device and the Expo dev server, then try again"
- No dev server detected: "Start your dev server and try again"
- testID not found: Suggest text-based selection as fallback, or ask user to add testID to component
- Video recording failed: Fall back to
maestro test(screenshots only, no video) and explain limitation - Maestro not installed: Provide install command:
curl -Ls "https://get.maestro.mobile.dev" | bash
Related Skills
- maestro-mobile-testing — deep patterns for writing Maestro tests
- webreel — scripted web demos with cursor animation
Context
This skill has companion files:
reference.md— complete action reference with tool mappingstemplates/maestro-flow.yaml— Maestro flow templatetemplates/webreel-config.json— webreel config templatetemplates/demo-report.md— report template
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: JuanMarchetto
- Source: JuanMarchetto/agent-skills
- License: MIT
- Homepage: https://github.com/JuanMarchetto/agent-skills
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.