skillZs
★ LIVE SKILL TAGS ★
>>> LIVE SKILLS INDEX <<<
* OPEN SOURCE *
NO LOGIN, NO TRACKING
※ REAL INSTALL DATA ※
← back to all skills
fellowship-dev/dogfooded-skills150 installs

visual-evidence

Use when capturing Playwright screenshots or GIF recordings for PR evidence — before/after comparisons and feature demos.

How do I install this agent skill?

npx skills add https://github.com/fellowship-dev/dogfooded-skills --skill visual-evidence
view source ↗

Is this agent skill safe to install?

  • Gen Agent Trust Hubfail

    The skill performs privilege escalation by using sudo to install system packages and executes dynamic shell commands for video processing. It also involves downloading external browser automation dependencies and creates a surface for indirect prompt injection by processing arbitrary interaction steps provided via JSON.

  • Socketpass

    No alerts

  • Snykwarn

    Risk: MEDIUM · 1 issue

What does this agent skill do?

visual-evidence

Capture browser screenshots and GIF recordings using Playwright headless Chromium. Upload to cloud storage for embedding in PRs and reports.

When to Use

  • After implementing a bug fix (before/after comparison)
  • After implementing a feature (demo key flows)
  • During PR review to verify visual changes
  • Interaction/timing bugs (double-click, loading states, race conditions) → use GIF recording

When NOT to Use

  • Backend-only changes, config/infra, test-only, CLI tools, APIs without UI
  • Skip if Playwright install fails — visual evidence is best-effort, never blocks the pipeline

Prerequisites

Install Playwright and Chromium in the remote environment:

cd /tmp && npm install playwright 2>&1 | tail -3 && \
  npx playwright install-deps chromium 2>&1 | tail -3 && \
  npx playwright install chromium 2>&1 | tail -3 && \
  echo PW_READY

For GIF recording, also install ffmpeg:

sudo apt-get update -qq && sudo apt-get install -y -qq ffmpeg 2>&1 | tail -1 && echo FFMPEG_READY

Workflow

1. Screenshot Capture

Create /tmp/screenshot.mjs:

import { chromium } from '/tmp/node_modules/playwright/index.mjs';
const [url, output] = process.argv.slice(2);
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
console.log(`Navigating to ${url}...`);
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
const title = await page.title();
console.log(`Page title: ${title}`);
await page.screenshot({ path: output, fullPage: true });
console.log(`Screenshot saved: ${output}`);
await browser.close();

Usage:

node /tmp/screenshot.mjs http://localhost:3000 /tmp/evidence-home.png
node /tmp/screenshot.mjs http://localhost:3000/admin /tmp/evidence-admin.png

2. GIF Recording (Interaction Evidence)

Create /tmp/record.mjs:

import { chromium } from '/tmp/node_modules/playwright/index.mjs';
const [url, output, ...actions] = process.argv.slice(2);
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
  viewport: { width: 1280, height: 720 },
  recordVideo: { dir: '/tmp/videos', size: { width: 1280, height: 720 } }
});
const page = await context.newPage();
console.log(`Navigating to ${url}...`);
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });

// Execute actions passed as JSON array
if (actions[0]) {
  const steps = JSON.parse(actions[0]);
  for (const step of steps) {
    if (step.action === 'click') {
      console.log(`Clicking ${step.selector}...`);
      await page.click(step.selector);
    } else if (step.action === 'dblclick') {
      console.log(`Double-clicking ${step.selector}...`);
      await page.click(step.selector);
      await page.waitForTimeout(200);
      await page.click(step.selector);
    } else if (step.action === 'wait') {
      console.log(`Waiting ${step.ms}ms...`);
      await page.waitForTimeout(step.ms);
    } else if (step.action === 'fill') {
      console.log(`Filling ${step.selector}...`);
      await page.fill(step.selector, step.value);
    } else if (step.action === 'screenshot') {
      console.log(`Snapshot: ${step.label}`);
      await page.screenshot({ path: `/tmp/evidence-${step.label}.png` });
    }
  }
}

await page.waitForTimeout(500);
await page.close();
await context.close();
await browser.close();

// Convert to GIF
const { execSync } = await import('child_process');
const webm = execSync('ls -t /tmp/videos/*.webm | head -1').toString().trim();
console.log(`Video: ${webm}`);
execSync(`ffmpeg -y -i "${webm}" -vf "fps=10,scale=1280:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 "${output}" 2>&1 | tail -3`);
console.log(`GIF saved: ${output}`);

Usage:

node /tmp/record.mjs http://localhost:3000/form /tmp/evidence-before.gif \
  '[{"action":"click","selector":".submit-btn"},{"action":"wait","ms":2000}]'

3. Upload to Cloud Storage

Use the evidence-upload skill to upload screenshots and GIFs to the pylot assets backend. $PYLOT_GATEWAY_URL and $PYLOT_DISPATCH_TOKEN are already in every operator/worker environment — no static AWS keys needed.

Run /evidence-upload for each captured file and capture the returned public_url for embedding in Step 4.

4. PR Embedding

Before/After (bug fixes):

## Visual Evidence

<details>
<summary>Before / After</summary>

**Before (bug present on default branch):**
![before](<url>/before.png)

**After (fix applied):**
![after](<url>/after.png)

</details>

Interaction demo (GIF):

## Visual Evidence

<details>
<summary>Interaction demo</summary>

![demo](<url>/demo.gif)
*Caption: what this demonstrates*

</details>

Decision Table: Screenshot vs GIF

ScenarioFormat
Visual layout changeScreenshot (PNG)
New page / featureScreenshot (PNG)
Button disable on clickGIF
Loading spinner / skeletonGIF
Double-click preventionGIF
Form validation feedbackGIF
Transition / animationGIF
Error → retry flowGIF

Error Handling

Visual evidence is best-effort. Never block the pipeline.

  • Playwright install fails → log, skip evidence, proceed
  • Dev server won't start → log, skip evidence, proceed
  • Screenshot fails (404, timeout) → log, skip evidence, proceed
  • Upload fails (no creds, permission denied) → log, skip evidence, proceed
  • Hard timeout: 120 seconds for the entire evidence phase. If exceeded, kill and proceed.

Critical Rules

  • Never block the pipeline — evidence is best-effort
  • 120 second hard timeout — kill and move on
  • Always verify upload before embedding URLs in PRs
  • Screenshots in /tmp/ are ephemeral — they disappear when the environment stops

Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.

<a href="https://skillzs.dev/skills/fellowship-dev/dogfooded-skills/visual-evidence">View visual-evidence on skillZs</a>