-
Notifications
You must be signed in to change notification settings - Fork 98
Add pr-screenshots skill for AI-assisted screenshot capture in PRs #1918
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,214 @@ | ||||||
| --- | ||||||
| name: pr-screenshots | ||||||
| description: "Capture, annotate, and include screenshots in pull requests for UI changes. Use when creating or updating PRs that touch agents-manage-ui components/pages, agents-docs content, or any web-facing surface. Also use when asked to add before/after screenshots, visual diffs, preview deployment links, or enrich PR descriptions. Triggers on: PR screenshots, before/after, visual diff, PR description, preview deployment, capture screenshot, PR images, enrich PR." | ||||||
| --- | ||||||
|
|
||||||
| # PR Screenshots | ||||||
|
|
||||||
| Capture, redact, annotate, and embed screenshots in GitHub PRs for UI changes. | ||||||
|
|
||||||
| ## When to use | ||||||
|
|
||||||
| - Creating/updating PRs touching `agents-manage-ui/src/components/**`, `agents-manage-ui/src/app/**`, or `agents-docs/content/**` | ||||||
| - User asks for screenshots, before/after comparisons, or PR body enrichment | ||||||
| - Skip for backend-only, test-only, or non-visual changes | ||||||
|
|
||||||
| ## Workflow | ||||||
|
|
||||||
| 1. **Identify affected routes** from the diff — see [affected-routes.md](references/affected-routes.md) | ||||||
| 2. **Capture screenshots** — run `scripts/capture.ts` | ||||||
| 3. **Validate no sensitive data** — run `scripts/validate-sensitive.ts` | ||||||
| 4. **Annotate** — run `scripts/annotate.ts` (labels, borders, side-by-side) | ||||||
| 5. **Upload & embed** — update PR body with images and preview links | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Step 1: Identify Affected Pages | ||||||
|
|
||||||
| Analyze the diff to determine which UI routes are impacted. Use the mapping in [references/affected-routes.md](references/affected-routes.md). | ||||||
|
|
||||||
| Example: changes to `components/agent/sidepane/nodes/model-selector.tsx` affect the **agent editor** at `/{tenantId}/projects/{projectId}/agents/{agentId}`. | ||||||
|
|
||||||
| If the diff only touches backend code, tests, or non-visual files, skip screenshot capture. | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Step 2: Capture Screenshots | ||||||
|
|
||||||
| ### Environment setup | ||||||
|
|
||||||
| | Environment | Base URL | How to start | | ||||||
| |---|---|---| | ||||||
| | **Local dev** | `http://localhost:3000` | `cd agents-manage-ui && pnpm dev` | | ||||||
| | **Vercel preview** | `https://agents-git-{branch}-inkeep.vercel.app` | Automatic on PR push | | ||||||
| | **Playwright server** | Connect via `--connect ws://localhost:3001` | See "Reusable server" below | | ||||||
|
|
||||||
| ### Capture command | ||||||
|
|
||||||
| ```bash | ||||||
| # Local dev | ||||||
| npx tsx .cursor/skills/pr-screenshots/scripts/capture.ts \ | ||||||
| --base-url http://localhost:3000 \ | ||||||
| --routes "/{tenantId}/projects/{projectId}/agents/{agentId}" \ | ||||||
| --output-dir ./pr-screenshots | ||||||
|
|
||||||
| # Vercel preview | ||||||
| npx tsx .cursor/skills/pr-screenshots/scripts/capture.ts \ | ||||||
| --base-url https://agents-git-my-branch-inkeep.vercel.app \ | ||||||
| --routes "/{tenantId}/projects/{projectId}/agents/{agentId}" \ | ||||||
| --output-dir ./pr-screenshots | ||||||
|
|
||||||
| # With Playwright server (reuses browser across captures) | ||||||
| npx tsx .cursor/skills/pr-screenshots/scripts/capture.ts \ | ||||||
| --connect ws://localhost:3001 \ | ||||||
| --base-url http://localhost:3000 \ | ||||||
| --routes "/t1/projects/p1/agents/a1,/t1/projects/p1/settings" \ | ||||||
| --output-dir ./pr-screenshots | ||||||
| ``` | ||||||
|
|
||||||
| ### All capture options | ||||||
|
|
||||||
| | Option | Default | Description | | ||||||
| |---|---|---| | ||||||
| | `--base-url <url>` | *required* | Target URL (local dev or preview) | | ||||||
| | `--routes <paths>` | *required* | Comma-separated route paths | | ||||||
| | `--output-dir <dir>` | `./pr-screenshots` | Where to save PNGs and DOM text | | ||||||
| | `--viewport <WxH>` | `1280x800` | Browser viewport size | | ||||||
| | `--connect <ws-url>` | — | Connect to existing Playwright server | | ||||||
| | `--mask-selectors <s>` | — | Additional CSS selectors to blur | | ||||||
| | `--wait <ms>` | `2000` | Wait after page load before capture | | ||||||
| | `--full-page` | `false` | Capture full scrollable page | | ||||||
| | `--auth-cookie <value>` | — | Session cookie for authenticated pages | | ||||||
|
|
||||||
| ### Reusable Playwright server | ||||||
|
|
||||||
| Start a server once, reuse across multiple captures: | ||||||
|
|
||||||
| ```bash | ||||||
| # Terminal 1: start server | ||||||
| npx tsx .cursor/skills/pr-screenshots/scripts/capture.ts --serve --port 3001 | ||||||
|
|
||||||
| # Terminal 2+: connect and capture | ||||||
| npx tsx .cursor/skills/pr-screenshots/scripts/capture.ts \ | ||||||
| --connect ws://localhost:3001 --base-url http://localhost:3000 \ | ||||||
| --routes "/..." --output-dir ./pr-screenshots | ||||||
| ``` | ||||||
|
|
||||||
| ### Using browser-use subagent (Cursor alternative) | ||||||
|
|
||||||
| When scripts are unavailable, use the `browser-use` subagent: | ||||||
| 1. Navigate to the target URL | ||||||
| 2. Call `browser_screenshot` to capture | ||||||
| 3. Download the image for annotation | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Step 3: Validate Sensitive Data | ||||||
|
|
||||||
| **Always run before uploading to GitHub.** | ||||||
|
|
||||||
| ```bash | ||||||
| npx tsx .cursor/skills/pr-screenshots/scripts/validate-sensitive.ts \ | ||||||
| --dir ./pr-screenshots | ||||||
| ``` | ||||||
|
|
||||||
| The script checks `.dom-text.txt` files (saved by capture) for: | ||||||
| - API keys (`sk-`, `ik_`, `sk-ant-`, `AKIA`, `sk_live_`) | ||||||
| - Tokens (Bearer, JWT, GitHub PATs) | ||||||
| - PEM private keys | ||||||
| - Connection strings with credentials | ||||||
| - Email addresses | ||||||
|
|
||||||
| Exit code 1 = sensitive data found. Re-capture with additional `--mask-selectors` or fix the source before proceeding. | ||||||
|
|
||||||
| ### Pre-capture masking (automatic) | ||||||
|
|
||||||
| The capture script automatically masks these before taking screenshots: | ||||||
|
|
||||||
| | Selector | What it catches | | ||||||
| |---|---| | ||||||
| | `input[type="password"]` | Password fields | | ||||||
| | `input[name="apiKeyToSet"]` | Credential API key inputs | | ||||||
| | `input[data-field="value"]` | Header value inputs (GenericKeyValueInput) | | ||||||
| | `[role="alertdialog"] pre` | API key display dialogs | | ||||||
| | Text matching `sk-`, `ik_`, `Bearer`, `eyJ`, `ghp_`, PEM headers | In-page tokens/keys | | ||||||
|
|
||||||
| Add more with `--mask-selectors "selector1,selector2"`. | ||||||
|
|
||||||
| ### What to check manually | ||||||
|
|
||||||
| - Screenshots of the **credentials** page (`/credentials/**`) | ||||||
| - Screenshots of the **API keys** page (`/api-keys`) | ||||||
| - Any page where users enter secrets (trigger auth headers, MCP server config) | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Step 4: Annotate Images | ||||||
|
|
||||||
| ```bash | ||||||
| # Add "Before" label with red border | ||||||
| npx tsx .cursor/skills/pr-screenshots/scripts/annotate.ts \ | ||||||
| --input before.png --label "Before" --border "#ef4444" --output before-labeled.png | ||||||
|
|
||||||
| # Add "After" label with green border | ||||||
| npx tsx .cursor/skills/pr-screenshots/scripts/annotate.ts \ | ||||||
| --input after.png --label "After" --border "#22c55e" --output after-labeled.png | ||||||
|
|
||||||
| # Side-by-side comparison | ||||||
| npx tsx .cursor/skills/pr-screenshots/scripts/annotate.ts \ | ||||||
| --stitch before.png after.png --labels "Before,After" --output comparison.png | ||||||
| ``` | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Step 5: Upload & Embed in PR | ||||||
|
|
||||||
| ### Upload images to GitHub | ||||||
|
|
||||||
| Images in PR markdown need permanent URLs. Use one of: | ||||||
|
|
||||||
| **Option A — PR comment with image** (simplest): | ||||||
| ```bash | ||||||
| # GitHub renders attached images with permanent CDN URLs | ||||||
| gh pr comment {pr-number} --body "" | ||||||
| ``` | ||||||
|
|
||||||
| **Option B — Update PR body directly**: | ||||||
| ```bash | ||||||
| gh pr edit {pr-number} --body "$(cat pr-body.md)" | ||||||
| ``` | ||||||
|
|
||||||
| ### PR body templates | ||||||
|
|
||||||
| Use the templates in [references/pr-templates.md](references/pr-templates.md) for consistent formatting. Include: | ||||||
|
|
||||||
| 1. **Visual Changes** section with before/after screenshots | ||||||
| 2. **Test URLs** section with links to preview deployment pages | ||||||
| 3. **Summary** of what changed and why | ||||||
|
|
||||||
| ### Generating preview URLs | ||||||
|
|
||||||
| Pattern: `https://agents-git-{branch}-inkeep.vercel.app/{tenantId}/projects/{projectId}/...` | ||||||
|
|
||||||
| Replace `{branch}` with the PR branch name (hyphens, not slashes). Use the affected-routes mapping to build the full paths. | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Dependencies | ||||||
|
|
||||||
| These scripts require packages available in the monorepo: | ||||||
|
|
||||||
| | Package | Source | Notes | | ||||||
| |---|---|---| | ||||||
| | `playwright` | `agents-manage-ui` workspace dep | Browser automation | | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 MINOR: Incorrect dependency source for playwright Issue: This table states playwright is an Why: Documentation should accurately reflect where dependencies come from. Users looking here for troubleshooting may be confused. Fix:
Suggested change
Refs:
|
||||||
| | `sharp` | Root dev dep | Image annotation | | ||||||
| | `tsx` | Used in root scripts | TypeScript runner | | ||||||
|
|
||||||
| If `sharp` is not installed: `pnpm add -Dw sharp` | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Additional Resources | ||||||
|
|
||||||
| - [references/affected-routes.md](references/affected-routes.md) — File path → UI route mapping | ||||||
| - [references/pr-templates.md](references/pr-templates.md) — PR body markdown templates | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| # Affected Routes Mapping | ||
|
|
||
| Map changed files to the UI routes they affect. Use this to determine which pages to screenshot. | ||
|
|
||
| ## How to use | ||
|
|
||
| 1. Look at the files changed in the PR diff | ||
| 2. Find the matching file pattern(s) below | ||
| 3. The corresponding route is the page to screenshot | ||
| 4. Replace `{tenantId}`, `{projectId}`, etc. with real IDs from the target environment | ||
|
|
||
| ## Component → Route Mapping | ||
|
|
||
| ### Agent Visual Builder | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/agent/nodes/**` | `/{tenantId}/projects/{projectId}/agents/{agentId}` | Agent graph nodes (agent, MCP, sub-agent) | | ||
| | `components/agent/edges/**` | `/{tenantId}/projects/{projectId}/agents/{agentId}` | Agent graph edges/connections | | ||
| | `components/agent/sidepane/**` | `/{tenantId}/projects/{projectId}/agents/{agentId}` | Side panel editors (model, prompt, tools) | | ||
| | `components/agent/sidepane/nodes/model-*` | `/{tenantId}/projects/{projectId}/agents/{agentId}` | Model selector, model section | | ||
| | `components/agent/sidepane/metadata/**` | `/{tenantId}/projects/{projectId}/agents/{agentId}` | Agent metadata editor | | ||
| | `components/agent/toolbar/**` | `/{tenantId}/projects/{projectId}/agents/{agentId}` | Agent toolbar (save, deploy, etc.) | | ||
| | `components/agent/playground/**` | `/{tenantId}/projects/{projectId}/agents/{agentId}` | In-editor chat playground | | ||
| | `components/agent/copilot/**` | `/{tenantId}/projects/{projectId}/agents/{agentId}` | Agent copilot panel | | ||
| | `features/agent/**` | `/{tenantId}/projects/{projectId}/agents/{agentId}` | Agent domain logic, state, commands | | ||
|
|
||
| ### Agent List | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/agents/**` | `/{tenantId}/projects/{projectId}/agents` | Agent list page | | ||
|
|
||
| ### Project Settings | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/projects/form/**` | `/{tenantId}/projects/{projectId}/settings` | Project settings form | | ||
| | `components/projects/form/project-models-section.*` | `/{tenantId}/projects/{projectId}/settings` | Project model configuration | | ||
| | `features/project/**` | `/{tenantId}/projects/{projectId}/settings` | Project state | | ||
|
|
||
| ### Projects List | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/projects/**` (not `form/`) | `/{tenantId}/projects` | Projects list page | | ||
|
|
||
| ### Credentials | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/credentials/**` | `/{tenantId}/projects/{projectId}/credentials` | Credentials list | | ||
| | `components/credentials/views/**` | `/{tenantId}/projects/{projectId}/credentials/new/bearer` | New credential form | | ||
|
|
||
| ### API Keys | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/api-keys/**` | `/{tenantId}/projects/{projectId}/api-keys` | API keys page | | ||
|
|
||
| ### MCP Servers | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/mcp-servers/**` | `/{tenantId}/projects/{projectId}/mcp-servers` | MCP server list | | ||
| | `components/mcp-servers/form/**` | `/{tenantId}/projects/{projectId}/mcp-servers/{mcpServerId}/edit` | MCP server edit form | | ||
| | `components/mcp-servers/selection/**` | `/{tenantId}/projects/{projectId}/mcp-servers/new` | MCP server selection | | ||
|
|
||
| ### External Agents | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/external-agents/**` | `/{tenantId}/projects/{projectId}/external-agents` | External agent list | | ||
| | `components/external-agents/form/**` | `/{tenantId}/projects/{projectId}/external-agents/new` | External agent form | | ||
|
|
||
| ### Triggers | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/triggers/**` | `/{tenantId}/projects/{projectId}/agents/{agentId}/triggers` | Triggers list | | ||
| | `components/triggers/trigger-form*` | `/{tenantId}/projects/{projectId}/agents/{agentId}/triggers/new` | New/edit trigger form | | ||
|
|
||
| ### Data Components | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/data-components/**` | `/{tenantId}/projects/{projectId}/components` | Data component list | | ||
| | `components/data-components/form/**` | `/{tenantId}/projects/{projectId}/components/new` | Data component form | | ||
|
|
||
| ### Artifact Components | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/artifact-components/**` | `/{tenantId}/projects/{projectId}/artifacts` | Artifact list | | ||
| | `components/artifact-components/form/**` | `/{tenantId}/projects/{projectId}/artifacts/new` | Artifact form | | ||
|
|
||
| ### Datasets & Evaluations | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/datasets/**` | `/{tenantId}/projects/{projectId}/datasets` | Dataset list | | ||
| | `components/dataset-items/**` | `/{tenantId}/projects/{projectId}/datasets/{datasetId}` | Dataset item view | | ||
| | `components/evaluations/**` | `/{tenantId}/projects/{projectId}/evaluations` | Evaluations page | | ||
| | `components/evaluation-run-configs/**` | `/{tenantId}/projects/{projectId}/evaluations/run-configs/{configId}` | Run config results | | ||
| | `components/evaluation-jobs/**` | `/{tenantId}/projects/{projectId}/evaluations/jobs/{configId}` | Evaluation job results | | ||
|
|
||
| ### Traces | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/traces/**` | `/{tenantId}/projects/{projectId}/traces` | Traces overview | | ||
| | `hooks/use-traces*` | `/{tenantId}/projects/{projectId}/traces` | Traces data hooks | | ||
|
|
||
| ### Organization Settings | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/settings/**` | `/{tenantId}/settings` | Org settings | | ||
| | `components/access/**` | `/{tenantId}/projects/{projectId}/members` | Project members | | ||
|
|
||
| ### Shared / Cross-cutting | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `components/shared/model-configuration.*` | Multiple: agent editor + project settings | Model config (appears in both) | | ||
| | `components/form/**` | Multiple | Generic form components, affect all forms | | ||
| | `components/editors/**` | Multiple | JSON/prompt editors, affect agent + project pages | | ||
| | `components/ui/**` | Multiple | Base UI primitives, affects everything | | ||
| | `components/layout/**` | Multiple | Page headers, empty states | | ||
| | `components/errors/**` | Multiple | Error pages | | ||
| | `components/icons/**` | Multiple | Icons | | ||
|
|
||
| ### Auth Pages | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `app/login/**` | `/login` | Login page | | ||
| | `app/reset-password/**` | `/reset-password` | Password reset | | ||
| | `app/accept-invitation/**` | `/accept-invitation/{invitationId}` | Invitation acceptance | | ||
|
|
||
| ### Documentation (agents-docs) | ||
|
|
||
| | File pattern | Route | Description | | ||
| |---|---|---| | ||
| | `agents-docs/content/**` | Preview docs site | Documentation pages | | ||
| | `agents-docs/_snippets/**` | Preview docs site | Reusable snippets | | ||
| | `agents-docs/public/images/**` | Preview docs site | Documentation images | | ||
|
|
||
| ## Pages with Sensitive Data (extra caution) | ||
|
|
||
| These pages may display or accept sensitive information. Always verify masking works correctly: | ||
|
|
||
| | Route | Sensitive content | | ||
| |---|---| | ||
| | `/{tenantId}/projects/{projectId}/credentials/**` | API keys, OAuth secrets, private keys | | ||
| | `/{tenantId}/projects/{projectId}/api-keys` | API key prefixes, new key display | | ||
| | `/{tenantId}/projects/{projectId}/agents/{agentId}/triggers/*/edit` | Auth header values | | ||
| | `/{tenantId}/projects/{projectId}/mcp-servers/*/edit` | Server URLs with credentials | | ||
|
|
||
| ## Vercel Preview URL Pattern | ||
|
|
||
| ``` | ||
| https://agents-git-{branch-name}-inkeep.vercel.app{route} | ||
| ``` | ||
|
|
||
| Replace `{branch-name}` with the PR branch (use hyphens, e.g., `bugfix/azure-model-selector` becomes `bugfix-azure-model-selector`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 MINOR: Documentation lists "Email addresses" but script doesn't check for them
Issue: The documentation states the validation script checks for "Email addresses" but
validate-sensitive.tshas no email pattern in itsSENSITIVE_PATTERNSarray.Why: Documentation should accurately reflect what the script does. Users relying on email detection would be surprised to find it's not implemented.
Fix: Either remove "Email addresses" from this list, or add the pattern to
validate-sensitive.ts:Refs: