{"owner":"warpdotdev","repo":"warp","hasSkills":true,"totalSkillsCount":150,"totalTokensCount":69157,"categories":["anthropic-skill","root-instruction","subagent-persona"],"hasMcp":true,"mcpConfig":{"mcpServers":{"warp":{"command":"npx","args":["-y","@modelcontextprotocol/server-warp"]}}},"found":[".agents/skills/changelog-draft/SKILL.md",".agents/skills/dedupe-issue-local/SKILL.md",".agents/skills/add-feature-flag/SKILL.md",".agents/skills/add-telemetry/SKILL.md",".agents/skills/classify-changelog-pr/SKILL.md",".agents/skills/cross-platform-cloud-verification/SKILL.md",".agents/skills/gui-create-launch-modal/SKILL.md",".agents/skills/gui-onboarding-verification-skill/SKILL.md",".agents/skills/gui-ui-guidelines/SKILL.md",".agents/skills/gui-reproduce-bug-report-local/SKILL.md",".agents/skills/gui-integration-test/SKILL.md",".agents/skills/gui-settings-ui/SKILL.md",".agents/skills/logging-and-error-reporting/SKILL.md",".agents/skills/promote-feature/SKILL.md",".agents/skills/remove-feature-flag/SKILL.md",".agents/skills/review-pr-local/SKILL.md",".agents/skills/rust-unit-tests/SKILL.md",".agents/skills/triage-issue-local/SKILL.md",".agents/skills/tui-testing/SKILL.md",".agents/skills/tui-ui-guidelines/SKILL.md",".agents/skills/tui-verify-change/SKILL.md",".warp/skills/gui-integration-test-video/SKILL.md","resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md","resources/bundled/harness_skills/oz-finish-task/SKILL.md","resources/bundled/harness_skills/oz-notify-user/SKILL.md","resources/bundled/harness_skills/oz-report-pr/SKILL.md","resources/bundled/harness_skills/oz-upload-file/SKILL.md","resources/bundled/skills/add-mcp-server/SKILL.md","resources/bundled/skills/change-keybinding/SKILL.md","resources/bundled/skills/claude-api/SKILL.md","resources/bundled/skills/create-skill/SKILL.md","resources/bundled/skills/create-tab-config/SKILL.md","resources/bundled/skills/factory-deferred-repositories/SKILL.md","resources/bundled/skills/factory-files/SKILL.md","resources/bundled/skills/factory-mcp/SKILL.md","resources/bundled/skills/modify-settings/SKILL.md","resources/bundled/skills/oz-platform/SKILL.md","resources/bundled/skills/pr-comments/SKILL.md","resources/bundled/skills/tab-configs/SKILL.md","resources/bundled/skills/tui-migrate-setup/SKILL.md","resources/bundled/skills/update-tab-config/SKILL.md","resources/bundled/skills/warpctrl/SKILL.md","resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md","resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md","resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md","resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md","resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md","resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md","resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md","resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md","resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md","resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md","resources/bundled/mcp_skills/figma/figma-use/SKILL.md","AGENTS.md","agents/specs/APP-4878 - fix TUI copy to use native clipboard.md","agents/specs/APP-4880 - TUI view-logs slash command.md","agents/specs/APP-4881-tui-starting-shell-indicator.md","agents/specs/APP-4882 - fix misleading oversized-file read error.md","agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md","agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md","agents/specs/APP-4901-add-fast-forward-slash-command.md","agents/specs/APP-4902 - window-scoped background focus suppression.md","agents/specs/APP-4903 improve TUI tab bar keybindings.md","agents/specs/APP-4907 - TUI input focus during agent drafting.md","agents/specs/APP-4913-tui-input-prompt-prefix.md","agents/specs/CODE-1884-voice-input-slash-command.md","agents/specs/CODE-1890-status-line-v2.md","agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md","agents/specs/CODE-1902 change TUI log storage.md","agents/specs/CODE-1908 TUI default profile uses AgentDecides.md","agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md","agents/specs/QUALITY-1169-richer-click-drag-annotations.md","agents/specs/REMOTE-2160 - macOS video recording for computer use.md",".agents/skills/changelog-draft/examples/changelog-draft-example.md","resources/bundled/skills/claude-api/csharp/claude-api.md","resources/bundled/skills/claude-api/curl/examples.md","resources/bundled/skills/claude-api/curl/managed-agents.md","resources/bundled/skills/claude-api/go/claude-api.md","resources/bundled/skills/claude-api/java/claude-api.md","resources/bundled/skills/claude-api/php/claude-api.md","resources/bundled/skills/claude-api/ruby/claude-api.md","resources/bundled/skills/claude-api/shared/agent-design.md","resources/bundled/skills/claude-api/shared/error-codes.md","resources/bundled/skills/claude-api/shared/live-sources.md","resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md","resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md","resources/bundled/skills/claude-api/shared/managed-agents-core.md","resources/bundled/skills/claude-api/shared/managed-agents-environments.md","resources/bundled/skills/claude-api/shared/managed-agents-events.md","resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md","resources/bundled/skills/claude-api/shared/managed-agents-overview.md","resources/bundled/skills/claude-api/shared/managed-agents-tools.md","resources/bundled/skills/claude-api/shared/model-migration.md","resources/bundled/skills/claude-api/shared/models.md","resources/bundled/skills/claude-api/shared/prompt-caching.md","resources/bundled/skills/claude-api/shared/tool-use-concepts.md","resources/bundled/skills/create-skill/agents/analyzer.md","resources/bundled/skills/create-skill/agents/comparator.md","resources/bundled/skills/create-skill/agents/grader.md","resources/bundled/skills/create-skill/references/schemas.md","resources/bundled/skills/factory-files/references/examples.md","resources/bundled/skills/factory-files/references/scorers.md","resources/bundled/skills/factory-files/references/validation.md","resources/bundled/skills/oz-platform/references/third-party-clis.md","resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md","resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md","resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md","resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md","resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md","resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md","resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md","resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md","resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md","resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md","resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md","resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md","resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md","resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md","resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md","resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md","resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md","resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md","resources/bundled/skills/claude-api/go/managed-agents/README.md","resources/bundled/skills/claude-api/java/managed-agents/README.md","resources/bundled/skills/claude-api/php/managed-agents/README.md","resources/bundled/skills/claude-api/python/claude-api/README.md","resources/bundled/skills/claude-api/python/claude-api/batches.md","resources/bundled/skills/claude-api/python/claude-api/files-api.md","resources/bundled/skills/claude-api/python/claude-api/streaming.md","resources/bundled/skills/claude-api/python/claude-api/tool-use.md","resources/bundled/skills/claude-api/python/managed-agents/README.md","resources/bundled/skills/claude-api/ruby/managed-agents/README.md","resources/bundled/skills/claude-api/typescript/claude-api/README.md","resources/bundled/skills/claude-api/typescript/claude-api/batches.md","resources/bundled/skills/claude-api/typescript/claude-api/files-api.md","resources/bundled/skills/claude-api/typescript/claude-api/streaming.md","resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md","resources/bundled/skills/claude-api/typescript/managed-agents/README.md","resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json","resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json","resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md"],"skills":{".agents/skills/changelog-draft/SKILL.md":"---\nname: changelog-draft\ndescription: Generate a reviewable changelog draft from PRs merged in a release range. Extracts explicit CHANGELOG markers, classifies unmarked PRs, adds external contributor attribution, and outputs markdown + JSON artifacts. Does NOT mutate channel_versions.json.\n---\n\n# Changelog Draft Generator\n\n## Inputs\n\n| Parameter | Required | Description |\n|-----------|----------|-------------|\n| `channel` | yes | Release channel: `stable`, `preview`, or `dev` |\n| `release_tag` | yes | The release tag to generate the changelog for (e.g. `v0.2026.05.06.09.12.stable_00`) |\n| `output_dir` | no | Directory to write output files. Defaults to `$RUNNER_TEMP` or `/tmp/changelog-draft` |\n| `attribution` | no | Attribution mode: `external-only` (default), `all`, or `none` |\n\n## Workflow\n\n### Step 1 — Determine the release range\n\nInfer the previous release **cut** for comparison. Release tags follow the pattern `v0.YYYY.MM.DD.HH.MM.<channel>_NN`, where `_NN` is the RC/hotfix number within that release cut. Multiple tags can share the same date prefix (e.g. `_00`, `_01`, `_02` are all part of one release cut).\n\nThe base tag must be the `_00` tag of the **previous** release cut (i.e. a different date), not just the previous tag. For example, if generating a changelog for `v0.2026.04.29.08.57.stable_01`, the base should be `v0.2026.04.22.08.57.stable_00`, not `v0.2026.04.29.08.57.stable_00`.\n\n```bash\n# 1. Extract the date prefix from the release_tag (everything before _NN)\nrelease_date_prefix=\"${release_tag%_*}\"\n\n# 2. List all _00 tags for the channel (these are release cut points), sorted descending\ngit tag --list \"v0.*.${channel}_00\" --sort=-version:refname\n\n# 3. Pick the first _00 tag whose date prefix differs from release_date_prefix\n```\n\nRecord the range as `previous_cut_tag..release_tag`.\n\n### Step 2 — Fetch PR data\n\nRun the `fetch_prs.py` script to collect all public-release PRs merged in the release range and extract explicit changelog markers. Pass the repository that the workflow checked out, not necessarily the public repository. Release workflows run from `warpdotdev/warp-internal`, and the script deterministically resolves `warp-repo-sync[bot]` PRs back to their original public `warpdotdev/warp` PR metadata before emitting JSON. When running from `warpdotdev/warp-internal`, the script intentionally omits PRs that were not authored by the repo-sync bot, because those are private internal changes that must not be exposed to the changelog agent or generated artifacts.\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/fetch_prs.py \\\n  --repo \"${GITHUB_REPOSITORY:-warpdotdev/warp}\" \\\n  --base-ref <previous_tag> \\\n  --head-ref <release_tag>\n```\n\nThe script outputs JSON to stdout with this structure:\n```json\n{\n  \"range\": { \"base\": \"<previous_tag>\", \"head\": \"<release_tag>\" },\n  \"prs\": [\n    {\n      \"number\": 1234,\n      \"url\": \"https://github.com/warpdotdev/warp/pull/1234\",\n      \"title\": \"...\",\n      \"commit_subject\": \"... (#1234)\",\n      \"author\": \"username\",\n      \"body\": \"...\",\n      \"labels\": [\"...\"],\n      \"merged_at\": \"2026-05-01T...\",\n      \"explicit_entries\": [\n        { \"category\": \"NEW-FEATURE\", \"text\": \"Added dark mode\" }\n      ],\n      \"linked_issues\": [5678],\n      \"changed_files\": [\"app/src/ai/agent.rs\", \"crates/warp_features/src/lib.rs\"],\n      \"source_repo\": \"warpdotdev/warp\",\n      \"internal_pr\": {\n        \"number\": 25712,\n        \"url\": \"https://github.com/warpdotdev/warp-internal/pull/25712\",\n        \"author\": \"warp-repo-sync[bot]\",\n        \"title\": \"...\",\n        \"repo\": \"warpdotdev/warp-internal\"\n      }\n    }\n  ]\n}\n```\n\nUse the top-level `number`, `url`, `commit_subject`, `author`, `body`, `labels`, `changed_files`, and `source_repo` fields as the source of truth. `internal_pr` is audit-only and must never be used for contributor attribution or user-facing changelog links. If `url` is empty, omit the PR link from user-facing markdown rather than synthesizing one.\n\n### Step 3 — Classify contributors\n\nRun the `classify_contributors.py` script with the unique author logins from Step 2:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/classify_contributors.py \\\n  --org warpdotdev \\\n  --authors author1,author2,author3\n```\n\nOutput JSON:\n```json\n{\n  \"internal\": [\"author1\"],\n  \"external\": [\"author3\"],\n  \"bot\": [\"author2\"],\n  \"unknown\": []\n}\n```\n\n### Step 4 — Extract feature flags\n\nRun the `extract_feature_flags.py` script to get the current flag gate lists:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/extract_feature_flags.py \\\n  --file crates/warp_features/src/lib.rs\n```\n\nOutput JSON:\n```json\n{\n  \"release_flags\": [\"Autoupdate\", \"Changelog\", ...],\n  \"preview_flags\": [\"Orchestration\", ...],\n  \"dogfood_flags\": [\"LogExpensiveFramesInSentry\", ...]\n}\n```\n\n### Step 5 — Fetch issue reporters\n\nCollect all unique `linked_issues` from Step 2 and fetch the original reporter for each. Pass `--org` so the script checks org membership and filters out internal reporters automatically:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/fetch_issue_reporters.py \\\n  --repo warpdotdev/warp \\\n  --org warpdotdev \\\n  --issues 5678,9012\n```\n\nOutput JSON (only external reporters are included):\n```json\n{\n  \"issue_reporters\": [\n    {\n      \"issue_number\": 5678,\n      \"title\": \"Crash when opening large file\",\n      \"reporter\": \"community-user\",\n      \"reporter_url\": \"https://github.com/community-user\",\n      \"url\": \"https://github.com/warpdotdev/warp/issues/5678\"\n    }\n  ]\n}\n```\n\nThe `--org` flag checks each reporter's org membership via the GitHub API, filtering out internal members so they aren't misattributed as external community reporters. These reporters will be credited in the \"Community\" section of the changelog.\nWhenever the markdown draft credits a PR author, contributor, or issue reporter, render the username as a GitHub profile link such as `[@username](https://github.com/username)`.\n\n### Step 6 — Classify unmarked PRs\nDetermine TUI impact independently from the regular changelog category for every PR. Explicit `CHANGELOG-TUI` and `CHANGELOG-OZ` markers are authoritative entries and may coexist with New Feature, Improvement, or Bug Fix entries. When a PR has an explicit TUI entry, preserve its other explicit entries. Otherwise, if `commit_subject` contains `TUI` as a standalone case-insensitive token, route any explicit New Feature, Improvement, or Bug Fix text to `TUI` instead of its regular category. This keeps clearly TUI-labeled changes out of the desktop changelog while allowing explicitly marked shared changes to appear on both surfaces.\n\nFor each PR that has no explicit `CHANGELOG-*` entries, decide whether to include it and under which category.\n\nFollow the classification guidance in `.agents/skills/classify-changelog-pr/SKILL.md`.\n\nFor each unmarked PR, produce a classification:\n```json\n{\n  \"pr_number\": 1234,\n  \"include\": true,\n  \"category\": \"IMPROVEMENT\",\n  \"text\": \"Proposed changelog line\",\n  \"impacts_tui\": true,\n  \"confidence\": \"high\",\n  \"rationale\": \"...\",\n  \"feature_flag\": null,\n  \"needs_review\": false\n}\n```\n\n**Key rules:**\n- PRs that only touch CI, tests, docs, or internal tooling → `include: false`\n- PRs behind dogfood-only feature flags → `include: false` for stable channel\n- PRs behind preview flags → `include: false` for stable, `include: true` for preview\n- Set `impacts_tui: true` when Warp Agent CLI users observe the change, including shared Agent capabilities such as tool-call or edit-file behavior\n- When in doubt, set `needs_review: true` and `confidence: \"low\"`\n- Bot PRs (dependabot, renovate, etc.) → `include: false`\n\n**Feature-flag detection:** Use the `changed_files` list from Step 2 to check if any PR touches `crates/warp_features/src/lib.rs` or references a `FeatureFlag` variant in its title/body. Cross-reference with the flag lists from Step 4 to determine channel visibility.\n\n**Unknown contributors:** Authors in the `unknown` bucket (org membership check failed due to auth) should be treated conservatively — do not attribute them as external. Note them in the output for manual verification.\n\n### Step 7 — Assemble the draft\n\nCombine explicit entries (Step 2) and inferred entries (Step 6) into the final report. Group by category in this order:\n\n1. `NEW-FEATURE` — New Features\n2. `IMPROVEMENT` — Improvements\n3. `BUG-FIX` — Bug Fixes\n4. `TUI` — TUI Updates\n5. `OZ` — Oz Updates\n\nPRs marked with `CHANGELOG-NONE` are explicitly opted out and must never appear in the changelog markdown.\n\nPreserve every explicit entry independently. For an inferred regular entry with `impacts_tui: true`, also create a `TUI` entry with the same user-facing text and PR metadata. Do not duplicate an inferred entry whose category is already `TUI`.\n\nWhen creating entries, copy `pr_number`, `url`, `author`, `source_repo`, and `internal_pr` from the normalized PR record. The release JSON converter uses `url` directly; do not invent public PR URLs from PR numbers.\n\n### Step 8 — Write output files\n\nWrite two files to `output_dir`:\n\n**`changelog-draft.md`** — Human-reviewable markdown, ready for Slack/Notion:\n\n```markdown\n# Changelog Draft\n**Channel:** stable\n**Range:** v0.2026.05.01... → v0.2026.05.06...\n**Generated:** 2026-05-06T15:00:00Z\n\n## New Features\n- Added dark mode ([#1234](https://github.com/warpdotdev/warp/pull/1234)) — [@external-contributor](https://github.com/external-contributor) ✨\n\n## Improvements\n- Faster tab switching ([#1235](https://github.com/warpdotdev/warp/pull/1235))\n\n## Bug Fixes\n- Fixed crash on startup ([#1236](https://github.com/warpdotdev/warp/pull/1236))\n\n## TUI Updates\n- Added inline command menus to Warp Agent CLI ([#1238](https://github.com/warpdotdev/warp/pull/1238))\n\n## Oz Updates\n- Improved agent memory ([#1237](https://github.com/warpdotdev/warp/pull/1237))\n\n## Community\n### Contributors\n- [@contributor1](https://github.com/contributor1) — [#1234](https://github.com/warpdotdev/warp/pull/1234)  ✨\n\n### Issue Reporters\nThanks to the community members who reported issues fixed in this release:\n- [@reporter1](https://github.com/reporter1) — [#5678](https://github.com/warpdotdev/warp/issues/5678) \"Crash when opening large file\"\n```\n\nThe markdown draft must **not** include \"Needs Review\" or \"Skipped PRs\" sections — those are internal details that belong only in the JSON audit artifact.\n\n**`changelog-draft.json`** — Machine-readable audit artifact (internal only):\n\n```json\n{\n  \"channel\": \"stable\",\n  \"range\": { \"base\": \"v0...\", \"head\": \"v0...\" },\n  \"generated_at\": \"2026-05-06T15:00:00Z\",\n  \"entries\": [\n    {\n      \"pr_number\": 1234,\n      \"url\": \"https://github.com/warpdotdev/warp/pull/1234\",\n      \"category\": \"NEW-FEATURE\",\n      \"text\": \"Added dark mode\",\n      \"source\": \"explicit\",\n      \"author\": \"external-contributor\",\n      \"is_external\": true,\n      \"confidence\": \"high\",\n      \"rationale\": null,\n      \"feature_flag\": null,\n      \"source_repo\": \"warpdotdev/warp\",\n      \"internal_pr\": null\n    }\n  ],\n  \"skipped\": [...],\n  \"needs_review\": [...],\n  \"issue_reporters\": [...]\n}\n```\n\nThe JSON artifact retains `skipped`, `needs_review`, and `issue_reporters` for audit purposes — every PR in the range must appear in either `entries`, `skipped`, or `needs_review`.\n\n### Step 9 — Generate release-pipeline JSON\n\nRun the conversion script to deterministically produce `changelog-release.json` from the audit artifact:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/convert_to_release_json.py \\\n  --input <output_dir>/changelog-draft.json \\\n  --output <output_dir>/changelog-release.json\n```\n\nThis produces the flat JSON structure consumed by the `create_release` workflow for Slack and the in-app \"What's New\" dialog. Do **not** generate this file manually — always use the script so the output is deterministic and consistent.\n\n## Constraints\n\n- **Never** write to `channel_versions.json` or any production config file.\n- **Never** push commits, create branches, or open PRs.\n- All output goes to `output_dir` only.\n- The markdown draft should be copy-pasteable into Slack or Notion for review.\n- Keep the JSON artifact complete enough for audit: every PR in the range should appear in either `entries`, `skipped`, or `needs_review`.\n\n## Validation\n\nAfter generating output, verify:\n1. Every PR in the range is accounted for (entries + skipped + needs_review = total PRs).\n2. Explicit marker entries match what `fetch_prs.py` extracted (no dropped markers).\n3. No duplicate PR numbers across sections.\n4. The markdown renders cleanly (no broken links or formatting).\n",".agents/skills/dedupe-issue-local/SKILL.md":"---\nname: dedupe-issue-local\nspecializes: dedupe-issue\nspecializes_source: warpdotdev/oz-for-oss:.agents/skills/dedupe-issue\ndescription: Repo-specific dedupe guidance for warp. Only the categories declared overridable by the core dedupe-issue skill may be specialized here.\n---\n\n# Repo-specific dedupe guidance for `warp`\n\n## Prerequisite: install the parent skill\n\nThis skill specializes the core `dedupe-issue` skill (named in the `specializes` frontmatter field) and is not functional on its own. Before applying its guidance, confirm the parent skill is installed and resolvable at `.agents/skills/dedupe-issue/SKILL.md`. If it is missing, install it first by copying the skill directory from the source declared in the `specializes_source` frontmatter field (`warpdotdev/oz-for-oss:.agents/skills/dedupe-issue`). Then continue with the guidance below.\n\nThis file is a companion to the core `dedupe-issue` skill. It does not\nredefine the duplicate-detection algorithm, the similarity thresholds,\nor the output contract. It only specializes the override categories the\ncore skill marks as overridable.\n\n## Repo-specific normalizations\n\n- Strip low-signal title prefixes such as `Bug:`, `Feature:`, `Request:`, `[Bug]`, `[Feature]`, `Warp:`, and platform tags like `[macOS]`, `[Linux]`, or `[Windows]` before comparing titles.\n- Treat app channel/version, OS version, and shell name as supporting evidence, not as duplicate blockers, when the core symptom and reproduction path are otherwise the same.\n- Do not collapse distinct Warp surfaces just because they share a word like \"agent\", \"terminal\", \"MCP\", \"settings\", \"search\", or \"sync\". Require overlap in the actual failing behavior or requested capability.\n- For terminal issues, compare shell/session context, command output behavior, prompt rendering, input behavior, and remote/tmux involvement before treating two reports as duplicates.\n- For agent or MCP issues, compare the trigger path, local vs cloud execution, MCP server/tool, visible error, and expected workflow before treating two reports as duplicates.\n- For UI/rendering issues, compare the affected surface and visible symptom. Similar screenshots or recordings are strong duplicate evidence when the title is vague.\n\n## Known-duplicate clusters\n\nNo known-duplicate clusters have been captured for this repository yet. The weekly `update-dedupe` loop will propose additions here over time when maintainers repeatedly close issues as duplicates of the same canonical thread.\n",".agents/skills/add-feature-flag/SKILL.md":"---\nname: add-feature-flag\ndescription: Add a new feature flag to gate code changes in the Warp codebase.\n---\n\n# add-feature-flag\n\nAdd a new feature flag to gate code changes in the Warp codebase.\n\n## Overview\n\nFeature flags in Warp are compile-time flags that allow features to be selectively enabled for different channels (e.g.: Dev, Stable). They use a small runtime plumbing layer that checks if a flag is enabled.\n\n## TUI note\nThe `FeatureFlag` enum (`warp_core/src/features.rs`) and the runtime `FeatureFlag::X.is_enabled()` check are SHARED by both front-ends: the GUI desktop app (`app/`) and the headless TUI (`crates/warp_tui`). The Cargo-feature steps in this skill (`app/Cargo.toml`, `app/src/lib.rs`) are GUI-app-specific. Prefer runtime `is_enabled()` checks so a flag works in both front-ends with no per-binary wiring. Only if you truly need a compile-time Cargo feature in the TUI, wire it into `crates/warp_tui/Cargo.toml` as well. Test the TUI with `./script/run-tui`.\n\n## Steps\n\n### 1. Add to Cargo.toml\nAdd the feature to `app/Cargo.toml` under the `[features]` section, but **NOT** under the `default` nested stanza:\n\n```toml\n[features]\nyour_feature_name = []\n```\n\n### 2. Add to FeatureFlag enum\nAdd a new variant to the `FeatureFlag` enum in `warp_core/src/features.rs`:\n\n```rust\n#[derive(Sequence)]\npub enum FeatureFlag {\n    YourFeatureName,\n}\n```\n\n### 3. Add conditional compilation directive\nAdd the feature to `app/src/lib.rs` with a corresponding `#[cfg(feature = \"...\")]` attribute to ensure it's only included when enabled:\n\n```rust\n#[cfg(feature = \"your_feature_name\")]\nYourFeatureName,\n```\n\n### 4. Gate code with runtime checks\nIn your code, use the runtime check to conditionally execute feature-gated code:\n\n```rust\nif FeatureFlag::YourFeatureName.is_enabled() {\n    // feature-gated behavior\n}\n```\n\n### 5. (Optional) Enable for dogfood builds\nTo enable the feature by default for Dev/dogfood builds, add it to the `DOGFOOD_FLAGS` array in `features.rs`:\n\n```rust\npub const DOGFOOD_FLAGS: &[FeatureFlag] = &[\n    FeatureFlag::YourFeatureName,\n];\n```\n\n### 6. Running with feature flags\nTo test locally with the feature enabled:\n\n```bash\ncargo run --features your_feature_name\n\n# Multiple features:\ncargo run --features your_feature_name,another_feature\n```\n\n## Keybindings with Feature Flags\n\nIf adding an `EditableBinding` or `FixedBinding` that's part of a gated feature, include an enabled predicate that checks the feature flag. This prevents the keybinding from appearing in keyboard settings when the feature is disabled.\n\nExample:\n```rust\nEditableBinding::new(\n    \"action:name\",\n    \"Action description\",\n    YourAction::Variant\n)\n.with_enabled(|| FeatureFlag::YourFeatureName.is_enabled())\n.with_key_binding(\"cmdorctrl-key\")\n```\n\n## Rolling Out to Stable\n\nWhen ready to enable the feature for all Warp Stable users, add it to the `default` array in `app/Cargo.toml`:\n\n```toml\n[features]\ndefault = [\n    \"your_feature_name\",\n    # other default features...\n]\n```\n\n## Best Practices\n\n- **Prefer runtime checks over cfg directives**: Use `FeatureFlag::YourFeatureName.is_enabled()` instead of `#[cfg(...)]` when possible, so flags can be toggled without recompilation and are easier to clean up later\n- Use `#[cfg(...)]` only when code cannot compile without the flag (e.g., platform-specific code or missing dependencies)\n- Keep flags high-level and product-focused rather than per-call-site\n- Remove flags and dead branches after launch has stabilized\n",".agents/skills/add-telemetry/SKILL.md":"---\nname: add-telemetry\ndescription: Add telemetry events to track user behavior or system events in the Warp codebase. Use when instrumenting new features, debugging issues, or measuring product metrics.\n---\n\n# add-telemetry\n\n## Overview\n\nWarp uses a trait-based telemetry system where feature-specific enums implement the `TelemetryEvent` trait. This approach keeps telemetry events organized by domain rather than in one giant enum.\n\n**Important**: Before implementing telemetry, collaborate with the user to:\n- Define what events should be tracked and when\n- Determine what data should be included in each event\n- Clarify the purpose and expected usage of the telemetry\n\nAdding telemetry code is straightforward, but designing meaningful instrumentation requires careful thought.\n\n## Steps\n\n### 1. Identify or create a telemetry module\n\nFind an existing feature-specific telemetry file (e.g., `app/src/antivirus/telemetry.rs`) or create a new one for your feature area.\n\n### 2. Define the telemetry event enum\n\nAdd a new variant to an enum that implements `TelemetryEvent`, or create a new enum:\n\n```rust\nuse serde_json::{json, Value};\nuse strum_macros::{EnumDiscriminants, EnumIter};\nuse warp_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};\n\n#[derive(Debug, EnumDiscriminants)]\n#[strum_discriminants(derive(EnumIter))]\npub enum YourFeatureTelemetryEvent {\n    ActionStarted {\n        duration_ms: u64,\n    },\n    ActionCompleted {\n        success: bool,\n        error: Option<String>,\n    },\n}\n```\n\n### 3. Implement the TelemetryEvent trait\n\n`EnablementState` allows you to control when events are sent:\n\n- `EnablementState::Always` - Always send the event\n- `EnablementState::Flag(FeatureFlag::YourFeature)` - Only send when the feature flag is enabled\n- `EnablementState::Channel(Channel::Dev)` - Only send in specific build channels\n\n```rust\nimpl TelemetryEvent for YourFeatureTelemetryEvent {\n    fn name(&self) -> &'static str {\n        YourFeatureTelemetryEventDiscriminants::from(self).name()\n    }\n\n    fn payload(&self) -> Option<Value> {\n        match self {\n            Self::ActionStarted { duration_ms } => Some(json!({\n                \"duration_ms\": duration_ms,\n            })),\n            Self::ActionCompleted { success, error } => Some(json!({\n                \"success\": success,\n                \"error\": error,\n            })),\n        }\n    }\n\n    fn description(&self) -> &'static str {\n        YourFeatureTelemetryEventDiscriminants::from(self).description()\n    }\n\n    fn enablement_state(&self) -> EnablementState {\n        YourFeatureTelemetryEventDiscriminants::from(self).enablement_state()\n    }\n\n    fn contains_ugc(&self) -> bool {\n        match self {\n            Self::ActionStarted { .. } => false,\n            Self::ActionCompleted { .. } => false,\n        }\n    }\n\n    fn event_descs() -> impl Iterator<Item = Box<dyn TelemetryEventDesc>> {\n        warp_core::telemetry::enum_events::<Self>()\n    }\n}\n```\n\n### 4. Implement TelemetryEventDesc for the discriminants\n\n```rust\nimpl TelemetryEventDesc for YourFeatureTelemetryEventDiscriminants {\n    fn name(&self) -> &'static str {\n        match self {\n            Self::ActionStarted => \"YourFeature.Action.Started\",\n            Self::ActionCompleted => \"YourFeature.Action.Completed\",\n        }\n    }\n\n    fn description(&self) -> &'static str {\n        match self {\n            Self::ActionStarted => \"User started the action\",\n            Self::ActionCompleted => \"User completed the action\",\n        }\n    }\n\n    fn enablement_state(&self) -> EnablementState {\n        match self {\n            Self::ActionStarted | Self::ActionCompleted => EnablementState::Always,\n            // Or gate behind a feature flag:\n            // EnablementState::Flag(FeatureFlag::YourFeature)\n        }\n    }\n}\n```\n\n### 5. Register the telemetry event\n\nAt the end of your telemetry module, register the event:\n\n```rust\nwarp_core::register_telemetry_event!(YourFeatureTelemetryEvent);\n```\n\n### 6. Send telemetry events from your code\n\nUse `send_telemetry_from_ctx!` in views or models with a `ViewContext` or `ModelContext`:\n\n```rust\nuse warp_core::send_telemetry_from_ctx;\n\n// In a view update or model method\nsend_telemetry_from_ctx!(\n    YourFeatureTelemetryEvent::ActionStarted {\n        duration_ms: 150,\n    },\n    ctx\n);\n```\n\nFor code with only `AppContext`, use `send_telemetry_from_app_ctx!` instead.\n\n### 7. Test locally\n\nRun Warp with the `log_named_telemetry_events` feature flag to see telemetry events logged to the console:\n\n```bash\ncargo run --features log_named_telemetry_events\n```\n\n## Best Practices\n\n- Keep telemetry enums feature-specific rather than adding to a global enum\n- Set `contains_ugc()` to `true` if the payload includes user-generated content\n- Use descriptive event names following the pattern `Feature.Action.Result`\n- Include only necessary data in payloads to minimize bandwidth and storage\n- Consider privacy implications when deciding what data to include\n- Avoid exhaustive matching with wildcards; handle all variants explicitly\n\n## Example Reference\n\nSee `app/src/antivirus/telemetry.rs` for a complete example of a feature-specific telemetry implementation.\n",".agents/skills/classify-changelog-pr/SKILL.md":"---\nname: classify-changelog-pr\ndescription: Reference guidance for classifying whether an unmarked PR should appear in the changelog and under which category. Used inline by the changelog-draft skill — not dispatched as a separate agent.\n---\n\n# Classify Changelog PR\n\nThis document provides classification rules for PRs that lack explicit `CHANGELOG-*` markers. The changelog-draft agent follows these rules inline when deciding whether to include an unmarked PR.\n\n## Categories\n\n- **NEW-FEATURE** — A substantial new user-facing capability. Reserve for features that would warrant docs, marketing, or social media attention.\n- **IMPROVEMENT** — Enhances an existing feature in a way users would notice (performance, UX, new options).\n- **BUG-FIX** — Fixes a user-visible bug or regression.\n- **OZ** — Changes to Oz / AI agent capabilities. At most 4 per release in the stable changelog.\n- **TUI** — User-visible changes that impact the Warp Agent CLI/headless TUI, including shared Agent capabilities.\n- **NONE** — Explicitly opt out of changelog inclusion. Handled upstream by `fetch_prs.py` marker extraction.\n\n## Decision rules\n\n### Always exclude\n- PRs with an explicit `CHANGELOG-NONE` marker (contributor opted out)\n- PRs authored by known bots (dependabot, renovate, github-actions, codecov)\n- PRs that exclusively modify CI workflows (`.github/workflows/`), test files, or dev tooling\n- PRs that only update internal docs, comments, or README files\n- Dependency bumps with no user-facing behavior change\n- Refactors with no observable behavior change (code moves, renames, formatting)\n\n### Always include\n- PRs with explicit `CHANGELOG-*` markers (handled before this guidance applies)\n- PRs that fix a crash, data loss, or security issue — even without a marker\n\n### TUI surface classification\n- Treat explicit `CHANGELOG-TUI` and `CHANGELOG-OZ` markers as authoritative entries. They are independent and may coexist with each other or with regular changelog categories.\n- Determine TUI impact independently from the regular New Feature, Improvement, or Bug Fix category. A shared Agent capability may belong in both its regular category and `TUI`.\n- Classify a user-visible entry as impacting `TUI` when `commit_subject` contains `TUI` as a standalone token.\n- For stable releases, also use the PR title/body and TUI-owned paths such as `crates/warp_tui` or `crates/warpui_core/src/elements/tui`. Shared capabilities such as Agent tool-call or edit-file behavior also impact TUI when Warp Agent CLI users observe the change. If metadata is ambiguous, inspect the commit diff.\n- Do not infer TUI impact from file paths alone; confirm that Warp Agent CLI users observe the changed behavior.\n- A TUI-only entry belongs only in the TUI category. An entry that impacts both TUI and another surface should appear in both `TUI` and its regular category.\n\n### Conditional on channel\n- **Stable channel:** Only include changes that are live for all users. Exclude PRs gated behind `DOGFOOD_FLAGS` or `PREVIEW_FLAGS`.\n- **Preview channel:** Include PRs gated behind `PREVIEW_FLAGS`. Still exclude `DOGFOOD_FLAGS`-only changes.\n- **Dev channel:** Include everything that's user-visible, regardless of flag gates.\n\n### Feature-flagged PRs\nIf a PR mentions a `FeatureFlag` variant in its diff or title:\n1. Check which flag list it belongs to (`RELEASE_FLAGS`, `PREVIEW_FLAGS`, `DOGFOOD_FLAGS`).\n2. Apply the channel rules above.\n3. If the flag is in `RELEASE_FLAGS` or enabled by default in `app/Cargo.toml`, treat it as live.\n4. Set `feature_flag` in the classification output to the flag name.\n\n### Confidence levels\n- **high** — Clear user-visible change with obvious category.\n- **medium** — Likely user-visible but category or scope is somewhat ambiguous.\n- **low** — Unclear whether users would notice; or the PR touches both internal and user-facing code. Set `needs_review: true`.\n\n## Writing changelog text\n\n- Write from the user's perspective: \"Added X\", \"Fixed Y\", \"Improved Z\".\n- Keep it to one sentence, ≤ 120 characters.\n- Don't reference internal implementation details, file paths, or function names.\n- Don't start with \"PR\" or the PR number — those are added as metadata.\n- Use active voice and present tense for new features (\"Adds dark mode\"), past tense for fixes (\"Fixed crash on startup\").\n",".agents/skills/cross-platform-cloud-verification/SKILL.md":"---\nname: cross-platform-cloud-verification\ndescription: Orchestrates cost-conscious cloud verification across available operating-system and architecture runners after cheaper checks pass. Use whenever a code change, bug fix, build, packaging flow, native dependency, UI behavior, filesystem behavior, or test has material cross-platform implications, even if the user only asks to verify or test the change. Consult domain verification skills for what to test; use this skill to choose and access the smallest relevant set of platforms.\ncompatibility: Requires the Oz CLI (`oz-dev`) for runner discovery and `run_agents` support for `remote.runner_id`.\n---\n\n# Cross-platform cloud verification\n\nVerify a change on the smallest useful set of cloud platforms. This skill owns\nrunner discovery, platform selection, child orchestration, and result\naggregation. It does not define the product-specific verification procedure.\n\nCross-platform execution consumes remote compute and credits, so use it as the\nlast verification layer rather than as an exploratory first step.\n\n## Workflow\n\n### 1. Establish the local verification gate\n\nFinish the cheaper feedback loops first:\n\n1. Inspect the change and identify the behavior being verified.\n2. Run the relevant local build, focused tests, lint, type checks, or manual\n   verification.\n3. Fix deterministic local failures before launching cloud runs.\n4. Confirm the exact commit or branch containing the change can be checked out\n   by cloud agents. Do not test the default branch when the intended change is\n   only local.\n\nDo not commit or push changes merely to satisfy this workflow unless the user\nhas authorized that action. If the change is not remotely reachable, report the\nprecondition and ask for the minimum action needed.\n\nSkip cloud fan-out when local verification already shows the change is broken.\nDo not use remote platforms as a substitute for diagnosing an ordinary local\nfailure.\n\n### 2. Delegate test design to verification skills\n\nInspect the available skill descriptions and read every skill that materially\ndefines how to verify the affected surface. Examples include GUI computer-use\nverification, TUI live verification, integration testing, unit testing, CI\ndiagnosis, packaging, or repository-specific validation.\n\nExtract from those skills:\n\n- setup and authentication requirements\n- commands or interactions to perform\n- expected observations and pass criteria\n- required screenshots, recordings, logs, or text captures\n- platform-specific caveats\n\nBuild one shared verification procedure from that guidance. Tell each child\nwhich verification skill to read when it is available in the child environment,\nand include the essential procedure directly so the run remains actionable if\nthe skill is unavailable there.\n\nThis skill decides where to run that procedure, not what the procedure should\nbe.\n\n### 3. Discover runners at execution time\n\nList runners immediately before selecting platforms:\n\n```sh\noz-dev runner list --output-format json\n```\n\nUse the returned runner metadata rather than hard-coding names or IDs. Relevant\nfields include `uid`, `name`, `os`, `arch`, compute capacity, image or macOS\nversion, and setup commands.\n\nIf runner discovery fails because of authentication, permissions, or service\navailability, report the blocker instead of inventing a runner matrix.\n\n### 4. Infer the affected platform dimensions\n\nDetermine which dimensions the change can plausibly affect from the diff,\nrepository configuration, reported bug, and verification guidance.\n\nTreat the change as **OS-sensitive** when it touches or depends on items such as:\n\n- OS-gated code or platform modules\n- windowing, desktop integration, installers, packaging, or signing\n- shells, process creation, permissions, filesystems, paths, or line endings\n- OS-specific APIs, toolchains, or user-facing behavior\n\nTreat the change as **architecture-sensitive** only with evidence such as:\n\n- architecture gates or assembly\n- unsafe code, FFI, ABI, alignment, endianness, or binary serialization\n- SIMD, native libraries, architecture-specific dependencies, or packaging\n- an architecture-specific bug report or explicit user requirement\n\nDo not infer architecture sensitivity merely because both x86-64 and AArch64\nrunners exist.\n\n### 5. Select the minimal runner matrix\n\nFilter discovered runners to those that can execute the verification procedure,\nthen apply these defaults:\n\n1. Select only affected operating systems.\n2. Select one representative runner per affected OS.\n3. Add another architecture for an OS only when the change is\n   architecture-sensitive.\n4. Do not add an unaffected control platform by default.\n5. Deduplicate equivalent OS/architecture candidates.\n\nWhen several runners cover the same platform, prefer in order:\n\n1. the exact OS/version/architecture from the bug report or target\n2. the repository's normal CI or release platform\n3. a runner whose image, setup, capacity, and tools match the procedure\n4. the lower-setup-cost candidate\n\nFor architecture-insensitive Linux changes, prefer the repository's primary\nLinux architecture; if the repository gives no signal, use x86-64 as the single\nrepresentative.\n\nBefore launching, record a concise selection rationale and explicitly list\nrelevant platforms omitted because no runner is available. Missing runners do\nnot block useful verification on available relevant platforms.\n\nState each omission once. After the matrix is decided, do not keep repeating\nthat an irrelevant or redundant platform was not selected in child prompts,\nresult rows, evidence, and the conclusion.\n\n### 6. Construct verification-only child prompts\n\nEvery child prompt should contain:\n\n- repository, branch, and exact commit to check out\n- selected runner name, OS, and architecture\n- verification skill names and the distilled procedure\n- setup and state preconditions\n- exact commands or interactions\n- expected results and required evidence\n- a prohibition on fixing or pushing code\n\nTrust orchestration to route the child to the requested runner. Do not make every\nchild re-confirm its OS and architecture. Ask for platform introspection only\nwhen the verification depends on an exact OS version or capability, or when\nthere is evidence of a routing problem.\n\nAsk each child to return:\n\n```text\nPlatform: <runner name; OS; architecture; version if relevant>\nCommit: <tested SHA>\nStatus: <passed | failed | blocked>\nChecks:\n- <command or interaction>: <result>\nEvidence:\n- <artifact, screenshot, log, or concise observation>\nDeviations:\n- <difference from the requested procedure, or none>\n```\n\nChildren may make temporary, uncommitted setup adjustments required by the\nverification skill, but they must report them and must not push source changes.\n\n### 7. Launch with `run_agents`\n\nUse `run_agents` so child IDs, messages, lifecycle events, and artifacts remain\npart of the parent orchestration flow. Use the same repository environment for\nevery selected platform and set `remote.runner_id` to the discovered runner UID.\n\n```text\nsummary: Verifying the change on <OS/architecture>.\nbase_prompt: <shared verification-only instructions>\nremote:\n  environment_id: <repository environment ID>\n  runner_id: <selected runner UID>\n  computer_use_enabled: <true only when the verification procedure needs it>\nagent_run_configs:\n- name: <short platform-specific name>\n  prompt: <platform-specific procedure and expected evidence>\n```\n\nIf no suitable repository environment is already known, inspect\n`oz-dev environment list` and choose one that checks out the target repository.\nDo not silently create or mutate an environment.\n\n`remote.runner_id` is run-wide, so runners with different UIDs require separate\n`run_agents` calls. Use one single-child batch per selected runner, or group\nmultiple independently useful children only when they share the same runner and\nrun-wide configuration. Distinct runner IDs are a legitimate reason for\nseparate batches; do not place children for different platforms in one batch.\n\nOmit `model_id` and `remote.harness` unless the user requested them. Attach\nrelevant verification skills when the children need them. If an approved\norchestration config is active, ensure its resolved runner matches the selected\nrunner because config resolution takes precedence over call fields.\n\nCapture each trusted `agent_id` from the launched result. Coordinate through\npushed child messages and lifecycle events; do not poll `oz-dev run get` or\n`list_messages_from_agents`. Read notified messages, intervene only for a\nfailure or actionable block, and use `wait_for_events` when no other work can\nproceed. Allow at most one retry for a clearly transient infrastructure\nfailure. A product failure is evidence, not a reason to spend more credits\nrepeating the same check.\n\n### 8. Aggregate without hiding gaps\n\nWait for every launched child, then report:\n\n```text\nCross-platform verification: <passed | failed | incomplete>\nLocal gate: <checks completed before cloud launch>\nResults:\n- <OS/arch; runner>: <why selected> - <passed/failed/blocked>\nUnverified:\n- <relevant OS/arch>: unverified because no suitable runner was available\n\nEvidence:\n- <platform>: <commands, observations, and artifact/run links>\n\nConclusion:\n- <what the results establish and what remains unverified>\n```\n\nUse `passed` only when every selected platform passed and no required platform\nis unavailable. Use `failed` when any verification check failed. Use\n`incomplete` when runs were blocked or a relevant platform lacked a runner.\n\nKeep dry-run proposals and final summaries compact. Include the local gate, the\nselected matrix, one concise omission rationale when it is material, the\nverification procedure, and verdict rules. Do not repeat setup mechanics or\nplatform exclusions in multiple sections. A dry-run proposal should usually fit\nin roughly 300-500 words; expand only when the domain verification procedure\ngenuinely requires more detail.\n\n## Cost and scope guardrails\n\n- Run this workflow after local verification, not during implementation.\n- Choose platforms from evidence in the change, not from runner availability.\n- Keep one child per selected platform unless distinct state variants are\n  independently necessary.\n- State runner-selection and omission decisions once.\n- Preserve negative results and platform gaps; do not broaden the matrix merely\n  to obtain a passing result.\n- Do not let children implement fixes. Return failures to the parent workflow,\n  fix locally, rerun cheap checks, and only then decide whether another cloud\n  verification pass is justified.\n",".agents/skills/gui-create-launch-modal/SKILL.md":"---\nname: gui-create-launch-modal\ndescription: GUI desktop app only. Create a one-time launch modal in the Warp client (feature announcement, onboarding, etc.). Use when adding a new modal that should appear exactly once per user on startup, gated by a feature flag, with colors sourced from Warp theme tokens and terminal theme colors.\n---\n\n# gui-create-launch-modal\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nCreate a one-time launch modal — the feature-announcement design used for launches like \"Orchestrate any agent, anywhere\" or \"Warp is now open-source.\"\n\n## Reference implementation\n\n`app/src/workspace/view/orchestration_launch_modal/` — the canonical, most up-to-date example of this pattern.\n\n## Checklist\n\n- [ ] Feature flag in `warp_features/src/lib.rs`\n- [ ] Settings field in `app/src/settings/ai.rs`\n- [ ] Trigger logic in `app/src/workspace/one_time_modal_model.rs`\n- [ ] View files under `app/src/workspace/view/<name>_launch_modal/`\n- [ ] Workspace wiring in `app/src/workspace/view.rs` and `app/src/workspace/mod.rs`\n- [ ] Debug actions in `app/src/workspace/action.rs`\n- [ ] Hero image at `app/assets/async/png/onboarding/<name>_launch_banner.png`\n- [ ] Any custom icons added to `crates/warp_core/src/ui/icons.rs` + SVG in `app/assets/bundled/svg/`\n\n---\n\n## Step 0 – Custom icons (if needed)\n\nIf the modal uses icons not yet in the `Icon` enum, add them before writing the view.\n\nIn `crates/warp_core/src/ui/icons.rs`:\n\n```rust\n// Add to enum\nYourIconName,\n\n// Add to From<Icon> for &'static str match\nIcon::YourIconName => \"bundled/svg/your-icon-name.svg\",\n```\n\nDrop the SVG file at `app/assets/bundled/svg/your-icon-name.svg`. Use the same 24×24 viewBox format as existing icons.\n\n---\n\n## Step 1 – Feature flag\n\nAdd to `crates/warp_features/src/lib.rs`:\n\n```rust\n/// Enables the <name> launch modal.\n<YourModalName>LaunchModal,\n```\n\nEnable for dogfood:\n\n```rust\npub const DOGFOOD_FLAGS: &[FeatureFlag] = &[\n    FeatureFlag::<YourModalName>LaunchModal,\n    // ...\n];\n```\n\n---\n\n## Step 2 – Settings field\n\nAdd to `app/src/settings/ai.rs` inside `define_settings_group!(AISettings, ...)`.\nPattern: one boolean field per modal, globally synced (not respecting user sync), private.\n\n```rust\n// This is not a user-visible setting - it's merely a one-time flag to track if the\n// <name> launch modal has been shown to the user.\n//\n// We model it as a setting so it's only shown once to a given user regardless of the number of\n// devices they use.\ndid_check_to_trigger_<name>_launch_modal: DidShow<Name>LaunchModal {\n    type: bool,\n    default: false,\n    supported_platforms: SupportedPlatforms::ALL,\n    sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::No),\n    private: true,\n}\n```\n\n---\n\n## Step 3 – OneTimeModalModel\n\nFile: `app/src/workspace/one_time_modal_model.rs`\n\n### 3a. Add field to struct\n\n```rust\nis_<name>_launch_modal_open: bool,\n```\n\n### 3b. Initialize to false in `new()`\n\n```rust\nis_<name>_launch_modal_open: false,\n```\n\n### 3c. Pre-dismiss for new users (critical)\n\nIn the `AuthComplete` → `!is_existing_user` branch, add to the `AISettings::handle` update block alongside the other pre-dismissals. **Without this, new users see the modal on their second startup after onboarding.**\n\n```rust\nif let Err(e) = settings\n    .did_check_to_trigger_<name>_launch_modal\n    .set_value(true, ctx)\n{\n    log::warn!(\"Failed to mark <name> launch modal as dismissed: {e}\");\n}\n```\n\n### 3d. Public API methods\n\n```rust\npub fn is_<name>_launch_modal_open(&self) -> bool {\n    self.is_<name>_launch_modal_open && self.target_window_id.is_some()\n}\n\npub fn mark_<name>_launch_modal_dismissed(&mut self, ctx: &mut ModelContext<Self>) {\n    self.set_<name>_launch_modal_open(false, ctx);\n}\n\n#[cfg(debug_assertions)]\npub fn force_open_<name>_launch_modal(&mut self, ctx: &mut ModelContext<Self>) {\n    self.set_<name>_launch_modal_open(true, ctx);\n}\n```\n\n### 3e. Private setter\n\n```rust\nfn set_<name>_launch_modal_open(&mut self, is_open: bool, ctx: &mut ModelContext<Self>) -> bool {\n    if self.is_<name>_launch_modal_open != is_open {\n        self.is_<name>_launch_modal_open = is_open;\n        ctx.emit(OneTimeModalEvent::VisibilityChanged { is_open });\n        return true;\n    }\n    false\n}\n```\n\n### 3f. Add to `is_any_modal_open`\n\n```rust\n|| self.is_<name>_launch_modal_open\n```\n\n### 3g. Trigger function\n\n```rust\nfn check_and_trigger_<name>_launch_modal(&mut self, ctx: &mut ModelContext<Self>) -> bool {\n    if !FeatureFlag::<Name>LaunchModal.is_enabled() {\n        return false;\n    }\n\n    let ai_settings = AISettings::as_ref(ctx);\n    if *ai_settings.did_check_to_trigger_<name>_launch_modal {\n        return false;\n    }\n\n    AISettings::handle(ctx).update(ctx, |settings, ctx| {\n        if let Err(e) = settings\n            .did_check_to_trigger_<name>_launch_modal\n            .set_value(true, ctx)\n        {\n            log::warn!(\"Failed to mark <name> launch modal as dismissed: {e}\");\n        }\n    });\n\n    let should_show = !matches!(ChannelState::channel(), Channel::Integration);\n    self.set_<name>_launch_modal_open(should_show, ctx);\n    should_show\n}\n```\n\n### 3h. Call from `check_and_trigger_all_modals`\n\nInsert before `check_and_trigger_hoa_onboarding`:\n\n```rust\nif self.check_and_trigger_<name>_launch_modal(ctx) {\n    return;\n}\n```\n\n---\n\n## Step 4 – View\n\nCreate `app/src/workspace/view/<name>_launch_modal/mod.rs`:\n\n```rust\nmod view;\npub use view::{init, <Name>LaunchModal, <Name>LaunchModalEvent};\n```\n\nCreate `app/src/workspace/view/<name>_launch_modal/view.rs`. Copy from `orchestration_launch_modal/view.rs` and adapt. Key details:\n\n### Color sources (important)\n\n- Prefer Warp theme tokens for modal backgrounds, text, overlays, and borders:\n  - background surfaces: `appearance.theme().surface_3()` (or another `surface_*` token when needed)\n  - primary/subtext: `appearance.theme().main_text_color(...)` and `appearance.theme().sub_text_color(...)`\n  - overlays/hover fills: `appearance.theme().surface_overlay_1()` / `surface_overlay_2()`\n  - subtle borders: `appearance.theme().outline()`\n- Use terminal theme colors for terminal-color accents (for example, magenta launch badge accents):\n  - `appearance.theme().terminal_colors().normal.magenta`\n  - `appearance.theme().ansi_overlay_1(magenta)` for low-alpha backgrounds\n- Avoid hardcoded hex colors.\n\n### Hero image\n\n- Store at `app/assets/async/png/onboarding/<name>_launch_banner.png`\n- **Aspect ratio matters**: if the image is wider than `MODAL_WIDTH/HERO_HEIGHT` (420/92 ≈ 4.57), wrap the hero `ConstrainedBox` in `Clipped::new(...)` to prevent horizontal bleed when `cover()` scales it\n- Images pre-sized to exactly 420×92 need no `Clipped`; images only taller (aspect ratio < 4.57) are fine without it\n\n```rust\nconst MODAL_WIDTH: f32 = 420.;\nconst HERO_HEIGHT: f32 = 92.;\nconst HERO_IMAGE_PATH: &str = \"async/png/onboarding/<name>_launch_banner.png\";\n\nfn render_hero(&self) -> Box<dyn Element> {\n    let hero = Clipped::new(          // only needed if image ratio > 4.57\n        ConstrainedBox::new(\n            Image::new(AssetSource::Bundled { path: HERO_IMAGE_PATH }, CacheOption::Original)\n                .with_corner_radius(CornerRadius::with_top(Radius::Pixels(8.)))\n                .cover()\n                .top_aligned()\n                .finish(),\n        )\n        .with_width(MODAL_WIDTH)\n        .with_height(HERO_HEIGHT)\n        .finish(),\n    )\n    .finish();\n    // ... close button overlay via Stack + add_positioned_child\n}\n```\n\n### \"New\" badge\n\nUse the standard badge — 24 px tall, 8 px horizontal padding, 14 px font, pill corners, with magenta sourced from terminal theme colors:\n\n```rust\nfn render_badge(appearance: &Appearance) -> Box<dyn Element> {\n    let magenta = appearance.theme().terminal_colors().normal.magenta;\n    let text = Text::new_inline(\"New\".to_string(), appearance.ui_font_family(), 14.)\n        .with_color(magenta.into())\n        .finish();\n    ConstrainedBox::new(\n        Container::new(\n            Flex::row()\n                .with_cross_axis_alignment(CrossAxisAlignment::Center)\n                .with_main_axis_size(MainAxisSize::Min)\n                .with_child(text)\n                .finish(),\n        )\n        .with_horizontal_padding(8.)\n        .with_background(Fill::Solid(appearance.theme().ansi_overlay_1(magenta)))\n        .with_corner_radius(CornerRadius::with_all(Radius::Percentage(50.)))\n        .finish(),\n    )\n    .with_height(24.)\n    .finish()\n}\n```\n\n### URLs\n\nAlways use `https://`, not `http://`:\n\n```rust\nconst LEARN_MORE_URL: &str = \"https://warp.dev/your-blog-link\";\n```\n\n---\n\n## Step 5 – Workspace wiring\n\n### `app/src/workspace/view.rs`\n\n```rust\n// Module declaration (top)\npub(crate) mod <name>_launch_modal;\n\n// Import\nuse crate::workspace::view::<name>_launch_modal::{<Name>LaunchModal, <Name>LaunchModalEvent};\n\n// Struct field\n<name>_launch_modal: ViewHandle<<Name>LaunchModal>,\n\n// In Workspace::new()\nlet <name>_launch_view = ctx.add_typed_action_view(<Name>LaunchModal::new);\nctx.subscribe_to_view(&<name>_launch_view, |me, _, event, ctx| {\n    me.handle_<name>_launch_modal_event(event, ctx);\n});\n\n// In struct initialization\n<name>_launch_modal: <name>_launch_view,\n\n// In OneTimeModalModel subscription handler\n} else if model_ref.is_<name>_launch_modal_open() {\n    me.focus_<name>_launch_modal(ctx);\n\n// In View::render (inside the should_show_modal block)\nif should_show_modal && one_time_modal_model.is_<name>_launch_modal_open() {\n    stack.add_child(ChildView::new(&self.<name>_launch_modal).finish());\n}\n```\n\nAdd event handler and focus helper:\n\n```rust\nfn handle_<name>_launch_modal_event(&mut self, event: &<Name>LaunchModalEvent, ctx: &mut ViewContext<Self>) {\n    match event {\n        <Name>LaunchModalEvent::Close => {\n            OneTimeModalModel::handle(ctx).update(ctx, |model, ctx| {\n                model.mark_<name>_launch_modal_dismissed(ctx);\n            });\n            self.focus_active_tab(ctx);\n            ctx.notify();\n        }\n    }\n}\n\nfn focus_<name>_launch_modal(&mut self, ctx: &mut ViewContext<Self>) {\n    ctx.focus(&self.<name>_launch_modal);\n}\n```\n\n### `app/src/workspace/mod.rs`\n\n```rust\n// In pub fn init()\nview::<name>_launch_modal::init(app);\n\n// In debug bindings block\nEditableBinding::new(\n    \"workspace:open_<name>_launch_modal\",\n    \"[Debug] Open <Name> Launch Modal\",\n    WorkspaceAction::Open<Name>LaunchModal,\n)\n.with_context_predicate(id!(\"Workspace\")),\nEditableBinding::new(\n    \"workspace:reset_<name>_launch_modal_state\",\n    \"[Debug] Reset <Name> Launch Modal State\",\n    WorkspaceAction::Reset<Name>LaunchModalState,\n)\n.with_context_predicate(id!(\"Workspace\")),\n```\n\n---\n\n## Step 6 – Debug actions\n\nIn `app/src/workspace/action.rs`:\n\n```rust\n/// Open the <Name> Launch Modal (for debugging)\n#[cfg(debug_assertions)]\nOpen<Name>LaunchModal,\n/// Reset the <name> launch modal dismissed state (for debugging)\n#[cfg(debug_assertions)]\nReset<Name>LaunchModalState,\n```\n\nAdd both variants to the `is_visible_in_command_palette` `false` arm.\n\nIn `app/src/workspace/view.rs` `TypedActionView::handle_action`:\n\n```rust\n#[cfg(debug_assertions)]\nOpen<Name>LaunchModal => {\n    OneTimeModalModel::handle(ctx).update(ctx, |model, ctx| {\n        model.force_open_<name>_launch_modal(ctx);\n    });\n    ctx.notify();\n}\n#[cfg(debug_assertions)]\nReset<Name>LaunchModalState => {\n    AISettings::handle(ctx).update(ctx, |settings, ctx| {\n        if let Err(e) = settings\n            .did_check_to_trigger_<name>_launch_modal\n            .set_value(false, ctx)\n        {\n            log::warn!(\"Failed to reset <name> launch modal state: {e}\");\n        }\n    });\n}\n```\n\n---\n\n## Behavior summary\n\n| User type | Sees modal? |\n|---|---|\n| New signup | No — pre-dismissed in `AuthComplete` new-user branch |\n| Not signed in | No — trigger never fires without `AuthComplete` |\n| Existing user, flag enabled | Yes — on first startup after cloud prefs load |\n| Integration channel | No — suppressed by `Channel::Integration` check |\n| Already seen it | No — setting persists globally across devices |\n",".agents/skills/gui-onboarding-verification-skill/SKILL.md":"---\nname: gui-onboarding-verification-skill\ndescription: GUI desktop app only. Launch two parallel Oz cloud agents with computer use to download and install the latest stable Linux Warp build, capture screenshots while walking through first-time onboarding in both logged-out and logged-in states, then selectively fan out follow-up cloud agents for distinct onboarding branches proposed by those initial explorers. Use this whenever the user asks to test, document, screenshot, or walk through the Warp first-time install/onboarding experience in a cloud Linux environment.\n---\n\n# Onboarding verification skill\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nUse this skill to verify the first-time Warp install and onboarding flow on Linux with broader branch coverage than a single linear walkthrough.\n\nThe parent agent should not perform the walkthrough locally. Launch two parallel Oz cloud agents with computer use. Both initial children install the latest stable Warp Linux package appropriate for their platform and capture screenshots at every visible onboarding step until Warp reaches a usable terminal session. One child verifies the login-free flow. The other child verifies the logged-in flow using the managed secret `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN`.\n\nThose two baseline explorers are also responsible for noticing meaningful alternate onboarding branches and returning concrete plans for follow-up cloud agents. The parent agent should synthesize those plans, deduplicate overlapping suggestions, and launch a bounded second wave of targeted follow-up agents to improve coverage of paths a real user might encounter.\n\n## Parent workflow\n\n1. Launch exactly two remote Oz cloud agents in a single parallel `run_agents` batch with computer use enabled.\n2. Use no environment-specific assumptions unless the user provided an environment. If no environment was provided, omit the environment ID and let Warp choose the default remote environment.\n3. Give both baseline child agents the shared child prompt below, plus the appropriate flow-specific prompt.\n4. Wait for both baseline agents' reports. Each report must include:\n   - The completed baseline walkthrough result and artifacts.\n   - A concise list of observed UI quality issues, suspected bugs, error states, or rough edges, with screenshots when visible.\n   - A prioritized follow-up coverage plan describing distinct onboarding paths worth exploring with additional cloud agents.\n5. Treat the authenticated baseline child as blocked if `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` is missing or does not authenticate successfully.\n6. Build a combined coverage map from the two baseline reports. Deduplicate suggestions that reach the same visible state or exercise the same decision surface.\n7. Launch a second `run_agents` batch with computer use enabled for the most valuable follow-up onboarding branches:\n   - Prefer branches that materially change visible UI, available controls, downstream screens, auth state, or setup outcomes.\n   - Favor paths likely to expose correctness, polish, layout, truncation, loading, or validation problems.\n   - Default to at most four follow-up agents total unless the user explicitly asked for exhaustive coverage or the baseline reports show more than four clearly distinct high-value branches.\n   - Do not launch speculative follow-ups when the baseline agents did not observe a concrete branch point; report that coverage stopped after the baseline pass instead.\n8. Give each follow-up child the shared child prompt, the follow-up flow prompt below, the logged-out or logged-in flow prompt that matches its assigned auth state, and one synthesized branch assignment from the baseline reports.\n9. Wait for all follow-up reports before summarizing coverage, issues, artifacts, and any still-unexplored branches worth a later run.\n\n## Managed FTUE auth secret\n\n- `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` is an internal-team managed secret for cloud agents, not a repo file or prompt literal.\n- The secret should authenticate as a dedicated non-employee, non-`warp.dev` FTUE test user.\n- Rotate the secret with `oz-dev secret update --team --value-file <private-token-file> ONBOARDING_AGENT_FTUE_REFRESH_TOKEN`.\n- Treat the private token file as local scratch material only. Do not read it into chat, print it, stage it, commit it, upload it, or include it in artifacts. Delete it after the managed secret is updated.\n- Children should receive the secret only through the managed environment variable injected into the remote run.\n\nUse the initial `run_agents` call shaped like this:\n\n```text\nsummary: Launching two baseline cloud agents with computer use to compare logged-out and logged-in Warp onboarding screenshots and propose follow-up coverage branches.\nremote.computer_use_enabled: true\nagent_run_configs:\n- name: \"warp-onboarding-logged-out\"\n  prompt: the logged-out flow prompt below\n- name: \"warp-onboarding-logged-in\"\n  prompt: the logged-in flow prompt below\nbase_prompt: the shared child prompt below\n```\n\nWhen the baseline reports identify concrete follow-up branches, use a second `run_agents` call shaped like this:\n\n```text\nsummary: Launching targeted cloud follow-up agents to explore distinct onboarding branches identified by the baseline onboarding explorers.\nremote.computer_use_enabled: true\nagent_run_configs:\n- name: \"warp-onboarding-followup-theme-choice\"\n  prompt: the follow-up flow prompt below, the logged-out flow prompt below, and one synthesized logged-out branch assignment\n- name: \"warp-onboarding-followup-model-choice\"\n  prompt: the follow-up flow prompt below, the logged-in flow prompt below, and one synthesized logged-in branch assignment\nbase_prompt: the shared child prompt below\n```\n\n## Shared child prompt\n\nGive both cloud agents these shared instructions:\n\n```text\nYou are verifying the first-time Warp install and onboarding experience on Linux.\n\nGoal:\n- Download and install the latest stable Warp Linux build appropriate for this cloud environment's distro and CPU architecture.\n- Launch Warp in a fresh first-run state.\n- Take a screenshot at every visible onboarding step.\n- Continue until Warp reaches a usable terminal session, or stop and report a blocker if the assigned flow cannot proceed.\n- Notice alternate onboarding decisions that lead to meaningfully different screens, states, or outcomes, and return concrete follow-up cloud-agent plans for the parent orchestrator.\n- Treat visual polish, missing assets, misalignment, overlapping content, clipped text, poor contrast, broken loading states, unexpected errors, and confusing controls as verification findings rather than ignoring them.\n\nInstall requirements:\n- Use official stable Warp downloads only.\n- Do not use Warp Preview, Alpha, source builds, or a repository development build.\n- Detect CPU architecture with `uname -m`.\n- Detect the package manager or distro before choosing the package format.\n- Prefer native packages over AppImage because they install dependencies and register the app normally.\n\nStable Linux package mapping:\n- Debian/Ubuntu with amd64 or x86_64: https://app.warp.dev/download?package=deb\n- Debian/Ubuntu with arm64 or aarch64: https://app.warp.dev/download?package=deb_arm64\n- Fedora/RHEL/CentOS/openSUSE with amd64 or x86_64: https://app.warp.dev/download?package=rpm\n- Fedora/RHEL/CentOS/openSUSE with arm64 or aarch64: https://app.warp.dev/download?package=rpm_arm64\n- Arch with amd64 or x86_64: https://app.warp.dev/download?package=pacman\n- Arch with arm64 or aarch64: https://app.warp.dev/download?package=pacman_arm64\n- If no native package path is available, use the AppImage fallback:\n  - amd64 or x86_64: https://app.warp.dev/download?package=appimage\n  - arm64 or aarch64: https://app.warp.dev/download?package=appimage_arm64\n\nBefore launch:\n- Create a flow-specific artifact directory such as `~/warp-onboarding-logged-out` or `~/warp-onboarding-logged-in`.\n- Ensure the run starts from a fresh Warp first-run state by removing only Warp-specific config/data/cache/state directories for the test user, such as `~/.config/warp-terminal`, `~/.local/share/warp-terminal`, `~/.local/state/warp-terminal`, and `~/.cache/warp-terminal` if they exist.\n- Do not delete unrelated user files or system directories.\n\nScreenshot workflow:\n- Take the first screenshot before interacting with the first visible Warp window.\n- Take one screenshot before every user action.\n- Take another screenshot after each action if the UI changes.\n- Use sequential filenames with a flow prefix, such as `01-logged-out-initial-window.png` or `01-logged-in-initial-window.png`.\n- If anything looks wrong, take an additional issue-focused screenshot that captures the problematic state as clearly as possible.\n- Maintain a manifest file in the artifact directory with, for each screenshot:\n  - filename\n  - timestamp\n  - what was visible\n  - what action was about to happen or just happened\n- For issue-focused screenshots, add the suspected issue category and the screen or step where it appeared.\n- Do not include secret values, refresh tokens, ID tokens, auth redirect URLs, or Authorization headers in the manifest, logs, shell history, screenshots, or final report.\n\nOnboarding behavior:\n- Baseline children choose the default or most conservative option at each step unless the flow-specific prompt says otherwise, while recording branch points that deserve separate follow-up coverage.\n- Follow-up children take the specifically assigned alternate branch, then use the default or most conservative option for unrelated decisions unless the branch assignment says otherwise.\n- If telemetry, shell, theme, editor-import, or agent integration choices appear, use the default path and document the choice in the manifest.\n- Continue until a normal terminal prompt is visible and usable.\n\nUI quality review:\n- Watch for screens that are visually broken, obviously unfinished, misaligned, truncated, clipped, crowded, low-contrast, unexpectedly blank, stuck loading, or inconsistent with adjacent steps.\n- Watch for actionable errors or validation states that appear during normal flow exploration, including auth failures, failed button transitions, controls that do not respond, duplicated overlays, missing images, or broken post-selection states.\n- For every suspicious state:\n  - Capture a screenshot.\n  - Record the screen, the action that led to it, what looked wrong, and whether it blocked progress.\n  - Describe the issue factually. If expected behavior is uncertain, say it appears suspicious rather than claiming a confirmed bug.\n\nTerminal verification:\n- Once a terminal session is visible, run a harmless flow-specific command:\n  - logged-out flow: `echo warp-onboarding-logged-out-ready`\n  - logged-in flow: `echo warp-onboarding-logged-in-ready`\n- Capture a final screenshot showing the usable terminal and command output.\n\nReport back:\n- Whether you were a baseline explorer or a follow-up branch explorer.\n- Which flow you ran: logged-out or logged-in.\n- OS and distro detected.\n- CPU architecture detected.\n- Package URL and install method used.\n- Launch command used.\n- Whether the walkthrough reached a usable terminal session.\n- Ordered screenshot list with short descriptions.\n- Artifact directory path.\n- Any built-in artifact IDs or attachment names if the harness supports artifact upload.\n- Any visual polish concern, suspected bug, error state, or unpolished/misaligned screen, including:\n  - screenshot filename\n  - screen or step\n  - action taken immediately before it appeared\n  - concise observed behavior\n  - whether it blocked progress\n- Any blocker, crash, missing dependency, display problem, auth failure, or step that required judgment.\n- For baseline explorers, include a `Follow-up coverage plan` section with zero or more proposed child-agent branches. Each proposal must include:\n  - suggested agent name\n  - logged-out or logged-in flow\n  - onboarding screen or decision point where the alternate branch begins\n  - exact alternate choice or action sequence to explore\n  - why it is materially distinct from the baseline path\n  - what user-visible state, setup outcome, or failure mode it could reveal\n  - any secret, auth, or environment dependency\n  - priority: high, medium, or low\n- For follow-up explorers, include whether the assigned branch was reachable and completed. If a new branch point appears while following the assigned path, record it as a later-run suggestion instead of recursively expanding the run yourself.\n\nDo not upload screenshots or logs to public external services. If the harness provides a built-in artifact or screenshot attachment mechanism, use that. Otherwise, leave the files in the artifact directory and report their paths.\n```\n\n## Logged-out flow prompt\n\nAppend this prompt to the shared child prompt for the logged-out child:\n\n```text\nYou own the logged-out onboarding flow.\n\nFlow-specific goal:\n- Do not create an account, log in, or use a real user identity.\n- Continue only through login-free or account-free paths until Warp reaches a usable terminal session.\n- Stop and report a blocker if the flow requires login or account creation with no skip/continue-without-account option.\n\nFlow-specific onboarding behavior:\n- If there is a skip, \"continue without account\", \"not now\", \"login later\", or equivalent option, use it.\n- Do not enter an email address, connect OAuth, paste an auth token, or create credentials.\n- Be especially alert for logged-out branch points around choosing terminal-only versus agentic experiences, customization/layout options, third-party integration toggles, and terminal theme selection. If they appear, propose follow-up branches that exercise materially different choices rather than trying all alternates inline.\n- Use the artifact directory `~/warp-onboarding-logged-out`.\n```\n\n## Logged-in flow prompt\n\nAppend this prompt to the shared child prompt for the logged-in child:\n\n```text\nYou own the logged-in onboarding flow.\n\nFlow-specific goal:\n- Use the managed secret environment variable `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` to authenticate as the dedicated non-employee, non-`warp.dev` FTUE test user.\n- Exercise onboarding screens that are available to an already-authenticated user.\n- Continue through the authenticated onboarding path until Warp reaches a usable terminal session.\n\nSecret handling requirements:\n- Before doing auth work, verify that `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` exists and is non-empty without printing it.\n- Never echo, log, screenshot, upload, or report the secret value.\n- Avoid shell tracing (`set -x`) and avoid writing commands that place the raw token in shell history or process lists.\n- Treat every auth redirect URL containing the refresh token as secret-bearing material, even after URL-encoding.\n- Do not pass a token-bearing redirect URL to a shell command, desktop URI handler, browser address bar, process argument, log, artifact, or report. In particular, do not use commands such as `xdg-open`, `gio open`, `open`, or equivalent with the redirect URL.\n- If you need to construct an auth redirect URL, keep it only in a clipboard value or a private temporary file with user-only permissions, paste it through Warp's visible Paste Auth Token flow, then delete the temporary file immediately after use.\n\nSecure Paste Auth Token process:\n1. Verify `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` exists and is non-empty without printing it.\n2. Start Warp's normal login flow and derive the current-run `state` from Warp's generated login URL.\n3. Normalize the managed secret privately:\n   - Trim surrounding whitespace and one pair of surrounding single or double quotes if present.\n   - If the secret parses as a URL with a `refresh_token` query parameter, extract that `refresh_token` value and ignore any stale `state` in the secret.\n   - Otherwise, treat the trimmed secret as the raw refresh token.\n4. URL-encode the extracted refresh token and current-run `state` separately as query parameter values.\n5. Construct the redirect URL only in a clipboard value or private temporary file with user-only permissions.\n6. Return to Warp and use the visible Paste Auth Token path:\n   - Click the `Click here to paste your token from the browser` link, `Paste Auth Token` button, or equivalent pasted-token control shown by Warp.\n   - Focus the auth token text input that appears.\n   - Paste the prepared redirect URL into that input and submit it through Warp's UI so Warp parses and validates it.\n7. Delete any private temporary files immediately after use and clear the clipboard if the environment supports doing so safely.\n8. If the Paste Auth Token UI cannot be reached or automated safely, stop and report an auth blocker instead of parsing the redirect in place of Warp, using a desktop URI handler, browser address bar, or shell command with the token-bearing URL.\n\nPreferred authenticated path:\n- Launch Warp in a fresh first-run state and choose the login/sign-in path from onboarding.\n- Use Warp's built-in Paste Auth Token flow rather than visiting real OAuth providers, invoking a desktop URI handler, or asking the agent to parse/validate the redirect URI itself.\n- Derive `<state>` from the login URL generated by Warp if the UI exposes a copied login URL or opens the browser. If the UI does not expose the state after reasonable effort, report that as an auth blocker rather than bypassing state validation.\n- Do not preflight the token with Firebase Secure Token before handing it to Warp. Warp's desktop redirect handler only requires `refresh_token` and `state`; `user_uid` is optional, and `deleted_anonymous_user=true` handles the anonymous-user override case.\n- Treat `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` as either of these secret shapes:\n  - a raw Firebase refresh token, or\n  - a complete Warp desktop auth redirect URL containing a `refresh_token` query parameter.\n- Normalize the secret into a current-run redirect URL without printing it:\n  - Trim surrounding whitespace and one pair of surrounding single or double quotes if present.\n  - If the secret parses as a URL with a `refresh_token` query parameter, extract that `refresh_token` value and ignore any stale `state` in the secret.\n  - Otherwise, treat the trimmed secret as the raw refresh token.\n  - URL-encode the extracted refresh token and the current-run `state` separately as query parameter values.\n  - Build `warp://auth/desktop_redirect?refresh_token=<url-encoded-normalized-refresh-token>&deleted_anonymous_user=true&state=<url-encoded-current-state>`.\n  - Do not include `user_uid` unless it is already present in a provided desktop redirect URL; it is not required for this flow.\n- Construct the normalized redirect URL in a clipboard value or private temporary file only, then hand it to Warp through the Paste Auth Token UI. Do not parse, validate, or route the redirect outside of Warp.\n- If the Paste Auth Token flow cannot be reached or automated safely, stop and report an auth blocker instead of using a desktop URI handler or any shell command that contains the token-bearing URL.\n\nFallback authenticated path:\n- If Warp rejects the normalized redirect, report the non-sensitive user-visible error and classify whether the secret appeared to be a raw token or a desktop redirect URL, without reporting any token contents.\n- If the Paste Auth Token flow is blocked by UI automation issues, report the blocker and include the exact non-sensitive step where automation failed.\n- Do not switch to a logged-out path for this child.\n\nFlow-specific onboarding behavior:\n- Choose login/sign-in rather than skip/login-later when presented with an auth choice.\n- After auth succeeds, continue through the remaining onboarding screens with default or conservative options.\n- Be especially alert for logged-in branch points around model selection, account-aware onboarding screens, AI/agent setup, workspace or project setup, and any decision that changes available product capability. If they appear, propose follow-up branches that exercise materially different choices rather than trying all alternates inline.\n- After the terminal verification succeeds, click the upper-right avatar/account control, open Settings from that menu, and capture an additional screenshot that clearly shows the logged-in user's email address in Warp settings or account/profile settings.\n- Include the account/settings email screenshot in the manifest and final report. The email address itself may be visible in the screenshot, but do not copy the email into logs, shell output, or the final text report unless the user explicitly asks for it.\n- Use the artifact directory `~/warp-onboarding-logged-in`.\n```\n\n## Follow-up flow prompt\n\nAppend this prompt to the shared child prompt for every second-wave child, followed by the matching logged-out or logged-in flow prompt and one branch assignment synthesized from the baseline reports:\n\n```text\nYou own one follow-up onboarding branch selected by the parent orchestrator from an earlier baseline exploration report.\n\nFollow-up branch behavior:\n- Start from a fresh first-run Warp state and install the same latest stable Linux build using the shared instructions.\n- Respect the assigned auth state: remain logged out for logged-out assignments, or use the managed authenticated flow for logged-in assignments.\n- Follow the exact alternate onboarding choice or action sequence in the branch assignment.\n- Capture screenshots before and after each assigned branch decision, then continue to a usable terminal session if the path allows it.\n- Apply the same UI quality review standard as the baseline explorers and call out anything that looks broken, rough, misaligned, confusing, or unexpectedly error-prone.\n- If the assigned branch is not reachable, capture the closest relevant screen, report why it was unreachable, and do not silently substitute a different branch.\n- If the assigned branch reveals another interesting alternate path, record it as a later-run suggestion rather than recursively launching more agents yourself.\n\nFinal report additions:\n- Repeat the exact branch assignment you attempted in concise non-sensitive terms.\n- State whether it was reachable, completed, blocked, or not applicable.\n- Compare the branch outcome against the likely baseline behavior when that comparison is visible from the UI.\n```\n\n## Success criteria\n\nThe run is successful when:\n\n- Warp stable was installed from an official Linux package or AppImage for the detected architecture.\n- Screenshots were captured for each onboarding screen and the final usable terminal.\n- The logged-out child reached a usable terminal without login, account creation, or a real user identity.\n- The logged-in child authenticated using `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` and reached a usable terminal in the authenticated FTUE path.\n- The logged-in child captured an additional post-login screenshot from the avatar/settings flow showing the logged-in user's email address.\n- Each terminal session was usable enough to run its flow-specific `echo` command.\n- Both baseline explorers returned either concrete follow-up coverage proposals or an explicit explanation that they did not observe meaningful additional branch points.\n- The parent orchestrator launched targeted second-wave agents for the highest-value concrete branch proposals, unless there were no such proposals or a prerequisite blocker made them infeasible.\n- Every reported visual polish concern, suspected bug, or error state includes a screenshot reference whenever the issue was visible on screen.\n\n## Common failure handling\n\n- If the package manager prompts for confirmation, use the non-interactive confirmation flag supported by that package manager.\n- If launching `warp-terminal` fails because of display setup, inspect the cloud environment's display variables and try launching from the desktop/app launcher if computer use provides one.\n- If the logged-out flow blocks on login with no skip path, stop at that screen, capture a screenshot, and report that as the terminal point for the logged-out flow.\n- If the logged-in flow cannot authenticate because the secret is missing, invalid, expired, revoked, or cannot be routed through Warp's auth redirect flow, stop at that screen, capture a screenshot, and report the non-sensitive blocker.\n- If the native package cannot be installed because dependencies are unavailable, fall back to the matching AppImage and clearly report the fallback.\n",".agents/skills/gui-ui-guidelines/SKILL.md":"---\nname: gui-ui-guidelines\ndescription: GUI desktop app only. Catalog of guidelines for writing UI code in the Warp client. Read whenever doing any UI work in this repo, up front before writing the change, so the relevant guidelines shape the implementation.\n---\n\n# gui-ui-guidelines\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nThis skill is a growing catalog of guidelines for working on Warp's UI code. Each guideline captures a lesson that would otherwise be re-learned through review — typically because an agent or contributor reinvented a component, drifted from the design system, or bypassed a shared abstraction.\n\n**How to use this skill:**\n\n- Read through the guidelines below once at the start of any UI task, then keep them in mind while implementing. The list is short enough to scan.\n- Each guideline is self-contained. Not every one will apply to every task — use judgment. But if a guideline *does* apply, follow it.\n- When in doubt, prefer reusing an existing abstraction over introducing a new one. The Warp UI has accumulated a well-factored set of shared components and themes; new one-offs almost always drift.\n\nFor work on the settings modal (`app/src/settings_view/`) — a settings page, a `SettingsWidget`, or anything that affects settings search — also read the `gui-settings-ui` skill, which covers the page-title, widget-gating, and widget-splitting rules specific to that surface.\n\nNew guidelines get added here over time. If you discover a recurring UI mistake that would have been caught by a written rule, add it.\n\n---\n\n## Guideline: Reuse button themes\n\nButton colors come from a shared set of `ActionButtonTheme` impls in `app/src/view_components/action_button.rs` (and the parallel `Theme` impls in `crates/ui_components/src/button/themes.rs`) — `PrimaryTheme`, `SecondaryTheme`, `NakedTheme`, `DangerPrimaryTheme`, etc. These encode the design system and keep button colors consistent across the app.\n\nWhen styling a button, **use one of the existing themes unchanged**. The shared themes are well-established and vetted; if one looks \"wrong\" for your use case, the most likely explanation is that you're reaching for the wrong theme, not that the theme is buggy.\n\nDo **not** modify a shared theme on your own initiative. Changing `PrimaryTheme`, `SecondaryTheme`, etc. affects every button in the app, and a tweak that fixes your screen can silently regress others. Only edit a shared theme when the user has explicitly confirmed that the design-system component itself needs to change.\n\nRed flags that you're about to make buttons inconsistent:\n\n- Writing a new `impl ActionButtonTheme for FooPrimaryTheme` that delegates to `PrimaryTheme` and only tweaks one method (usually `text_color`). Almost always the right move is to use `PrimaryTheme` directly and accept the result.\n- Hard-coding `ColorU::new(...)` instead of using `appearance.theme()` accessors (`accent`, `font_color(bg)`, `foreground`, etc.).\n- Setting `should_opt_out_of_contrast_adjustment` to `true` to force a specific label color.\n- Naming a theme after a feature or view (`FooPrimaryTheme`, `BarSubmitTheme`) rather than a design-system role.\n\nIf an existing theme genuinely doesn't fit and you think the shared theme should change, surface that to the user before editing it, rather than either editing it unilaterally or papering over it with a one-off.\n",".agents/skills/gui-reproduce-bug-report-local/SKILL.md":"---\nname: gui-reproduce-bug-report-local\nspecializes: reproduce-bug-report\nspecializes_source: warpdotdev/common-skills:.agents/skills/reproduce-bug-report\ndescription: GUI desktop app only. Repo-specific bug reproduction guidance for Warp. Specializes the core reproduce-bug-report skill for logged-out Warp UI repros, exact reporter-version installs, and login-free onboarding.\n---\n\n# Repo-specific bug reproduction guidance for `warp`\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\n## Prerequisite: install the parent skill\n\nThis skill specializes the core `reproduce-bug-report` skill (named in the `specializes` frontmatter field) and is not functional on its own. Before applying its guidance, confirm the parent skill is installed and resolvable at `.agents/skills/reproduce-bug-report/SKILL.md`. If it is missing, install it first by copying the skill directory from the source declared in the `specializes_source` frontmatter field (`warpdotdev/common-skills:.agents/skills/reproduce-bug-report`). Then continue with the guidance below.\n\nThis file is a companion to the core `reproduce-bug-report` skill. It does not redefine the shared Oz computer-use orchestration, artifact handling, safety rules, or reporting format. It specializes scope and setup for Warp bug reports.\n\n## Scope\n\n- Use this workflow only for Warp bugs that can be exercised while the app remains logged out.\n- Apply it to UI-visible Warp bugs, interaction bugs, rendering/layout bugs, logged-out onboarding bugs, settings bugs, editor/display bugs, terminal-display bugs, and other visual or interactive issues where screenshots or recordings would help.\n- Do not use it for authenticated-user flows, account-specific state, cloud-synced state, logged-in onboarding, or AI behaviors that require login.\n- If a report requires authentication, account state, cloud sync, or another logged-in-only capability, do not launch a repro agent with this local specialization; report that it is out of scope for the current logged-out Warp workflow.\n\n## Warp version and install strategy\n\n- Prefer reproducing against the exact Warp version/build and channel reported by the user.\n- Do not build Warp from source by default. Install the matching Linux package or binary release for the reporter's version/channel instead.\n- If the bug report names a macOS or Windows build, use the corresponding Linux build from the same version/channel when a matching Linux artifact exists, and state that this is a Linux proxy for the reporter's platform.\n- Use the repository's or Warp release tooling/docs available in the environment to find and install the exact versioned Linux artifact. Do not silently substitute the latest stable build when an exact matching version can be installed.\n- If the exact version/build cannot be found or installed, report that clearly, explain what was attempted, and use the closest justified fallback only when it is useful for continuing the investigation.\n- Record the requested reporter Warp version, the installed Linux version, the source of the installed artifact, and any fallback decision in the manifest and final report.\n\n## Logged-out Warp baseline\n\n- Keep Warp logged out for the entire repro attempt. Do not create an account, sign in, paste auth tokens, or use real user credentials.\n- Launch Warp and complete the login-free / continue-without-account onboarding path until a normal logged-out terminal session is usable.\n- Capture a post-onboarding baseline screenshot before attempting the bug-specific reproduction.\n- If the assigned bug cannot be exercised after entering a normal logged-out Warp session, stop and report the blocker instead of improvising an authenticated flow.\n\n## Local prompt additions\n\nWhen applying the core skill to Warp, ensure the parent prompt and child prompts include:\n\n- Reporter Warp version/build/channel: the exact value from the report, or `unknown`.\n- Build/app target: the exact versioned Linux Warp package/binary to install, or the justified fallback if an exact artifact is unavailable.\n- Assigned Warp state: first-run logged-out state, completed logged-out onboarding, terminal/session/layout/settings state, or the targeted code-path hypothesis.\n- A reminder that Warp must remain logged out and that logged-in-only reports are blocked for this specialization.\n\n## Local reproduction priorities\n\n- Match the reporter's Warp version/build/channel before broadening the search space.\n- Follow the issue's exact steps first, then test at most two targeted variations supported by the issue or by a code-path hypothesis.\n- Prefer targeted hypotheses derived from Warp UI strings, settings names, feature names, telemetry names, route names, and relevant components over broad exploratory clicking.\n- In the final report, include:\n  - the reporter-requested Warp version/build/channel\n  - the installed Linux Warp version/build/channel\n  - the package or binary source\n  - whether a fallback was used\n  - whether the test was a Linux proxy for a macOS or Windows report\n",".agents/skills/gui-integration-test/SKILL.md":"---\nname: gui-integration-test\ndescription: GUI desktop app only. Writes, runs, and debugs Warp integration tests using the custom Builder/TestStep framework in `crates/integration`. Use when adding a new integration test, fixing a failing integration test, wiring a test into the manual runner or nextest suite, or verifying end-to-end UI and terminal behavior in Warp.\n---\n\n# Warp Integration Tests\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nUse this skill for Rust integration tests in Warp's custom framework under `crates/integration/`.\n\nThese are not ordinary unit tests. They boot a real Warp app instance, give it an isolated test home directory, drive it with synthetic UI and terminal events, and poll assertions until success or timeout.\n\n## When an integration test is the right tool\n\nIntegration tests are the most expensive tests in the repo. They boot the app, they are orders of magnitude slower than a unit test, and they are the most likely to go flaky. Write one when the risk you are covering genuinely lives *between* components:\n\n- Real terminal/PTY and shell-integration behavior, especially across bash, zsh, and fish.\n- Wiring and configuration: settings, keybindings, and user preferences actually taking effect end to end.\n- Cross-component flows a unit test cannot see — command palette into editor into terminal, focus and pane management, tab/window lifecycle.\n- Input and rendering paths that only exist once a real app (and sometimes a real display) is running.\n- Regressions for user-visible bugs that escaped unit tests.\n\nDo **not** reach for an integration test when:\n\n- The logic is deterministic and reachable in-process. Push it into a unit test (`rust-unit-tests`); it will run in milliseconds and point straight at the failure.\n- You are re-covering branch logic a unit test already covers. This harness is for the seams between components, not for re-testing conditionals at full app-boot cost.\n- You only need TUI rendering. That is `tui-testing` — this harness does not drive the TUI at all.\n- What you actually want is a screenshot or a manual look. Use `computer_use` or the `gui-integration-test-video` skill instead of attaching weak assertions to a full app boot.\n\nIf a behavior is hard to reach from a unit test *only* because of how the code is structured, prefer fixing the structure over writing a slow test around it.\n\n## Framework map\n\nThe core pieces are:\n\n- `crates/integration/src/bin/integration.rs`\n  - Manual integration test runner binary.\n  - Registers test names to `Builder` factories.\n  - Runs exactly one named test per invocation.\n- `crates/integration/tests/common/mod.rs`\n  - The outer Rust test harness used by `cargo test` and `cargo nextest`.\n  - Shells out to the integration binary.\n  - Forwards a limited set of env vars (`PATH`, `RUST_*`, `WARP_*`, `WARPUI_*`, `WGPU_*`, display-related vars).\n  - Re-runs tests up to 10 times when the integration binary exits with the special rerun code.\n- `crates/integration/src/test.rs`\n  - Module hub for integration tests.\n  - Add new test modules here and `pub use` their functions so the runner can see them.\n- `crates/integration/tests/integration/ui_tests.rs`\n  - List of UI-oriented integration tests that nextest should run.\n- `crates/integration/tests/integration/shell_integration_tests.rs`\n  - List of tests that must run against every shell or a specific shell matrix.\n- `crates/integration/src/builder.rs`\n  - Warp-specific wrapper around the lower-level WarpUI integration builder.\n  - Sets default timeout, hermetic home directory, shell rc files, user prefs, and real-display mode when requested.\n- `crates/warpui_core/src/integration/driver.rs`\n  - Executes steps, handles retries, precondition reruns, screenshots, video capture, artifact export, and `on_finish`.\n- `crates/warpui_core/src/integration/step.rs`\n  - Defines `TestStep`, input/event APIs, assertion polling, step-to-step data passing, and screenshot/recording hooks.\n- `app/src/integration_testing/`\n  - High-level helpers and assertions for common Warp behaviors.\n  - Prefer these helpers over raw low-level event plumbing whenever they fit.\n\n## How the framework actually runs a test\n\n1. A Rust test from `crates/integration/tests/integration/*.rs` calls `run_integration_test(\"test_name\")`.\n2. That harness launches the `integration` binary with the test name.\n3. The binary in `crates/integration/src/bin/integration.rs` looks up the name in `register_tests()`, builds the `Builder`, and turns it into a `TestDriver`.\n4. `Builder::build(...)` creates an isolated temp directory, points `HOME` at it, writes minimal rc files, and initializes file-backed user preferences.\n5. The driver runs each `TestStep` in order:\n   - setup callbacks\n   - synthetic events\n   - actions\n   - assertion polling until success or timeout\n6. If an assertion returns `PreconditionFailed`, the binary exits with the rerun code and the outer harness retries the whole test.\n7. On success, failure, or cancellation, the driver can run `on_finish` and export artifacts/runtime tags.\n\nThis means integration tests should be written for a hermetic environment. Do not rely on the developer's real shell dotfiles, home directory contents, or persisted Warp settings.\n\n## Where to put a new test\n\nAdd the actual test function in a module under `crates/integration/src/test/`.\n\nUse these heuristics:\n\n- Put the test in an existing module when it matches that feature area.\n- Create a new module when the feature does not fit an existing one cleanly.\n- Add the test to `crates/integration/tests/integration/ui_tests.rs` if it is primarily a UI/app behavior test.\n- Add the test to `crates/integration/tests/integration/shell_integration_tests.rs` if it needs to run against every shell, or depends on a specific shell/set of shells.\n\nBeing present in `crates/integration/src/test/*.rs` is not enough. For a test to run under `cargo nextest`, it also needs to be listed in one of the macro files in `crates/integration/tests/integration/`.\n\n## Authoring checklist for a new test\n\nWhen adding a new integration test, do all of the following:\n\n1. Implement `pub fn test_name() -> Builder` in a module under `crates/integration/src/test/`.\n2. Add the module to `crates/integration/src/test.rs`.\n3. `pub use` the new module's exports from `crates/integration/src/test.rs`.\n4. Add `register_test!(test_name);` in `crates/integration/src/bin/integration.rs`.\n5. Add `test_name` to either:\n   - `crates/integration/tests/integration/ui_tests.rs`, or\n   - `crates/integration/tests/integration/shell_integration_tests.rs`\n6. Default to making the test run in CI once it is added to one of those macro lists. Only mark it `#[ignore]` when the task explicitly calls for manual-only coverage or there is a concrete, documented reason it cannot run reliably in CI.\n7. Run the test manually first, then through nextest once it is stable enough for the suite you chose.\n\n## Writing the test body\n\nThe normal shape is:\n\n```rust\nuse crate::Builder;\nuse warp::integration_testing::step::new_step_with_default_assertions;\nuse warp::integration_testing::terminal::{\n    clear_blocklist_to_remove_bootstrapped_blocks,\n    execute_command_for_single_terminal_in_tab,\n    wait_until_bootstrapped_single_pane_for_tab,\n    util::ExpectedExitStatus,\n};\n\npub fn test_example() -> Builder {\n    Builder::new()\n        .with_step(wait_until_bootstrapped_single_pane_for_tab(0))\n        .with_step(clear_blocklist_to_remove_bootstrapped_blocks())\n        .with_step(execute_command_for_single_terminal_in_tab(\n            0,\n            \"echo hello\".to_string(),\n            ExpectedExitStatus::Success,\n            \"hello\".to_string(),\n        ))\n        .with_step(\n            new_step_with_default_assertions(\"Assert some UI state\")\n                .add_named_assertion(\"specific assertion name\", |app, window_id| {\n                    // inspect app state and return AssertionOutcome\n                    warpui::integration::AssertionOutcome::Success\n                }),\n        )\n}\n```\n\nPrefer a small number of focused steps with descriptive names over a huge monolithic test.\n\n## Builder guidance\n\n### `Builder::new()`\n\nStart here almost every time.\n\nWarp's wrapper automatically gives you:\n\n- a per-test root directory\n- isolated `HOME`\n- generated rc files for Bash, Zsh, and Fish\n- file-backed user preferences\n- a default 2-minute hard timeout\n- real-display support if `WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS` is present\n\n### `with_setup(...)`\n\nUse this for filesystem or environment setup before the app runs.\n\nCommon patterns:\n\n- `utils.set_env(\"NAME\", Some(value))`\n- creating files under `utils.test_dir()`\n- writing fixture config files\n\nPrefer this over reaching into the real filesystem.\n\n### `with_user_defaults(...)`\n\nUse this to set persisted Warp preferences before the test starts.\n\nThis is the right tool for settings backed by user preferences rather than environment variables.\n\n### `set_should_run_test(...)`\n\nUse this to gate tests on shell/platform/runtime capabilities when the test genuinely cannot run everywhere.\n\n### `with_on_finish(...)`\n\nUse this for final verification or artifact inspection that should happen after all steps complete, such as checking that screenshots or recordings were written.\n\n### `with_real_display()`\n\nUse this explicitly when the test needs a real display for frame capture or visual workflows. Video/screenshot tests should normally be manual or ignored in CI unless there is a stable real-display path.\n\n## `TestStep` guidance\n\n`TestStep` is the unit of execution. Each step can have:\n\n- setup callbacks\n- input events\n- actions\n- assertions\n- a timeout\n- retry count\n- failure handling\n\n### Start from helper constructors\n\nPrefer:\n\n- `wait_until_bootstrapped_single_pane_for_tab(0)`\n- `new_step_with_default_assertions(\"...\")`\n- `new_step_with_default_assertions_for_pane(\"...\", tab, pane)`\n\nThe default step helpers already assert:\n\n- no pending model events\n- no block executing\n\nThese are good baseline invariants for most UI interactions.\n\n### Prefer helper APIs over raw event plumbing\n\nUse high-level helpers from `app/src/integration_testing/` whenever possible:\n\n- terminal command execution helpers\n- block list helpers\n- command palette helpers\n- navigation helpers\n- settings helpers\n- workflow/file tree/notebook helpers\n\nDrop to raw `with_event(...)`, `with_event_fn(...)`, or saved-position mouse events only when there is no suitable helper.\n\n### Use named assertions\n\nPrefer `add_named_assertion(...)` over unnamed assertions. Named assertions make failure output and runtime tags much easier to interpret.\n\n### Use polling assertions instead of sleeps\n\nAssertions are polled until success or timeout. Lean on that model instead of hardcoding sleeps.\n\nGood pattern:\n\n- trigger an event or action\n- assert on the eventual UI/model state\n\nAvoid brittle timing assumptions.\n\n### Use step data when one step computes something for the next\n\nIf a later step needs data from an earlier one, use:\n\n- `add_named_assertion_with_data_from_prior_step(...)`\n- `StepDataMap`\n\nThis is useful for saving measured positions, counts, IDs, or other values from prior frames.\n\n### Use retries sparingly\n\n`set_retries(...)` can help for a legitimately retryable step, but do not use it to hide deterministic failures. Prefer making the step more robust first.\n\n### Use `PreconditionFailed` for genuinely environmental flakes\n\nIf the environment reaches a state where the rest of the test is invalid, return `AssertionOutcome::PreconditionFailed(...)` instead of failing hard. The outer harness can rerun the entire test up to 10 times. The existing bootstrap helper is a good model for this.\n\nUse this deliberately. The rerun mechanism exists for conditions the test genuinely cannot control, such as bootstrap racing or shell startup timing. It is not a way to turn an intermittently failing test green. A real bug that reproduces one run in five will pass under rerun and ship to users.\n\nBefore reaching for `PreconditionFailed`, confirm the failure is actually environmental by looking at the failure rate and the failure mode:\n\n```bash\nfor i in {0..50}; do\n  RUST_BACKTRACE=full cargo run -p integration --bin integration -- test_name || break\ndone\n```\n\nIf the same assertion fails in different ways, or fails while the environment looks fine, it is a product or test bug. Fix it rather than absorbing it into a rerun.\n\n## Designing a good integration test\n\n### Keep the scope as small as the behavior allows\n\nThe scope of a test follows the scope of what you boot and drive, so cover one user journey per test and let separate tests cover separate journeys. When a flow is long, prefer several shorter tests that each verify one hop over a single test that walks the whole path. A long test is slower, harder to diagnose, and fails for many unrelated reasons.\n\n### Assert on behavior a user could observe\n\nThe framework can see internal state, which makes it tempting to assert on it. Anchor each test on the user-observable outcome:\n\n- output visible in the terminal\n- focus moved where expected\n- UI element opened or closed\n- selection changed\n- settings applied\n\nInternal state assertions are still useful, but they should support the visible behavior rather than replace it. Assertions that mirror internal call sequences become change detectors: they break on every refactor and catch no bugs.\n\n### Make the failure diagnosable by someone else\n\nAn integration test spans processes, so a stack trace tells the next engineer almost nothing. The failure output has to carry the context instead:\n\n- Name every assertion with what it expects, not what it touches: `\"terminal shows command output\"`, not `\"check blocks\"`.\n- Give steps descriptive names; they are the breadcrumb trail through the run.\n- Put expected vs. actual into the assertion output rather than returning a bare failure.\n\nAssume the person reading the failure has never seen this test and does not own the code it covers.\n\n### Keep the test hermetic\n\n`Builder::new()` already gives you an isolated `HOME`, generated rc files, and file-backed preferences. Preserve that. Set up everything you depend on inside the test via `with_setup(...)` and `with_user_defaults(...)`, and never rely on the developer's dotfiles, real settings, network access, or state left behind by another test. A test that assumes a resource is already in the right state will fail for the wrong reason on someone else's machine or in CI.\n\n### Own the test\n\nLarger tests span components, so ownership is ambiguous by default and unowned tests rot. If you add one, you are the person who fixes it when it breaks. Put it in the module matching the feature area it covers so the next person can find the right owner.\n\n## Common test-writing patterns\n\n### 1. Wait for bootstrap first\n\nFor most terminal-facing tests, the first real step should be:\n\n- `wait_until_bootstrapped_single_pane_for_tab(0)`\n\nDo not start asserting on terminal UI before bootstrap completes.\n\n### 2. Clear the bootstrapped blocks if block indices matter\n\nIf the test relies on saved positions like `block_index:0`, clear the block list after bootstrap:\n\n- `clear_blocklist_to_remove_bootstrapped_blocks()`\n\nOtherwise the first user-generated block index depends on bootstrap output and the active shell.\n\n### 3. Use helper command runners\n\nPrefer helpers like:\n\n- `execute_command_for_single_terminal_in_tab(...)`\n- `execute_echo(...)`\n- `execute_echo_str(...)`\n- `execute_long_running_command(...)`\n\nThese helpers already handle a lot of correctness and output validation.\n\n## Running tests\n\n### Run one test directly through the integration binary\n\nUse this first while authoring:\n\n```bash\ncargo run -p integration --bin integration -- test_name\n```\n\nThis is the fastest way to iterate on a specific test because it bypasses the outer Rust test wrapper and runs the named test directly.\n\n### Run one test through nextest\n\nOnce it is wired into one of the `tests/integration/*.rs` macro lists, run it with nextest:\n\n```bash\ncargo nextest run --no-fail-fast --workspace test_name\n```\n\n### Run with a real display when needed\n\nFor screenshot/video or other real-display flows:\n\n```bash\nWARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo run -p integration --bin integration -- test_name\n```\n\nOr with nextest:\n\n```bash\nWARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo nextest run --no-fail-fast --workspace test_name\n```\n\n## Debugging and investigation\n\n### Get a backtrace on failures\n\n```bash\nRUST_BACKTRACE=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Pause on failure\n\nThis is useful when running locally and you want to inspect the failed UI state:\n\n```bash\nWARPUI_PAUSE_INTEGRATION_TEST_ON_FAILURE=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Pause after every step\n\nUseful for understanding exactly what the test is doing:\n\n```bash\nWARPUI_PAUSE_INTEGRATION_TEST_AT_EVERY_STEP=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Video and screenshots\n\nIf the task is specifically about recording a test, collecting screenshots, or validating overlay/video artifacts, also use the `gui-integration-test-video` skill (located at `.warp/skills/gui-integration-test-video/SKILL.md`).\n\n### Environment variable gotcha\n\n`utils.set_env(...)` affects runtime environment lookups such as `std::env::var(...)`.\n\nIt does not affect compile-time lookups like `option_env!(...)`. If the product code uses `option_env!`, changing the env var inside the test will not change that behavior without rebuilding.\n\n## Verification checklist\n\nBefore considering a new integration test done, verify all of the following:\n\n- The test function lives under `crates/integration/src/test/`.\n- The module is added and re-exported in `crates/integration/src/test.rs`.\n- The test is registered in `crates/integration/src/bin/integration.rs`.\n- The test is listed in the correct nextest macro file and will run in CI by default, unless it was explicitly made manual-only with a documented reason.\n- The test passes when run directly through the integration binary.\n- The test passes through nextest if it is meant to be part of the automated suite.\n- An integration test is the right level for this behavior; it is not re-covering logic a unit test could reach.\n- The assertions check the intended user-visible behavior.\n- Every assertion is named, and the failure output would be actionable to someone who has never seen the test.\n- The test does not depend on the developer's real home directory, shell config, or machine state.\n- If the test uses screenshots/video, the produced artifacts were actually inspected rather than only assuming they exist.\n\n## Anti-patterns to avoid\n\n- Writing a test only in `src/test/*.rs` and forgetting the nextest macro list.\n- Asserting on bootstrap-sensitive block indices without clearing the bootstrapped blocks first.\n- Using raw events everywhere when a helper already exists.\n- Adding sleeps instead of assertion polling.\n- Making the test depend on personal dotfiles, real settings, or non-hermetic filesystem state.\n- Using retries or `PreconditionFailed` to paper over a deterministic bug.\n- Re-testing branch logic that a unit test already covers, at full app-boot cost.\n- Asserting on internal call sequences instead of user-visible behavior.\n- Unnamed assertions that produce failure output nobody can act on.\n- One long test that walks an entire user journey instead of several focused ones.\n- Leaving a real-display/manual test enabled in CI without a stable path.\n\n## Good workflow for agents\n\nWhen asked to add or fix an integration test:\n\n1. Find the closest existing integration test module for the feature.\n2. Reuse helper assertions and step constructors before inventing new low-level plumbing.\n3. Register the test in all required places, not just the implementation file.\n4. Run the test manually first.\n5. If it belongs in automation, run it with nextest too.\n6. If the test exercises visual behavior, verify the resulting UI behavior or artifacts directly.\n",".agents/skills/gui-settings-ui/SKILL.md":"---\nname: gui-settings-ui\ndescription: GUI desktop app only. How to build a Settings page in the Warp client (app/src/settings_view) so its widgets and settings search behave correctly — picking a PageType, deciding whether a heading belongs in the page-title slot or inside a widget, gating a widget, and scoping search_terms per widget. Use when adding or editing a settings page, a SettingsWidget, or anything that affects settings search.\n---\n\n# gui-settings-ui\n\n**Scope — GUI desktop app only.** This skill covers the settings modal in `app/src/settings_view/`, part of Warp's **GUI** desktop front-end. It does not apply to the headless TUI (`crates/warp_tui`). For general UI conventions see `gui-ui-guidelines`.\n\nSettings pages look simple, so they get written by pattern-matching the nearest neighbor — and the nearest neighbor is often wrong. The same two mistakes have produced five Linear tickets (APP-5060, APP-5058, APP-4910, APP-4922, APP-5059, one of which turned out to be a false positive). Read this before writing a settings page so the sixth doesn't happen.\n\n## The model\n\nA settings page is a `PageType` (`app/src/settings_view/settings_page.rs`). It holds **a list of searchable widgets** plus **an optional page title**:\n\n```rust\nPageType::new_uncategorized(widgets, Some(\"Knowledge\"))\nPageType::new_categorized(categories, None)\nPageType::new_monolith(widget, Some(\"Billing and Usage\"), /* is_dual_scrollable */ true)\n```\n\n- **`Uncategorized`** — a flat list of widgets. The common shape.\n- **`Categorized`** — widgets grouped under `Category`s, each with a subheader (rendered via `render_sub_header`) and an optional subtitle.\n- **`Monolith`** — the whole page is a single widget because its content can't be split for search (Keybindings, Teams, About, Environments).\n\nEach widget implements `SettingsWidget` (`settings_page.rs`):\n\n- `search_terms(&self) -> &str` — the terms this widget matches on.\n- `render(..)` — the widget's rows.\n- `should_render(&self, app) -> bool` — defaults to `true`. This is one of **two** ways to conditionally show a setting, and usually not the right one — see \"Two ways to gate a widget\" below.\n- `widget_id()` / `static_widget_id()` — default to `std::any::type_name::<Self>()`, used for scroll-to and deeplinks.\n\n**How search filters.** `PageType::update_filter` keeps a widget when\n`widget.should_render(app) && search_terms_match(widget.search_terms(), query)`.\n`search_terms_match` requires **every whitespace-delimited word** of the query to appear (case-insensitively, as a substring) somewhere in the widget's terms; an empty query matches everything. So filtering happens **per widget** — a widget is the smallest unit that can survive or disappear.\n\n**Whether the page title survives a search depends on the variant.** `PageType::render_page` treats the title differently for widget-list pages than for a monolith, and that difference is the easiest thing here to get wrong.\n\nFor **`Uncategorized` and `Categorized`**, the title is drawn **once, before the loop** over the filtered widget list, unconditionally:\n\n```rust\nlet mut page = Flex::column();\nif let Some(title) = title {\n    page.add_child(render_page_title(title, HEADER_FONT_SIZE, appearance));\n}\nfor widget in widgets { /* … only the widgets that matched … */ }\n```\n\nThe filter cannot reach it. That is what makes the title slot a fix for bug class 1: a title passed through `PageType` survives filtering on these two variants, while a title rendered inside a widget does not.\n\nFor **`Monolith`**, the title is **gated on the sole widget surviving the filter**:\n\n```rust\nFilteredPageType::Monolith { widget, title, .. } => {\n    let mut page = Empty::new().finish();\n    if let Some(widget) = widget          // None when the widget didn't match\n        && widget.should_render(app)\n    {\n        if let Some(title) = title { /* … title, then the widget … */ }\n    }\n    page                                  // otherwise nothing renders at all\n}\n```\n\n`get_filtered` sets that `widget` to `filter.then_some(..)`, so a non-matching query makes it `None` and the page renders empty — **title included**. On a monolith the title slot buys no search protection; the page is all-or-nothing, title and all.\n\nOne more thing worth knowing before you go looking for a title stranded over zero rows: you won't find one. `SettingsView::filtered_pages` (`app/src/settings_view/mod.rs`) drops any page whose `MatchData` is falsy from the sidebar and auto-selects the first page that still matches, so a fully non-matching page is never displayed. The state the title slot actually protects is the **partial** match — some widgets survive, some don't — and only widget-list pages can be in it.\n\n## Two ways to gate a widget\n\nThere are two mechanisms for conditionally showing a setting, and they are not interchangeable.\n\n**1. An `if` at page-build time — never create the widget.** This is how most gating in `AISettingsPageView::build_page` (`ai_page.rs`) is written:\n\n```rust\nif FeatureFlag::AIRules.is_enabled() {\n    widgets.extend(Self::knowledge_widgets());\n}\nif cfg!(feature = \"voice_input\")\n    && ai_settings.voice_input_enabled_internal.is_supported_on_current_platform()\n{\n    widgets.push(Box::new(VoiceWidget::default()));\n}\n```\n\n**2. `should_render(&self, app) -> bool` — create the widget and let it opt out per pass.**\n\n**Which one: can the value change while the app is running?**\n\n- **Fixed for the process** — a feature flag, a `cfg!` feature, a platform-support check. → **Use the build-time `if`.** This is the preferred default: the widget never exists, so there is nothing to filter, render, or reason about.\n- **Can change at runtime** — a setting the user toggles, auth state, an availability check that can flip mid-session. → **Use `should_render`.**\n\nThe reason is *when each is evaluated*. The build-time `if` runs once, when the page is constructed, and its result is frozen until something rebuilds the page (for AI/Code subpages, only switching subpages does — see below). `should_render` is re-evaluated on every filter and render pass, so it tracks a value that changes while the settings page is open. Gate a runtime-changing value with a build-time `if` and the page goes stale; gate a static flag with `should_render` and you carry a widget around for nothing.\n\nThe real `should_render` users are all the runtime kind: `SettingsSyncWidget` (`main_page.rs`) on auth state, `WarpDriveToggleWidget` (`warp_drive_page.rs`) on `WarpDriveSettings::is_warp_drive_available`, and the CLI-agent rich-input widgets (`ai_page.rs`) on the user-toggleable footer setting, via `should_render_cli_agent_rich_input`.\n\nEither mechanism keeps search honest: an uncreated widget isn't in the list, and `update_filter` already skips a widget whose `should_render` is false. What you must never do is hide rows inside `render` while `search_terms` still advertises them — that makes the page match a query and then show nothing for it.\n\n## The decision that matters: where does the heading live?\n\nFor any heading on a settings page, ask **what does it name?**\n\n- **It names the whole page** → it belongs in the `PageType` title slot. On `Uncategorized` / `Categorized` that is also what keeps it on screen while the page is filtered; on a `Monolith` it is a structural choice only (see above).\n- **It names one section among several** → it belongs inside the widget (or `Category`) that owns that section, so it disappears together with its own rows.\n\nGetting this wrong in the first direction is bug class 1 below. Getting it wrong in the second direction leaves a section header stranded above unrelated rows.\n\n### Page-shape taxonomy\n\nClassify the page before you write it:\n\n- **Single-topic page** — one heading names everything on it, but the content is still made of separately-matchable widgets. Knowledge, Third party CLI agents, Editor and Code Review, Account, Scripting. → **Title in the `PageType` slot.**\n- **Multi-section page** — several independent sections, each with its own heading. Warp Agent, Agent profiles, Appearance, Features. → **Per-section headings live in widgets/categories** and correctly disappear with their rows. (For `Categorized`, `get_filtered` drops categories whose widgets all filtered out, so their subheaders vanish automatically — that's the behavior you want.)\n- **Monolith page** — Keybindings, Teams, About, Environments, Codebase Indexing. There is no partial-match state: the sole widget either matches, and the whole page renders, or it doesn't, and the whole page renders empty and drops out of the sidebar. So a monolith can never strand an orphaned setting under a missing heading — it is **not** affected by bug class 1, but for that reason, *not* because its title is protected. Passing the title through the slot is still the tidier structure (Billing and Usage and Referrals do), just don't expect it to keep the title on screen during a non-matching search; on a monolith it will not. Codebase Indexing looks single-topic but is really one unfilterable widget covering the whole page (`CodePageWidget`, wrapped by `CodeIndexingPageWidget`) — building it as `Uncategorized` instead of `Monolith` made the sidebar show a permanent, misleading \"(1)\" on any match ([APP-5530]).\n\nThe two are not mutually exclusive: a page can name itself in the title slot **and** have per-section subheaders inside its widgets. Privacy does exactly that — `PageType::new_uncategorized(widgets, Some(\"Privacy\"))` plus `render_sub_header` calls inside individual widgets. The rule is per heading, not per page.\n\nWorked positive example: **Scripting** (`scripting_page.rs`) is a small page done right — two focused widgets (`WarpControlCliInstallWidget`, `LocalControlModeWidget`) with their own `search_terms`, and `PageType::new_uncategorized(widgets, Some(\"Scripting\"))`. A ticket was once filed claiming Scripting needed splitting; it was canceled because the premise was wrong. A small page is not automatically a mega-widget.\n\n## Bug class 1 — the vanishing title\n\n**Symptom:** on a single-topic `Uncategorized` or `Categorized` page, typing a search term that matches one row makes the page heading disappear along with the non-matching rows, leaving an unlabeled orphan setting. (A monolith can't reach this state — see the taxonomy above.)\n\n**Cause:** the only heading was rendered inside a widget — via `build_sub_header` / `render_page_title` in that widget's `render`, or via a header-only widget that exists just to draw a title. Filtering removes the widget, and the heading goes with it.\n\nCanonical fix — commit `ddadcee` ([APP-5060], #14519), Knowledge. Before, `AIFactWidget::render` opened with:\n\n```rust\nlet header = build_sub_header(appearance, \"Knowledge\", …).finish();\nlet mut column = Flex::column().with_child(header) /* … all the Knowledge rows … */;\n```\n\nAfter, the heading moved to page chrome and the rows became focused widgets:\n\n```rust\nlet title = match subpage {\n    AISubpage::Knowledge => Some(\"Knowledge\"),\n    AISubpage::ThirdPartyCLIAgents => Some(\"Third party CLI agents\"),\n    AISubpage::WarpAgent | AISubpage::Profiles => None,\n};\nPageType::new_uncategorized(widgets, title)\n```\n\n(The `ThirdPartyCLIAgents` arm and the exhaustive `match` came from #14524; note the deliberate absence of a `_` arm, so a new subpage forces this decision.)\n\nThe same commit (#14524) deleted `CodeSubpageHeaderWidget` from the then-combined Code page — a widget whose entire job was `build_sub_header(appearance, self.title, None)` — and replaced it with a title passed through `PageType`. Those two halves are now separate pages, `code_indexing_page.rs` and `code_editor_review_page.rs`, each passing its own `PAGE_TITLE` through the slot. **A header-only widget is always this bug.** If a widget renders nothing but a title, delete it and use the title slot.\n\n## Bug class 2 — the unfilterable mega-widget\n\n**Symptom:** searching a term that should isolate one row shows every row on the page, and the sidebar match count is 1 no matter how specific the query is.\n\n**Cause:** one widget renders many unrelated settings and declares one mega `search_terms()` blob covering all of them. The widget is the filter unit, so it's all-or-nothing.\n\nBefore (`CLIAgentWidget`, pre-#14524) — one widget, one blob, seven settings:\n\n```rust\nfn search_terms(&self) -> &str {\n    \"third party cli coding agent claude codex gemini toolbar footer layout chip chips \\\n     rearrange re-arrange bar command regex auto show rich input dismiss ctrl enter submit newline\"\n}\n```\n\nAfter — one widget per setting, each with terms scoped to just that setting:\n\n```rust\nfn cli_agent_widgets() -> Vec<Box<dyn SettingsWidget<View = AISettingsPageView>>> {\n    vec![\n        Box::new(CLIAgentWidget::default()),\n        Box::new(CLIAgentAutoToggleRichInputWidget::default()),\n        Box::new(CLIAgentAutoOpenRichInputWidget::default()),\n        Box::new(CLIAgentAutoDismissRichInputWidget::default()),\n        Box::new(CLIAgentSubmitRichInputWidget::default()),\n        Box::new(CLIAgentCommandsWidget),\n        Box::new(CLIAgentToolbarLayoutWidget),\n    ]\n}\n```\n\nRules of thumb when splitting:\n\n- **One widget ≈ one setting row** (or one tightly-coupled group that always shows and hides together).\n- **Scope `search_terms` to that widget only.** Keep enough shared context that a page-level query still matches (each CLI-agent widget above keeps `\"third party cli coding agent\"`), then add the terms unique to the row.\n- **Give each row the right gating mechanism** (see \"Two ways to gate a widget\"). A row that used to sit behind an `if … { … }` inside a mega-`render` becomes its own widget: if its condition is a static feature flag or platform check, gate it with an `if` in the page's build function and never create it; if it can change at runtime, give it `should_render`. The CLI-agent rich-input rows are the runtime case — they hang off the user-toggleable footer setting — so they use `should_render` and share the predicate through a small free function (`should_render_cli_agent_rich_input(app)`) rather than duplicating the condition.\n- **Move per-row state with the row.** Each `SwitchStateHandle` / `MouseStateHandle` moves to the widget that owns its control. Never create one inline while rendering (see `gui-ui-guidelines` and the AGENTS.md note on `MouseStateHandle`).\n- **Watch the widget ids.** `widget_id()` is `std::any::type_name::<Self>()`, so splitting a widget changes ids. `settings_widget_deeplink_target` in `app/src/settings_view/mod.rs` maps stable public slugs (`warp://settings?widget=<slug>`) onto them. The CLI-agent split deliberately kept `CLIAgentWidget` as the first widget so `cli_agent_settings_widget_id()` — the target of the `cli_agents` deeplink — stayed valid. If you rename or remove a widget that backs a deeplink, re-point the accessor.\n\n## Subpages rebuild their `PageType` — reapply the filter\n\nAI subpages rebuild their `PageType` when the active subpage changes (`AISettingsPageView::set_active_subpage` / `build_page`). A fresh `PageType` starts with **every** widget in its filter, so a live search query is silently dropped unless it's reapplied. `SettingsView::reapply_search_filter_to_active_subpage` in `app/src/settings_view/mod.rs` exists for exactly this ([APP-4922], #14116). If you add a code path that rebuilds a subpage's page while search may be active, call it.\nThe Code umbrella no longer works this way: `CodeIndexing` and `EditorAndCodeReview` are separate pages that each own their widgets outright, so nothing rebuilds and there is no filter to reapply. Prefer that shape for new umbrella children — a subpage that owns its own page needs none of this machinery.\n\n## Known anti-examples still in the tree\n\nUseful to read, not to copy:\n\n- **Warpify** (`warpify_page.rs`) — `PageType::new_categorized(categories, None)` where the first category is `Category::new(\"\", vec![Box::new(TitleWidget::default())])` and `TitleWidget::render` calls `render_page_title(\"Warpify\", …)`. Single-topic page, title inside a widget: bug class 1.\n- **Warp Drive** (`warp_drive_page.rs`) — `PageType::new_uncategorized([WarpDriveHeaderWidget, WarpDriveToggleWidget], None)` with no title slot at all. `WarpDriveHeaderWidget` is a conditional sign-up banner, so a signed-in user sees only a bare toggle row and no page heading.\n\n## How to verify a settings-page change\n\nVerification is cheap here; do both.\n\n1. **Unit-test the filter.** `app/src/settings_view/mod_tests.rs` has the harness — `StubWidget`, `stub_widgets_page`, and `visible_widget_count` (which reads `PageType::get_filtered`). Assert that a term unique to one widget yields exactly one visible widget, and that clearing the query restores all of them. Follow the `${filename}_tests.rs` convention from AGENTS.md; run with `cargo nextest run -p warp settings_view`.\n2. **Exercise the real search.** Open Settings, go to the page, and check three things:\n   - a term matching exactly one row leaves **only** that row,\n   - the **page title is still visible** while that filter is active — expect this on an `Uncategorized` / `Categorized` page; a monolith correctly disappears whole instead,\n   - clearing the search restores the full page.\n\n## Anti-patterns\n\n```rust\n// A widget whose whole job is to draw the page heading. Delete it and pass the\n// title through PageType instead.\nstruct FooSubpageHeaderWidget { title: &'static str }\n\n// A single-topic page with no page title, drawing its heading inside a widget.\n// Any non-matching search term erases the heading.\nPageType::new_uncategorized(widgets, None)   // and build_sub_header(\"Foo\", …) in a widget\n\n// One widget, one mega search_terms blob, many unrelated rows: nothing can be\n// filtered or attributed individually.\nfn search_terms(&self) -> &str { \"foo bar baz qux quux corge grault\" }\n\n// Conditional rows hidden inside render() while search_terms still advertises\n// them — the page matches a query and then renders nothing for it.\nfn render(&self, …) { if flag_enabled { /* the only rows */ } }\n// Gate the widget instead: an `if` at page-build time for a static flag, or\n// should_render() when the condition can change at runtime.\n```\n",".agents/skills/logging-and-error-reporting/SKILL.md":"# logging-and-error-reporting\n\nPath: `.agents/skills/logging-and-error-reporting/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/logging-and-error-reporting/SKILL.md)",".agents/skills/promote-feature/SKILL.md":"# promote-feature\n\nPath: `.agents/skills/promote-feature/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/promote-feature/SKILL.md)",".agents/skills/remove-feature-flag/SKILL.md":"# remove-feature-flag\n\nPath: `.agents/skills/remove-feature-flag/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/remove-feature-flag/SKILL.md)",".agents/skills/review-pr-local/SKILL.md":"# review-pr-local\n\nPath: `.agents/skills/review-pr-local/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/review-pr-local/SKILL.md)",".agents/skills/rust-unit-tests/SKILL.md":"# rust-unit-tests\n\nPath: `.agents/skills/rust-unit-tests/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/rust-unit-tests/SKILL.md)",".agents/skills/triage-issue-local/SKILL.md":"# triage-issue-local\n\nPath: `.agents/skills/triage-issue-local/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/triage-issue-local/SKILL.md)",".agents/skills/tui-testing/SKILL.md":"# tui-testing\n\nPath: `.agents/skills/tui-testing/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-testing/SKILL.md)",".agents/skills/tui-ui-guidelines/SKILL.md":"# tui-ui-guidelines\n\nPath: `.agents/skills/tui-ui-guidelines/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-ui-guidelines/SKILL.md)",".agents/skills/tui-verify-change/SKILL.md":"# tui-verify-change\n\nPath: `.agents/skills/tui-verify-change/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-verify-change/SKILL.md)",".warp/skills/gui-integration-test-video/SKILL.md":"# gui-integration-test-video\n\nPath: `.warp/skills/gui-integration-test-video/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.warp/skills/gui-integration-test-video/SKILL.md)","resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md":"# oz-child-agent-orchestration\n\nPath: `resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md)","resources/bundled/harness_skills/oz-finish-task/SKILL.md":"# oz-finish-task\n\nPath: `resources/bundled/harness_skills/oz-finish-task/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-finish-task/SKILL.md)","resources/bundled/harness_skills/oz-notify-user/SKILL.md":"# oz-notify-user\n\nPath: `resources/bundled/harness_skills/oz-notify-user/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-notify-user/SKILL.md)","resources/bundled/harness_skills/oz-report-pr/SKILL.md":"# oz-report-pr\n\nPath: `resources/bundled/harness_skills/oz-report-pr/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-report-pr/SKILL.md)","resources/bundled/harness_skills/oz-upload-file/SKILL.md":"# oz-upload-file\n\nPath: `resources/bundled/harness_skills/oz-upload-file/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-upload-file/SKILL.md)","resources/bundled/skills/add-mcp-server/SKILL.md":"# add-mcp-server\n\nPath: `resources/bundled/skills/add-mcp-server/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/add-mcp-server/SKILL.md)","resources/bundled/skills/change-keybinding/SKILL.md":"# change-keybinding\n\nPath: `resources/bundled/skills/change-keybinding/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/change-keybinding/SKILL.md)","resources/bundled/skills/claude-api/SKILL.md":"# claude-api\n\nPath: `resources/bundled/skills/claude-api/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/SKILL.md)","resources/bundled/skills/create-skill/SKILL.md":"# create-skill\n\nPath: `resources/bundled/skills/create-skill/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/SKILL.md)","resources/bundled/skills/create-tab-config/SKILL.md":"# create-tab-config\n\nPath: `resources/bundled/skills/create-tab-config/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-tab-config/SKILL.md)","resources/bundled/skills/factory-deferred-repositories/SKILL.md":"# factory-deferred-repositories\n\nPath: `resources/bundled/skills/factory-deferred-repositories/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-deferred-repositories/SKILL.md)","resources/bundled/skills/factory-files/SKILL.md":"# factory-files\n\nPath: `resources/bundled/skills/factory-files/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/SKILL.md)","resources/bundled/skills/factory-mcp/SKILL.md":"# factory-mcp\n\nPath: `resources/bundled/skills/factory-mcp/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-mcp/SKILL.md)","resources/bundled/skills/modify-settings/SKILL.md":"# modify-settings\n\nPath: `resources/bundled/skills/modify-settings/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/modify-settings/SKILL.md)","resources/bundled/skills/oz-platform/SKILL.md":"# oz-platform\n\nPath: `resources/bundled/skills/oz-platform/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/oz-platform/SKILL.md)","resources/bundled/skills/pr-comments/SKILL.md":"# pr-comments\n\nPath: `resources/bundled/skills/pr-comments/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/pr-comments/SKILL.md)","resources/bundled/skills/tab-configs/SKILL.md":"# tab-configs\n\nPath: `resources/bundled/skills/tab-configs/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tab-configs/SKILL.md)","resources/bundled/skills/tui-migrate-setup/SKILL.md":"# tui-migrate-setup\n\nPath: `resources/bundled/skills/tui-migrate-setup/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/SKILL.md)","resources/bundled/skills/update-tab-config/SKILL.md":"# update-tab-config\n\nPath: `resources/bundled/skills/update-tab-config/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/update-tab-config/SKILL.md)","resources/bundled/skills/warpctrl/SKILL.md":"# warpctrl\n\nPath: `resources/bundled/skills/warpctrl/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/warpctrl/SKILL.md)","resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md":"# test-warp-ui\n\nPath: `resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md)","resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md":"# triage-vulnerabilities\n\nPath: `resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md)","resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md":"# verify-ui-change-in-cloud\n\nPath: `resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md)","resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md":"# edit-figma-design\n\nPath: `resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md)","resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md":"# figma-code-connect-components\n\nPath: `resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md)","resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md":"# figma-create-design-system-rules\n\nPath: `resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md)","resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md":"# figma-create-new-file\n\nPath: `resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md)","resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md":"# figma-generate-design\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md)","resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md":"# figma-generate-library\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md)","resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md":"# figma-implement-design\n\nPath: `resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md)","resources/bundled/mcp_skills/figma/figma-use/SKILL.md":"# figma-use\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/SKILL.md)","AGENTS.md":"# AI Agent Protocol & Instructions\n\nPath: `AGENTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/AGENTS.md)","agents/specs/APP-4878 - fix TUI copy to use native clipboard.md":"# Subagent: app-4878 - fix tui copy to use native clipboard\n\nPath: `agents/specs/APP-4878 - fix TUI copy to use native clipboard.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4878 - fix TUI copy to use native clipboard.md)","agents/specs/APP-4880 - TUI view-logs slash command.md":"# Subagent: app-4880 - tui view-logs slash command\n\nPath: `agents/specs/APP-4880 - TUI view-logs slash command.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4880 - TUI view-logs slash command.md)","agents/specs/APP-4881-tui-starting-shell-indicator.md":"# Subagent: app-4881-tui-starting-shell-indicator\n\nPath: `agents/specs/APP-4881-tui-starting-shell-indicator.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4881-tui-starting-shell-indicator.md)","agents/specs/APP-4882 - fix misleading oversized-file read error.md":"# Subagent: app-4882 - fix misleading oversized-file read error\n\nPath: `agents/specs/APP-4882 - fix misleading oversized-file read error.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4882 - fix misleading oversized-file read error.md)","agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md":"# Subagent: app-4889 - fixed-height expandable toasts with 3-active cap\n\nPath: `agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md)","agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md":"# Subagent: app-4896 - forward ctrl+shift+c to pty in fullscreen tui\n\nPath: `agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md)","agents/specs/APP-4901-add-fast-forward-slash-command.md":"# Subagent: app-4901-add-fast-forward-slash-command\n\nPath: `agents/specs/APP-4901-add-fast-forward-slash-command.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4901-add-fast-forward-slash-command.md)","agents/specs/APP-4902 - window-scoped background focus suppression.md":"# Subagent: app-4902 - window-scoped background focus suppression\n\nPath: `agents/specs/APP-4902 - window-scoped background focus suppression.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4902 - window-scoped background focus suppression.md)","agents/specs/APP-4903 improve TUI tab bar keybindings.md":"# Subagent: app-4903 improve tui tab bar keybindings\n\nPath: `agents/specs/APP-4903 improve TUI tab bar keybindings.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4903 improve TUI tab bar keybindings.md)","agents/specs/APP-4907 - TUI input focus during agent drafting.md":"# Subagent: app-4907 - tui input focus during agent drafting\n\nPath: `agents/specs/APP-4907 - TUI input focus during agent drafting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4907 - TUI input focus during agent drafting.md)","agents/specs/APP-4913-tui-input-prompt-prefix.md":"# Subagent: app-4913-tui-input-prompt-prefix\n\nPath: `agents/specs/APP-4913-tui-input-prompt-prefix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4913-tui-input-prompt-prefix.md)","agents/specs/CODE-1884-voice-input-slash-command.md":"# Subagent: code-1884-voice-input-slash-command\n\nPath: `agents/specs/CODE-1884-voice-input-slash-command.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1884-voice-input-slash-command.md)","agents/specs/CODE-1890-status-line-v2.md":"# Subagent: code-1890-status-line-v2\n\nPath: `agents/specs/CODE-1890-status-line-v2.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1890-status-line-v2.md)","agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md":"# Subagent: code-1895-tui-lrc-typeahead-wiring\n\nPath: `agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md)","agents/specs/CODE-1902 change TUI log storage.md":"# Subagent: code-1902 change tui log storage\n\nPath: `agents/specs/CODE-1902 change TUI log storage.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1902 change TUI log storage.md)","agents/specs/CODE-1908 TUI default profile uses AgentDecides.md":"# Subagent: code-1908 tui default profile uses agentdecides\n\nPath: `agents/specs/CODE-1908 TUI default profile uses AgentDecides.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1908 TUI default profile uses AgentDecides.md)","agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md":"# Subagent: quality-1112-smart-variable-speedcut-for-computer-use-recordings\n\nPath: `agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md)","agents/specs/QUALITY-1169-richer-click-drag-annotations.md":"# Subagent: quality-1169-richer-click-drag-annotations\n\nPath: `agents/specs/QUALITY-1169-richer-click-drag-annotations.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/QUALITY-1169-richer-click-drag-annotations.md)","agents/specs/REMOTE-2160 - macOS video recording for computer use.md":"# Subagent: remote-2160 - macos video recording for computer use\n\nPath: `agents/specs/REMOTE-2160 - macOS video recording for computer use.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/REMOTE-2160 - macOS video recording for computer use.md)",".agents/skills/changelog-draft/examples/changelog-draft-example.md":"# Examples Skill\n\nPath: `.agents/skills/changelog-draft/examples/changelog-draft-example.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/changelog-draft/examples/changelog-draft-example.md)","resources/bundled/skills/claude-api/csharp/claude-api.md":"# Csharp Skill\n\nPath: `resources/bundled/skills/claude-api/csharp/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/csharp/claude-api.md)","resources/bundled/skills/claude-api/curl/examples.md":"# Curl Skill\n\nPath: `resources/bundled/skills/claude-api/curl/examples.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/curl/examples.md)","resources/bundled/skills/claude-api/curl/managed-agents.md":"# Curl Skill\n\nPath: `resources/bundled/skills/claude-api/curl/managed-agents.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/curl/managed-agents.md)","resources/bundled/skills/claude-api/go/claude-api.md":"# Go Skill\n\nPath: `resources/bundled/skills/claude-api/go/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/go/claude-api.md)","resources/bundled/skills/claude-api/java/claude-api.md":"# Java Skill\n\nPath: `resources/bundled/skills/claude-api/java/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/java/claude-api.md)","resources/bundled/skills/claude-api/php/claude-api.md":"# Php Skill\n\nPath: `resources/bundled/skills/claude-api/php/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/php/claude-api.md)","resources/bundled/skills/claude-api/ruby/claude-api.md":"# Ruby Skill\n\nPath: `resources/bundled/skills/claude-api/ruby/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/ruby/claude-api.md)","resources/bundled/skills/claude-api/shared/agent-design.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/agent-design.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/agent-design.md)","resources/bundled/skills/claude-api/shared/error-codes.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/error-codes.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/error-codes.md)","resources/bundled/skills/claude-api/shared/live-sources.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/live-sources.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/live-sources.md)","resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md)","resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md)","resources/bundled/skills/claude-api/shared/managed-agents-core.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-core.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-core.md)","resources/bundled/skills/claude-api/shared/managed-agents-environments.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-environments.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-environments.md)","resources/bundled/skills/claude-api/shared/managed-agents-events.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-events.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-events.md)","resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md)","resources/bundled/skills/claude-api/shared/managed-agents-overview.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-overview.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-overview.md)","resources/bundled/skills/claude-api/shared/managed-agents-tools.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-tools.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-tools.md)","resources/bundled/skills/claude-api/shared/model-migration.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/model-migration.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/model-migration.md)","resources/bundled/skills/claude-api/shared/models.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/models.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/models.md)","resources/bundled/skills/claude-api/shared/prompt-caching.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/prompt-caching.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/prompt-caching.md)","resources/bundled/skills/claude-api/shared/tool-use-concepts.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/tool-use-concepts.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/tool-use-concepts.md)","resources/bundled/skills/create-skill/agents/analyzer.md":"# Subagent: analyzer\n\nPath: `resources/bundled/skills/create-skill/agents/analyzer.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/analyzer.md)","resources/bundled/skills/create-skill/agents/comparator.md":"# Subagent: comparator\n\nPath: `resources/bundled/skills/create-skill/agents/comparator.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/comparator.md)","resources/bundled/skills/create-skill/agents/grader.md":"# Subagent: grader\n\nPath: `resources/bundled/skills/create-skill/agents/grader.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/grader.md)","resources/bundled/skills/create-skill/references/schemas.md":"# References Skill\n\nPath: `resources/bundled/skills/create-skill/references/schemas.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/references/schemas.md)","resources/bundled/skills/factory-files/references/examples.md":"# References Skill\n\nPath: `resources/bundled/skills/factory-files/references/examples.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/examples.md)","resources/bundled/skills/factory-files/references/scorers.md":"# References Skill\n\nPath: `resources/bundled/skills/factory-files/references/scorers.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/scorers.md)","resources/bundled/skills/factory-files/references/validation.md":"# References Skill\n\nPath: `resources/bundled/skills/factory-files/references/validation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/validation.md)","resources/bundled/skills/oz-platform/references/third-party-clis.md":"# References Skill\n\nPath: `resources/bundled/skills/oz-platform/references/third-party-clis.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/oz-platform/references/third-party-clis.md)","resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md":"# References Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md)","resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md)","resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md)","resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md)","resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md)","resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md)","resources/bundled/skills/claude-api/go/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/go/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/go/managed-agents/README.md)","resources/bundled/skills/claude-api/java/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/java/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/java/managed-agents/README.md)","resources/bundled/skills/claude-api/php/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/php/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/php/managed-agents/README.md)","resources/bundled/skills/claude-api/python/claude-api/README.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/README.md)","resources/bundled/skills/claude-api/python/claude-api/batches.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/batches.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/batches.md)","resources/bundled/skills/claude-api/python/claude-api/files-api.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/files-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/files-api.md)","resources/bundled/skills/claude-api/python/claude-api/streaming.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/streaming.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/streaming.md)","resources/bundled/skills/claude-api/python/claude-api/tool-use.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/tool-use.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/tool-use.md)","resources/bundled/skills/claude-api/python/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/python/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/managed-agents/README.md)","resources/bundled/skills/claude-api/ruby/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/ruby/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/ruby/managed-agents/README.md)","resources/bundled/skills/claude-api/typescript/claude-api/README.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/README.md)","resources/bundled/skills/claude-api/typescript/claude-api/batches.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/batches.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/batches.md)","resources/bundled/skills/claude-api/typescript/claude-api/files-api.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/files-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/files-api.md)","resources/bundled/skills/claude-api/typescript/claude-api/streaming.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/streaming.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/streaming.md)","resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md)","resources/bundled/skills/claude-api/typescript/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/managed-agents/README.md)","resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json":"# Fixtures Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json)","resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json":"# Fixtures Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json)","resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json":"# Fixtures Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md)"},"files":{".agents/skills/changelog-draft/SKILL.md":"---\nname: changelog-draft\ndescription: Generate a reviewable changelog draft from PRs merged in a release range. Extracts explicit CHANGELOG markers, classifies unmarked PRs, adds external contributor attribution, and outputs markdown + JSON artifacts. Does NOT mutate channel_versions.json.\n---\n\n# Changelog Draft Generator\n\n## Inputs\n\n| Parameter | Required | Description |\n|-----------|----------|-------------|\n| `channel` | yes | Release channel: `stable`, `preview`, or `dev` |\n| `release_tag` | yes | The release tag to generate the changelog for (e.g. `v0.2026.05.06.09.12.stable_00`) |\n| `output_dir` | no | Directory to write output files. Defaults to `$RUNNER_TEMP` or `/tmp/changelog-draft` |\n| `attribution` | no | Attribution mode: `external-only` (default), `all`, or `none` |\n\n## Workflow\n\n### Step 1 — Determine the release range\n\nInfer the previous release **cut** for comparison. Release tags follow the pattern `v0.YYYY.MM.DD.HH.MM.<channel>_NN`, where `_NN` is the RC/hotfix number within that release cut. Multiple tags can share the same date prefix (e.g. `_00`, `_01`, `_02` are all part of one release cut).\n\nThe base tag must be the `_00` tag of the **previous** release cut (i.e. a different date), not just the previous tag. For example, if generating a changelog for `v0.2026.04.29.08.57.stable_01`, the base should be `v0.2026.04.22.08.57.stable_00`, not `v0.2026.04.29.08.57.stable_00`.\n\n```bash\n# 1. Extract the date prefix from the release_tag (everything before _NN)\nrelease_date_prefix=\"${release_tag%_*}\"\n\n# 2. List all _00 tags for the channel (these are release cut points), sorted descending\ngit tag --list \"v0.*.${channel}_00\" --sort=-version:refname\n\n# 3. Pick the first _00 tag whose date prefix differs from release_date_prefix\n```\n\nRecord the range as `previous_cut_tag..release_tag`.\n\n### Step 2 — Fetch PR data\n\nRun the `fetch_prs.py` script to collect all public-release PRs merged in the release range and extract explicit changelog markers. Pass the repository that the workflow checked out, not necessarily the public repository. Release workflows run from `warpdotdev/warp-internal`, and the script deterministically resolves `warp-repo-sync[bot]` PRs back to their original public `warpdotdev/warp` PR metadata before emitting JSON. When running from `warpdotdev/warp-internal`, the script intentionally omits PRs that were not authored by the repo-sync bot, because those are private internal changes that must not be exposed to the changelog agent or generated artifacts.\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/fetch_prs.py \\\n  --repo \"${GITHUB_REPOSITORY:-warpdotdev/warp}\" \\\n  --base-ref <previous_tag> \\\n  --head-ref <release_tag>\n```\n\nThe script outputs JSON to stdout with this structure:\n```json\n{\n  \"range\": { \"base\": \"<previous_tag>\", \"head\": \"<release_tag>\" },\n  \"prs\": [\n    {\n      \"number\": 1234,\n      \"url\": \"https://github.com/warpdotdev/warp/pull/1234\",\n      \"title\": \"...\",\n      \"commit_subject\": \"... (#1234)\",\n      \"author\": \"username\",\n      \"body\": \"...\",\n      \"labels\": [\"...\"],\n      \"merged_at\": \"2026-05-01T...\",\n      \"explicit_entries\": [\n        { \"category\": \"NEW-FEATURE\", \"text\": \"Added dark mode\" }\n      ],\n      \"linked_issues\": [5678],\n      \"changed_files\": [\"app/src/ai/agent.rs\", \"crates/warp_features/src/lib.rs\"],\n      \"source_repo\": \"warpdotdev/warp\",\n      \"internal_pr\": {\n        \"number\": 25712,\n        \"url\": \"https://github.com/warpdotdev/warp-internal/pull/25712\",\n        \"author\": \"warp-repo-sync[bot]\",\n        \"title\": \"...\",\n        \"repo\": \"warpdotdev/warp-internal\"\n      }\n    }\n  ]\n}\n```\n\nUse the top-level `number`, `url`, `commit_subject`, `author`, `body`, `labels`, `changed_files`, and `source_repo` fields as the source of truth. `internal_pr` is audit-only and must never be used for contributor attribution or user-facing changelog links. If `url` is empty, omit the PR link from user-facing markdown rather than synthesizing one.\n\n### Step 3 — Classify contributors\n\nRun the `classify_contributors.py` script with the unique author logins from Step 2:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/classify_contributors.py \\\n  --org warpdotdev \\\n  --authors author1,author2,author3\n```\n\nOutput JSON:\n```json\n{\n  \"internal\": [\"author1\"],\n  \"external\": [\"author3\"],\n  \"bot\": [\"author2\"],\n  \"unknown\": []\n}\n```\n\n### Step 4 — Extract feature flags\n\nRun the `extract_feature_flags.py` script to get the current flag gate lists:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/extract_feature_flags.py \\\n  --file crates/warp_features/src/lib.rs\n```\n\nOutput JSON:\n```json\n{\n  \"release_flags\": [\"Autoupdate\", \"Changelog\", ...],\n  \"preview_flags\": [\"Orchestration\", ...],\n  \"dogfood_flags\": [\"LogExpensiveFramesInSentry\", ...]\n}\n```\n\n### Step 5 — Fetch issue reporters\n\nCollect all unique `linked_issues` from Step 2 and fetch the original reporter for each. Pass `--org` so the script checks org membership and filters out internal reporters automatically:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/fetch_issue_reporters.py \\\n  --repo warpdotdev/warp \\\n  --org warpdotdev \\\n  --issues 5678,9012\n```\n\nOutput JSON (only external reporters are included):\n```json\n{\n  \"issue_reporters\": [\n    {\n      \"issue_number\": 5678,\n      \"title\": \"Crash when opening large file\",\n      \"reporter\": \"community-user\",\n      \"reporter_url\": \"https://github.com/community-user\",\n      \"url\": \"https://github.com/warpdotdev/warp/issues/5678\"\n    }\n  ]\n}\n```\n\nThe `--org` flag checks each reporter's org membership via the GitHub API, filtering out internal members so they aren't misattributed as external community reporters. These reporters will be credited in the \"Community\" section of the changelog.\nWhenever the markdown draft credits a PR author, contributor, or issue reporter, render the username as a GitHub profile link such as `[@username](https://github.com/username)`.\n\n### Step 6 — Classify unmarked PRs\nDetermine TUI impact independently from the regular changelog category for every PR. Explicit `CHANGELOG-TUI` and `CHANGELOG-OZ` markers are authoritative entries and may coexist with New Feature, Improvement, or Bug Fix entries. When a PR has an explicit TUI entry, preserve its other explicit entries. Otherwise, if `commit_subject` contains `TUI` as a standalone case-insensitive token, route any explicit New Feature, Improvement, or Bug Fix text to `TUI` instead of its regular category. This keeps clearly TUI-labeled changes out of the desktop changelog while allowing explicitly marked shared changes to appear on both surfaces.\n\nFor each PR that has no explicit `CHANGELOG-*` entries, decide whether to include it and under which category.\n\nFollow the classification guidance in `.agents/skills/classify-changelog-pr/SKILL.md`.\n\nFor each unmarked PR, produce a classification:\n```json\n{\n  \"pr_number\": 1234,\n  \"include\": true,\n  \"category\": \"IMPROVEMENT\",\n  \"text\": \"Proposed changelog line\",\n  \"impacts_tui\": true,\n  \"confidence\": \"high\",\n  \"rationale\": \"...\",\n  \"feature_flag\": null,\n  \"needs_review\": false\n}\n```\n\n**Key rules:**\n- PRs that only touch CI, tests, docs, or internal tooling → `include: false`\n- PRs behind dogfood-only feature flags → `include: false` for stable channel\n- PRs behind preview flags → `include: false` for stable, `include: true` for preview\n- Set `impacts_tui: true` when Warp Agent CLI users observe the change, including shared Agent capabilities such as tool-call or edit-file behavior\n- When in doubt, set `needs_review: true` and `confidence: \"low\"`\n- Bot PRs (dependabot, renovate, etc.) → `include: false`\n\n**Feature-flag detection:** Use the `changed_files` list from Step 2 to check if any PR touches `crates/warp_features/src/lib.rs` or references a `FeatureFlag` variant in its title/body. Cross-reference with the flag lists from Step 4 to determine channel visibility.\n\n**Unknown contributors:** Authors in the `unknown` bucket (org membership check failed due to auth) should be treated conservatively — do not attribute them as external. Note them in the output for manual verification.\n\n### Step 7 — Assemble the draft\n\nCombine explicit entries (Step 2) and inferred entries (Step 6) into the final report. Group by category in this order:\n\n1. `NEW-FEATURE` — New Features\n2. `IMPROVEMENT` — Improvements\n3. `BUG-FIX` — Bug Fixes\n4. `TUI` — TUI Updates\n5. `OZ` — Oz Updates\n\nPRs marked with `CHANGELOG-NONE` are explicitly opted out and must never appear in the changelog markdown.\n\nPreserve every explicit entry independently. For an inferred regular entry with `impacts_tui: true`, also create a `TUI` entry with the same user-facing text and PR metadata. Do not duplicate an inferred entry whose category is already `TUI`.\n\nWhen creating entries, copy `pr_number`, `url`, `author`, `source_repo`, and `internal_pr` from the normalized PR record. The release JSON converter uses `url` directly; do not invent public PR URLs from PR numbers.\n\n### Step 8 — Write output files\n\nWrite two files to `output_dir`:\n\n**`changelog-draft.md`** — Human-reviewable markdown, ready for Slack/Notion:\n\n```markdown\n# Changelog Draft\n**Channel:** stable\n**Range:** v0.2026.05.01... → v0.2026.05.06...\n**Generated:** 2026-05-06T15:00:00Z\n\n## New Features\n- Added dark mode ([#1234](https://github.com/warpdotdev/warp/pull/1234)) — [@external-contributor](https://github.com/external-contributor) ✨\n\n## Improvements\n- Faster tab switching ([#1235](https://github.com/warpdotdev/warp/pull/1235))\n\n## Bug Fixes\n- Fixed crash on startup ([#1236](https://github.com/warpdotdev/warp/pull/1236))\n\n## TUI Updates\n- Added inline command menus to Warp Agent CLI ([#1238](https://github.com/warpdotdev/warp/pull/1238))\n\n## Oz Updates\n- Improved agent memory ([#1237](https://github.com/warpdotdev/warp/pull/1237))\n\n## Community\n### Contributors\n- [@contributor1](https://github.com/contributor1) — [#1234](https://github.com/warpdotdev/warp/pull/1234)  ✨\n\n### Issue Reporters\nThanks to the community members who reported issues fixed in this release:\n- [@reporter1](https://github.com/reporter1) — [#5678](https://github.com/warpdotdev/warp/issues/5678) \"Crash when opening large file\"\n```\n\nThe markdown draft must **not** include \"Needs Review\" or \"Skipped PRs\" sections — those are internal details that belong only in the JSON audit artifact.\n\n**`changelog-draft.json`** — Machine-readable audit artifact (internal only):\n\n```json\n{\n  \"channel\": \"stable\",\n  \"range\": { \"base\": \"v0...\", \"head\": \"v0...\" },\n  \"generated_at\": \"2026-05-06T15:00:00Z\",\n  \"entries\": [\n    {\n      \"pr_number\": 1234,\n      \"url\": \"https://github.com/warpdotdev/warp/pull/1234\",\n      \"category\": \"NEW-FEATURE\",\n      \"text\": \"Added dark mode\",\n      \"source\": \"explicit\",\n      \"author\": \"external-contributor\",\n      \"is_external\": true,\n      \"confidence\": \"high\",\n      \"rationale\": null,\n      \"feature_flag\": null,\n      \"source_repo\": \"warpdotdev/warp\",\n      \"internal_pr\": null\n    }\n  ],\n  \"skipped\": [...],\n  \"needs_review\": [...],\n  \"issue_reporters\": [...]\n}\n```\n\nThe JSON artifact retains `skipped`, `needs_review`, and `issue_reporters` for audit purposes — every PR in the range must appear in either `entries`, `skipped`, or `needs_review`.\n\n### Step 9 — Generate release-pipeline JSON\n\nRun the conversion script to deterministically produce `changelog-release.json` from the audit artifact:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/convert_to_release_json.py \\\n  --input <output_dir>/changelog-draft.json \\\n  --output <output_dir>/changelog-release.json\n```\n\nThis produces the flat JSON structure consumed by the `create_release` workflow for Slack and the in-app \"What's New\" dialog. Do **not** generate this file manually — always use the script so the output is deterministic and consistent.\n\n## Constraints\n\n- **Never** write to `channel_versions.json` or any production config file.\n- **Never** push commits, create branches, or open PRs.\n- All output goes to `output_dir` only.\n- The markdown draft should be copy-pasteable into Slack or Notion for review.\n- Keep the JSON artifact complete enough for audit: every PR in the range should appear in either `entries`, `skipped`, or `needs_review`.\n\n## Validation\n\nAfter generating output, verify:\n1. Every PR in the range is accounted for (entries + skipped + needs_review = total PRs).\n2. Explicit marker entries match what `fetch_prs.py` extracted (no dropped markers).\n3. No duplicate PR numbers across sections.\n4. The markdown renders cleanly (no broken links or formatting).\n",".agents/skills/dedupe-issue-local/SKILL.md":"---\nname: dedupe-issue-local\nspecializes: dedupe-issue\nspecializes_source: warpdotdev/oz-for-oss:.agents/skills/dedupe-issue\ndescription: Repo-specific dedupe guidance for warp. Only the categories declared overridable by the core dedupe-issue skill may be specialized here.\n---\n\n# Repo-specific dedupe guidance for `warp`\n\n## Prerequisite: install the parent skill\n\nThis skill specializes the core `dedupe-issue` skill (named in the `specializes` frontmatter field) and is not functional on its own. Before applying its guidance, confirm the parent skill is installed and resolvable at `.agents/skills/dedupe-issue/SKILL.md`. If it is missing, install it first by copying the skill directory from the source declared in the `specializes_source` frontmatter field (`warpdotdev/oz-for-oss:.agents/skills/dedupe-issue`). Then continue with the guidance below.\n\nThis file is a companion to the core `dedupe-issue` skill. It does not\nredefine the duplicate-detection algorithm, the similarity thresholds,\nor the output contract. It only specializes the override categories the\ncore skill marks as overridable.\n\n## Repo-specific normalizations\n\n- Strip low-signal title prefixes such as `Bug:`, `Feature:`, `Request:`, `[Bug]`, `[Feature]`, `Warp:`, and platform tags like `[macOS]`, `[Linux]`, or `[Windows]` before comparing titles.\n- Treat app channel/version, OS version, and shell name as supporting evidence, not as duplicate blockers, when the core symptom and reproduction path are otherwise the same.\n- Do not collapse distinct Warp surfaces just because they share a word like \"agent\", \"terminal\", \"MCP\", \"settings\", \"search\", or \"sync\". Require overlap in the actual failing behavior or requested capability.\n- For terminal issues, compare shell/session context, command output behavior, prompt rendering, input behavior, and remote/tmux involvement before treating two reports as duplicates.\n- For agent or MCP issues, compare the trigger path, local vs cloud execution, MCP server/tool, visible error, and expected workflow before treating two reports as duplicates.\n- For UI/rendering issues, compare the affected surface and visible symptom. Similar screenshots or recordings are strong duplicate evidence when the title is vague.\n\n## Known-duplicate clusters\n\nNo known-duplicate clusters have been captured for this repository yet. The weekly `update-dedupe` loop will propose additions here over time when maintainers repeatedly close issues as duplicates of the same canonical thread.\n",".agents/skills/add-feature-flag/SKILL.md":"---\nname: add-feature-flag\ndescription: Add a new feature flag to gate code changes in the Warp codebase.\n---\n\n# add-feature-flag\n\nAdd a new feature flag to gate code changes in the Warp codebase.\n\n## Overview\n\nFeature flags in Warp are compile-time flags that allow features to be selectively enabled for different channels (e.g.: Dev, Stable). They use a small runtime plumbing layer that checks if a flag is enabled.\n\n## TUI note\nThe `FeatureFlag` enum (`warp_core/src/features.rs`) and the runtime `FeatureFlag::X.is_enabled()` check are SHARED by both front-ends: the GUI desktop app (`app/`) and the headless TUI (`crates/warp_tui`). The Cargo-feature steps in this skill (`app/Cargo.toml`, `app/src/lib.rs`) are GUI-app-specific. Prefer runtime `is_enabled()` checks so a flag works in both front-ends with no per-binary wiring. Only if you truly need a compile-time Cargo feature in the TUI, wire it into `crates/warp_tui/Cargo.toml` as well. Test the TUI with `./script/run-tui`.\n\n## Steps\n\n### 1. Add to Cargo.toml\nAdd the feature to `app/Cargo.toml` under the `[features]` section, but **NOT** under the `default` nested stanza:\n\n```toml\n[features]\nyour_feature_name = []\n```\n\n### 2. Add to FeatureFlag enum\nAdd a new variant to the `FeatureFlag` enum in `warp_core/src/features.rs`:\n\n```rust\n#[derive(Sequence)]\npub enum FeatureFlag {\n    YourFeatureName,\n}\n```\n\n### 3. Add conditional compilation directive\nAdd the feature to `app/src/lib.rs` with a corresponding `#[cfg(feature = \"...\")]` attribute to ensure it's only included when enabled:\n\n```rust\n#[cfg(feature = \"your_feature_name\")]\nYourFeatureName,\n```\n\n### 4. Gate code with runtime checks\nIn your code, use the runtime check to conditionally execute feature-gated code:\n\n```rust\nif FeatureFlag::YourFeatureName.is_enabled() {\n    // feature-gated behavior\n}\n```\n\n### 5. (Optional) Enable for dogfood builds\nTo enable the feature by default for Dev/dogfood builds, add it to the `DOGFOOD_FLAGS` array in `features.rs`:\n\n```rust\npub const DOGFOOD_FLAGS: &[FeatureFlag] = &[\n    FeatureFlag::YourFeatureName,\n];\n```\n\n### 6. Running with feature flags\nTo test locally with the feature enabled:\n\n```bash\ncargo run --features your_feature_name\n\n# Multiple features:\ncargo run --features your_feature_name,another_feature\n```\n\n## Keybindings with Feature Flags\n\nIf adding an `EditableBinding` or `FixedBinding` that's part of a gated feature, include an enabled predicate that checks the feature flag. This prevents the keybinding from appearing in keyboard settings when the feature is disabled.\n\nExample:\n```rust\nEditableBinding::new(\n    \"action:name\",\n    \"Action description\",\n    YourAction::Variant\n)\n.with_enabled(|| FeatureFlag::YourFeatureName.is_enabled())\n.with_key_binding(\"cmdorctrl-key\")\n```\n\n## Rolling Out to Stable\n\nWhen ready to enable the feature for all Warp Stable users, add it to the `default` array in `app/Cargo.toml`:\n\n```toml\n[features]\ndefault = [\n    \"your_feature_name\",\n    # other default features...\n]\n```\n\n## Best Practices\n\n- **Prefer runtime checks over cfg directives**: Use `FeatureFlag::YourFeatureName.is_enabled()` instead of `#[cfg(...)]` when possible, so flags can be toggled without recompilation and are easier to clean up later\n- Use `#[cfg(...)]` only when code cannot compile without the flag (e.g., platform-specific code or missing dependencies)\n- Keep flags high-level and product-focused rather than per-call-site\n- Remove flags and dead branches after launch has stabilized\n",".agents/skills/add-telemetry/SKILL.md":"---\nname: add-telemetry\ndescription: Add telemetry events to track user behavior or system events in the Warp codebase. Use when instrumenting new features, debugging issues, or measuring product metrics.\n---\n\n# add-telemetry\n\n## Overview\n\nWarp uses a trait-based telemetry system where feature-specific enums implement the `TelemetryEvent` trait. This approach keeps telemetry events organized by domain rather than in one giant enum.\n\n**Important**: Before implementing telemetry, collaborate with the user to:\n- Define what events should be tracked and when\n- Determine what data should be included in each event\n- Clarify the purpose and expected usage of the telemetry\n\nAdding telemetry code is straightforward, but designing meaningful instrumentation requires careful thought.\n\n## Steps\n\n### 1. Identify or create a telemetry module\n\nFind an existing feature-specific telemetry file (e.g., `app/src/antivirus/telemetry.rs`) or create a new one for your feature area.\n\n### 2. Define the telemetry event enum\n\nAdd a new variant to an enum that implements `TelemetryEvent`, or create a new enum:\n\n```rust\nuse serde_json::{json, Value};\nuse strum_macros::{EnumDiscriminants, EnumIter};\nuse warp_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};\n\n#[derive(Debug, EnumDiscriminants)]\n#[strum_discriminants(derive(EnumIter))]\npub enum YourFeatureTelemetryEvent {\n    ActionStarted {\n        duration_ms: u64,\n    },\n    ActionCompleted {\n        success: bool,\n        error: Option<String>,\n    },\n}\n```\n\n### 3. Implement the TelemetryEvent trait\n\n`EnablementState` allows you to control when events are sent:\n\n- `EnablementState::Always` - Always send the event\n- `EnablementState::Flag(FeatureFlag::YourFeature)` - Only send when the feature flag is enabled\n- `EnablementState::Channel(Channel::Dev)` - Only send in specific build channels\n\n```rust\nimpl TelemetryEvent for YourFeatureTelemetryEvent {\n    fn name(&self) -> &'static str {\n        YourFeatureTelemetryEventDiscriminants::from(self).name()\n    }\n\n    fn payload(&self) -> Option<Value> {\n        match self {\n            Self::ActionStarted { duration_ms } => Some(json!({\n                \"duration_ms\": duration_ms,\n            })),\n            Self::ActionCompleted { success, error } => Some(json!({\n                \"success\": success,\n                \"error\": error,\n            })),\n        }\n    }\n\n    fn description(&self) -> &'static str {\n        YourFeatureTelemetryEventDiscriminants::from(self).description()\n    }\n\n    fn enablement_state(&self) -> EnablementState {\n        YourFeatureTelemetryEventDiscriminants::from(self).enablement_state()\n    }\n\n    fn contains_ugc(&self) -> bool {\n        match self {\n            Self::ActionStarted { .. } => false,\n            Self::ActionCompleted { .. } => false,\n        }\n    }\n\n    fn event_descs() -> impl Iterator<Item = Box<dyn TelemetryEventDesc>> {\n        warp_core::telemetry::enum_events::<Self>()\n    }\n}\n```\n\n### 4. Implement TelemetryEventDesc for the discriminants\n\n```rust\nimpl TelemetryEventDesc for YourFeatureTelemetryEventDiscriminants {\n    fn name(&self) -> &'static str {\n        match self {\n            Self::ActionStarted => \"YourFeature.Action.Started\",\n            Self::ActionCompleted => \"YourFeature.Action.Completed\",\n        }\n    }\n\n    fn description(&self) -> &'static str {\n        match self {\n            Self::ActionStarted => \"User started the action\",\n            Self::ActionCompleted => \"User completed the action\",\n        }\n    }\n\n    fn enablement_state(&self) -> EnablementState {\n        match self {\n            Self::ActionStarted | Self::ActionCompleted => EnablementState::Always,\n            // Or gate behind a feature flag:\n            // EnablementState::Flag(FeatureFlag::YourFeature)\n        }\n    }\n}\n```\n\n### 5. Register the telemetry event\n\nAt the end of your telemetry module, register the event:\n\n```rust\nwarp_core::register_telemetry_event!(YourFeatureTelemetryEvent);\n```\n\n### 6. Send telemetry events from your code\n\nUse `send_telemetry_from_ctx!` in views or models with a `ViewContext` or `ModelContext`:\n\n```rust\nuse warp_core::send_telemetry_from_ctx;\n\n// In a view update or model method\nsend_telemetry_from_ctx!(\n    YourFeatureTelemetryEvent::ActionStarted {\n        duration_ms: 150,\n    },\n    ctx\n);\n```\n\nFor code with only `AppContext`, use `send_telemetry_from_app_ctx!` instead.\n\n### 7. Test locally\n\nRun Warp with the `log_named_telemetry_events` feature flag to see telemetry events logged to the console:\n\n```bash\ncargo run --features log_named_telemetry_events\n```\n\n## Best Practices\n\n- Keep telemetry enums feature-specific rather than adding to a global enum\n- Set `contains_ugc()` to `true` if the payload includes user-generated content\n- Use descriptive event names following the pattern `Feature.Action.Result`\n- Include only necessary data in payloads to minimize bandwidth and storage\n- Consider privacy implications when deciding what data to include\n- Avoid exhaustive matching with wildcards; handle all variants explicitly\n\n## Example Reference\n\nSee `app/src/antivirus/telemetry.rs` for a complete example of a feature-specific telemetry implementation.\n",".agents/skills/classify-changelog-pr/SKILL.md":"---\nname: classify-changelog-pr\ndescription: Reference guidance for classifying whether an unmarked PR should appear in the changelog and under which category. Used inline by the changelog-draft skill — not dispatched as a separate agent.\n---\n\n# Classify Changelog PR\n\nThis document provides classification rules for PRs that lack explicit `CHANGELOG-*` markers. The changelog-draft agent follows these rules inline when deciding whether to include an unmarked PR.\n\n## Categories\n\n- **NEW-FEATURE** — A substantial new user-facing capability. Reserve for features that would warrant docs, marketing, or social media attention.\n- **IMPROVEMENT** — Enhances an existing feature in a way users would notice (performance, UX, new options).\n- **BUG-FIX** — Fixes a user-visible bug or regression.\n- **OZ** — Changes to Oz / AI agent capabilities. At most 4 per release in the stable changelog.\n- **TUI** — User-visible changes that impact the Warp Agent CLI/headless TUI, including shared Agent capabilities.\n- **NONE** — Explicitly opt out of changelog inclusion. Handled upstream by `fetch_prs.py` marker extraction.\n\n## Decision rules\n\n### Always exclude\n- PRs with an explicit `CHANGELOG-NONE` marker (contributor opted out)\n- PRs authored by known bots (dependabot, renovate, github-actions, codecov)\n- PRs that exclusively modify CI workflows (`.github/workflows/`), test files, or dev tooling\n- PRs that only update internal docs, comments, or README files\n- Dependency bumps with no user-facing behavior change\n- Refactors with no observable behavior change (code moves, renames, formatting)\n\n### Always include\n- PRs with explicit `CHANGELOG-*` markers (handled before this guidance applies)\n- PRs that fix a crash, data loss, or security issue — even without a marker\n\n### TUI surface classification\n- Treat explicit `CHANGELOG-TUI` and `CHANGELOG-OZ` markers as authoritative entries. They are independent and may coexist with each other or with regular changelog categories.\n- Determine TUI impact independently from the regular New Feature, Improvement, or Bug Fix category. A shared Agent capability may belong in both its regular category and `TUI`.\n- Classify a user-visible entry as impacting `TUI` when `commit_subject` contains `TUI` as a standalone token.\n- For stable releases, also use the PR title/body and TUI-owned paths such as `crates/warp_tui` or `crates/warpui_core/src/elements/tui`. Shared capabilities such as Agent tool-call or edit-file behavior also impact TUI when Warp Agent CLI users observe the change. If metadata is ambiguous, inspect the commit diff.\n- Do not infer TUI impact from file paths alone; confirm that Warp Agent CLI users observe the changed behavior.\n- A TUI-only entry belongs only in the TUI category. An entry that impacts both TUI and another surface should appear in both `TUI` and its regular category.\n\n### Conditional on channel\n- **Stable channel:** Only include changes that are live for all users. Exclude PRs gated behind `DOGFOOD_FLAGS` or `PREVIEW_FLAGS`.\n- **Preview channel:** Include PRs gated behind `PREVIEW_FLAGS`. Still exclude `DOGFOOD_FLAGS`-only changes.\n- **Dev channel:** Include everything that's user-visible, regardless of flag gates.\n\n### Feature-flagged PRs\nIf a PR mentions a `FeatureFlag` variant in its diff or title:\n1. Check which flag list it belongs to (`RELEASE_FLAGS`, `PREVIEW_FLAGS`, `DOGFOOD_FLAGS`).\n2. Apply the channel rules above.\n3. If the flag is in `RELEASE_FLAGS` or enabled by default in `app/Cargo.toml`, treat it as live.\n4. Set `feature_flag` in the classification output to the flag name.\n\n### Confidence levels\n- **high** — Clear user-visible change with obvious category.\n- **medium** — Likely user-visible but category or scope is somewhat ambiguous.\n- **low** — Unclear whether users would notice; or the PR touches both internal and user-facing code. Set `needs_review: true`.\n\n## Writing changelog text\n\n- Write from the user's perspective: \"Added X\", \"Fixed Y\", \"Improved Z\".\n- Keep it to one sentence, ≤ 120 characters.\n- Don't reference internal implementation details, file paths, or function names.\n- Don't start with \"PR\" or the PR number — those are added as metadata.\n- Use active voice and present tense for new features (\"Adds dark mode\"), past tense for fixes (\"Fixed crash on startup\").\n",".agents/skills/cross-platform-cloud-verification/SKILL.md":"---\nname: cross-platform-cloud-verification\ndescription: Orchestrates cost-conscious cloud verification across available operating-system and architecture runners after cheaper checks pass. Use whenever a code change, bug fix, build, packaging flow, native dependency, UI behavior, filesystem behavior, or test has material cross-platform implications, even if the user only asks to verify or test the change. Consult domain verification skills for what to test; use this skill to choose and access the smallest relevant set of platforms.\ncompatibility: Requires the Oz CLI (`oz-dev`) for runner discovery and `run_agents` support for `remote.runner_id`.\n---\n\n# Cross-platform cloud verification\n\nVerify a change on the smallest useful set of cloud platforms. This skill owns\nrunner discovery, platform selection, child orchestration, and result\naggregation. It does not define the product-specific verification procedure.\n\nCross-platform execution consumes remote compute and credits, so use it as the\nlast verification layer rather than as an exploratory first step.\n\n## Workflow\n\n### 1. Establish the local verification gate\n\nFinish the cheaper feedback loops first:\n\n1. Inspect the change and identify the behavior being verified.\n2. Run the relevant local build, focused tests, lint, type checks, or manual\n   verification.\n3. Fix deterministic local failures before launching cloud runs.\n4. Confirm the exact commit or branch containing the change can be checked out\n   by cloud agents. Do not test the default branch when the intended change is\n   only local.\n\nDo not commit or push changes merely to satisfy this workflow unless the user\nhas authorized that action. If the change is not remotely reachable, report the\nprecondition and ask for the minimum action needed.\n\nSkip cloud fan-out when local verification already shows the change is broken.\nDo not use remote platforms as a substitute for diagnosing an ordinary local\nfailure.\n\n### 2. Delegate test design to verification skills\n\nInspect the available skill descriptions and read every skill that materially\ndefines how to verify the affected surface. Examples include GUI computer-use\nverification, TUI live verification, integration testing, unit testing, CI\ndiagnosis, packaging, or repository-specific validation.\n\nExtract from those skills:\n\n- setup and authentication requirements\n- commands or interactions to perform\n- expected observations and pass criteria\n- required screenshots, recordings, logs, or text captures\n- platform-specific caveats\n\nBuild one shared verification procedure from that guidance. Tell each child\nwhich verification skill to read when it is available in the child environment,\nand include the essential procedure directly so the run remains actionable if\nthe skill is unavailable there.\n\nThis skill decides where to run that procedure, not what the procedure should\nbe.\n\n### 3. Discover runners at execution time\n\nList runners immediately before selecting platforms:\n\n```sh\noz-dev runner list --output-format json\n```\n\nUse the returned runner metadata rather than hard-coding names or IDs. Relevant\nfields include `uid`, `name`, `os`, `arch`, compute capacity, image or macOS\nversion, and setup commands.\n\nIf runner discovery fails because of authentication, permissions, or service\navailability, report the blocker instead of inventing a runner matrix.\n\n### 4. Infer the affected platform dimensions\n\nDetermine which dimensions the change can plausibly affect from the diff,\nrepository configuration, reported bug, and verification guidance.\n\nTreat the change as **OS-sensitive** when it touches or depends on items such as:\n\n- OS-gated code or platform modules\n- windowing, desktop integration, installers, packaging, or signing\n- shells, process creation, permissions, filesystems, paths, or line endings\n- OS-specific APIs, toolchains, or user-facing behavior\n\nTreat the change as **architecture-sensitive** only with evidence such as:\n\n- architecture gates or assembly\n- unsafe code, FFI, ABI, alignment, endianness, or binary serialization\n- SIMD, native libraries, architecture-specific dependencies, or packaging\n- an architecture-specific bug report or explicit user requirement\n\nDo not infer architecture sensitivity merely because both x86-64 and AArch64\nrunners exist.\n\n### 5. Select the minimal runner matrix\n\nFilter discovered runners to those that can execute the verification procedure,\nthen apply these defaults:\n\n1. Select only affected operating systems.\n2. Select one representative runner per affected OS.\n3. Add another architecture for an OS only when the change is\n   architecture-sensitive.\n4. Do not add an unaffected control platform by default.\n5. Deduplicate equivalent OS/architecture candidates.\n\nWhen several runners cover the same platform, prefer in order:\n\n1. the exact OS/version/architecture from the bug report or target\n2. the repository's normal CI or release platform\n3. a runner whose image, setup, capacity, and tools match the procedure\n4. the lower-setup-cost candidate\n\nFor architecture-insensitive Linux changes, prefer the repository's primary\nLinux architecture; if the repository gives no signal, use x86-64 as the single\nrepresentative.\n\nBefore launching, record a concise selection rationale and explicitly list\nrelevant platforms omitted because no runner is available. Missing runners do\nnot block useful verification on available relevant platforms.\n\nState each omission once. After the matrix is decided, do not keep repeating\nthat an irrelevant or redundant platform was not selected in child prompts,\nresult rows, evidence, and the conclusion.\n\n### 6. Construct verification-only child prompts\n\nEvery child prompt should contain:\n\n- repository, branch, and exact commit to check out\n- selected runner name, OS, and architecture\n- verification skill names and the distilled procedure\n- setup and state preconditions\n- exact commands or interactions\n- expected results and required evidence\n- a prohibition on fixing or pushing code\n\nTrust orchestration to route the child to the requested runner. Do not make every\nchild re-confirm its OS and architecture. Ask for platform introspection only\nwhen the verification depends on an exact OS version or capability, or when\nthere is evidence of a routing problem.\n\nAsk each child to return:\n\n```text\nPlatform: <runner name; OS; architecture; version if relevant>\nCommit: <tested SHA>\nStatus: <passed | failed | blocked>\nChecks:\n- <command or interaction>: <result>\nEvidence:\n- <artifact, screenshot, log, or concise observation>\nDeviations:\n- <difference from the requested procedure, or none>\n```\n\nChildren may make temporary, uncommitted setup adjustments required by the\nverification skill, but they must report them and must not push source changes.\n\n### 7. Launch with `run_agents`\n\nUse `run_agents` so child IDs, messages, lifecycle events, and artifacts remain\npart of the parent orchestration flow. Use the same repository environment for\nevery selected platform and set `remote.runner_id` to the discovered runner UID.\n\n```text\nsummary: Verifying the change on <OS/architecture>.\nbase_prompt: <shared verification-only instructions>\nremote:\n  environment_id: <repository environment ID>\n  runner_id: <selected runner UID>\n  computer_use_enabled: <true only when the verification procedure needs it>\nagent_run_configs:\n- name: <short platform-specific name>\n  prompt: <platform-specific procedure and expected evidence>\n```\n\nIf no suitable repository environment is already known, inspect\n`oz-dev environment list` and choose one that checks out the target repository.\nDo not silently create or mutate an environment.\n\n`remote.runner_id` is run-wide, so runners with different UIDs require separate\n`run_agents` calls. Use one single-child batch per selected runner, or group\nmultiple independently useful children only when they share the same runner and\nrun-wide configuration. Distinct runner IDs are a legitimate reason for\nseparate batches; do not place children for different platforms in one batch.\n\nOmit `model_id` and `remote.harness` unless the user requested them. Attach\nrelevant verification skills when the children need them. If an approved\norchestration config is active, ensure its resolved runner matches the selected\nrunner because config resolution takes precedence over call fields.\n\nCapture each trusted `agent_id` from the launched result. Coordinate through\npushed child messages and lifecycle events; do not poll `oz-dev run get` or\n`list_messages_from_agents`. Read notified messages, intervene only for a\nfailure or actionable block, and use `wait_for_events` when no other work can\nproceed. Allow at most one retry for a clearly transient infrastructure\nfailure. A product failure is evidence, not a reason to spend more credits\nrepeating the same check.\n\n### 8. Aggregate without hiding gaps\n\nWait for every launched child, then report:\n\n```text\nCross-platform verification: <passed | failed | incomplete>\nLocal gate: <checks completed before cloud launch>\nResults:\n- <OS/arch; runner>: <why selected> - <passed/failed/blocked>\nUnverified:\n- <relevant OS/arch>: unverified because no suitable runner was available\n\nEvidence:\n- <platform>: <commands, observations, and artifact/run links>\n\nConclusion:\n- <what the results establish and what remains unverified>\n```\n\nUse `passed` only when every selected platform passed and no required platform\nis unavailable. Use `failed` when any verification check failed. Use\n`incomplete` when runs were blocked or a relevant platform lacked a runner.\n\nKeep dry-run proposals and final summaries compact. Include the local gate, the\nselected matrix, one concise omission rationale when it is material, the\nverification procedure, and verdict rules. Do not repeat setup mechanics or\nplatform exclusions in multiple sections. A dry-run proposal should usually fit\nin roughly 300-500 words; expand only when the domain verification procedure\ngenuinely requires more detail.\n\n## Cost and scope guardrails\n\n- Run this workflow after local verification, not during implementation.\n- Choose platforms from evidence in the change, not from runner availability.\n- Keep one child per selected platform unless distinct state variants are\n  independently necessary.\n- State runner-selection and omission decisions once.\n- Preserve negative results and platform gaps; do not broaden the matrix merely\n  to obtain a passing result.\n- Do not let children implement fixes. Return failures to the parent workflow,\n  fix locally, rerun cheap checks, and only then decide whether another cloud\n  verification pass is justified.\n",".agents/skills/gui-create-launch-modal/SKILL.md":"---\nname: gui-create-launch-modal\ndescription: GUI desktop app only. Create a one-time launch modal in the Warp client (feature announcement, onboarding, etc.). Use when adding a new modal that should appear exactly once per user on startup, gated by a feature flag, with colors sourced from Warp theme tokens and terminal theme colors.\n---\n\n# gui-create-launch-modal\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nCreate a one-time launch modal — the feature-announcement design used for launches like \"Orchestrate any agent, anywhere\" or \"Warp is now open-source.\"\n\n## Reference implementation\n\n`app/src/workspace/view/orchestration_launch_modal/` — the canonical, most up-to-date example of this pattern.\n\n## Checklist\n\n- [ ] Feature flag in `warp_features/src/lib.rs`\n- [ ] Settings field in `app/src/settings/ai.rs`\n- [ ] Trigger logic in `app/src/workspace/one_time_modal_model.rs`\n- [ ] View files under `app/src/workspace/view/<name>_launch_modal/`\n- [ ] Workspace wiring in `app/src/workspace/view.rs` and `app/src/workspace/mod.rs`\n- [ ] Debug actions in `app/src/workspace/action.rs`\n- [ ] Hero image at `app/assets/async/png/onboarding/<name>_launch_banner.png`\n- [ ] Any custom icons added to `crates/warp_core/src/ui/icons.rs` + SVG in `app/assets/bundled/svg/`\n\n---\n\n## Step 0 – Custom icons (if needed)\n\nIf the modal uses icons not yet in the `Icon` enum, add them before writing the view.\n\nIn `crates/warp_core/src/ui/icons.rs`:\n\n```rust\n// Add to enum\nYourIconName,\n\n// Add to From<Icon> for &'static str match\nIcon::YourIconName => \"bundled/svg/your-icon-name.svg\",\n```\n\nDrop the SVG file at `app/assets/bundled/svg/your-icon-name.svg`. Use the same 24×24 viewBox format as existing icons.\n\n---\n\n## Step 1 – Feature flag\n\nAdd to `crates/warp_features/src/lib.rs`:\n\n```rust\n/// Enables the <name> launch modal.\n<YourModalName>LaunchModal,\n```\n\nEnable for dogfood:\n\n```rust\npub const DOGFOOD_FLAGS: &[FeatureFlag] = &[\n    FeatureFlag::<YourModalName>LaunchModal,\n    // ...\n];\n```\n\n---\n\n## Step 2 – Settings field\n\nAdd to `app/src/settings/ai.rs` inside `define_settings_group!(AISettings, ...)`.\nPattern: one boolean field per modal, globally synced (not respecting user sync), private.\n\n```rust\n// This is not a user-visible setting - it's merely a one-time flag to track if the\n// <name> launch modal has been shown to the user.\n//\n// We model it as a setting so it's only shown once to a given user regardless of the number of\n// devices they use.\ndid_check_to_trigger_<name>_launch_modal: DidShow<Name>LaunchModal {\n    type: bool,\n    default: false,\n    supported_platforms: SupportedPlatforms::ALL,\n    sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::No),\n    private: true,\n}\n```\n\n---\n\n## Step 3 – OneTimeModalModel\n\nFile: `app/src/workspace/one_time_modal_model.rs`\n\n### 3a. Add field to struct\n\n```rust\nis_<name>_launch_modal_open: bool,\n```\n\n### 3b. Initialize to false in `new()`\n\n```rust\nis_<name>_launch_modal_open: false,\n```\n\n### 3c. Pre-dismiss for new users (critical)\n\nIn the `AuthComplete` → `!is_existing_user` branch, add to the `AISettings::handle` update block alongside the other pre-dismissals. **Without this, new users see the modal on their second startup after onboarding.**\n\n```rust\nif let Err(e) = settings\n    .did_check_to_trigger_<name>_launch_modal\n    .set_value(true, ctx)\n{\n    log::warn!(\"Failed to mark <name> launch modal as dismissed: {e}\");\n}\n```\n\n### 3d. Public API methods\n\n```rust\npub fn is_<name>_launch_modal_open(&self) -> bool {\n    self.is_<name>_launch_modal_open && self.target_window_id.is_some()\n}\n\npub fn mark_<name>_launch_modal_dismissed(&mut self, ctx: &mut ModelContext<Self>) {\n    self.set_<name>_launch_modal_open(false, ctx);\n}\n\n#[cfg(debug_assertions)]\npub fn force_open_<name>_launch_modal(&mut self, ctx: &mut ModelContext<Self>) {\n    self.set_<name>_launch_modal_open(true, ctx);\n}\n```\n\n### 3e. Private setter\n\n```rust\nfn set_<name>_launch_modal_open(&mut self, is_open: bool, ctx: &mut ModelContext<Self>) -> bool {\n    if self.is_<name>_launch_modal_open != is_open {\n        self.is_<name>_launch_modal_open = is_open;\n        ctx.emit(OneTimeModalEvent::VisibilityChanged { is_open });\n        return true;\n    }\n    false\n}\n```\n\n### 3f. Add to `is_any_modal_open`\n\n```rust\n|| self.is_<name>_launch_modal_open\n```\n\n### 3g. Trigger function\n\n```rust\nfn check_and_trigger_<name>_launch_modal(&mut self, ctx: &mut ModelContext<Self>) -> bool {\n    if !FeatureFlag::<Name>LaunchModal.is_enabled() {\n        return false;\n    }\n\n    let ai_settings = AISettings::as_ref(ctx);\n    if *ai_settings.did_check_to_trigger_<name>_launch_modal {\n        return false;\n    }\n\n    AISettings::handle(ctx).update(ctx, |settings, ctx| {\n        if let Err(e) = settings\n            .did_check_to_trigger_<name>_launch_modal\n            .set_value(true, ctx)\n        {\n            log::warn!(\"Failed to mark <name> launch modal as dismissed: {e}\");\n        }\n    });\n\n    let should_show = !matches!(ChannelState::channel(), Channel::Integration);\n    self.set_<name>_launch_modal_open(should_show, ctx);\n    should_show\n}\n```\n\n### 3h. Call from `check_and_trigger_all_modals`\n\nInsert before `check_and_trigger_hoa_onboarding`:\n\n```rust\nif self.check_and_trigger_<name>_launch_modal(ctx) {\n    return;\n}\n```\n\n---\n\n## Step 4 – View\n\nCreate `app/src/workspace/view/<name>_launch_modal/mod.rs`:\n\n```rust\nmod view;\npub use view::{init, <Name>LaunchModal, <Name>LaunchModalEvent};\n```\n\nCreate `app/src/workspace/view/<name>_launch_modal/view.rs`. Copy from `orchestration_launch_modal/view.rs` and adapt. Key details:\n\n### Color sources (important)\n\n- Prefer Warp theme tokens for modal backgrounds, text, overlays, and borders:\n  - background surfaces: `appearance.theme().surface_3()` (or another `surface_*` token when needed)\n  - primary/subtext: `appearance.theme().main_text_color(...)` and `appearance.theme().sub_text_color(...)`\n  - overlays/hover fills: `appearance.theme().surface_overlay_1()` / `surface_overlay_2()`\n  - subtle borders: `appearance.theme().outline()`\n- Use terminal theme colors for terminal-color accents (for example, magenta launch badge accents):\n  - `appearance.theme().terminal_colors().normal.magenta`\n  - `appearance.theme().ansi_overlay_1(magenta)` for low-alpha backgrounds\n- Avoid hardcoded hex colors.\n\n### Hero image\n\n- Store at `app/assets/async/png/onboarding/<name>_launch_banner.png`\n- **Aspect ratio matters**: if the image is wider than `MODAL_WIDTH/HERO_HEIGHT` (420/92 ≈ 4.57), wrap the hero `ConstrainedBox` in `Clipped::new(...)` to prevent horizontal bleed when `cover()` scales it\n- Images pre-sized to exactly 420×92 need no `Clipped`; images only taller (aspect ratio < 4.57) are fine without it\n\n```rust\nconst MODAL_WIDTH: f32 = 420.;\nconst HERO_HEIGHT: f32 = 92.;\nconst HERO_IMAGE_PATH: &str = \"async/png/onboarding/<name>_launch_banner.png\";\n\nfn render_hero(&self) -> Box<dyn Element> {\n    let hero = Clipped::new(          // only needed if image ratio > 4.57\n        ConstrainedBox::new(\n            Image::new(AssetSource::Bundled { path: HERO_IMAGE_PATH }, CacheOption::Original)\n                .with_corner_radius(CornerRadius::with_top(Radius::Pixels(8.)))\n                .cover()\n                .top_aligned()\n                .finish(),\n        )\n        .with_width(MODAL_WIDTH)\n        .with_height(HERO_HEIGHT)\n        .finish(),\n    )\n    .finish();\n    // ... close button overlay via Stack + add_positioned_child\n}\n```\n\n### \"New\" badge\n\nUse the standard badge — 24 px tall, 8 px horizontal padding, 14 px font, pill corners, with magenta sourced from terminal theme colors:\n\n```rust\nfn render_badge(appearance: &Appearance) -> Box<dyn Element> {\n    let magenta = appearance.theme().terminal_colors().normal.magenta;\n    let text = Text::new_inline(\"New\".to_string(), appearance.ui_font_family(), 14.)\n        .with_color(magenta.into())\n        .finish();\n    ConstrainedBox::new(\n        Container::new(\n            Flex::row()\n                .with_cross_axis_alignment(CrossAxisAlignment::Center)\n                .with_main_axis_size(MainAxisSize::Min)\n                .with_child(text)\n                .finish(),\n        )\n        .with_horizontal_padding(8.)\n        .with_background(Fill::Solid(appearance.theme().ansi_overlay_1(magenta)))\n        .with_corner_radius(CornerRadius::with_all(Radius::Percentage(50.)))\n        .finish(),\n    )\n    .with_height(24.)\n    .finish()\n}\n```\n\n### URLs\n\nAlways use `https://`, not `http://`:\n\n```rust\nconst LEARN_MORE_URL: &str = \"https://warp.dev/your-blog-link\";\n```\n\n---\n\n## Step 5 – Workspace wiring\n\n### `app/src/workspace/view.rs`\n\n```rust\n// Module declaration (top)\npub(crate) mod <name>_launch_modal;\n\n// Import\nuse crate::workspace::view::<name>_launch_modal::{<Name>LaunchModal, <Name>LaunchModalEvent};\n\n// Struct field\n<name>_launch_modal: ViewHandle<<Name>LaunchModal>,\n\n// In Workspace::new()\nlet <name>_launch_view = ctx.add_typed_action_view(<Name>LaunchModal::new);\nctx.subscribe_to_view(&<name>_launch_view, |me, _, event, ctx| {\n    me.handle_<name>_launch_modal_event(event, ctx);\n});\n\n// In struct initialization\n<name>_launch_modal: <name>_launch_view,\n\n// In OneTimeModalModel subscription handler\n} else if model_ref.is_<name>_launch_modal_open() {\n    me.focus_<name>_launch_modal(ctx);\n\n// In View::render (inside the should_show_modal block)\nif should_show_modal && one_time_modal_model.is_<name>_launch_modal_open() {\n    stack.add_child(ChildView::new(&self.<name>_launch_modal).finish());\n}\n```\n\nAdd event handler and focus helper:\n\n```rust\nfn handle_<name>_launch_modal_event(&mut self, event: &<Name>LaunchModalEvent, ctx: &mut ViewContext<Self>) {\n    match event {\n        <Name>LaunchModalEvent::Close => {\n            OneTimeModalModel::handle(ctx).update(ctx, |model, ctx| {\n                model.mark_<name>_launch_modal_dismissed(ctx);\n            });\n            self.focus_active_tab(ctx);\n            ctx.notify();\n        }\n    }\n}\n\nfn focus_<name>_launch_modal(&mut self, ctx: &mut ViewContext<Self>) {\n    ctx.focus(&self.<name>_launch_modal);\n}\n```\n\n### `app/src/workspace/mod.rs`\n\n```rust\n// In pub fn init()\nview::<name>_launch_modal::init(app);\n\n// In debug bindings block\nEditableBinding::new(\n    \"workspace:open_<name>_launch_modal\",\n    \"[Debug] Open <Name> Launch Modal\",\n    WorkspaceAction::Open<Name>LaunchModal,\n)\n.with_context_predicate(id!(\"Workspace\")),\nEditableBinding::new(\n    \"workspace:reset_<name>_launch_modal_state\",\n    \"[Debug] Reset <Name> Launch Modal State\",\n    WorkspaceAction::Reset<Name>LaunchModalState,\n)\n.with_context_predicate(id!(\"Workspace\")),\n```\n\n---\n\n## Step 6 – Debug actions\n\nIn `app/src/workspace/action.rs`:\n\n```rust\n/// Open the <Name> Launch Modal (for debugging)\n#[cfg(debug_assertions)]\nOpen<Name>LaunchModal,\n/// Reset the <name> launch modal dismissed state (for debugging)\n#[cfg(debug_assertions)]\nReset<Name>LaunchModalState,\n```\n\nAdd both variants to the `is_visible_in_command_palette` `false` arm.\n\nIn `app/src/workspace/view.rs` `TypedActionView::handle_action`:\n\n```rust\n#[cfg(debug_assertions)]\nOpen<Name>LaunchModal => {\n    OneTimeModalModel::handle(ctx).update(ctx, |model, ctx| {\n        model.force_open_<name>_launch_modal(ctx);\n    });\n    ctx.notify();\n}\n#[cfg(debug_assertions)]\nReset<Name>LaunchModalState => {\n    AISettings::handle(ctx).update(ctx, |settings, ctx| {\n        if let Err(e) = settings\n            .did_check_to_trigger_<name>_launch_modal\n            .set_value(false, ctx)\n        {\n            log::warn!(\"Failed to reset <name> launch modal state: {e}\");\n        }\n    });\n}\n```\n\n---\n\n## Behavior summary\n\n| User type | Sees modal? |\n|---|---|\n| New signup | No — pre-dismissed in `AuthComplete` new-user branch |\n| Not signed in | No — trigger never fires without `AuthComplete` |\n| Existing user, flag enabled | Yes — on first startup after cloud prefs load |\n| Integration channel | No — suppressed by `Channel::Integration` check |\n| Already seen it | No — setting persists globally across devices |\n",".agents/skills/gui-onboarding-verification-skill/SKILL.md":"---\nname: gui-onboarding-verification-skill\ndescription: GUI desktop app only. Launch two parallel Oz cloud agents with computer use to download and install the latest stable Linux Warp build, capture screenshots while walking through first-time onboarding in both logged-out and logged-in states, then selectively fan out follow-up cloud agents for distinct onboarding branches proposed by those initial explorers. Use this whenever the user asks to test, document, screenshot, or walk through the Warp first-time install/onboarding experience in a cloud Linux environment.\n---\n\n# Onboarding verification skill\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nUse this skill to verify the first-time Warp install and onboarding flow on Linux with broader branch coverage than a single linear walkthrough.\n\nThe parent agent should not perform the walkthrough locally. Launch two parallel Oz cloud agents with computer use. Both initial children install the latest stable Warp Linux package appropriate for their platform and capture screenshots at every visible onboarding step until Warp reaches a usable terminal session. One child verifies the login-free flow. The other child verifies the logged-in flow using the managed secret `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN`.\n\nThose two baseline explorers are also responsible for noticing meaningful alternate onboarding branches and returning concrete plans for follow-up cloud agents. The parent agent should synthesize those plans, deduplicate overlapping suggestions, and launch a bounded second wave of targeted follow-up agents to improve coverage of paths a real user might encounter.\n\n## Parent workflow\n\n1. Launch exactly two remote Oz cloud agents in a single parallel `run_agents` batch with computer use enabled.\n2. Use no environment-specific assumptions unless the user provided an environment. If no environment was provided, omit the environment ID and let Warp choose the default remote environment.\n3. Give both baseline child agents the shared child prompt below, plus the appropriate flow-specific prompt.\n4. Wait for both baseline agents' reports. Each report must include:\n   - The completed baseline walkthrough result and artifacts.\n   - A concise list of observed UI quality issues, suspected bugs, error states, or rough edges, with screenshots when visible.\n   - A prioritized follow-up coverage plan describing distinct onboarding paths worth exploring with additional cloud agents.\n5. Treat the authenticated baseline child as blocked if `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` is missing or does not authenticate successfully.\n6. Build a combined coverage map from the two baseline reports. Deduplicate suggestions that reach the same visible state or exercise the same decision surface.\n7. Launch a second `run_agents` batch with computer use enabled for the most valuable follow-up onboarding branches:\n   - Prefer branches that materially change visible UI, available controls, downstream screens, auth state, or setup outcomes.\n   - Favor paths likely to expose correctness, polish, layout, truncation, loading, or validation problems.\n   - Default to at most four follow-up agents total unless the user explicitly asked for exhaustive coverage or the baseline reports show more than four clearly distinct high-value branches.\n   - Do not launch speculative follow-ups when the baseline agents did not observe a concrete branch point; report that coverage stopped after the baseline pass instead.\n8. Give each follow-up child the shared child prompt, the follow-up flow prompt below, the logged-out or logged-in flow prompt that matches its assigned auth state, and one synthesized branch assignment from the baseline reports.\n9. Wait for all follow-up reports before summarizing coverage, issues, artifacts, and any still-unexplored branches worth a later run.\n\n## Managed FTUE auth secret\n\n- `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` is an internal-team managed secret for cloud agents, not a repo file or prompt literal.\n- The secret should authenticate as a dedicated non-employee, non-`warp.dev` FTUE test user.\n- Rotate the secret with `oz-dev secret update --team --value-file <private-token-file> ONBOARDING_AGENT_FTUE_REFRESH_TOKEN`.\n- Treat the private token file as local scratch material only. Do not read it into chat, print it, stage it, commit it, upload it, or include it in artifacts. Delete it after the managed secret is updated.\n- Children should receive the secret only through the managed environment variable injected into the remote run.\n\nUse the initial `run_agents` call shaped like this:\n\n```text\nsummary: Launching two baseline cloud agents with computer use to compare logged-out and logged-in Warp onboarding screenshots and propose follow-up coverage branches.\nremote.computer_use_enabled: true\nagent_run_configs:\n- name: \"warp-onboarding-logged-out\"\n  prompt: the logged-out flow prompt below\n- name: \"warp-onboarding-logged-in\"\n  prompt: the logged-in flow prompt below\nbase_prompt: the shared child prompt below\n```\n\nWhen the baseline reports identify concrete follow-up branches, use a second `run_agents` call shaped like this:\n\n```text\nsummary: Launching targeted cloud follow-up agents to explore distinct onboarding branches identified by the baseline onboarding explorers.\nremote.computer_use_enabled: true\nagent_run_configs:\n- name: \"warp-onboarding-followup-theme-choice\"\n  prompt: the follow-up flow prompt below, the logged-out flow prompt below, and one synthesized logged-out branch assignment\n- name: \"warp-onboarding-followup-model-choice\"\n  prompt: the follow-up flow prompt below, the logged-in flow prompt below, and one synthesized logged-in branch assignment\nbase_prompt: the shared child prompt below\n```\n\n## Shared child prompt\n\nGive both cloud agents these shared instructions:\n\n```text\nYou are verifying the first-time Warp install and onboarding experience on Linux.\n\nGoal:\n- Download and install the latest stable Warp Linux build appropriate for this cloud environment's distro and CPU architecture.\n- Launch Warp in a fresh first-run state.\n- Take a screenshot at every visible onboarding step.\n- Continue until Warp reaches a usable terminal session, or stop and report a blocker if the assigned flow cannot proceed.\n- Notice alternate onboarding decisions that lead to meaningfully different screens, states, or outcomes, and return concrete follow-up cloud-agent plans for the parent orchestrator.\n- Treat visual polish, missing assets, misalignment, overlapping content, clipped text, poor contrast, broken loading states, unexpected errors, and confusing controls as verification findings rather than ignoring them.\n\nInstall requirements:\n- Use official stable Warp downloads only.\n- Do not use Warp Preview, Alpha, source builds, or a repository development build.\n- Detect CPU architecture with `uname -m`.\n- Detect the package manager or distro before choosing the package format.\n- Prefer native packages over AppImage because they install dependencies and register the app normally.\n\nStable Linux package mapping:\n- Debian/Ubuntu with amd64 or x86_64: https://app.warp.dev/download?package=deb\n- Debian/Ubuntu with arm64 or aarch64: https://app.warp.dev/download?package=deb_arm64\n- Fedora/RHEL/CentOS/openSUSE with amd64 or x86_64: https://app.warp.dev/download?package=rpm\n- Fedora/RHEL/CentOS/openSUSE with arm64 or aarch64: https://app.warp.dev/download?package=rpm_arm64\n- Arch with amd64 or x86_64: https://app.warp.dev/download?package=pacman\n- Arch with arm64 or aarch64: https://app.warp.dev/download?package=pacman_arm64\n- If no native package path is available, use the AppImage fallback:\n  - amd64 or x86_64: https://app.warp.dev/download?package=appimage\n  - arm64 or aarch64: https://app.warp.dev/download?package=appimage_arm64\n\nBefore launch:\n- Create a flow-specific artifact directory such as `~/warp-onboarding-logged-out` or `~/warp-onboarding-logged-in`.\n- Ensure the run starts from a fresh Warp first-run state by removing only Warp-specific config/data/cache/state directories for the test user, such as `~/.config/warp-terminal`, `~/.local/share/warp-terminal`, `~/.local/state/warp-terminal`, and `~/.cache/warp-terminal` if they exist.\n- Do not delete unrelated user files or system directories.\n\nScreenshot workflow:\n- Take the first screenshot before interacting with the first visible Warp window.\n- Take one screenshot before every user action.\n- Take another screenshot after each action if the UI changes.\n- Use sequential filenames with a flow prefix, such as `01-logged-out-initial-window.png` or `01-logged-in-initial-window.png`.\n- If anything looks wrong, take an additional issue-focused screenshot that captures the problematic state as clearly as possible.\n- Maintain a manifest file in the artifact directory with, for each screenshot:\n  - filename\n  - timestamp\n  - what was visible\n  - what action was about to happen or just happened\n- For issue-focused screenshots, add the suspected issue category and the screen or step where it appeared.\n- Do not include secret values, refresh tokens, ID tokens, auth redirect URLs, or Authorization headers in the manifest, logs, shell history, screenshots, or final report.\n\nOnboarding behavior:\n- Baseline children choose the default or most conservative option at each step unless the flow-specific prompt says otherwise, while recording branch points that deserve separate follow-up coverage.\n- Follow-up children take the specifically assigned alternate branch, then use the default or most conservative option for unrelated decisions unless the branch assignment says otherwise.\n- If telemetry, shell, theme, editor-import, or agent integration choices appear, use the default path and document the choice in the manifest.\n- Continue until a normal terminal prompt is visible and usable.\n\nUI quality review:\n- Watch for screens that are visually broken, obviously unfinished, misaligned, truncated, clipped, crowded, low-contrast, unexpectedly blank, stuck loading, or inconsistent with adjacent steps.\n- Watch for actionable errors or validation states that appear during normal flow exploration, including auth failures, failed button transitions, controls that do not respond, duplicated overlays, missing images, or broken post-selection states.\n- For every suspicious state:\n  - Capture a screenshot.\n  - Record the screen, the action that led to it, what looked wrong, and whether it blocked progress.\n  - Describe the issue factually. If expected behavior is uncertain, say it appears suspicious rather than claiming a confirmed bug.\n\nTerminal verification:\n- Once a terminal session is visible, run a harmless flow-specific command:\n  - logged-out flow: `echo warp-onboarding-logged-out-ready`\n  - logged-in flow: `echo warp-onboarding-logged-in-ready`\n- Capture a final screenshot showing the usable terminal and command output.\n\nReport back:\n- Whether you were a baseline explorer or a follow-up branch explorer.\n- Which flow you ran: logged-out or logged-in.\n- OS and distro detected.\n- CPU architecture detected.\n- Package URL and install method used.\n- Launch command used.\n- Whether the walkthrough reached a usable terminal session.\n- Ordered screenshot list with short descriptions.\n- Artifact directory path.\n- Any built-in artifact IDs or attachment names if the harness supports artifact upload.\n- Any visual polish concern, suspected bug, error state, or unpolished/misaligned screen, including:\n  - screenshot filename\n  - screen or step\n  - action taken immediately before it appeared\n  - concise observed behavior\n  - whether it blocked progress\n- Any blocker, crash, missing dependency, display problem, auth failure, or step that required judgment.\n- For baseline explorers, include a `Follow-up coverage plan` section with zero or more proposed child-agent branches. Each proposal must include:\n  - suggested agent name\n  - logged-out or logged-in flow\n  - onboarding screen or decision point where the alternate branch begins\n  - exact alternate choice or action sequence to explore\n  - why it is materially distinct from the baseline path\n  - what user-visible state, setup outcome, or failure mode it could reveal\n  - any secret, auth, or environment dependency\n  - priority: high, medium, or low\n- For follow-up explorers, include whether the assigned branch was reachable and completed. If a new branch point appears while following the assigned path, record it as a later-run suggestion instead of recursively expanding the run yourself.\n\nDo not upload screenshots or logs to public external services. If the harness provides a built-in artifact or screenshot attachment mechanism, use that. Otherwise, leave the files in the artifact directory and report their paths.\n```\n\n## Logged-out flow prompt\n\nAppend this prompt to the shared child prompt for the logged-out child:\n\n```text\nYou own the logged-out onboarding flow.\n\nFlow-specific goal:\n- Do not create an account, log in, or use a real user identity.\n- Continue only through login-free or account-free paths until Warp reaches a usable terminal session.\n- Stop and report a blocker if the flow requires login or account creation with no skip/continue-without-account option.\n\nFlow-specific onboarding behavior:\n- If there is a skip, \"continue without account\", \"not now\", \"login later\", or equivalent option, use it.\n- Do not enter an email address, connect OAuth, paste an auth token, or create credentials.\n- Be especially alert for logged-out branch points around choosing terminal-only versus agentic experiences, customization/layout options, third-party integration toggles, and terminal theme selection. If they appear, propose follow-up branches that exercise materially different choices rather than trying all alternates inline.\n- Use the artifact directory `~/warp-onboarding-logged-out`.\n```\n\n## Logged-in flow prompt\n\nAppend this prompt to the shared child prompt for the logged-in child:\n\n```text\nYou own the logged-in onboarding flow.\n\nFlow-specific goal:\n- Use the managed secret environment variable `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` to authenticate as the dedicated non-employee, non-`warp.dev` FTUE test user.\n- Exercise onboarding screens that are available to an already-authenticated user.\n- Continue through the authenticated onboarding path until Warp reaches a usable terminal session.\n\nSecret handling requirements:\n- Before doing auth work, verify that `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` exists and is non-empty without printing it.\n- Never echo, log, screenshot, upload, or report the secret value.\n- Avoid shell tracing (`set -x`) and avoid writing commands that place the raw token in shell history or process lists.\n- Treat every auth redirect URL containing the refresh token as secret-bearing material, even after URL-encoding.\n- Do not pass a token-bearing redirect URL to a shell command, desktop URI handler, browser address bar, process argument, log, artifact, or report. In particular, do not use commands such as `xdg-open`, `gio open`, `open`, or equivalent with the redirect URL.\n- If you need to construct an auth redirect URL, keep it only in a clipboard value or a private temporary file with user-only permissions, paste it through Warp's visible Paste Auth Token flow, then delete the temporary file immediately after use.\n\nSecure Paste Auth Token process:\n1. Verify `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` exists and is non-empty without printing it.\n2. Start Warp's normal login flow and derive the current-run `state` from Warp's generated login URL.\n3. Normalize the managed secret privately:\n   - Trim surrounding whitespace and one pair of surrounding single or double quotes if present.\n   - If the secret parses as a URL with a `refresh_token` query parameter, extract that `refresh_token` value and ignore any stale `state` in the secret.\n   - Otherwise, treat the trimmed secret as the raw refresh token.\n4. URL-encode the extracted refresh token and current-run `state` separately as query parameter values.\n5. Construct the redirect URL only in a clipboard value or private temporary file with user-only permissions.\n6. Return to Warp and use the visible Paste Auth Token path:\n   - Click the `Click here to paste your token from the browser` link, `Paste Auth Token` button, or equivalent pasted-token control shown by Warp.\n   - Focus the auth token text input that appears.\n   - Paste the prepared redirect URL into that input and submit it through Warp's UI so Warp parses and validates it.\n7. Delete any private temporary files immediately after use and clear the clipboard if the environment supports doing so safely.\n8. If the Paste Auth Token UI cannot be reached or automated safely, stop and report an auth blocker instead of parsing the redirect in place of Warp, using a desktop URI handler, browser address bar, or shell command with the token-bearing URL.\n\nPreferred authenticated path:\n- Launch Warp in a fresh first-run state and choose the login/sign-in path from onboarding.\n- Use Warp's built-in Paste Auth Token flow rather than visiting real OAuth providers, invoking a desktop URI handler, or asking the agent to parse/validate the redirect URI itself.\n- Derive `<state>` from the login URL generated by Warp if the UI exposes a copied login URL or opens the browser. If the UI does not expose the state after reasonable effort, report that as an auth blocker rather than bypassing state validation.\n- Do not preflight the token with Firebase Secure Token before handing it to Warp. Warp's desktop redirect handler only requires `refresh_token` and `state`; `user_uid` is optional, and `deleted_anonymous_user=true` handles the anonymous-user override case.\n- Treat `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` as either of these secret shapes:\n  - a raw Firebase refresh token, or\n  - a complete Warp desktop auth redirect URL containing a `refresh_token` query parameter.\n- Normalize the secret into a current-run redirect URL without printing it:\n  - Trim surrounding whitespace and one pair of surrounding single or double quotes if present.\n  - If the secret parses as a URL with a `refresh_token` query parameter, extract that `refresh_token` value and ignore any stale `state` in the secret.\n  - Otherwise, treat the trimmed secret as the raw refresh token.\n  - URL-encode the extracted refresh token and the current-run `state` separately as query parameter values.\n  - Build `warp://auth/desktop_redirect?refresh_token=<url-encoded-normalized-refresh-token>&deleted_anonymous_user=true&state=<url-encoded-current-state>`.\n  - Do not include `user_uid` unless it is already present in a provided desktop redirect URL; it is not required for this flow.\n- Construct the normalized redirect URL in a clipboard value or private temporary file only, then hand it to Warp through the Paste Auth Token UI. Do not parse, validate, or route the redirect outside of Warp.\n- If the Paste Auth Token flow cannot be reached or automated safely, stop and report an auth blocker instead of using a desktop URI handler or any shell command that contains the token-bearing URL.\n\nFallback authenticated path:\n- If Warp rejects the normalized redirect, report the non-sensitive user-visible error and classify whether the secret appeared to be a raw token or a desktop redirect URL, without reporting any token contents.\n- If the Paste Auth Token flow is blocked by UI automation issues, report the blocker and include the exact non-sensitive step where automation failed.\n- Do not switch to a logged-out path for this child.\n\nFlow-specific onboarding behavior:\n- Choose login/sign-in rather than skip/login-later when presented with an auth choice.\n- After auth succeeds, continue through the remaining onboarding screens with default or conservative options.\n- Be especially alert for logged-in branch points around model selection, account-aware onboarding screens, AI/agent setup, workspace or project setup, and any decision that changes available product capability. If they appear, propose follow-up branches that exercise materially different choices rather than trying all alternates inline.\n- After the terminal verification succeeds, click the upper-right avatar/account control, open Settings from that menu, and capture an additional screenshot that clearly shows the logged-in user's email address in Warp settings or account/profile settings.\n- Include the account/settings email screenshot in the manifest and final report. The email address itself may be visible in the screenshot, but do not copy the email into logs, shell output, or the final text report unless the user explicitly asks for it.\n- Use the artifact directory `~/warp-onboarding-logged-in`.\n```\n\n## Follow-up flow prompt\n\nAppend this prompt to the shared child prompt for every second-wave child, followed by the matching logged-out or logged-in flow prompt and one branch assignment synthesized from the baseline reports:\n\n```text\nYou own one follow-up onboarding branch selected by the parent orchestrator from an earlier baseline exploration report.\n\nFollow-up branch behavior:\n- Start from a fresh first-run Warp state and install the same latest stable Linux build using the shared instructions.\n- Respect the assigned auth state: remain logged out for logged-out assignments, or use the managed authenticated flow for logged-in assignments.\n- Follow the exact alternate onboarding choice or action sequence in the branch assignment.\n- Capture screenshots before and after each assigned branch decision, then continue to a usable terminal session if the path allows it.\n- Apply the same UI quality review standard as the baseline explorers and call out anything that looks broken, rough, misaligned, confusing, or unexpectedly error-prone.\n- If the assigned branch is not reachable, capture the closest relevant screen, report why it was unreachable, and do not silently substitute a different branch.\n- If the assigned branch reveals another interesting alternate path, record it as a later-run suggestion rather than recursively launching more agents yourself.\n\nFinal report additions:\n- Repeat the exact branch assignment you attempted in concise non-sensitive terms.\n- State whether it was reachable, completed, blocked, or not applicable.\n- Compare the branch outcome against the likely baseline behavior when that comparison is visible from the UI.\n```\n\n## Success criteria\n\nThe run is successful when:\n\n- Warp stable was installed from an official Linux package or AppImage for the detected architecture.\n- Screenshots were captured for each onboarding screen and the final usable terminal.\n- The logged-out child reached a usable terminal without login, account creation, or a real user identity.\n- The logged-in child authenticated using `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` and reached a usable terminal in the authenticated FTUE path.\n- The logged-in child captured an additional post-login screenshot from the avatar/settings flow showing the logged-in user's email address.\n- Each terminal session was usable enough to run its flow-specific `echo` command.\n- Both baseline explorers returned either concrete follow-up coverage proposals or an explicit explanation that they did not observe meaningful additional branch points.\n- The parent orchestrator launched targeted second-wave agents for the highest-value concrete branch proposals, unless there were no such proposals or a prerequisite blocker made them infeasible.\n- Every reported visual polish concern, suspected bug, or error state includes a screenshot reference whenever the issue was visible on screen.\n\n## Common failure handling\n\n- If the package manager prompts for confirmation, use the non-interactive confirmation flag supported by that package manager.\n- If launching `warp-terminal` fails because of display setup, inspect the cloud environment's display variables and try launching from the desktop/app launcher if computer use provides one.\n- If the logged-out flow blocks on login with no skip path, stop at that screen, capture a screenshot, and report that as the terminal point for the logged-out flow.\n- If the logged-in flow cannot authenticate because the secret is missing, invalid, expired, revoked, or cannot be routed through Warp's auth redirect flow, stop at that screen, capture a screenshot, and report the non-sensitive blocker.\n- If the native package cannot be installed because dependencies are unavailable, fall back to the matching AppImage and clearly report the fallback.\n",".agents/skills/gui-ui-guidelines/SKILL.md":"---\nname: gui-ui-guidelines\ndescription: GUI desktop app only. Catalog of guidelines for writing UI code in the Warp client. Read whenever doing any UI work in this repo, up front before writing the change, so the relevant guidelines shape the implementation.\n---\n\n# gui-ui-guidelines\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nThis skill is a growing catalog of guidelines for working on Warp's UI code. Each guideline captures a lesson that would otherwise be re-learned through review — typically because an agent or contributor reinvented a component, drifted from the design system, or bypassed a shared abstraction.\n\n**How to use this skill:**\n\n- Read through the guidelines below once at the start of any UI task, then keep them in mind while implementing. The list is short enough to scan.\n- Each guideline is self-contained. Not every one will apply to every task — use judgment. But if a guideline *does* apply, follow it.\n- When in doubt, prefer reusing an existing abstraction over introducing a new one. The Warp UI has accumulated a well-factored set of shared components and themes; new one-offs almost always drift.\n\nFor work on the settings modal (`app/src/settings_view/`) — a settings page, a `SettingsWidget`, or anything that affects settings search — also read the `gui-settings-ui` skill, which covers the page-title, widget-gating, and widget-splitting rules specific to that surface.\n\nNew guidelines get added here over time. If you discover a recurring UI mistake that would have been caught by a written rule, add it.\n\n---\n\n## Guideline: Reuse button themes\n\nButton colors come from a shared set of `ActionButtonTheme` impls in `app/src/view_components/action_button.rs` (and the parallel `Theme` impls in `crates/ui_components/src/button/themes.rs`) — `PrimaryTheme`, `SecondaryTheme`, `NakedTheme`, `DangerPrimaryTheme`, etc. These encode the design system and keep button colors consistent across the app.\n\nWhen styling a button, **use one of the existing themes unchanged**. The shared themes are well-established and vetted; if one looks \"wrong\" for your use case, the most likely explanation is that you're reaching for the wrong theme, not that the theme is buggy.\n\nDo **not** modify a shared theme on your own initiative. Changing `PrimaryTheme`, `SecondaryTheme`, etc. affects every button in the app, and a tweak that fixes your screen can silently regress others. Only edit a shared theme when the user has explicitly confirmed that the design-system component itself needs to change.\n\nRed flags that you're about to make buttons inconsistent:\n\n- Writing a new `impl ActionButtonTheme for FooPrimaryTheme` that delegates to `PrimaryTheme` and only tweaks one method (usually `text_color`). Almost always the right move is to use `PrimaryTheme` directly and accept the result.\n- Hard-coding `ColorU::new(...)` instead of using `appearance.theme()` accessors (`accent`, `font_color(bg)`, `foreground`, etc.).\n- Setting `should_opt_out_of_contrast_adjustment` to `true` to force a specific label color.\n- Naming a theme after a feature or view (`FooPrimaryTheme`, `BarSubmitTheme`) rather than a design-system role.\n\nIf an existing theme genuinely doesn't fit and you think the shared theme should change, surface that to the user before editing it, rather than either editing it unilaterally or papering over it with a one-off.\n",".agents/skills/gui-reproduce-bug-report-local/SKILL.md":"---\nname: gui-reproduce-bug-report-local\nspecializes: reproduce-bug-report\nspecializes_source: warpdotdev/common-skills:.agents/skills/reproduce-bug-report\ndescription: GUI desktop app only. Repo-specific bug reproduction guidance for Warp. Specializes the core reproduce-bug-report skill for logged-out Warp UI repros, exact reporter-version installs, and login-free onboarding.\n---\n\n# Repo-specific bug reproduction guidance for `warp`\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\n## Prerequisite: install the parent skill\n\nThis skill specializes the core `reproduce-bug-report` skill (named in the `specializes` frontmatter field) and is not functional on its own. Before applying its guidance, confirm the parent skill is installed and resolvable at `.agents/skills/reproduce-bug-report/SKILL.md`. If it is missing, install it first by copying the skill directory from the source declared in the `specializes_source` frontmatter field (`warpdotdev/common-skills:.agents/skills/reproduce-bug-report`). Then continue with the guidance below.\n\nThis file is a companion to the core `reproduce-bug-report` skill. It does not redefine the shared Oz computer-use orchestration, artifact handling, safety rules, or reporting format. It specializes scope and setup for Warp bug reports.\n\n## Scope\n\n- Use this workflow only for Warp bugs that can be exercised while the app remains logged out.\n- Apply it to UI-visible Warp bugs, interaction bugs, rendering/layout bugs, logged-out onboarding bugs, settings bugs, editor/display bugs, terminal-display bugs, and other visual or interactive issues where screenshots or recordings would help.\n- Do not use it for authenticated-user flows, account-specific state, cloud-synced state, logged-in onboarding, or AI behaviors that require login.\n- If a report requires authentication, account state, cloud sync, or another logged-in-only capability, do not launch a repro agent with this local specialization; report that it is out of scope for the current logged-out Warp workflow.\n\n## Warp version and install strategy\n\n- Prefer reproducing against the exact Warp version/build and channel reported by the user.\n- Do not build Warp from source by default. Install the matching Linux package or binary release for the reporter's version/channel instead.\n- If the bug report names a macOS or Windows build, use the corresponding Linux build from the same version/channel when a matching Linux artifact exists, and state that this is a Linux proxy for the reporter's platform.\n- Use the repository's or Warp release tooling/docs available in the environment to find and install the exact versioned Linux artifact. Do not silently substitute the latest stable build when an exact matching version can be installed.\n- If the exact version/build cannot be found or installed, report that clearly, explain what was attempted, and use the closest justified fallback only when it is useful for continuing the investigation.\n- Record the requested reporter Warp version, the installed Linux version, the source of the installed artifact, and any fallback decision in the manifest and final report.\n\n## Logged-out Warp baseline\n\n- Keep Warp logged out for the entire repro attempt. Do not create an account, sign in, paste auth tokens, or use real user credentials.\n- Launch Warp and complete the login-free / continue-without-account onboarding path until a normal logged-out terminal session is usable.\n- Capture a post-onboarding baseline screenshot before attempting the bug-specific reproduction.\n- If the assigned bug cannot be exercised after entering a normal logged-out Warp session, stop and report the blocker instead of improvising an authenticated flow.\n\n## Local prompt additions\n\nWhen applying the core skill to Warp, ensure the parent prompt and child prompts include:\n\n- Reporter Warp version/build/channel: the exact value from the report, or `unknown`.\n- Build/app target: the exact versioned Linux Warp package/binary to install, or the justified fallback if an exact artifact is unavailable.\n- Assigned Warp state: first-run logged-out state, completed logged-out onboarding, terminal/session/layout/settings state, or the targeted code-path hypothesis.\n- A reminder that Warp must remain logged out and that logged-in-only reports are blocked for this specialization.\n\n## Local reproduction priorities\n\n- Match the reporter's Warp version/build/channel before broadening the search space.\n- Follow the issue's exact steps first, then test at most two targeted variations supported by the issue or by a code-path hypothesis.\n- Prefer targeted hypotheses derived from Warp UI strings, settings names, feature names, telemetry names, route names, and relevant components over broad exploratory clicking.\n- In the final report, include:\n  - the reporter-requested Warp version/build/channel\n  - the installed Linux Warp version/build/channel\n  - the package or binary source\n  - whether a fallback was used\n  - whether the test was a Linux proxy for a macOS or Windows report\n",".agents/skills/gui-integration-test/SKILL.md":"---\nname: gui-integration-test\ndescription: GUI desktop app only. Writes, runs, and debugs Warp integration tests using the custom Builder/TestStep framework in `crates/integration`. Use when adding a new integration test, fixing a failing integration test, wiring a test into the manual runner or nextest suite, or verifying end-to-end UI and terminal behavior in Warp.\n---\n\n# Warp Integration Tests\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nUse this skill for Rust integration tests in Warp's custom framework under `crates/integration/`.\n\nThese are not ordinary unit tests. They boot a real Warp app instance, give it an isolated test home directory, drive it with synthetic UI and terminal events, and poll assertions until success or timeout.\n\n## When an integration test is the right tool\n\nIntegration tests are the most expensive tests in the repo. They boot the app, they are orders of magnitude slower than a unit test, and they are the most likely to go flaky. Write one when the risk you are covering genuinely lives *between* components:\n\n- Real terminal/PTY and shell-integration behavior, especially across bash, zsh, and fish.\n- Wiring and configuration: settings, keybindings, and user preferences actually taking effect end to end.\n- Cross-component flows a unit test cannot see — command palette into editor into terminal, focus and pane management, tab/window lifecycle.\n- Input and rendering paths that only exist once a real app (and sometimes a real display) is running.\n- Regressions for user-visible bugs that escaped unit tests.\n\nDo **not** reach for an integration test when:\n\n- The logic is deterministic and reachable in-process. Push it into a unit test (`rust-unit-tests`); it will run in milliseconds and point straight at the failure.\n- You are re-covering branch logic a unit test already covers. This harness is for the seams between components, not for re-testing conditionals at full app-boot cost.\n- You only need TUI rendering. That is `tui-testing` — this harness does not drive the TUI at all.\n- What you actually want is a screenshot or a manual look. Use `computer_use` or the `gui-integration-test-video` skill instead of attaching weak assertions to a full app boot.\n\nIf a behavior is hard to reach from a unit test *only* because of how the code is structured, prefer fixing the structure over writing a slow test around it.\n\n## Framework map\n\nThe core pieces are:\n\n- `crates/integration/src/bin/integration.rs`\n  - Manual integration test runner binary.\n  - Registers test names to `Builder` factories.\n  - Runs exactly one named test per invocation.\n- `crates/integration/tests/common/mod.rs`\n  - The outer Rust test harness used by `cargo test` and `cargo nextest`.\n  - Shells out to the integration binary.\n  - Forwards a limited set of env vars (`PATH`, `RUST_*`, `WARP_*`, `WARPUI_*`, `WGPU_*`, display-related vars).\n  - Re-runs tests up to 10 times when the integration binary exits with the special rerun code.\n- `crates/integration/src/test.rs`\n  - Module hub for integration tests.\n  - Add new test modules here and `pub use` their functions so the runner can see them.\n- `crates/integration/tests/integration/ui_tests.rs`\n  - List of UI-oriented integration tests that nextest should run.\n- `crates/integration/tests/integration/shell_integration_tests.rs`\n  - List of tests that must run against every shell or a specific shell matrix.\n- `crates/integration/src/builder.rs`\n  - Warp-specific wrapper around the lower-level WarpUI integration builder.\n  - Sets default timeout, hermetic home directory, shell rc files, user prefs, and real-display mode when requested.\n- `crates/warpui_core/src/integration/driver.rs`\n  - Executes steps, handles retries, precondition reruns, screenshots, video capture, artifact export, and `on_finish`.\n- `crates/warpui_core/src/integration/step.rs`\n  - Defines `TestStep`, input/event APIs, assertion polling, step-to-step data passing, and screenshot/recording hooks.\n- `app/src/integration_testing/`\n  - High-level helpers and assertions for common Warp behaviors.\n  - Prefer these helpers over raw low-level event plumbing whenever they fit.\n\n## How the framework actually runs a test\n\n1. A Rust test from `crates/integration/tests/integration/*.rs` calls `run_integration_test(\"test_name\")`.\n2. That harness launches the `integration` binary with the test name.\n3. The binary in `crates/integration/src/bin/integration.rs` looks up the name in `register_tests()`, builds the `Builder`, and turns it into a `TestDriver`.\n4. `Builder::build(...)` creates an isolated temp directory, points `HOME` at it, writes minimal rc files, and initializes file-backed user preferences.\n5. The driver runs each `TestStep` in order:\n   - setup callbacks\n   - synthetic events\n   - actions\n   - assertion polling until success or timeout\n6. If an assertion returns `PreconditionFailed`, the binary exits with the rerun code and the outer harness retries the whole test.\n7. On success, failure, or cancellation, the driver can run `on_finish` and export artifacts/runtime tags.\n\nThis means integration tests should be written for a hermetic environment. Do not rely on the developer's real shell dotfiles, home directory contents, or persisted Warp settings.\n\n## Where to put a new test\n\nAdd the actual test function in a module under `crates/integration/src/test/`.\n\nUse these heuristics:\n\n- Put the test in an existing module when it matches that feature area.\n- Create a new module when the feature does not fit an existing one cleanly.\n- Add the test to `crates/integration/tests/integration/ui_tests.rs` if it is primarily a UI/app behavior test.\n- Add the test to `crates/integration/tests/integration/shell_integration_tests.rs` if it needs to run against every shell, or depends on a specific shell/set of shells.\n\nBeing present in `crates/integration/src/test/*.rs` is not enough. For a test to run under `cargo nextest`, it also needs to be listed in one of the macro files in `crates/integration/tests/integration/`.\n\n## Authoring checklist for a new test\n\nWhen adding a new integration test, do all of the following:\n\n1. Implement `pub fn test_name() -> Builder` in a module under `crates/integration/src/test/`.\n2. Add the module to `crates/integration/src/test.rs`.\n3. `pub use` the new module's exports from `crates/integration/src/test.rs`.\n4. Add `register_test!(test_name);` in `crates/integration/src/bin/integration.rs`.\n5. Add `test_name` to either:\n   - `crates/integration/tests/integration/ui_tests.rs`, or\n   - `crates/integration/tests/integration/shell_integration_tests.rs`\n6. Default to making the test run in CI once it is added to one of those macro lists. Only mark it `#[ignore]` when the task explicitly calls for manual-only coverage or there is a concrete, documented reason it cannot run reliably in CI.\n7. Run the test manually first, then through nextest once it is stable enough for the suite you chose.\n\n## Writing the test body\n\nThe normal shape is:\n\n```rust\nuse crate::Builder;\nuse warp::integration_testing::step::new_step_with_default_assertions;\nuse warp::integration_testing::terminal::{\n    clear_blocklist_to_remove_bootstrapped_blocks,\n    execute_command_for_single_terminal_in_tab,\n    wait_until_bootstrapped_single_pane_for_tab,\n    util::ExpectedExitStatus,\n};\n\npub fn test_example() -> Builder {\n    Builder::new()\n        .with_step(wait_until_bootstrapped_single_pane_for_tab(0))\n        .with_step(clear_blocklist_to_remove_bootstrapped_blocks())\n        .with_step(execute_command_for_single_terminal_in_tab(\n            0,\n            \"echo hello\".to_string(),\n            ExpectedExitStatus::Success,\n            \"hello\".to_string(),\n        ))\n        .with_step(\n            new_step_with_default_assertions(\"Assert some UI state\")\n                .add_named_assertion(\"specific assertion name\", |app, window_id| {\n                    // inspect app state and return AssertionOutcome\n                    warpui::integration::AssertionOutcome::Success\n                }),\n        )\n}\n```\n\nPrefer a small number of focused steps with descriptive names over a huge monolithic test.\n\n## Builder guidance\n\n### `Builder::new()`\n\nStart here almost every time.\n\nWarp's wrapper automatically gives you:\n\n- a per-test root directory\n- isolated `HOME`\n- generated rc files for Bash, Zsh, and Fish\n- file-backed user preferences\n- a default 2-minute hard timeout\n- real-display support if `WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS` is present\n\n### `with_setup(...)`\n\nUse this for filesystem or environment setup before the app runs.\n\nCommon patterns:\n\n- `utils.set_env(\"NAME\", Some(value))`\n- creating files under `utils.test_dir()`\n- writing fixture config files\n\nPrefer this over reaching into the real filesystem.\n\n### `with_user_defaults(...)`\n\nUse this to set persisted Warp preferences before the test starts.\n\nThis is the right tool for settings backed by user preferences rather than environment variables.\n\n### `set_should_run_test(...)`\n\nUse this to gate tests on shell/platform/runtime capabilities when the test genuinely cannot run everywhere.\n\n### `with_on_finish(...)`\n\nUse this for final verification or artifact inspection that should happen after all steps complete, such as checking that screenshots or recordings were written.\n\n### `with_real_display()`\n\nUse this explicitly when the test needs a real display for frame capture or visual workflows. Video/screenshot tests should normally be manual or ignored in CI unless there is a stable real-display path.\n\n## `TestStep` guidance\n\n`TestStep` is the unit of execution. Each step can have:\n\n- setup callbacks\n- input events\n- actions\n- assertions\n- a timeout\n- retry count\n- failure handling\n\n### Start from helper constructors\n\nPrefer:\n\n- `wait_until_bootstrapped_single_pane_for_tab(0)`\n- `new_step_with_default_assertions(\"...\")`\n- `new_step_with_default_assertions_for_pane(\"...\", tab, pane)`\n\nThe default step helpers already assert:\n\n- no pending model events\n- no block executing\n\nThese are good baseline invariants for most UI interactions.\n\n### Prefer helper APIs over raw event plumbing\n\nUse high-level helpers from `app/src/integration_testing/` whenever possible:\n\n- terminal command execution helpers\n- block list helpers\n- command palette helpers\n- navigation helpers\n- settings helpers\n- workflow/file tree/notebook helpers\n\nDrop to raw `with_event(...)`, `with_event_fn(...)`, or saved-position mouse events only when there is no suitable helper.\n\n### Use named assertions\n\nPrefer `add_named_assertion(...)` over unnamed assertions. Named assertions make failure output and runtime tags much easier to interpret.\n\n### Use polling assertions instead of sleeps\n\nAssertions are polled until success or timeout. Lean on that model instead of hardcoding sleeps.\n\nGood pattern:\n\n- trigger an event or action\n- assert on the eventual UI/model state\n\nAvoid brittle timing assumptions.\n\n### Use step data when one step computes something for the next\n\nIf a later step needs data from an earlier one, use:\n\n- `add_named_assertion_with_data_from_prior_step(...)`\n- `StepDataMap`\n\nThis is useful for saving measured positions, counts, IDs, or other values from prior frames.\n\n### Use retries sparingly\n\n`set_retries(...)` can help for a legitimately retryable step, but do not use it to hide deterministic failures. Prefer making the step more robust first.\n\n### Use `PreconditionFailed` for genuinely environmental flakes\n\nIf the environment reaches a state where the rest of the test is invalid, return `AssertionOutcome::PreconditionFailed(...)` instead of failing hard. The outer harness can rerun the entire test up to 10 times. The existing bootstrap helper is a good model for this.\n\nUse this deliberately. The rerun mechanism exists for conditions the test genuinely cannot control, such as bootstrap racing or shell startup timing. It is not a way to turn an intermittently failing test green. A real bug that reproduces one run in five will pass under rerun and ship to users.\n\nBefore reaching for `PreconditionFailed`, confirm the failure is actually environmental by looking at the failure rate and the failure mode:\n\n```bash\nfor i in {0..50}; do\n  RUST_BACKTRACE=full cargo run -p integration --bin integration -- test_name || break\ndone\n```\n\nIf the same assertion fails in different ways, or fails while the environment looks fine, it is a product or test bug. Fix it rather than absorbing it into a rerun.\n\n## Designing a good integration test\n\n### Keep the scope as small as the behavior allows\n\nThe scope of a test follows the scope of what you boot and drive, so cover one user journey per test and let separate tests cover separate journeys. When a flow is long, prefer several shorter tests that each verify one hop over a single test that walks the whole path. A long test is slower, harder to diagnose, and fails for many unrelated reasons.\n\n### Assert on behavior a user could observe\n\nThe framework can see internal state, which makes it tempting to assert on it. Anchor each test on the user-observable outcome:\n\n- output visible in the terminal\n- focus moved where expected\n- UI element opened or closed\n- selection changed\n- settings applied\n\nInternal state assertions are still useful, but they should support the visible behavior rather than replace it. Assertions that mirror internal call sequences become change detectors: they break on every refactor and catch no bugs.\n\n### Make the failure diagnosable by someone else\n\nAn integration test spans processes, so a stack trace tells the next engineer almost nothing. The failure output has to carry the context instead:\n\n- Name every assertion with what it expects, not what it touches: `\"terminal shows command output\"`, not `\"check blocks\"`.\n- Give steps descriptive names; they are the breadcrumb trail through the run.\n- Put expected vs. actual into the assertion output rather than returning a bare failure.\n\nAssume the person reading the failure has never seen this test and does not own the code it covers.\n\n### Keep the test hermetic\n\n`Builder::new()` already gives you an isolated `HOME`, generated rc files, and file-backed preferences. Preserve that. Set up everything you depend on inside the test via `with_setup(...)` and `with_user_defaults(...)`, and never rely on the developer's dotfiles, real settings, network access, or state left behind by another test. A test that assumes a resource is already in the right state will fail for the wrong reason on someone else's machine or in CI.\n\n### Own the test\n\nLarger tests span components, so ownership is ambiguous by default and unowned tests rot. If you add one, you are the person who fixes it when it breaks. Put it in the module matching the feature area it covers so the next person can find the right owner.\n\n## Common test-writing patterns\n\n### 1. Wait for bootstrap first\n\nFor most terminal-facing tests, the first real step should be:\n\n- `wait_until_bootstrapped_single_pane_for_tab(0)`\n\nDo not start asserting on terminal UI before bootstrap completes.\n\n### 2. Clear the bootstrapped blocks if block indices matter\n\nIf the test relies on saved positions like `block_index:0`, clear the block list after bootstrap:\n\n- `clear_blocklist_to_remove_bootstrapped_blocks()`\n\nOtherwise the first user-generated block index depends on bootstrap output and the active shell.\n\n### 3. Use helper command runners\n\nPrefer helpers like:\n\n- `execute_command_for_single_terminal_in_tab(...)`\n- `execute_echo(...)`\n- `execute_echo_str(...)`\n- `execute_long_running_command(...)`\n\nThese helpers already handle a lot of correctness and output validation.\n\n## Running tests\n\n### Run one test directly through the integration binary\n\nUse this first while authoring:\n\n```bash\ncargo run -p integration --bin integration -- test_name\n```\n\nThis is the fastest way to iterate on a specific test because it bypasses the outer Rust test wrapper and runs the named test directly.\n\n### Run one test through nextest\n\nOnce it is wired into one of the `tests/integration/*.rs` macro lists, run it with nextest:\n\n```bash\ncargo nextest run --no-fail-fast --workspace test_name\n```\n\n### Run with a real display when needed\n\nFor screenshot/video or other real-display flows:\n\n```bash\nWARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo run -p integration --bin integration -- test_name\n```\n\nOr with nextest:\n\n```bash\nWARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo nextest run --no-fail-fast --workspace test_name\n```\n\n## Debugging and investigation\n\n### Get a backtrace on failures\n\n```bash\nRUST_BACKTRACE=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Pause on failure\n\nThis is useful when running locally and you want to inspect the failed UI state:\n\n```bash\nWARPUI_PAUSE_INTEGRATION_TEST_ON_FAILURE=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Pause after every step\n\nUseful for understanding exactly what the test is doing:\n\n```bash\nWARPUI_PAUSE_INTEGRATION_TEST_AT_EVERY_STEP=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Video and screenshots\n\nIf the task is specifically about recording a test, collecting screenshots, or validating overlay/video artifacts, also use the `gui-integration-test-video` skill (located at `.warp/skills/gui-integration-test-video/SKILL.md`).\n\n### Environment variable gotcha\n\n`utils.set_env(...)` affects runtime environment lookups such as `std::env::var(...)`.\n\nIt does not affect compile-time lookups like `option_env!(...)`. If the product code uses `option_env!`, changing the env var inside the test will not change that behavior without rebuilding.\n\n## Verification checklist\n\nBefore considering a new integration test done, verify all of the following:\n\n- The test function lives under `crates/integration/src/test/`.\n- The module is added and re-exported in `crates/integration/src/test.rs`.\n- The test is registered in `crates/integration/src/bin/integration.rs`.\n- The test is listed in the correct nextest macro file and will run in CI by default, unless it was explicitly made manual-only with a documented reason.\n- The test passes when run directly through the integration binary.\n- The test passes through nextest if it is meant to be part of the automated suite.\n- An integration test is the right level for this behavior; it is not re-covering logic a unit test could reach.\n- The assertions check the intended user-visible behavior.\n- Every assertion is named, and the failure output would be actionable to someone who has never seen the test.\n- The test does not depend on the developer's real home directory, shell config, or machine state.\n- If the test uses screenshots/video, the produced artifacts were actually inspected rather than only assuming they exist.\n\n## Anti-patterns to avoid\n\n- Writing a test only in `src/test/*.rs` and forgetting the nextest macro list.\n- Asserting on bootstrap-sensitive block indices without clearing the bootstrapped blocks first.\n- Using raw events everywhere when a helper already exists.\n- Adding sleeps instead of assertion polling.\n- Making the test depend on personal dotfiles, real settings, or non-hermetic filesystem state.\n- Using retries or `PreconditionFailed` to paper over a deterministic bug.\n- Re-testing branch logic that a unit test already covers, at full app-boot cost.\n- Asserting on internal call sequences instead of user-visible behavior.\n- Unnamed assertions that produce failure output nobody can act on.\n- One long test that walks an entire user journey instead of several focused ones.\n- Leaving a real-display/manual test enabled in CI without a stable path.\n\n## Good workflow for agents\n\nWhen asked to add or fix an integration test:\n\n1. Find the closest existing integration test module for the feature.\n2. Reuse helper assertions and step constructors before inventing new low-level plumbing.\n3. Register the test in all required places, not just the implementation file.\n4. Run the test manually first.\n5. If it belongs in automation, run it with nextest too.\n6. If the test exercises visual behavior, verify the resulting UI behavior or artifacts directly.\n",".agents/skills/gui-settings-ui/SKILL.md":"---\nname: gui-settings-ui\ndescription: GUI desktop app only. How to build a Settings page in the Warp client (app/src/settings_view) so its widgets and settings search behave correctly — picking a PageType, deciding whether a heading belongs in the page-title slot or inside a widget, gating a widget, and scoping search_terms per widget. Use when adding or editing a settings page, a SettingsWidget, or anything that affects settings search.\n---\n\n# gui-settings-ui\n\n**Scope — GUI desktop app only.** This skill covers the settings modal in `app/src/settings_view/`, part of Warp's **GUI** desktop front-end. It does not apply to the headless TUI (`crates/warp_tui`). For general UI conventions see `gui-ui-guidelines`.\n\nSettings pages look simple, so they get written by pattern-matching the nearest neighbor — and the nearest neighbor is often wrong. The same two mistakes have produced five Linear tickets (APP-5060, APP-5058, APP-4910, APP-4922, APP-5059, one of which turned out to be a false positive). Read this before writing a settings page so the sixth doesn't happen.\n\n## The model\n\nA settings page is a `PageType` (`app/src/settings_view/settings_page.rs`). It holds **a list of searchable widgets** plus **an optional page title**:\n\n```rust\nPageType::new_uncategorized(widgets, Some(\"Knowledge\"))\nPageType::new_categorized(categories, None)\nPageType::new_monolith(widget, Some(\"Billing and Usage\"), /* is_dual_scrollable */ true)\n```\n\n- **`Uncategorized`** — a flat list of widgets. The common shape.\n- **`Categorized`** — widgets grouped under `Category`s, each with a subheader (rendered via `render_sub_header`) and an optional subtitle.\n- **`Monolith`** — the whole page is a single widget because its content can't be split for search (Keybindings, Teams, About, Environments).\n\nEach widget implements `SettingsWidget` (`settings_page.rs`):\n\n- `search_terms(&self) -> &str` — the terms this widget matches on.\n- `render(..)` — the widget's rows.\n- `should_render(&self, app) -> bool` — defaults to `true`. This is one of **two** ways to conditionally show a setting, and usually not the right one — see \"Two ways to gate a widget\" below.\n- `widget_id()` / `static_widget_id()` — default to `std::any::type_name::<Self>()`, used for scroll-to and deeplinks.\n\n**How search filters.** `PageType::update_filter` keeps a widget when\n`widget.should_render(app) && search_terms_match(widget.search_terms(), query)`.\n`search_terms_match` requires **every whitespace-delimited word** of the query to appear (case-insensitively, as a substring) somewhere in the widget's terms; an empty query matches everything. So filtering happens **per widget** — a widget is the smallest unit that can survive or disappear.\n\n**Whether the page title survives a search depends on the variant.** `PageType::render_page` treats the title differently for widget-list pages than for a monolith, and that difference is the easiest thing here to get wrong.\n\nFor **`Uncategorized` and `Categorized`**, the title is drawn **once, before the loop** over the filtered widget list, unconditionally:\n\n```rust\nlet mut page = Flex::column();\nif let Some(title) = title {\n    page.add_child(render_page_title(title, HEADER_FONT_SIZE, appearance));\n}\nfor widget in widgets { /* … only the widgets that matched … */ }\n```\n\nThe filter cannot reach it. That is what makes the title slot a fix for bug class 1: a title passed through `PageType` survives filtering on these two variants, while a title rendered inside a widget does not.\n\nFor **`Monolith`**, the title is **gated on the sole widget surviving the filter**:\n\n```rust\nFilteredPageType::Monolith { widget, title, .. } => {\n    let mut page = Empty::new().finish();\n    if let Some(widget) = widget          // None when the widget didn't match\n        && widget.should_render(app)\n    {\n        if let Some(title) = title { /* … title, then the widget … */ }\n    }\n    page                                  // otherwise nothing renders at all\n}\n```\n\n`get_filtered` sets that `widget` to `filter.then_some(..)`, so a non-matching query makes it `None` and the page renders empty — **title included**. On a monolith the title slot buys no search protection; the page is all-or-nothing, title and all.\n\nOne more thing worth knowing before you go looking for a title stranded over zero rows: you won't find one. `SettingsView::filtered_pages` (`app/src/settings_view/mod.rs`) drops any page whose `MatchData` is falsy from the sidebar and auto-selects the first page that still matches, so a fully non-matching page is never displayed. The state the title slot actually protects is the **partial** match — some widgets survive, some don't — and only widget-list pages can be in it.\n\n## Two ways to gate a widget\n\nThere are two mechanisms for conditionally showing a setting, and they are not interchangeable.\n\n**1. An `if` at page-build time — never create the widget.** This is how most gating in `AISettingsPageView::build_page` (`ai_page.rs`) is written:\n\n```rust\nif FeatureFlag::AIRules.is_enabled() {\n    widgets.extend(Self::knowledge_widgets());\n}\nif cfg!(feature = \"voice_input\")\n    && ai_settings.voice_input_enabled_internal.is_supported_on_current_platform()\n{\n    widgets.push(Box::new(VoiceWidget::default()));\n}\n```\n\n**2. `should_render(&self, app) -> bool` — create the widget and let it opt out per pass.**\n\n**Which one: can the value change while the app is running?**\n\n- **Fixed for the process** — a feature flag, a `cfg!` feature, a platform-support check. → **Use the build-time `if`.** This is the preferred default: the widget never exists, so there is nothing to filter, render, or reason about.\n- **Can change at runtime** — a setting the user toggles, auth state, an availability check that can flip mid-session. → **Use `should_render`.**\n\nThe reason is *when each is evaluated*. The build-time `if` runs once, when the page is constructed, and its result is frozen until something rebuilds the page (for AI/Code subpages, only switching subpages does — see below). `should_render` is re-evaluated on every filter and render pass, so it tracks a value that changes while the settings page is open. Gate a runtime-changing value with a build-time `if` and the page goes stale; gate a static flag with `should_render` and you carry a widget around for nothing.\n\nThe real `should_render` users are all the runtime kind: `SettingsSyncWidget` (`main_page.rs`) on auth state, `WarpDriveToggleWidget` (`warp_drive_page.rs`) on `WarpDriveSettings::is_warp_drive_available`, and the CLI-agent rich-input widgets (`ai_page.rs`) on the user-toggleable footer setting, via `should_render_cli_agent_rich_input`.\n\nEither mechanism keeps search honest: an uncreated widget isn't in the list, and `update_filter` already skips a widget whose `should_render` is false. What you must never do is hide rows inside `render` while `search_terms` still advertises them — that makes the page match a query and then show nothing for it.\n\n## The decision that matters: where does the heading live?\n\nFor any heading on a settings page, ask **what does it name?**\n\n- **It names the whole page** → it belongs in the `PageType` title slot. On `Uncategorized` / `Categorized` that is also what keeps it on screen while the page is filtered; on a `Monolith` it is a structural choice only (see above).\n- **It names one section among several** → it belongs inside the widget (or `Category`) that owns that section, so it disappears together with its own rows.\n\nGetting this wrong in the first direction is bug class 1 below. Getting it wrong in the second direction leaves a section header stranded above unrelated rows.\n\n### Page-shape taxonomy\n\nClassify the page before you write it:\n\n- **Single-topic page** — one heading names everything on it, but the content is still made of separately-matchable widgets. Knowledge, Third party CLI agents, Editor and Code Review, Account, Scripting. → **Title in the `PageType` slot.**\n- **Multi-section page** — several independent sections, each with its own heading. Warp Agent, Agent profiles, Appearance, Features. → **Per-section headings live in widgets/categories** and correctly disappear with their rows. (For `Categorized`, `get_filtered` drops categories whose widgets all filtered out, so their subheaders vanish automatically — that's the behavior you want.)\n- **Monolith page** — Keybindings, Teams, About, Environments, Codebase Indexing. There is no partial-match state: the sole widget either matches, and the whole page renders, or it doesn't, and the whole page renders empty and drops out of the sidebar. So a monolith can never strand an orphaned setting under a missing heading — it is **not** affected by bug class 1, but for that reason, *not* because its title is protected. Passing the title through the slot is still the tidier structure (Billing and Usage and Referrals do), just don't expect it to keep the title on screen during a non-matching search; on a monolith it will not. Codebase Indexing looks single-topic but is really one unfilterable widget covering the whole page (`CodePageWidget`, wrapped by `CodeIndexingPageWidget`) — building it as `Uncategorized` instead of `Monolith` made the sidebar show a permanent, misleading \"(1)\" on any match ([APP-5530]).\n\nThe two are not mutually exclusive: a page can name itself in the title slot **and** have per-section subheaders inside its widgets. Privacy does exactly that — `PageType::new_uncategorized(widgets, Some(\"Privacy\"))` plus `render_sub_header` calls inside individual widgets. The rule is per heading, not per page.\n\nWorked positive example: **Scripting** (`scripting_page.rs`) is a small page done right — two focused widgets (`WarpControlCliInstallWidget`, `LocalControlModeWidget`) with their own `search_terms`, and `PageType::new_uncategorized(widgets, Some(\"Scripting\"))`. A ticket was once filed claiming Scripting needed splitting; it was canceled because the premise was wrong. A small page is not automatically a mega-widget.\n\n## Bug class 1 — the vanishing title\n\n**Symptom:** on a single-topic `Uncategorized` or `Categorized` page, typing a search term that matches one row makes the page heading disappear along with the non-matching rows, leaving an unlabeled orphan setting. (A monolith can't reach this state — see the taxonomy above.)\n\n**Cause:** the only heading was rendered inside a widget — via `build_sub_header` / `render_page_title` in that widget's `render`, or via a header-only widget that exists just to draw a title. Filtering removes the widget, and the heading goes with it.\n\nCanonical fix — commit `ddadcee` ([APP-5060], #14519), Knowledge. Before, `AIFactWidget::render` opened with:\n\n```rust\nlet header = build_sub_header(appearance, \"Knowledge\", …).finish();\nlet mut column = Flex::column().with_child(header) /* … all the Knowledge rows … */;\n```\n\nAfter, the heading moved to page chrome and the rows became focused widgets:\n\n```rust\nlet title = match subpage {\n    AISubpage::Knowledge => Some(\"Knowledge\"),\n    AISubpage::ThirdPartyCLIAgents => Some(\"Third party CLI agents\"),\n    AISubpage::WarpAgent | AISubpage::Profiles => None,\n};\nPageType::new_uncategorized(widgets, title)\n```\n\n(The `ThirdPartyCLIAgents` arm and the exhaustive `match` came from #14524; note the deliberate absence of a `_` arm, so a new subpage forces this decision.)\n\nThe same commit (#14524) deleted `CodeSubpageHeaderWidget` from the then-combined Code page — a widget whose entire job was `build_sub_header(appearance, self.title, None)` — and replaced it with a title passed through `PageType`. Those two halves are now separate pages, `code_indexing_page.rs` and `code_editor_review_page.rs`, each passing its own `PAGE_TITLE` through the slot. **A header-only widget is always this bug.** If a widget renders nothing but a title, delete it and use the title slot.\n\n## Bug class 2 — the unfilterable mega-widget\n\n**Symptom:** searching a term that should isolate one row shows every row on the page, and the sidebar match count is 1 no matter how specific the query is.\n\n**Cause:** one widget renders many unrelated settings and declares one mega `search_terms()` blob covering all of them. The widget is the filter unit, so it's all-or-nothing.\n\nBefore (`CLIAgentWidget`, pre-#14524) — one widget, one blob, seven settings:\n\n```rust\nfn search_terms(&self) -> &str {\n    \"third party cli coding agent claude codex gemini toolbar footer layout chip chips \\\n     rearrange re-arrange bar command regex auto show rich input dismiss ctrl enter submit newline\"\n}\n```\n\nAfter — one widget per setting, each with terms scoped to just that setting:\n\n```rust\nfn cli_agent_widgets() -> Vec<Box<dyn SettingsWidget<View = AISettingsPageView>>> {\n    vec![\n        Box::new(CLIAgentWidget::default()),\n        Box::new(CLIAgentAutoToggleRichInputWidget::default()),\n        Box::new(CLIAgentAutoOpenRichInputWidget::default()),\n        Box::new(CLIAgentAutoDismissRichInputWidget::default()),\n        Box::new(CLIAgentSubmitRichInputWidget::default()),\n        Box::new(CLIAgentCommandsWidget),\n        Box::new(CLIAgentToolbarLayoutWidget),\n    ]\n}\n```\n\nRules of thumb when splitting:\n\n- **One widget ≈ one setting row** (or one tightly-coupled group that always shows and hides together).\n- **Scope `search_terms` to that widget only.** Keep enough shared context that a page-level query still matches (each CLI-agent widget above keeps `\"third party cli coding agent\"`), then add the terms unique to the row.\n- **Give each row the right gating mechanism** (see \"Two ways to gate a widget\"). A row that used to sit behind an `if … { … }` inside a mega-`render` becomes its own widget: if its condition is a static feature flag or platform check, gate it with an `if` in the page's build function and never create it; if it can change at runtime, give it `should_render`. The CLI-agent rich-input rows are the runtime case — they hang off the user-toggleable footer setting — so they use `should_render` and share the predicate through a small free function (`should_render_cli_agent_rich_input(app)`) rather than duplicating the condition.\n- **Move per-row state with the row.** Each `SwitchStateHandle` / `MouseStateHandle` moves to the widget that owns its control. Never create one inline while rendering (see `gui-ui-guidelines` and the AGENTS.md note on `MouseStateHandle`).\n- **Watch the widget ids.** `widget_id()` is `std::any::type_name::<Self>()`, so splitting a widget changes ids. `settings_widget_deeplink_target` in `app/src/settings_view/mod.rs` maps stable public slugs (`warp://settings?widget=<slug>`) onto them. The CLI-agent split deliberately kept `CLIAgentWidget` as the first widget so `cli_agent_settings_widget_id()` — the target of the `cli_agents` deeplink — stayed valid. If you rename or remove a widget that backs a deeplink, re-point the accessor.\n\n## Subpages rebuild their `PageType` — reapply the filter\n\nAI subpages rebuild their `PageType` when the active subpage changes (`AISettingsPageView::set_active_subpage` / `build_page`). A fresh `PageType` starts with **every** widget in its filter, so a live search query is silently dropped unless it's reapplied. `SettingsView::reapply_search_filter_to_active_subpage` in `app/src/settings_view/mod.rs` exists for exactly this ([APP-4922], #14116). If you add a code path that rebuilds a subpage's page while search may be active, call it.\nThe Code umbrella no longer works this way: `CodeIndexing` and `EditorAndCodeReview` are separate pages that each own their widgets outright, so nothing rebuilds and there is no filter to reapply. Prefer that shape for new umbrella children — a subpage that owns its own page needs none of this machinery.\n\n## Known anti-examples still in the tree\n\nUseful to read, not to copy:\n\n- **Warpify** (`warpify_page.rs`) — `PageType::new_categorized(categories, None)` where the first category is `Category::new(\"\", vec![Box::new(TitleWidget::default())])` and `TitleWidget::render` calls `render_page_title(\"Warpify\", …)`. Single-topic page, title inside a widget: bug class 1.\n- **Warp Drive** (`warp_drive_page.rs`) — `PageType::new_uncategorized([WarpDriveHeaderWidget, WarpDriveToggleWidget], None)` with no title slot at all. `WarpDriveHeaderWidget` is a conditional sign-up banner, so a signed-in user sees only a bare toggle row and no page heading.\n\n## How to verify a settings-page change\n\nVerification is cheap here; do both.\n\n1. **Unit-test the filter.** `app/src/settings_view/mod_tests.rs` has the harness — `StubWidget`, `stub_widgets_page`, and `visible_widget_count` (which reads `PageType::get_filtered`). Assert that a term unique to one widget yields exactly one visible widget, and that clearing the query restores all of them. Follow the `${filename}_tests.rs` convention from AGENTS.md; run with `cargo nextest run -p warp settings_view`.\n2. **Exercise the real search.** Open Settings, go to the page, and check three things:\n   - a term matching exactly one row leaves **only** that row,\n   - the **page title is still visible** while that filter is active — expect this on an `Uncategorized` / `Categorized` page; a monolith correctly disappears whole instead,\n   - clearing the search restores the full page.\n\n## Anti-patterns\n\n```rust\n// A widget whose whole job is to draw the page heading. Delete it and pass the\n// title through PageType instead.\nstruct FooSubpageHeaderWidget { title: &'static str }\n\n// A single-topic page with no page title, drawing its heading inside a widget.\n// Any non-matching search term erases the heading.\nPageType::new_uncategorized(widgets, None)   // and build_sub_header(\"Foo\", …) in a widget\n\n// One widget, one mega search_terms blob, many unrelated rows: nothing can be\n// filtered or attributed individually.\nfn search_terms(&self) -> &str { \"foo bar baz qux quux corge grault\" }\n\n// Conditional rows hidden inside render() while search_terms still advertises\n// them — the page matches a query and then renders nothing for it.\nfn render(&self, …) { if flag_enabled { /* the only rows */ } }\n// Gate the widget instead: an `if` at page-build time for a static flag, or\n// should_render() when the condition can change at runtime.\n```\n",".agents/skills/logging-and-error-reporting/SKILL.md":"# logging-and-error-reporting\n\nPath: `.agents/skills/logging-and-error-reporting/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/logging-and-error-reporting/SKILL.md)",".agents/skills/promote-feature/SKILL.md":"# promote-feature\n\nPath: `.agents/skills/promote-feature/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/promote-feature/SKILL.md)",".agents/skills/remove-feature-flag/SKILL.md":"# remove-feature-flag\n\nPath: `.agents/skills/remove-feature-flag/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/remove-feature-flag/SKILL.md)",".agents/skills/review-pr-local/SKILL.md":"# review-pr-local\n\nPath: `.agents/skills/review-pr-local/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/review-pr-local/SKILL.md)",".agents/skills/rust-unit-tests/SKILL.md":"# rust-unit-tests\n\nPath: `.agents/skills/rust-unit-tests/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/rust-unit-tests/SKILL.md)",".agents/skills/triage-issue-local/SKILL.md":"# triage-issue-local\n\nPath: `.agents/skills/triage-issue-local/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/triage-issue-local/SKILL.md)",".agents/skills/tui-testing/SKILL.md":"# tui-testing\n\nPath: `.agents/skills/tui-testing/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-testing/SKILL.md)",".agents/skills/tui-ui-guidelines/SKILL.md":"# tui-ui-guidelines\n\nPath: `.agents/skills/tui-ui-guidelines/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-ui-guidelines/SKILL.md)",".agents/skills/tui-verify-change/SKILL.md":"# tui-verify-change\n\nPath: `.agents/skills/tui-verify-change/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-verify-change/SKILL.md)",".warp/skills/gui-integration-test-video/SKILL.md":"# gui-integration-test-video\n\nPath: `.warp/skills/gui-integration-test-video/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.warp/skills/gui-integration-test-video/SKILL.md)","resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md":"# oz-child-agent-orchestration\n\nPath: `resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md)","resources/bundled/harness_skills/oz-finish-task/SKILL.md":"# oz-finish-task\n\nPath: `resources/bundled/harness_skills/oz-finish-task/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-finish-task/SKILL.md)","resources/bundled/harness_skills/oz-notify-user/SKILL.md":"# oz-notify-user\n\nPath: `resources/bundled/harness_skills/oz-notify-user/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-notify-user/SKILL.md)","resources/bundled/harness_skills/oz-report-pr/SKILL.md":"# oz-report-pr\n\nPath: `resources/bundled/harness_skills/oz-report-pr/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-report-pr/SKILL.md)","resources/bundled/harness_skills/oz-upload-file/SKILL.md":"# oz-upload-file\n\nPath: `resources/bundled/harness_skills/oz-upload-file/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-upload-file/SKILL.md)","resources/bundled/skills/add-mcp-server/SKILL.md":"# add-mcp-server\n\nPath: `resources/bundled/skills/add-mcp-server/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/add-mcp-server/SKILL.md)","resources/bundled/skills/change-keybinding/SKILL.md":"# change-keybinding\n\nPath: `resources/bundled/skills/change-keybinding/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/change-keybinding/SKILL.md)","resources/bundled/skills/claude-api/SKILL.md":"# claude-api\n\nPath: `resources/bundled/skills/claude-api/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/SKILL.md)","resources/bundled/skills/create-skill/SKILL.md":"# create-skill\n\nPath: `resources/bundled/skills/create-skill/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/SKILL.md)","resources/bundled/skills/create-tab-config/SKILL.md":"# create-tab-config\n\nPath: `resources/bundled/skills/create-tab-config/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-tab-config/SKILL.md)","resources/bundled/skills/factory-deferred-repositories/SKILL.md":"# factory-deferred-repositories\n\nPath: `resources/bundled/skills/factory-deferred-repositories/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-deferred-repositories/SKILL.md)","resources/bundled/skills/factory-files/SKILL.md":"# factory-files\n\nPath: `resources/bundled/skills/factory-files/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/SKILL.md)","resources/bundled/skills/factory-mcp/SKILL.md":"# factory-mcp\n\nPath: `resources/bundled/skills/factory-mcp/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-mcp/SKILL.md)","resources/bundled/skills/modify-settings/SKILL.md":"# modify-settings\n\nPath: `resources/bundled/skills/modify-settings/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/modify-settings/SKILL.md)","resources/bundled/skills/oz-platform/SKILL.md":"# oz-platform\n\nPath: `resources/bundled/skills/oz-platform/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/oz-platform/SKILL.md)","resources/bundled/skills/pr-comments/SKILL.md":"# pr-comments\n\nPath: `resources/bundled/skills/pr-comments/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/pr-comments/SKILL.md)","resources/bundled/skills/tab-configs/SKILL.md":"# tab-configs\n\nPath: `resources/bundled/skills/tab-configs/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tab-configs/SKILL.md)","resources/bundled/skills/tui-migrate-setup/SKILL.md":"# tui-migrate-setup\n\nPath: `resources/bundled/skills/tui-migrate-setup/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/SKILL.md)","resources/bundled/skills/update-tab-config/SKILL.md":"# update-tab-config\n\nPath: `resources/bundled/skills/update-tab-config/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/update-tab-config/SKILL.md)","resources/bundled/skills/warpctrl/SKILL.md":"# warpctrl\n\nPath: `resources/bundled/skills/warpctrl/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/warpctrl/SKILL.md)","resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md":"# test-warp-ui\n\nPath: `resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md)","resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md":"# triage-vulnerabilities\n\nPath: `resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md)","resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md":"# verify-ui-change-in-cloud\n\nPath: `resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md)","resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md":"# edit-figma-design\n\nPath: `resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md)","resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md":"# figma-code-connect-components\n\nPath: `resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md)","resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md":"# figma-create-design-system-rules\n\nPath: `resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md)","resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md":"# figma-create-new-file\n\nPath: `resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md)","resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md":"# figma-generate-design\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md)","resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md":"# figma-generate-library\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md)","resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md":"# figma-implement-design\n\nPath: `resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md)","resources/bundled/mcp_skills/figma/figma-use/SKILL.md":"# figma-use\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/SKILL.md)","AGENTS.md":"# AI Agent Protocol & Instructions\n\nPath: `AGENTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/AGENTS.md)","agents/specs/APP-4878 - fix TUI copy to use native clipboard.md":"# Subagent: app-4878 - fix tui copy to use native clipboard\n\nPath: `agents/specs/APP-4878 - fix TUI copy to use native clipboard.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4878 - fix TUI copy to use native clipboard.md)","agents/specs/APP-4880 - TUI view-logs slash command.md":"# Subagent: app-4880 - tui view-logs slash command\n\nPath: `agents/specs/APP-4880 - TUI view-logs slash command.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4880 - TUI view-logs slash command.md)","agents/specs/APP-4881-tui-starting-shell-indicator.md":"# Subagent: app-4881-tui-starting-shell-indicator\n\nPath: `agents/specs/APP-4881-tui-starting-shell-indicator.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4881-tui-starting-shell-indicator.md)","agents/specs/APP-4882 - fix misleading oversized-file read error.md":"# Subagent: app-4882 - fix misleading oversized-file read error\n\nPath: `agents/specs/APP-4882 - fix misleading oversized-file read error.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4882 - fix misleading oversized-file read error.md)","agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md":"# Subagent: app-4889 - fixed-height expandable toasts with 3-active cap\n\nPath: `agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md)","agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md":"# Subagent: app-4896 - forward ctrl+shift+c to pty in fullscreen tui\n\nPath: `agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md)","agents/specs/APP-4901-add-fast-forward-slash-command.md":"# Subagent: app-4901-add-fast-forward-slash-command\n\nPath: `agents/specs/APP-4901-add-fast-forward-slash-command.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4901-add-fast-forward-slash-command.md)","agents/specs/APP-4902 - window-scoped background focus suppression.md":"# Subagent: app-4902 - window-scoped background focus suppression\n\nPath: `agents/specs/APP-4902 - window-scoped background focus suppression.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4902 - window-scoped background focus suppression.md)","agents/specs/APP-4903 improve TUI tab bar keybindings.md":"# Subagent: app-4903 improve tui tab bar keybindings\n\nPath: `agents/specs/APP-4903 improve TUI tab bar keybindings.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4903 improve TUI tab bar keybindings.md)","agents/specs/APP-4907 - TUI input focus during agent drafting.md":"# Subagent: app-4907 - tui input focus during agent drafting\n\nPath: `agents/specs/APP-4907 - TUI input focus during agent drafting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4907 - TUI input focus during agent drafting.md)","agents/specs/APP-4913-tui-input-prompt-prefix.md":"# Subagent: app-4913-tui-input-prompt-prefix\n\nPath: `agents/specs/APP-4913-tui-input-prompt-prefix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4913-tui-input-prompt-prefix.md)","agents/specs/CODE-1884-voice-input-slash-command.md":"# Subagent: code-1884-voice-input-slash-command\n\nPath: `agents/specs/CODE-1884-voice-input-slash-command.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1884-voice-input-slash-command.md)","agents/specs/CODE-1890-status-line-v2.md":"# Subagent: code-1890-status-line-v2\n\nPath: `agents/specs/CODE-1890-status-line-v2.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1890-status-line-v2.md)","agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md":"# Subagent: code-1895-tui-lrc-typeahead-wiring\n\nPath: `agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md)","agents/specs/CODE-1902 change TUI log storage.md":"# Subagent: code-1902 change tui log storage\n\nPath: `agents/specs/CODE-1902 change TUI log storage.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1902 change TUI log storage.md)","agents/specs/CODE-1908 TUI default profile uses AgentDecides.md":"# Subagent: code-1908 tui default profile uses agentdecides\n\nPath: `agents/specs/CODE-1908 TUI default profile uses AgentDecides.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1908 TUI default profile uses AgentDecides.md)","agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md":"# Subagent: quality-1112-smart-variable-speedcut-for-computer-use-recordings\n\nPath: `agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md)","agents/specs/QUALITY-1169-richer-click-drag-annotations.md":"# Subagent: quality-1169-richer-click-drag-annotations\n\nPath: `agents/specs/QUALITY-1169-richer-click-drag-annotations.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/QUALITY-1169-richer-click-drag-annotations.md)","agents/specs/REMOTE-2160 - macOS video recording for computer use.md":"# Subagent: remote-2160 - macos video recording for computer use\n\nPath: `agents/specs/REMOTE-2160 - macOS video recording for computer use.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/REMOTE-2160 - macOS video recording for computer use.md)",".agents/skills/changelog-draft/examples/changelog-draft-example.md":"# Examples Skill\n\nPath: `.agents/skills/changelog-draft/examples/changelog-draft-example.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/changelog-draft/examples/changelog-draft-example.md)","resources/bundled/skills/claude-api/csharp/claude-api.md":"# Csharp Skill\n\nPath: `resources/bundled/skills/claude-api/csharp/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/csharp/claude-api.md)","resources/bundled/skills/claude-api/curl/examples.md":"# Curl Skill\n\nPath: `resources/bundled/skills/claude-api/curl/examples.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/curl/examples.md)","resources/bundled/skills/claude-api/curl/managed-agents.md":"# Curl Skill\n\nPath: `resources/bundled/skills/claude-api/curl/managed-agents.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/curl/managed-agents.md)","resources/bundled/skills/claude-api/go/claude-api.md":"# Go Skill\n\nPath: `resources/bundled/skills/claude-api/go/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/go/claude-api.md)","resources/bundled/skills/claude-api/java/claude-api.md":"# Java Skill\n\nPath: `resources/bundled/skills/claude-api/java/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/java/claude-api.md)","resources/bundled/skills/claude-api/php/claude-api.md":"# Php Skill\n\nPath: `resources/bundled/skills/claude-api/php/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/php/claude-api.md)","resources/bundled/skills/claude-api/ruby/claude-api.md":"# Ruby Skill\n\nPath: `resources/bundled/skills/claude-api/ruby/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/ruby/claude-api.md)","resources/bundled/skills/claude-api/shared/agent-design.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/agent-design.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/agent-design.md)","resources/bundled/skills/claude-api/shared/error-codes.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/error-codes.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/error-codes.md)","resources/bundled/skills/claude-api/shared/live-sources.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/live-sources.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/live-sources.md)","resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md)","resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md)","resources/bundled/skills/claude-api/shared/managed-agents-core.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-core.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-core.md)","resources/bundled/skills/claude-api/shared/managed-agents-environments.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-environments.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-environments.md)","resources/bundled/skills/claude-api/shared/managed-agents-events.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-events.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-events.md)","resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md)","resources/bundled/skills/claude-api/shared/managed-agents-overview.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-overview.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-overview.md)","resources/bundled/skills/claude-api/shared/managed-agents-tools.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-tools.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-tools.md)","resources/bundled/skills/claude-api/shared/model-migration.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/model-migration.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/model-migration.md)","resources/bundled/skills/claude-api/shared/models.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/models.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/models.md)","resources/bundled/skills/claude-api/shared/prompt-caching.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/prompt-caching.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/prompt-caching.md)","resources/bundled/skills/claude-api/shared/tool-use-concepts.md":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/tool-use-concepts.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/tool-use-concepts.md)","resources/bundled/skills/create-skill/agents/analyzer.md":"# Subagent: analyzer\n\nPath: `resources/bundled/skills/create-skill/agents/analyzer.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/analyzer.md)","resources/bundled/skills/create-skill/agents/comparator.md":"# Subagent: comparator\n\nPath: `resources/bundled/skills/create-skill/agents/comparator.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/comparator.md)","resources/bundled/skills/create-skill/agents/grader.md":"# Subagent: grader\n\nPath: `resources/bundled/skills/create-skill/agents/grader.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/grader.md)","resources/bundled/skills/create-skill/references/schemas.md":"# References Skill\n\nPath: `resources/bundled/skills/create-skill/references/schemas.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/references/schemas.md)","resources/bundled/skills/factory-files/references/examples.md":"# References Skill\n\nPath: `resources/bundled/skills/factory-files/references/examples.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/examples.md)","resources/bundled/skills/factory-files/references/scorers.md":"# References Skill\n\nPath: `resources/bundled/skills/factory-files/references/scorers.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/scorers.md)","resources/bundled/skills/factory-files/references/validation.md":"# References Skill\n\nPath: `resources/bundled/skills/factory-files/references/validation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/validation.md)","resources/bundled/skills/oz-platform/references/third-party-clis.md":"# References Skill\n\nPath: `resources/bundled/skills/oz-platform/references/third-party-clis.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/oz-platform/references/third-party-clis.md)","resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md":"# References Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md)","resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md)","resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md)","resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md)","resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md)","resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md)","resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md)","resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md)","resources/bundled/skills/claude-api/go/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/go/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/go/managed-agents/README.md)","resources/bundled/skills/claude-api/java/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/java/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/java/managed-agents/README.md)","resources/bundled/skills/claude-api/php/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/php/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/php/managed-agents/README.md)","resources/bundled/skills/claude-api/python/claude-api/README.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/README.md)","resources/bundled/skills/claude-api/python/claude-api/batches.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/batches.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/batches.md)","resources/bundled/skills/claude-api/python/claude-api/files-api.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/files-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/files-api.md)","resources/bundled/skills/claude-api/python/claude-api/streaming.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/streaming.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/streaming.md)","resources/bundled/skills/claude-api/python/claude-api/tool-use.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/tool-use.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/tool-use.md)","resources/bundled/skills/claude-api/python/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/python/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/managed-agents/README.md)","resources/bundled/skills/claude-api/ruby/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/ruby/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/ruby/managed-agents/README.md)","resources/bundled/skills/claude-api/typescript/claude-api/README.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/README.md)","resources/bundled/skills/claude-api/typescript/claude-api/batches.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/batches.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/batches.md)","resources/bundled/skills/claude-api/typescript/claude-api/files-api.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/files-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/files-api.md)","resources/bundled/skills/claude-api/typescript/claude-api/streaming.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/streaming.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/streaming.md)","resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md)","resources/bundled/skills/claude-api/typescript/managed-agents/README.md":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/managed-agents/README.md)","resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json":"# Fixtures Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json)","resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json":"# Fixtures Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json)","resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json":"# Fixtures Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md)","resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md)"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AI Agent Protocol & Instructions\n\nPath: `AGENTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/AGENTS.md)","tokens":300,"sizeBytes":149},{"name":"SKILL.md","path":".agents/skills/add-feature-flag/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/add-feature-flag/SKILL.md","title":"add-feature-flag","category":"anthropic-skill","format":"markdown","content":"---\nname: add-feature-flag\ndescription: Add a new feature flag to gate code changes in the Warp codebase.\n---\n\n# add-feature-flag\n\nAdd a new feature flag to gate code changes in the Warp codebase.\n\n## Overview\n\nFeature flags in Warp are compile-time flags that allow features to be selectively enabled for different channels (e.g.: Dev, Stable). They use a small runtime plumbing layer that checks if a flag is enabled.\n\n## TUI note\nThe `FeatureFlag` enum (`warp_core/src/features.rs`) and the runtime `FeatureFlag::X.is_enabled()` check are SHARED by both front-ends: the GUI desktop app (`app/`) and the headless TUI (`crates/warp_tui`). The Cargo-feature steps in this skill (`app/Cargo.toml`, `app/src/lib.rs`) are GUI-app-specific. Prefer runtime `is_enabled()` checks so a flag works in both front-ends with no per-binary wiring. Only if you truly need a compile-time Cargo feature in the TUI, wire it into `crates/warp_tui/Cargo.toml` as well. Test the TUI with `./script/run-tui`.\n\n## Steps\n\n### 1. Add to Cargo.toml\nAdd the feature to `app/Cargo.toml` under the `[features]` section, but **NOT** under the `default` nested stanza:\n\n```toml\n[features]\nyour_feature_name = []\n```\n\n### 2. Add to FeatureFlag enum\nAdd a new variant to the `FeatureFlag` enum in `warp_core/src/features.rs`:\n\n```rust\n#[derive(Sequence)]\npub enum FeatureFlag {\n    YourFeatureName,\n}\n```\n\n### 3. Add conditional compilation directive\nAdd the feature to `app/src/lib.rs` with a corresponding `#[cfg(feature = \"...\")]` attribute to ensure it's only included when enabled:\n\n```rust\n#[cfg(feature = \"your_feature_name\")]\nYourFeatureName,\n```\n\n### 4. Gate code with runtime checks\nIn your code, use the runtime check to conditionally execute feature-gated code:\n\n```rust\nif FeatureFlag::YourFeatureName.is_enabled() {\n    // feature-gated behavior\n}\n```\n\n### 5. (Optional) Enable for dogfood builds\nTo enable the feature by default for Dev/dogfood builds, add it to the `DOGFOOD_FLAGS` array in `features.rs`:\n\n```rust\npub const DOGFOOD_FLAGS: &[FeatureFlag] = &[\n    FeatureFlag::YourFeatureName,\n];\n```\n\n### 6. Running with feature flags\nTo test locally with the feature enabled:\n\n```bash\ncargo run --features your_feature_name\n\n# Multiple features:\ncargo run --features your_feature_name,another_feature\n```\n\n## Keybindings with Feature Flags\n\nIf adding an `EditableBinding` or `FixedBinding` that's part of a gated feature, include an enabled predicate that checks the feature flag. This prevents the keybinding from appearing in keyboard settings when the feature is disabled.\n\nExample:\n```rust\nEditableBinding::new(\n    \"action:name\",\n    \"Action description\",\n    YourAction::Variant\n)\n.with_enabled(|| FeatureFlag::YourFeatureName.is_enabled())\n.with_key_binding(\"cmdorctrl-key\")\n```\n\n## Rolling Out to Stable\n\nWhen ready to enable the feature for all Warp Stable users, add it to the `default` array in `app/Cargo.toml`:\n\n```toml\n[features]\ndefault = [\n    \"your_feature_name\",\n    # other default features...\n]\n```\n\n## Best Practices\n\n- **Prefer runtime checks over cfg directives**: Use `FeatureFlag::YourFeatureName.is_enabled()` instead of `#[cfg(...)]` when possible, so flags can be toggled without recompilation and are easier to clean up later\n- Use `#[cfg(...)]` only when code cannot compile without the flag (e.g., platform-specific code or missing dependencies)\n- Keep flags high-level and product-focused rather than per-call-site\n- Remove flags and dead branches after launch has stabilized\n","frontmatter":{"name":"add-feature-flag","description":"Add a new feature flag to gate code changes in the Warp codebase."},"isInternal":false,"tokens":848,"sizeBytes":3495},{"name":"SKILL.md","path":".agents/skills/add-telemetry/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/add-telemetry/SKILL.md","title":"add-telemetry","category":"anthropic-skill","format":"markdown","content":"---\nname: add-telemetry\ndescription: Add telemetry events to track user behavior or system events in the Warp codebase. Use when instrumenting new features, debugging issues, or measuring product metrics.\n---\n\n# add-telemetry\n\n## Overview\n\nWarp uses a trait-based telemetry system where feature-specific enums implement the `TelemetryEvent` trait. This approach keeps telemetry events organized by domain rather than in one giant enum.\n\n**Important**: Before implementing telemetry, collaborate with the user to:\n- Define what events should be tracked and when\n- Determine what data should be included in each event\n- Clarify the purpose and expected usage of the telemetry\n\nAdding telemetry code is straightforward, but designing meaningful instrumentation requires careful thought.\n\n## Steps\n\n### 1. Identify or create a telemetry module\n\nFind an existing feature-specific telemetry file (e.g., `app/src/antivirus/telemetry.rs`) or create a new one for your feature area.\n\n### 2. Define the telemetry event enum\n\nAdd a new variant to an enum that implements `TelemetryEvent`, or create a new enum:\n\n```rust\nuse serde_json::{json, Value};\nuse strum_macros::{EnumDiscriminants, EnumIter};\nuse warp_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};\n\n#[derive(Debug, EnumDiscriminants)]\n#[strum_discriminants(derive(EnumIter))]\npub enum YourFeatureTelemetryEvent {\n    ActionStarted {\n        duration_ms: u64,\n    },\n    ActionCompleted {\n        success: bool,\n        error: Option<String>,\n    },\n}\n```\n\n### 3. Implement the TelemetryEvent trait\n\n`EnablementState` allows you to control when events are sent:\n\n- `EnablementState::Always` - Always send the event\n- `EnablementState::Flag(FeatureFlag::YourFeature)` - Only send when the feature flag is enabled\n- `EnablementState::Channel(Channel::Dev)` - Only send in specific build channels\n\n```rust\nimpl TelemetryEvent for YourFeatureTelemetryEvent {\n    fn name(&self) -> &'static str {\n        YourFeatureTelemetryEventDiscriminants::from(self).name()\n    }\n\n    fn payload(&self) -> Option<Value> {\n        match self {\n            Self::ActionStarted { duration_ms } => Some(json!({\n                \"duration_ms\": duration_ms,\n            })),\n            Self::ActionCompleted { success, error } => Some(json!({\n                \"success\": success,\n                \"error\": error,\n            })),\n        }\n    }\n\n    fn description(&self) -> &'static str {\n        YourFeatureTelemetryEventDiscriminants::from(self).description()\n    }\n\n    fn enablement_state(&self) -> EnablementState {\n        YourFeatureTelemetryEventDiscriminants::from(self).enablement_state()\n    }\n\n    fn contains_ugc(&self) -> bool {\n        match self {\n            Self::ActionStarted { .. } => false,\n            Self::ActionCompleted { .. } => false,\n        }\n    }\n\n    fn event_descs() -> impl Iterator<Item = Box<dyn TelemetryEventDesc>> {\n        warp_core::telemetry::enum_events::<Self>()\n    }\n}\n```\n\n### 4. Implement TelemetryEventDesc for the discriminants\n\n```rust\nimpl TelemetryEventDesc for YourFeatureTelemetryEventDiscriminants {\n    fn name(&self) -> &'static str {\n        match self {\n            Self::ActionStarted => \"YourFeature.Action.Started\",\n            Self::ActionCompleted => \"YourFeature.Action.Completed\",\n        }\n    }\n\n    fn description(&self) -> &'static str {\n        match self {\n            Self::ActionStarted => \"User started the action\",\n            Self::ActionCompleted => \"User completed the action\",\n        }\n    }\n\n    fn enablement_state(&self) -> EnablementState {\n        match self {\n            Self::ActionStarted | Self::ActionCompleted => EnablementState::Always,\n            // Or gate behind a feature flag:\n            // EnablementState::Flag(FeatureFlag::YourFeature)\n        }\n    }\n}\n```\n\n### 5. Register the telemetry event\n\nAt the end of your telemetry module, register the event:\n\n```rust\nwarp_core::register_telemetry_event!(YourFeatureTelemetryEvent);\n```\n\n### 6. Send telemetry events from your code\n\nUse `send_telemetry_from_ctx!` in views or models with a `ViewContext` or `ModelContext`:\n\n```rust\nuse warp_core::send_telemetry_from_ctx;\n\n// In a view update or model method\nsend_telemetry_from_ctx!(\n    YourFeatureTelemetryEvent::ActionStarted {\n        duration_ms: 150,\n    },\n    ctx\n);\n```\n\nFor code with only `AppContext`, use `send_telemetry_from_app_ctx!` instead.\n\n### 7. Test locally\n\nRun Warp with the `log_named_telemetry_events` feature flag to see telemetry events logged to the console:\n\n```bash\ncargo run --features log_named_telemetry_events\n```\n\n## Best Practices\n\n- Keep telemetry enums feature-specific rather than adding to a global enum\n- Set `contains_ugc()` to `true` if the payload includes user-generated content\n- Use descriptive event names following the pattern `Feature.Action.Result`\n- Include only necessary data in payloads to minimize bandwidth and storage\n- Consider privacy implications when deciding what data to include\n- Avoid exhaustive matching with wildcards; handle all variants explicitly\n\n## Example Reference\n\nSee `app/src/antivirus/telemetry.rs` for a complete example of a feature-specific telemetry implementation.\n","frontmatter":{"name":"add-telemetry","description":"Add telemetry events to track user behavior or system events in the Warp codebase. Use when instrumenting new features, debugging issues, or measuring product metrics."},"isInternal":false,"tokens":1166,"sizeBytes":5199},{"name":"changelog-draft-example.md","path":".agents/skills/changelog-draft/examples/changelog-draft-example.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/changelog-draft/examples/changelog-draft-example.md","title":"Examples Skill","category":"anthropic-skill","format":"markdown","content":"# Examples Skill\n\nPath: `.agents/skills/changelog-draft/examples/changelog-draft-example.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/changelog-draft/examples/changelog-draft-example.md)","tokens":300,"sizeBytes":245},{"name":"SKILL.md","path":".agents/skills/changelog-draft/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/changelog-draft/SKILL.md","title":"changelog-draft","category":"anthropic-skill","format":"markdown","content":"---\nname: changelog-draft\ndescription: Generate a reviewable changelog draft from PRs merged in a release range. Extracts explicit CHANGELOG markers, classifies unmarked PRs, adds external contributor attribution, and outputs markdown + JSON artifacts. Does NOT mutate channel_versions.json.\n---\n\n# Changelog Draft Generator\n\n## Inputs\n\n| Parameter | Required | Description |\n|-----------|----------|-------------|\n| `channel` | yes | Release channel: `stable`, `preview`, or `dev` |\n| `release_tag` | yes | The release tag to generate the changelog for (e.g. `v0.2026.05.06.09.12.stable_00`) |\n| `output_dir` | no | Directory to write output files. Defaults to `$RUNNER_TEMP` or `/tmp/changelog-draft` |\n| `attribution` | no | Attribution mode: `external-only` (default), `all`, or `none` |\n\n## Workflow\n\n### Step 1 — Determine the release range\n\nInfer the previous release **cut** for comparison. Release tags follow the pattern `v0.YYYY.MM.DD.HH.MM.<channel>_NN`, where `_NN` is the RC/hotfix number within that release cut. Multiple tags can share the same date prefix (e.g. `_00`, `_01`, `_02` are all part of one release cut).\n\nThe base tag must be the `_00` tag of the **previous** release cut (i.e. a different date), not just the previous tag. For example, if generating a changelog for `v0.2026.04.29.08.57.stable_01`, the base should be `v0.2026.04.22.08.57.stable_00`, not `v0.2026.04.29.08.57.stable_00`.\n\n```bash\n# 1. Extract the date prefix from the release_tag (everything before _NN)\nrelease_date_prefix=\"${release_tag%_*}\"\n\n# 2. List all _00 tags for the channel (these are release cut points), sorted descending\ngit tag --list \"v0.*.${channel}_00\" --sort=-version:refname\n\n# 3. Pick the first _00 tag whose date prefix differs from release_date_prefix\n```\n\nRecord the range as `previous_cut_tag..release_tag`.\n\n### Step 2 — Fetch PR data\n\nRun the `fetch_prs.py` script to collect all public-release PRs merged in the release range and extract explicit changelog markers. Pass the repository that the workflow checked out, not necessarily the public repository. Release workflows run from `warpdotdev/warp-internal`, and the script deterministically resolves `warp-repo-sync[bot]` PRs back to their original public `warpdotdev/warp` PR metadata before emitting JSON. When running from `warpdotdev/warp-internal`, the script intentionally omits PRs that were not authored by the repo-sync bot, because those are private internal changes that must not be exposed to the changelog agent or generated artifacts.\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/fetch_prs.py \\\n  --repo \"${GITHUB_REPOSITORY:-warpdotdev/warp}\" \\\n  --base-ref <previous_tag> \\\n  --head-ref <release_tag>\n```\n\nThe script outputs JSON to stdout with this structure:\n```json\n{\n  \"range\": { \"base\": \"<previous_tag>\", \"head\": \"<release_tag>\" },\n  \"prs\": [\n    {\n      \"number\": 1234,\n      \"url\": \"https://github.com/warpdotdev/warp/pull/1234\",\n      \"title\": \"...\",\n      \"commit_subject\": \"... (#1234)\",\n      \"author\": \"username\",\n      \"body\": \"...\",\n      \"labels\": [\"...\"],\n      \"merged_at\": \"2026-05-01T...\",\n      \"explicit_entries\": [\n        { \"category\": \"NEW-FEATURE\", \"text\": \"Added dark mode\" }\n      ],\n      \"linked_issues\": [5678],\n      \"changed_files\": [\"app/src/ai/agent.rs\", \"crates/warp_features/src/lib.rs\"],\n      \"source_repo\": \"warpdotdev/warp\",\n      \"internal_pr\": {\n        \"number\": 25712,\n        \"url\": \"https://github.com/warpdotdev/warp-internal/pull/25712\",\n        \"author\": \"warp-repo-sync[bot]\",\n        \"title\": \"...\",\n        \"repo\": \"warpdotdev/warp-internal\"\n      }\n    }\n  ]\n}\n```\n\nUse the top-level `number`, `url`, `commit_subject`, `author`, `body`, `labels`, `changed_files`, and `source_repo` fields as the source of truth. `internal_pr` is audit-only and must never be used for contributor attribution or user-facing changelog links. If `url` is empty, omit the PR link from user-facing markdown rather than synthesizing one.\n\n### Step 3 — Classify contributors\n\nRun the `classify_contributors.py` script with the unique author logins from Step 2:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/classify_contributors.py \\\n  --org warpdotdev \\\n  --authors author1,author2,author3\n```\n\nOutput JSON:\n```json\n{\n  \"internal\": [\"author1\"],\n  \"external\": [\"author3\"],\n  \"bot\": [\"author2\"],\n  \"unknown\": []\n}\n```\n\n### Step 4 — Extract feature flags\n\nRun the `extract_feature_flags.py` script to get the current flag gate lists:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/extract_feature_flags.py \\\n  --file crates/warp_features/src/lib.rs\n```\n\nOutput JSON:\n```json\n{\n  \"release_flags\": [\"Autoupdate\", \"Changelog\", ...],\n  \"preview_flags\": [\"Orchestration\", ...],\n  \"dogfood_flags\": [\"LogExpensiveFramesInSentry\", ...]\n}\n```\n\n### Step 5 — Fetch issue reporters\n\nCollect all unique `linked_issues` from Step 2 and fetch the original reporter for each. Pass `--org` so the script checks org membership and filters out internal reporters automatically:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/fetch_issue_reporters.py \\\n  --repo warpdotdev/warp \\\n  --org warpdotdev \\\n  --issues 5678,9012\n```\n\nOutput JSON (only external reporters are included):\n```json\n{\n  \"issue_reporters\": [\n    {\n      \"issue_number\": 5678,\n      \"title\": \"Crash when opening large file\",\n      \"reporter\": \"community-user\",\n      \"reporter_url\": \"https://github.com/community-user\",\n      \"url\": \"https://github.com/warpdotdev/warp/issues/5678\"\n    }\n  ]\n}\n```\n\nThe `--org` flag checks each reporter's org membership via the GitHub API, filtering out internal members so they aren't misattributed as external community reporters. These reporters will be credited in the \"Community\" section of the changelog.\nWhenever the markdown draft credits a PR author, contributor, or issue reporter, render the username as a GitHub profile link such as `[@username](https://github.com/username)`.\n\n### Step 6 — Classify unmarked PRs\nDetermine TUI impact independently from the regular changelog category for every PR. Explicit `CHANGELOG-TUI` and `CHANGELOG-OZ` markers are authoritative entries and may coexist with New Feature, Improvement, or Bug Fix entries. When a PR has an explicit TUI entry, preserve its other explicit entries. Otherwise, if `commit_subject` contains `TUI` as a standalone case-insensitive token, route any explicit New Feature, Improvement, or Bug Fix text to `TUI` instead of its regular category. This keeps clearly TUI-labeled changes out of the desktop changelog while allowing explicitly marked shared changes to appear on both surfaces.\n\nFor each PR that has no explicit `CHANGELOG-*` entries, decide whether to include it and under which category.\n\nFollow the classification guidance in `.agents/skills/classify-changelog-pr/SKILL.md`.\n\nFor each unmarked PR, produce a classification:\n```json\n{\n  \"pr_number\": 1234,\n  \"include\": true,\n  \"category\": \"IMPROVEMENT\",\n  \"text\": \"Proposed changelog line\",\n  \"impacts_tui\": true,\n  \"confidence\": \"high\",\n  \"rationale\": \"...\",\n  \"feature_flag\": null,\n  \"needs_review\": false\n}\n```\n\n**Key rules:**\n- PRs that only touch CI, tests, docs, or internal tooling → `include: false`\n- PRs behind dogfood-only feature flags → `include: false` for stable channel\n- PRs behind preview flags → `include: false` for stable, `include: true` for preview\n- Set `impacts_tui: true` when Warp Agent CLI users observe the change, including shared Agent capabilities such as tool-call or edit-file behavior\n- When in doubt, set `needs_review: true` and `confidence: \"low\"`\n- Bot PRs (dependabot, renovate, etc.) → `include: false`\n\n**Feature-flag detection:** Use the `changed_files` list from Step 2 to check if any PR touches `crates/warp_features/src/lib.rs` or references a `FeatureFlag` variant in its title/body. Cross-reference with the flag lists from Step 4 to determine channel visibility.\n\n**Unknown contributors:** Authors in the `unknown` bucket (org membership check failed due to auth) should be treated conservatively — do not attribute them as external. Note them in the output for manual verification.\n\n### Step 7 — Assemble the draft\n\nCombine explicit entries (Step 2) and inferred entries (Step 6) into the final report. Group by category in this order:\n\n1. `NEW-FEATURE` — New Features\n2. `IMPROVEMENT` — Improvements\n3. `BUG-FIX` — Bug Fixes\n4. `TUI` — TUI Updates\n5. `OZ` — Oz Updates\n\nPRs marked with `CHANGELOG-NONE` are explicitly opted out and must never appear in the changelog markdown.\n\nPreserve every explicit entry independently. For an inferred regular entry with `impacts_tui: true`, also create a `TUI` entry with the same user-facing text and PR metadata. Do not duplicate an inferred entry whose category is already `TUI`.\n\nWhen creating entries, copy `pr_number`, `url`, `author`, `source_repo`, and `internal_pr` from the normalized PR record. The release JSON converter uses `url` directly; do not invent public PR URLs from PR numbers.\n\n### Step 8 — Write output files\n\nWrite two files to `output_dir`:\n\n**`changelog-draft.md`** — Human-reviewable markdown, ready for Slack/Notion:\n\n```markdown\n# Changelog Draft\n**Channel:** stable\n**Range:** v0.2026.05.01... → v0.2026.05.06...\n**Generated:** 2026-05-06T15:00:00Z\n\n## New Features\n- Added dark mode ([#1234](https://github.com/warpdotdev/warp/pull/1234)) — [@external-contributor](https://github.com/external-contributor) ✨\n\n## Improvements\n- Faster tab switching ([#1235](https://github.com/warpdotdev/warp/pull/1235))\n\n## Bug Fixes\n- Fixed crash on startup ([#1236](https://github.com/warpdotdev/warp/pull/1236))\n\n## TUI Updates\n- Added inline command menus to Warp Agent CLI ([#1238](https://github.com/warpdotdev/warp/pull/1238))\n\n## Oz Updates\n- Improved agent memory ([#1237](https://github.com/warpdotdev/warp/pull/1237))\n\n## Community\n### Contributors\n- [@contributor1](https://github.com/contributor1) — [#1234](https://github.com/warpdotdev/warp/pull/1234)  ✨\n\n### Issue Reporters\nThanks to the community members who reported issues fixed in this release:\n- [@reporter1](https://github.com/reporter1) — [#5678](https://github.com/warpdotdev/warp/issues/5678) \"Crash when opening large file\"\n```\n\nThe markdown draft must **not** include \"Needs Review\" or \"Skipped PRs\" sections — those are internal details that belong only in the JSON audit artifact.\n\n**`changelog-draft.json`** — Machine-readable audit artifact (internal only):\n\n```json\n{\n  \"channel\": \"stable\",\n  \"range\": { \"base\": \"v0...\", \"head\": \"v0...\" },\n  \"generated_at\": \"2026-05-06T15:00:00Z\",\n  \"entries\": [\n    {\n      \"pr_number\": 1234,\n      \"url\": \"https://github.com/warpdotdev/warp/pull/1234\",\n      \"category\": \"NEW-FEATURE\",\n      \"text\": \"Added dark mode\",\n      \"source\": \"explicit\",\n      \"author\": \"external-contributor\",\n      \"is_external\": true,\n      \"confidence\": \"high\",\n      \"rationale\": null,\n      \"feature_flag\": null,\n      \"source_repo\": \"warpdotdev/warp\",\n      \"internal_pr\": null\n    }\n  ],\n  \"skipped\": [...],\n  \"needs_review\": [...],\n  \"issue_reporters\": [...]\n}\n```\n\nThe JSON artifact retains `skipped`, `needs_review`, and `issue_reporters` for audit purposes — every PR in the range must appear in either `entries`, `skipped`, or `needs_review`.\n\n### Step 9 — Generate release-pipeline JSON\n\nRun the conversion script to deterministically produce `changelog-release.json` from the audit artifact:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/convert_to_release_json.py \\\n  --input <output_dir>/changelog-draft.json \\\n  --output <output_dir>/changelog-release.json\n```\n\nThis produces the flat JSON structure consumed by the `create_release` workflow for Slack and the in-app \"What's New\" dialog. Do **not** generate this file manually — always use the script so the output is deterministic and consistent.\n\n## Constraints\n\n- **Never** write to `channel_versions.json` or any production config file.\n- **Never** push commits, create branches, or open PRs.\n- All output goes to `output_dir` only.\n- The markdown draft should be copy-pasteable into Slack or Notion for review.\n- Keep the JSON artifact complete enough for audit: every PR in the range should appear in either `entries`, `skipped`, or `needs_review`.\n\n## Validation\n\nAfter generating output, verify:\n1. Every PR in the range is accounted for (entries + skipped + needs_review = total PRs).\n2. Explicit marker entries match what `fetch_prs.py` extracted (no dropped markers).\n3. No duplicate PR numbers across sections.\n4. The markdown renders cleanly (no broken links or formatting).\n","frontmatter":{"name":"changelog-draft","description":"Generate a reviewable changelog draft from PRs merged in a release range. Extracts explicit CHANGELOG markers, classifies unmarked PRs, adds external contributor attribution, and outputs markdown + JSON artifacts. Does NOT mutate channel_versions.json."},"isInternal":false,"tokens":3263,"sizeBytes":12606},{"name":"SKILL.md","path":".agents/skills/classify-changelog-pr/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/classify-changelog-pr/SKILL.md","title":"classify-changelog-pr","category":"anthropic-skill","format":"markdown","content":"---\nname: classify-changelog-pr\ndescription: Reference guidance for classifying whether an unmarked PR should appear in the changelog and under which category. Used inline by the changelog-draft skill — not dispatched as a separate agent.\n---\n\n# Classify Changelog PR\n\nThis document provides classification rules for PRs that lack explicit `CHANGELOG-*` markers. The changelog-draft agent follows these rules inline when deciding whether to include an unmarked PR.\n\n## Categories\n\n- **NEW-FEATURE** — A substantial new user-facing capability. Reserve for features that would warrant docs, marketing, or social media attention.\n- **IMPROVEMENT** — Enhances an existing feature in a way users would notice (performance, UX, new options).\n- **BUG-FIX** — Fixes a user-visible bug or regression.\n- **OZ** — Changes to Oz / AI agent capabilities. At most 4 per release in the stable changelog.\n- **TUI** — User-visible changes that impact the Warp Agent CLI/headless TUI, including shared Agent capabilities.\n- **NONE** — Explicitly opt out of changelog inclusion. Handled upstream by `fetch_prs.py` marker extraction.\n\n## Decision rules\n\n### Always exclude\n- PRs with an explicit `CHANGELOG-NONE` marker (contributor opted out)\n- PRs authored by known bots (dependabot, renovate, github-actions, codecov)\n- PRs that exclusively modify CI workflows (`.github/workflows/`), test files, or dev tooling\n- PRs that only update internal docs, comments, or README files\n- Dependency bumps with no user-facing behavior change\n- Refactors with no observable behavior change (code moves, renames, formatting)\n\n### Always include\n- PRs with explicit `CHANGELOG-*` markers (handled before this guidance applies)\n- PRs that fix a crash, data loss, or security issue — even without a marker\n\n### TUI surface classification\n- Treat explicit `CHANGELOG-TUI` and `CHANGELOG-OZ` markers as authoritative entries. They are independent and may coexist with each other or with regular changelog categories.\n- Determine TUI impact independently from the regular New Feature, Improvement, or Bug Fix category. A shared Agent capability may belong in both its regular category and `TUI`.\n- Classify a user-visible entry as impacting `TUI` when `commit_subject` contains `TUI` as a standalone token.\n- For stable releases, also use the PR title/body and TUI-owned paths such as `crates/warp_tui` or `crates/warpui_core/src/elements/tui`. Shared capabilities such as Agent tool-call or edit-file behavior also impact TUI when Warp Agent CLI users observe the change. If metadata is ambiguous, inspect the commit diff.\n- Do not infer TUI impact from file paths alone; confirm that Warp Agent CLI users observe the changed behavior.\n- A TUI-only entry belongs only in the TUI category. An entry that impacts both TUI and another surface should appear in both `TUI` and its regular category.\n\n### Conditional on channel\n- **Stable channel:** Only include changes that are live for all users. Exclude PRs gated behind `DOGFOOD_FLAGS` or `PREVIEW_FLAGS`.\n- **Preview channel:** Include PRs gated behind `PREVIEW_FLAGS`. Still exclude `DOGFOOD_FLAGS`-only changes.\n- **Dev channel:** Include everything that's user-visible, regardless of flag gates.\n\n### Feature-flagged PRs\nIf a PR mentions a `FeatureFlag` variant in its diff or title:\n1. Check which flag list it belongs to (`RELEASE_FLAGS`, `PREVIEW_FLAGS`, `DOGFOOD_FLAGS`).\n2. Apply the channel rules above.\n3. If the flag is in `RELEASE_FLAGS` or enabled by default in `app/Cargo.toml`, treat it as live.\n4. Set `feature_flag` in the classification output to the flag name.\n\n### Confidence levels\n- **high** — Clear user-visible change with obvious category.\n- **medium** — Likely user-visible but category or scope is somewhat ambiguous.\n- **low** — Unclear whether users would notice; or the PR touches both internal and user-facing code. Set `needs_review: true`.\n\n## Writing changelog text\n\n- Write from the user's perspective: \"Added X\", \"Fixed Y\", \"Improved Z\".\n- Keep it to one sentence, ≤ 120 characters.\n- Don't reference internal implementation details, file paths, or function names.\n- Don't start with \"PR\" or the PR number — those are added as metadata.\n- Use active voice and present tense for new features (\"Adds dark mode\"), past tense for fixes (\"Fixed crash on startup\").\n","frontmatter":{"name":"classify-changelog-pr","description":"Reference guidance for classifying whether an unmarked PR should appear in the changelog and under which category. Used inline by the changelog-draft skill — not dispatched as a separate agent."},"isInternal":false,"tokens":985,"sizeBytes":4337},{"name":"SKILL.md","path":".agents/skills/cross-platform-cloud-verification/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/cross-platform-cloud-verification/SKILL.md","title":"cross-platform-cloud-verification","category":"anthropic-skill","format":"markdown","content":"---\nname: cross-platform-cloud-verification\ndescription: Orchestrates cost-conscious cloud verification across available operating-system and architecture runners after cheaper checks pass. Use whenever a code change, bug fix, build, packaging flow, native dependency, UI behavior, filesystem behavior, or test has material cross-platform implications, even if the user only asks to verify or test the change. Consult domain verification skills for what to test; use this skill to choose and access the smallest relevant set of platforms.\ncompatibility: Requires the Oz CLI (`oz-dev`) for runner discovery and `run_agents` support for `remote.runner_id`.\n---\n\n# Cross-platform cloud verification\n\nVerify a change on the smallest useful set of cloud platforms. This skill owns\nrunner discovery, platform selection, child orchestration, and result\naggregation. It does not define the product-specific verification procedure.\n\nCross-platform execution consumes remote compute and credits, so use it as the\nlast verification layer rather than as an exploratory first step.\n\n## Workflow\n\n### 1. Establish the local verification gate\n\nFinish the cheaper feedback loops first:\n\n1. Inspect the change and identify the behavior being verified.\n2. Run the relevant local build, focused tests, lint, type checks, or manual\n   verification.\n3. Fix deterministic local failures before launching cloud runs.\n4. Confirm the exact commit or branch containing the change can be checked out\n   by cloud agents. Do not test the default branch when the intended change is\n   only local.\n\nDo not commit or push changes merely to satisfy this workflow unless the user\nhas authorized that action. If the change is not remotely reachable, report the\nprecondition and ask for the minimum action needed.\n\nSkip cloud fan-out when local verification already shows the change is broken.\nDo not use remote platforms as a substitute for diagnosing an ordinary local\nfailure.\n\n### 2. Delegate test design to verification skills\n\nInspect the available skill descriptions and read every skill that materially\ndefines how to verify the affected surface. Examples include GUI computer-use\nverification, TUI live verification, integration testing, unit testing, CI\ndiagnosis, packaging, or repository-specific validation.\n\nExtract from those skills:\n\n- setup and authentication requirements\n- commands or interactions to perform\n- expected observations and pass criteria\n- required screenshots, recordings, logs, or text captures\n- platform-specific caveats\n\nBuild one shared verification procedure from that guidance. Tell each child\nwhich verification skill to read when it is available in the child environment,\nand include the essential procedure directly so the run remains actionable if\nthe skill is unavailable there.\n\nThis skill decides where to run that procedure, not what the procedure should\nbe.\n\n### 3. Discover runners at execution time\n\nList runners immediately before selecting platforms:\n\n```sh\noz-dev runner list --output-format json\n```\n\nUse the returned runner metadata rather than hard-coding names or IDs. Relevant\nfields include `uid`, `name`, `os`, `arch`, compute capacity, image or macOS\nversion, and setup commands.\n\nIf runner discovery fails because of authentication, permissions, or service\navailability, report the blocker instead of inventing a runner matrix.\n\n### 4. Infer the affected platform dimensions\n\nDetermine which dimensions the change can plausibly affect from the diff,\nrepository configuration, reported bug, and verification guidance.\n\nTreat the change as **OS-sensitive** when it touches or depends on items such as:\n\n- OS-gated code or platform modules\n- windowing, desktop integration, installers, packaging, or signing\n- shells, process creation, permissions, filesystems, paths, or line endings\n- OS-specific APIs, toolchains, or user-facing behavior\n\nTreat the change as **architecture-sensitive** only with evidence such as:\n\n- architecture gates or assembly\n- unsafe code, FFI, ABI, alignment, endianness, or binary serialization\n- SIMD, native libraries, architecture-specific dependencies, or packaging\n- an architecture-specific bug report or explicit user requirement\n\nDo not infer architecture sensitivity merely because both x86-64 and AArch64\nrunners exist.\n\n### 5. Select the minimal runner matrix\n\nFilter discovered runners to those that can execute the verification procedure,\nthen apply these defaults:\n\n1. Select only affected operating systems.\n2. Select one representative runner per affected OS.\n3. Add another architecture for an OS only when the change is\n   architecture-sensitive.\n4. Do not add an unaffected control platform by default.\n5. Deduplicate equivalent OS/architecture candidates.\n\nWhen several runners cover the same platform, prefer in order:\n\n1. the exact OS/version/architecture from the bug report or target\n2. the repository's normal CI or release platform\n3. a runner whose image, setup, capacity, and tools match the procedure\n4. the lower-setup-cost candidate\n\nFor architecture-insensitive Linux changes, prefer the repository's primary\nLinux architecture; if the repository gives no signal, use x86-64 as the single\nrepresentative.\n\nBefore launching, record a concise selection rationale and explicitly list\nrelevant platforms omitted because no runner is available. Missing runners do\nnot block useful verification on available relevant platforms.\n\nState each omission once. After the matrix is decided, do not keep repeating\nthat an irrelevant or redundant platform was not selected in child prompts,\nresult rows, evidence, and the conclusion.\n\n### 6. Construct verification-only child prompts\n\nEvery child prompt should contain:\n\n- repository, branch, and exact commit to check out\n- selected runner name, OS, and architecture\n- verification skill names and the distilled procedure\n- setup and state preconditions\n- exact commands or interactions\n- expected results and required evidence\n- a prohibition on fixing or pushing code\n\nTrust orchestration to route the child to the requested runner. Do not make every\nchild re-confirm its OS and architecture. Ask for platform introspection only\nwhen the verification depends on an exact OS version or capability, or when\nthere is evidence of a routing problem.\n\nAsk each child to return:\n\n```text\nPlatform: <runner name; OS; architecture; version if relevant>\nCommit: <tested SHA>\nStatus: <passed | failed | blocked>\nChecks:\n- <command or interaction>: <result>\nEvidence:\n- <artifact, screenshot, log, or concise observation>\nDeviations:\n- <difference from the requested procedure, or none>\n```\n\nChildren may make temporary, uncommitted setup adjustments required by the\nverification skill, but they must report them and must not push source changes.\n\n### 7. Launch with `run_agents`\n\nUse `run_agents` so child IDs, messages, lifecycle events, and artifacts remain\npart of the parent orchestration flow. Use the same repository environment for\nevery selected platform and set `remote.runner_id` to the discovered runner UID.\n\n```text\nsummary: Verifying the change on <OS/architecture>.\nbase_prompt: <shared verification-only instructions>\nremote:\n  environment_id: <repository environment ID>\n  runner_id: <selected runner UID>\n  computer_use_enabled: <true only when the verification procedure needs it>\nagent_run_configs:\n- name: <short platform-specific name>\n  prompt: <platform-specific procedure and expected evidence>\n```\n\nIf no suitable repository environment is already known, inspect\n`oz-dev environment list` and choose one that checks out the target repository.\nDo not silently create or mutate an environment.\n\n`remote.runner_id` is run-wide, so runners with different UIDs require separate\n`run_agents` calls. Use one single-child batch per selected runner, or group\nmultiple independently useful children only when they share the same runner and\nrun-wide configuration. Distinct runner IDs are a legitimate reason for\nseparate batches; do not place children for different platforms in one batch.\n\nOmit `model_id` and `remote.harness` unless the user requested them. Attach\nrelevant verification skills when the children need them. If an approved\norchestration config is active, ensure its resolved runner matches the selected\nrunner because config resolution takes precedence over call fields.\n\nCapture each trusted `agent_id` from the launched result. Coordinate through\npushed child messages and lifecycle events; do not poll `oz-dev run get` or\n`list_messages_from_agents`. Read notified messages, intervene only for a\nfailure or actionable block, and use `wait_for_events` when no other work can\nproceed. Allow at most one retry for a clearly transient infrastructure\nfailure. A product failure is evidence, not a reason to spend more credits\nrepeating the same check.\n\n### 8. Aggregate without hiding gaps\n\nWait for every launched child, then report:\n\n```text\nCross-platform verification: <passed | failed | incomplete>\nLocal gate: <checks completed before cloud launch>\nResults:\n- <OS/arch; runner>: <why selected> - <passed/failed/blocked>\nUnverified:\n- <relevant OS/arch>: unverified because no suitable runner was available\n\nEvidence:\n- <platform>: <commands, observations, and artifact/run links>\n\nConclusion:\n- <what the results establish and what remains unverified>\n```\n\nUse `passed` only when every selected platform passed and no required platform\nis unavailable. Use `failed` when any verification check failed. Use\n`incomplete` when runs were blocked or a relevant platform lacked a runner.\n\nKeep dry-run proposals and final summaries compact. Include the local gate, the\nselected matrix, one concise omission rationale when it is material, the\nverification procedure, and verdict rules. Do not repeat setup mechanics or\nplatform exclusions in multiple sections. A dry-run proposal should usually fit\nin roughly 300-500 words; expand only when the domain verification procedure\ngenuinely requires more detail.\n\n## Cost and scope guardrails\n\n- Run this workflow after local verification, not during implementation.\n- Choose platforms from evidence in the change, not from runner availability.\n- Keep one child per selected platform unless distinct state variants are\n  independently necessary.\n- State runner-selection and omission decisions once.\n- Preserve negative results and platform gaps; do not broaden the matrix merely\n  to obtain a passing result.\n- Do not let children implement fixes. Return failures to the parent workflow,\n  fix locally, rerun cheap checks, and only then decide whether another cloud\n  verification pass is justified.\n","frontmatter":{"name":"cross-platform-cloud-verification","description":"Orchestrates cost-conscious cloud verification across available operating-system and architecture runners after cheaper checks pass. Use whenever a code change, bug fix, build, packaging flow, native dependency, UI behavior, filesystem behavior, or test has material cross-platform implications, even if the user only asks to verify or test the change. Consult domain verification skills for what to test; use this skill to choose and access the smallest relevant set of platforms.","compatibility":"Requires the Oz CLI (`oz-dev`) for runner discovery and `run_agents` support for `remote.runner_id`."},"isInternal":false,"tokens":2108,"sizeBytes":10586},{"name":"SKILL.md","path":".agents/skills/dedupe-issue-local/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/dedupe-issue-local/SKILL.md","title":"dedupe-issue-local","category":"anthropic-skill","format":"markdown","content":"---\nname: dedupe-issue-local\nspecializes: dedupe-issue\nspecializes_source: warpdotdev/oz-for-oss:.agents/skills/dedupe-issue\ndescription: Repo-specific dedupe guidance for warp. Only the categories declared overridable by the core dedupe-issue skill may be specialized here.\n---\n\n# Repo-specific dedupe guidance for `warp`\n\n## Prerequisite: install the parent skill\n\nThis skill specializes the core `dedupe-issue` skill (named in the `specializes` frontmatter field) and is not functional on its own. Before applying its guidance, confirm the parent skill is installed and resolvable at `.agents/skills/dedupe-issue/SKILL.md`. If it is missing, install it first by copying the skill directory from the source declared in the `specializes_source` frontmatter field (`warpdotdev/oz-for-oss:.agents/skills/dedupe-issue`). Then continue with the guidance below.\n\nThis file is a companion to the core `dedupe-issue` skill. It does not\nredefine the duplicate-detection algorithm, the similarity thresholds,\nor the output contract. It only specializes the override categories the\ncore skill marks as overridable.\n\n## Repo-specific normalizations\n\n- Strip low-signal title prefixes such as `Bug:`, `Feature:`, `Request:`, `[Bug]`, `[Feature]`, `Warp:`, and platform tags like `[macOS]`, `[Linux]`, or `[Windows]` before comparing titles.\n- Treat app channel/version, OS version, and shell name as supporting evidence, not as duplicate blockers, when the core symptom and reproduction path are otherwise the same.\n- Do not collapse distinct Warp surfaces just because they share a word like \"agent\", \"terminal\", \"MCP\", \"settings\", \"search\", or \"sync\". Require overlap in the actual failing behavior or requested capability.\n- For terminal issues, compare shell/session context, command output behavior, prompt rendering, input behavior, and remote/tmux involvement before treating two reports as duplicates.\n- For agent or MCP issues, compare the trigger path, local vs cloud execution, MCP server/tool, visible error, and expected workflow before treating two reports as duplicates.\n- For UI/rendering issues, compare the affected surface and visible symptom. Similar screenshots or recordings are strong duplicate evidence when the title is vague.\n\n## Known-duplicate clusters\n\nNo known-duplicate clusters have been captured for this repository yet. The weekly `update-dedupe` loop will propose additions here over time when maintainers repeatedly close issues as duplicates of the same canonical thread.\n","frontmatter":{"name":"dedupe-issue-local","specializes":"dedupe-issue","specializes_source":"warpdotdev/oz-for-oss:.agents/skills/dedupe-issue","description":"Repo-specific dedupe guidance for warp. Only the categories declared overridable by the core dedupe-issue skill may be specialized here."},"isInternal":false,"tokens":534,"sizeBytes":2498},{"name":"SKILL.md","path":".agents/skills/gui-create-launch-modal/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/gui-create-launch-modal/SKILL.md","title":"gui-create-launch-modal","category":"anthropic-skill","format":"markdown","content":"---\nname: gui-create-launch-modal\ndescription: GUI desktop app only. Create a one-time launch modal in the Warp client (feature announcement, onboarding, etc.). Use when adding a new modal that should appear exactly once per user on startup, gated by a feature flag, with colors sourced from Warp theme tokens and terminal theme colors.\n---\n\n# gui-create-launch-modal\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nCreate a one-time launch modal — the feature-announcement design used for launches like \"Orchestrate any agent, anywhere\" or \"Warp is now open-source.\"\n\n## Reference implementation\n\n`app/src/workspace/view/orchestration_launch_modal/` — the canonical, most up-to-date example of this pattern.\n\n## Checklist\n\n- [ ] Feature flag in `warp_features/src/lib.rs`\n- [ ] Settings field in `app/src/settings/ai.rs`\n- [ ] Trigger logic in `app/src/workspace/one_time_modal_model.rs`\n- [ ] View files under `app/src/workspace/view/<name>_launch_modal/`\n- [ ] Workspace wiring in `app/src/workspace/view.rs` and `app/src/workspace/mod.rs`\n- [ ] Debug actions in `app/src/workspace/action.rs`\n- [ ] Hero image at `app/assets/async/png/onboarding/<name>_launch_banner.png`\n- [ ] Any custom icons added to `crates/warp_core/src/ui/icons.rs` + SVG in `app/assets/bundled/svg/`\n\n---\n\n## Step 0 – Custom icons (if needed)\n\nIf the modal uses icons not yet in the `Icon` enum, add them before writing the view.\n\nIn `crates/warp_core/src/ui/icons.rs`:\n\n```rust\n// Add to enum\nYourIconName,\n\n// Add to From<Icon> for &'static str match\nIcon::YourIconName => \"bundled/svg/your-icon-name.svg\",\n```\n\nDrop the SVG file at `app/assets/bundled/svg/your-icon-name.svg`. Use the same 24×24 viewBox format as existing icons.\n\n---\n\n## Step 1 – Feature flag\n\nAdd to `crates/warp_features/src/lib.rs`:\n\n```rust\n/// Enables the <name> launch modal.\n<YourModalName>LaunchModal,\n```\n\nEnable for dogfood:\n\n```rust\npub const DOGFOOD_FLAGS: &[FeatureFlag] = &[\n    FeatureFlag::<YourModalName>LaunchModal,\n    // ...\n];\n```\n\n---\n\n## Step 2 – Settings field\n\nAdd to `app/src/settings/ai.rs` inside `define_settings_group!(AISettings, ...)`.\nPattern: one boolean field per modal, globally synced (not respecting user sync), private.\n\n```rust\n// This is not a user-visible setting - it's merely a one-time flag to track if the\n// <name> launch modal has been shown to the user.\n//\n// We model it as a setting so it's only shown once to a given user regardless of the number of\n// devices they use.\ndid_check_to_trigger_<name>_launch_modal: DidShow<Name>LaunchModal {\n    type: bool,\n    default: false,\n    supported_platforms: SupportedPlatforms::ALL,\n    sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::No),\n    private: true,\n}\n```\n\n---\n\n## Step 3 – OneTimeModalModel\n\nFile: `app/src/workspace/one_time_modal_model.rs`\n\n### 3a. Add field to struct\n\n```rust\nis_<name>_launch_modal_open: bool,\n```\n\n### 3b. Initialize to false in `new()`\n\n```rust\nis_<name>_launch_modal_open: false,\n```\n\n### 3c. Pre-dismiss for new users (critical)\n\nIn the `AuthComplete` → `!is_existing_user` branch, add to the `AISettings::handle` update block alongside the other pre-dismissals. **Without this, new users see the modal on their second startup after onboarding.**\n\n```rust\nif let Err(e) = settings\n    .did_check_to_trigger_<name>_launch_modal\n    .set_value(true, ctx)\n{\n    log::warn!(\"Failed to mark <name> launch modal as dismissed: {e}\");\n}\n```\n\n### 3d. Public API methods\n\n```rust\npub fn is_<name>_launch_modal_open(&self) -> bool {\n    self.is_<name>_launch_modal_open && self.target_window_id.is_some()\n}\n\npub fn mark_<name>_launch_modal_dismissed(&mut self, ctx: &mut ModelContext<Self>) {\n    self.set_<name>_launch_modal_open(false, ctx);\n}\n\n#[cfg(debug_assertions)]\npub fn force_open_<name>_launch_modal(&mut self, ctx: &mut ModelContext<Self>) {\n    self.set_<name>_launch_modal_open(true, ctx);\n}\n```\n\n### 3e. Private setter\n\n```rust\nfn set_<name>_launch_modal_open(&mut self, is_open: bool, ctx: &mut ModelContext<Self>) -> bool {\n    if self.is_<name>_launch_modal_open != is_open {\n        self.is_<name>_launch_modal_open = is_open;\n        ctx.emit(OneTimeModalEvent::VisibilityChanged { is_open });\n        return true;\n    }\n    false\n}\n```\n\n### 3f. Add to `is_any_modal_open`\n\n```rust\n|| self.is_<name>_launch_modal_open\n```\n\n### 3g. Trigger function\n\n```rust\nfn check_and_trigger_<name>_launch_modal(&mut self, ctx: &mut ModelContext<Self>) -> bool {\n    if !FeatureFlag::<Name>LaunchModal.is_enabled() {\n        return false;\n    }\n\n    let ai_settings = AISettings::as_ref(ctx);\n    if *ai_settings.did_check_to_trigger_<name>_launch_modal {\n        return false;\n    }\n\n    AISettings::handle(ctx).update(ctx, |settings, ctx| {\n        if let Err(e) = settings\n            .did_check_to_trigger_<name>_launch_modal\n            .set_value(true, ctx)\n        {\n            log::warn!(\"Failed to mark <name> launch modal as dismissed: {e}\");\n        }\n    });\n\n    let should_show = !matches!(ChannelState::channel(), Channel::Integration);\n    self.set_<name>_launch_modal_open(should_show, ctx);\n    should_show\n}\n```\n\n### 3h. Call from `check_and_trigger_all_modals`\n\nInsert before `check_and_trigger_hoa_onboarding`:\n\n```rust\nif self.check_and_trigger_<name>_launch_modal(ctx) {\n    return;\n}\n```\n\n---\n\n## Step 4 – View\n\nCreate `app/src/workspace/view/<name>_launch_modal/mod.rs`:\n\n```rust\nmod view;\npub use view::{init, <Name>LaunchModal, <Name>LaunchModalEvent};\n```\n\nCreate `app/src/workspace/view/<name>_launch_modal/view.rs`. Copy from `orchestration_launch_modal/view.rs` and adapt. Key details:\n\n### Color sources (important)\n\n- Prefer Warp theme tokens for modal backgrounds, text, overlays, and borders:\n  - background surfaces: `appearance.theme().surface_3()` (or another `surface_*` token when needed)\n  - primary/subtext: `appearance.theme().main_text_color(...)` and `appearance.theme().sub_text_color(...)`\n  - overlays/hover fills: `appearance.theme().surface_overlay_1()` / `surface_overlay_2()`\n  - subtle borders: `appearance.theme().outline()`\n- Use terminal theme colors for terminal-color accents (for example, magenta launch badge accents):\n  - `appearance.theme().terminal_colors().normal.magenta`\n  - `appearance.theme().ansi_overlay_1(magenta)` for low-alpha backgrounds\n- Avoid hardcoded hex colors.\n\n### Hero image\n\n- Store at `app/assets/async/png/onboarding/<name>_launch_banner.png`\n- **Aspect ratio matters**: if the image is wider than `MODAL_WIDTH/HERO_HEIGHT` (420/92 ≈ 4.57), wrap the hero `ConstrainedBox` in `Clipped::new(...)` to prevent horizontal bleed when `cover()` scales it\n- Images pre-sized to exactly 420×92 need no `Clipped`; images only taller (aspect ratio < 4.57) are fine without it\n\n```rust\nconst MODAL_WIDTH: f32 = 420.;\nconst HERO_HEIGHT: f32 = 92.;\nconst HERO_IMAGE_PATH: &str = \"async/png/onboarding/<name>_launch_banner.png\";\n\nfn render_hero(&self) -> Box<dyn Element> {\n    let hero = Clipped::new(          // only needed if image ratio > 4.57\n        ConstrainedBox::new(\n            Image::new(AssetSource::Bundled { path: HERO_IMAGE_PATH }, CacheOption::Original)\n                .with_corner_radius(CornerRadius::with_top(Radius::Pixels(8.)))\n                .cover()\n                .top_aligned()\n                .finish(),\n        )\n        .with_width(MODAL_WIDTH)\n        .with_height(HERO_HEIGHT)\n        .finish(),\n    )\n    .finish();\n    // ... close button overlay via Stack + add_positioned_child\n}\n```\n\n### \"New\" badge\n\nUse the standard badge — 24 px tall, 8 px horizontal padding, 14 px font, pill corners, with magenta sourced from terminal theme colors:\n\n```rust\nfn render_badge(appearance: &Appearance) -> Box<dyn Element> {\n    let magenta = appearance.theme().terminal_colors().normal.magenta;\n    let text = Text::new_inline(\"New\".to_string(), appearance.ui_font_family(), 14.)\n        .with_color(magenta.into())\n        .finish();\n    ConstrainedBox::new(\n        Container::new(\n            Flex::row()\n                .with_cross_axis_alignment(CrossAxisAlignment::Center)\n                .with_main_axis_size(MainAxisSize::Min)\n                .with_child(text)\n                .finish(),\n        )\n        .with_horizontal_padding(8.)\n        .with_background(Fill::Solid(appearance.theme().ansi_overlay_1(magenta)))\n        .with_corner_radius(CornerRadius::with_all(Radius::Percentage(50.)))\n        .finish(),\n    )\n    .with_height(24.)\n    .finish()\n}\n```\n\n### URLs\n\nAlways use `https://`, not `http://`:\n\n```rust\nconst LEARN_MORE_URL: &str = \"https://warp.dev/your-blog-link\";\n```\n\n---\n\n## Step 5 – Workspace wiring\n\n### `app/src/workspace/view.rs`\n\n```rust\n// Module declaration (top)\npub(crate) mod <name>_launch_modal;\n\n// Import\nuse crate::workspace::view::<name>_launch_modal::{<Name>LaunchModal, <Name>LaunchModalEvent};\n\n// Struct field\n<name>_launch_modal: ViewHandle<<Name>LaunchModal>,\n\n// In Workspace::new()\nlet <name>_launch_view = ctx.add_typed_action_view(<Name>LaunchModal::new);\nctx.subscribe_to_view(&<name>_launch_view, |me, _, event, ctx| {\n    me.handle_<name>_launch_modal_event(event, ctx);\n});\n\n// In struct initialization\n<name>_launch_modal: <name>_launch_view,\n\n// In OneTimeModalModel subscription handler\n} else if model_ref.is_<name>_launch_modal_open() {\n    me.focus_<name>_launch_modal(ctx);\n\n// In View::render (inside the should_show_modal block)\nif should_show_modal && one_time_modal_model.is_<name>_launch_modal_open() {\n    stack.add_child(ChildView::new(&self.<name>_launch_modal).finish());\n}\n```\n\nAdd event handler and focus helper:\n\n```rust\nfn handle_<name>_launch_modal_event(&mut self, event: &<Name>LaunchModalEvent, ctx: &mut ViewContext<Self>) {\n    match event {\n        <Name>LaunchModalEvent::Close => {\n            OneTimeModalModel::handle(ctx).update(ctx, |model, ctx| {\n                model.mark_<name>_launch_modal_dismissed(ctx);\n            });\n            self.focus_active_tab(ctx);\n            ctx.notify();\n        }\n    }\n}\n\nfn focus_<name>_launch_modal(&mut self, ctx: &mut ViewContext<Self>) {\n    ctx.focus(&self.<name>_launch_modal);\n}\n```\n\n### `app/src/workspace/mod.rs`\n\n```rust\n// In pub fn init()\nview::<name>_launch_modal::init(app);\n\n// In debug bindings block\nEditableBinding::new(\n    \"workspace:open_<name>_launch_modal\",\n    \"[Debug] Open <Name> Launch Modal\",\n    WorkspaceAction::Open<Name>LaunchModal,\n)\n.with_context_predicate(id!(\"Workspace\")),\nEditableBinding::new(\n    \"workspace:reset_<name>_launch_modal_state\",\n    \"[Debug] Reset <Name> Launch Modal State\",\n    WorkspaceAction::Reset<Name>LaunchModalState,\n)\n.with_context_predicate(id!(\"Workspace\")),\n```\n\n---\n\n## Step 6 – Debug actions\n\nIn `app/src/workspace/action.rs`:\n\n```rust\n/// Open the <Name> Launch Modal (for debugging)\n#[cfg(debug_assertions)]\nOpen<Name>LaunchModal,\n/// Reset the <name> launch modal dismissed state (for debugging)\n#[cfg(debug_assertions)]\nReset<Name>LaunchModalState,\n```\n\nAdd both variants to the `is_visible_in_command_palette` `false` arm.\n\nIn `app/src/workspace/view.rs` `TypedActionView::handle_action`:\n\n```rust\n#[cfg(debug_assertions)]\nOpen<Name>LaunchModal => {\n    OneTimeModalModel::handle(ctx).update(ctx, |model, ctx| {\n        model.force_open_<name>_launch_modal(ctx);\n    });\n    ctx.notify();\n}\n#[cfg(debug_assertions)]\nReset<Name>LaunchModalState => {\n    AISettings::handle(ctx).update(ctx, |settings, ctx| {\n        if let Err(e) = settings\n            .did_check_to_trigger_<name>_launch_modal\n            .set_value(false, ctx)\n        {\n            log::warn!(\"Failed to reset <name> launch modal state: {e}\");\n        }\n    });\n}\n```\n\n---\n\n## Behavior summary\n\n| User type | Sees modal? |\n|---|---|\n| New signup | No — pre-dismissed in `AuthComplete` new-user branch |\n| Not signed in | No — trigger never fires without `AuthComplete` |\n| Existing user, flag enabled | Yes — on first startup after cloud prefs load |\n| Integration channel | No — suppressed by `Channel::Integration` check |\n| Already seen it | No — setting persists globally across devices |\n","frontmatter":{"name":"gui-create-launch-modal","description":"GUI desktop app only. Create a one-time launch modal in the Warp client (feature announcement, onboarding, etc.). Use when adding a new modal that should appear exactly once per user on startup, gated by a feature flag, with colors sourced from Warp theme tokens and terminal theme colors."},"isInternal":false,"tokens":3169,"sizeBytes":12506},{"name":"SKILL.md","path":".agents/skills/gui-integration-test/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/gui-integration-test/SKILL.md","title":"gui-integration-test","category":"anthropic-skill","format":"markdown","content":"---\nname: gui-integration-test\ndescription: GUI desktop app only. Writes, runs, and debugs Warp integration tests using the custom Builder/TestStep framework in `crates/integration`. Use when adding a new integration test, fixing a failing integration test, wiring a test into the manual runner or nextest suite, or verifying end-to-end UI and terminal behavior in Warp.\n---\n\n# Warp Integration Tests\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nUse this skill for Rust integration tests in Warp's custom framework under `crates/integration/`.\n\nThese are not ordinary unit tests. They boot a real Warp app instance, give it an isolated test home directory, drive it with synthetic UI and terminal events, and poll assertions until success or timeout.\n\n## When an integration test is the right tool\n\nIntegration tests are the most expensive tests in the repo. They boot the app, they are orders of magnitude slower than a unit test, and they are the most likely to go flaky. Write one when the risk you are covering genuinely lives *between* components:\n\n- Real terminal/PTY and shell-integration behavior, especially across bash, zsh, and fish.\n- Wiring and configuration: settings, keybindings, and user preferences actually taking effect end to end.\n- Cross-component flows a unit test cannot see — command palette into editor into terminal, focus and pane management, tab/window lifecycle.\n- Input and rendering paths that only exist once a real app (and sometimes a real display) is running.\n- Regressions for user-visible bugs that escaped unit tests.\n\nDo **not** reach for an integration test when:\n\n- The logic is deterministic and reachable in-process. Push it into a unit test (`rust-unit-tests`); it will run in milliseconds and point straight at the failure.\n- You are re-covering branch logic a unit test already covers. This harness is for the seams between components, not for re-testing conditionals at full app-boot cost.\n- You only need TUI rendering. That is `tui-testing` — this harness does not drive the TUI at all.\n- What you actually want is a screenshot or a manual look. Use `computer_use` or the `gui-integration-test-video` skill instead of attaching weak assertions to a full app boot.\n\nIf a behavior is hard to reach from a unit test *only* because of how the code is structured, prefer fixing the structure over writing a slow test around it.\n\n## Framework map\n\nThe core pieces are:\n\n- `crates/integration/src/bin/integration.rs`\n  - Manual integration test runner binary.\n  - Registers test names to `Builder` factories.\n  - Runs exactly one named test per invocation.\n- `crates/integration/tests/common/mod.rs`\n  - The outer Rust test harness used by `cargo test` and `cargo nextest`.\n  - Shells out to the integration binary.\n  - Forwards a limited set of env vars (`PATH`, `RUST_*`, `WARP_*`, `WARPUI_*`, `WGPU_*`, display-related vars).\n  - Re-runs tests up to 10 times when the integration binary exits with the special rerun code.\n- `crates/integration/src/test.rs`\n  - Module hub for integration tests.\n  - Add new test modules here and `pub use` their functions so the runner can see them.\n- `crates/integration/tests/integration/ui_tests.rs`\n  - List of UI-oriented integration tests that nextest should run.\n- `crates/integration/tests/integration/shell_integration_tests.rs`\n  - List of tests that must run against every shell or a specific shell matrix.\n- `crates/integration/src/builder.rs`\n  - Warp-specific wrapper around the lower-level WarpUI integration builder.\n  - Sets default timeout, hermetic home directory, shell rc files, user prefs, and real-display mode when requested.\n- `crates/warpui_core/src/integration/driver.rs`\n  - Executes steps, handles retries, precondition reruns, screenshots, video capture, artifact export, and `on_finish`.\n- `crates/warpui_core/src/integration/step.rs`\n  - Defines `TestStep`, input/event APIs, assertion polling, step-to-step data passing, and screenshot/recording hooks.\n- `app/src/integration_testing/`\n  - High-level helpers and assertions for common Warp behaviors.\n  - Prefer these helpers over raw low-level event plumbing whenever they fit.\n\n## How the framework actually runs a test\n\n1. A Rust test from `crates/integration/tests/integration/*.rs` calls `run_integration_test(\"test_name\")`.\n2. That harness launches the `integration` binary with the test name.\n3. The binary in `crates/integration/src/bin/integration.rs` looks up the name in `register_tests()`, builds the `Builder`, and turns it into a `TestDriver`.\n4. `Builder::build(...)` creates an isolated temp directory, points `HOME` at it, writes minimal rc files, and initializes file-backed user preferences.\n5. The driver runs each `TestStep` in order:\n   - setup callbacks\n   - synthetic events\n   - actions\n   - assertion polling until success or timeout\n6. If an assertion returns `PreconditionFailed`, the binary exits with the rerun code and the outer harness retries the whole test.\n7. On success, failure, or cancellation, the driver can run `on_finish` and export artifacts/runtime tags.\n\nThis means integration tests should be written for a hermetic environment. Do not rely on the developer's real shell dotfiles, home directory contents, or persisted Warp settings.\n\n## Where to put a new test\n\nAdd the actual test function in a module under `crates/integration/src/test/`.\n\nUse these heuristics:\n\n- Put the test in an existing module when it matches that feature area.\n- Create a new module when the feature does not fit an existing one cleanly.\n- Add the test to `crates/integration/tests/integration/ui_tests.rs` if it is primarily a UI/app behavior test.\n- Add the test to `crates/integration/tests/integration/shell_integration_tests.rs` if it needs to run against every shell, or depends on a specific shell/set of shells.\n\nBeing present in `crates/integration/src/test/*.rs` is not enough. For a test to run under `cargo nextest`, it also needs to be listed in one of the macro files in `crates/integration/tests/integration/`.\n\n## Authoring checklist for a new test\n\nWhen adding a new integration test, do all of the following:\n\n1. Implement `pub fn test_name() -> Builder` in a module under `crates/integration/src/test/`.\n2. Add the module to `crates/integration/src/test.rs`.\n3. `pub use` the new module's exports from `crates/integration/src/test.rs`.\n4. Add `register_test!(test_name);` in `crates/integration/src/bin/integration.rs`.\n5. Add `test_name` to either:\n   - `crates/integration/tests/integration/ui_tests.rs`, or\n   - `crates/integration/tests/integration/shell_integration_tests.rs`\n6. Default to making the test run in CI once it is added to one of those macro lists. Only mark it `#[ignore]` when the task explicitly calls for manual-only coverage or there is a concrete, documented reason it cannot run reliably in CI.\n7. Run the test manually first, then through nextest once it is stable enough for the suite you chose.\n\n## Writing the test body\n\nThe normal shape is:\n\n```rust\nuse crate::Builder;\nuse warp::integration_testing::step::new_step_with_default_assertions;\nuse warp::integration_testing::terminal::{\n    clear_blocklist_to_remove_bootstrapped_blocks,\n    execute_command_for_single_terminal_in_tab,\n    wait_until_bootstrapped_single_pane_for_tab,\n    util::ExpectedExitStatus,\n};\n\npub fn test_example() -> Builder {\n    Builder::new()\n        .with_step(wait_until_bootstrapped_single_pane_for_tab(0))\n        .with_step(clear_blocklist_to_remove_bootstrapped_blocks())\n        .with_step(execute_command_for_single_terminal_in_tab(\n            0,\n            \"echo hello\".to_string(),\n            ExpectedExitStatus::Success,\n            \"hello\".to_string(),\n        ))\n        .with_step(\n            new_step_with_default_assertions(\"Assert some UI state\")\n                .add_named_assertion(\"specific assertion name\", |app, window_id| {\n                    // inspect app state and return AssertionOutcome\n                    warpui::integration::AssertionOutcome::Success\n                }),\n        )\n}\n```\n\nPrefer a small number of focused steps with descriptive names over a huge monolithic test.\n\n## Builder guidance\n\n### `Builder::new()`\n\nStart here almost every time.\n\nWarp's wrapper automatically gives you:\n\n- a per-test root directory\n- isolated `HOME`\n- generated rc files for Bash, Zsh, and Fish\n- file-backed user preferences\n- a default 2-minute hard timeout\n- real-display support if `WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS` is present\n\n### `with_setup(...)`\n\nUse this for filesystem or environment setup before the app runs.\n\nCommon patterns:\n\n- `utils.set_env(\"NAME\", Some(value))`\n- creating files under `utils.test_dir()`\n- writing fixture config files\n\nPrefer this over reaching into the real filesystem.\n\n### `with_user_defaults(...)`\n\nUse this to set persisted Warp preferences before the test starts.\n\nThis is the right tool for settings backed by user preferences rather than environment variables.\n\n### `set_should_run_test(...)`\n\nUse this to gate tests on shell/platform/runtime capabilities when the test genuinely cannot run everywhere.\n\n### `with_on_finish(...)`\n\nUse this for final verification or artifact inspection that should happen after all steps complete, such as checking that screenshots or recordings were written.\n\n### `with_real_display()`\n\nUse this explicitly when the test needs a real display for frame capture or visual workflows. Video/screenshot tests should normally be manual or ignored in CI unless there is a stable real-display path.\n\n## `TestStep` guidance\n\n`TestStep` is the unit of execution. Each step can have:\n\n- setup callbacks\n- input events\n- actions\n- assertions\n- a timeout\n- retry count\n- failure handling\n\n### Start from helper constructors\n\nPrefer:\n\n- `wait_until_bootstrapped_single_pane_for_tab(0)`\n- `new_step_with_default_assertions(\"...\")`\n- `new_step_with_default_assertions_for_pane(\"...\", tab, pane)`\n\nThe default step helpers already assert:\n\n- no pending model events\n- no block executing\n\nThese are good baseline invariants for most UI interactions.\n\n### Prefer helper APIs over raw event plumbing\n\nUse high-level helpers from `app/src/integration_testing/` whenever possible:\n\n- terminal command execution helpers\n- block list helpers\n- command palette helpers\n- navigation helpers\n- settings helpers\n- workflow/file tree/notebook helpers\n\nDrop to raw `with_event(...)`, `with_event_fn(...)`, or saved-position mouse events only when there is no suitable helper.\n\n### Use named assertions\n\nPrefer `add_named_assertion(...)` over unnamed assertions. Named assertions make failure output and runtime tags much easier to interpret.\n\n### Use polling assertions instead of sleeps\n\nAssertions are polled until success or timeout. Lean on that model instead of hardcoding sleeps.\n\nGood pattern:\n\n- trigger an event or action\n- assert on the eventual UI/model state\n\nAvoid brittle timing assumptions.\n\n### Use step data when one step computes something for the next\n\nIf a later step needs data from an earlier one, use:\n\n- `add_named_assertion_with_data_from_prior_step(...)`\n- `StepDataMap`\n\nThis is useful for saving measured positions, counts, IDs, or other values from prior frames.\n\n### Use retries sparingly\n\n`set_retries(...)` can help for a legitimately retryable step, but do not use it to hide deterministic failures. Prefer making the step more robust first.\n\n### Use `PreconditionFailed` for genuinely environmental flakes\n\nIf the environment reaches a state where the rest of the test is invalid, return `AssertionOutcome::PreconditionFailed(...)` instead of failing hard. The outer harness can rerun the entire test up to 10 times. The existing bootstrap helper is a good model for this.\n\nUse this deliberately. The rerun mechanism exists for conditions the test genuinely cannot control, such as bootstrap racing or shell startup timing. It is not a way to turn an intermittently failing test green. A real bug that reproduces one run in five will pass under rerun and ship to users.\n\nBefore reaching for `PreconditionFailed`, confirm the failure is actually environmental by looking at the failure rate and the failure mode:\n\n```bash\nfor i in {0..50}; do\n  RUST_BACKTRACE=full cargo run -p integration --bin integration -- test_name || break\ndone\n```\n\nIf the same assertion fails in different ways, or fails while the environment looks fine, it is a product or test bug. Fix it rather than absorbing it into a rerun.\n\n## Designing a good integration test\n\n### Keep the scope as small as the behavior allows\n\nThe scope of a test follows the scope of what you boot and drive, so cover one user journey per test and let separate tests cover separate journeys. When a flow is long, prefer several shorter tests that each verify one hop over a single test that walks the whole path. A long test is slower, harder to diagnose, and fails for many unrelated reasons.\n\n### Assert on behavior a user could observe\n\nThe framework can see internal state, which makes it tempting to assert on it. Anchor each test on the user-observable outcome:\n\n- output visible in the terminal\n- focus moved where expected\n- UI element opened or closed\n- selection changed\n- settings applied\n\nInternal state assertions are still useful, but they should support the visible behavior rather than replace it. Assertions that mirror internal call sequences become change detectors: they break on every refactor and catch no bugs.\n\n### Make the failure diagnosable by someone else\n\nAn integration test spans processes, so a stack trace tells the next engineer almost nothing. The failure output has to carry the context instead:\n\n- Name every assertion with what it expects, not what it touches: `\"terminal shows command output\"`, not `\"check blocks\"`.\n- Give steps descriptive names; they are the breadcrumb trail through the run.\n- Put expected vs. actual into the assertion output rather than returning a bare failure.\n\nAssume the person reading the failure has never seen this test and does not own the code it covers.\n\n### Keep the test hermetic\n\n`Builder::new()` already gives you an isolated `HOME`, generated rc files, and file-backed preferences. Preserve that. Set up everything you depend on inside the test via `with_setup(...)` and `with_user_defaults(...)`, and never rely on the developer's dotfiles, real settings, network access, or state left behind by another test. A test that assumes a resource is already in the right state will fail for the wrong reason on someone else's machine or in CI.\n\n### Own the test\n\nLarger tests span components, so ownership is ambiguous by default and unowned tests rot. If you add one, you are the person who fixes it when it breaks. Put it in the module matching the feature area it covers so the next person can find the right owner.\n\n## Common test-writing patterns\n\n### 1. Wait for bootstrap first\n\nFor most terminal-facing tests, the first real step should be:\n\n- `wait_until_bootstrapped_single_pane_for_tab(0)`\n\nDo not start asserting on terminal UI before bootstrap completes.\n\n### 2. Clear the bootstrapped blocks if block indices matter\n\nIf the test relies on saved positions like `block_index:0`, clear the block list after bootstrap:\n\n- `clear_blocklist_to_remove_bootstrapped_blocks()`\n\nOtherwise the first user-generated block index depends on bootstrap output and the active shell.\n\n### 3. Use helper command runners\n\nPrefer helpers like:\n\n- `execute_command_for_single_terminal_in_tab(...)`\n- `execute_echo(...)`\n- `execute_echo_str(...)`\n- `execute_long_running_command(...)`\n\nThese helpers already handle a lot of correctness and output validation.\n\n## Running tests\n\n### Run one test directly through the integration binary\n\nUse this first while authoring:\n\n```bash\ncargo run -p integration --bin integration -- test_name\n```\n\nThis is the fastest way to iterate on a specific test because it bypasses the outer Rust test wrapper and runs the named test directly.\n\n### Run one test through nextest\n\nOnce it is wired into one of the `tests/integration/*.rs` macro lists, run it with nextest:\n\n```bash\ncargo nextest run --no-fail-fast --workspace test_name\n```\n\n### Run with a real display when needed\n\nFor screenshot/video or other real-display flows:\n\n```bash\nWARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo run -p integration --bin integration -- test_name\n```\n\nOr with nextest:\n\n```bash\nWARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo nextest run --no-fail-fast --workspace test_name\n```\n\n## Debugging and investigation\n\n### Get a backtrace on failures\n\n```bash\nRUST_BACKTRACE=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Pause on failure\n\nThis is useful when running locally and you want to inspect the failed UI state:\n\n```bash\nWARPUI_PAUSE_INTEGRATION_TEST_ON_FAILURE=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Pause after every step\n\nUseful for understanding exactly what the test is doing:\n\n```bash\nWARPUI_PAUSE_INTEGRATION_TEST_AT_EVERY_STEP=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Video and screenshots\n\nIf the task is specifically about recording a test, collecting screenshots, or validating overlay/video artifacts, also use the `gui-integration-test-video` skill (located at `.warp/skills/gui-integration-test-video/SKILL.md`).\n\n### Environment variable gotcha\n\n`utils.set_env(...)` affects runtime environment lookups such as `std::env::var(...)`.\n\nIt does not affect compile-time lookups like `option_env!(...)`. If the product code uses `option_env!`, changing the env var inside the test will not change that behavior without rebuilding.\n\n## Verification checklist\n\nBefore considering a new integration test done, verify all of the following:\n\n- The test function lives under `crates/integration/src/test/`.\n- The module is added and re-exported in `crates/integration/src/test.rs`.\n- The test is registered in `crates/integration/src/bin/integration.rs`.\n- The test is listed in the correct nextest macro file and will run in CI by default, unless it was explicitly made manual-only with a documented reason.\n- The test passes when run directly through the integration binary.\n- The test passes through nextest if it is meant to be part of the automated suite.\n- An integration test is the right level for this behavior; it is not re-covering logic a unit test could reach.\n- The assertions check the intended user-visible behavior.\n- Every assertion is named, and the failure output would be actionable to someone who has never seen the test.\n- The test does not depend on the developer's real home directory, shell config, or machine state.\n- If the test uses screenshots/video, the produced artifacts were actually inspected rather than only assuming they exist.\n\n## Anti-patterns to avoid\n\n- Writing a test only in `src/test/*.rs` and forgetting the nextest macro list.\n- Asserting on bootstrap-sensitive block indices without clearing the bootstrapped blocks first.\n- Using raw events everywhere when a helper already exists.\n- Adding sleeps instead of assertion polling.\n- Making the test depend on personal dotfiles, real settings, or non-hermetic filesystem state.\n- Using retries or `PreconditionFailed` to paper over a deterministic bug.\n- Re-testing branch logic that a unit test already covers, at full app-boot cost.\n- Asserting on internal call sequences instead of user-visible behavior.\n- Unnamed assertions that produce failure output nobody can act on.\n- One long test that walks an entire user journey instead of several focused ones.\n- Leaving a real-display/manual test enabled in CI without a stable path.\n\n## Good workflow for agents\n\nWhen asked to add or fix an integration test:\n\n1. Find the closest existing integration test module for the feature.\n2. Reuse helper assertions and step constructors before inventing new low-level plumbing.\n3. Register the test in all required places, not just the implementation file.\n4. Run the test manually first.\n5. If it belongs in automation, run it with nextest too.\n6. If the test exercises visual behavior, verify the resulting UI behavior or artifacts directly.\n","frontmatter":{"name":"gui-integration-test","description":"GUI desktop app only. Writes, runs, and debugs Warp integration tests using the custom Builder/TestStep framework in `crates/integration`. Use when adding a new integration test, fixing a failing integration test, wiring a test into the manual runner or nextest suite, or verifying end-to-end UI and terminal behavior in Warp."},"isInternal":false,"tokens":4463,"sizeBytes":20516},{"name":"SKILL.md","path":".agents/skills/gui-onboarding-verification-skill/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/gui-onboarding-verification-skill/SKILL.md","title":"gui-onboarding-verification-skill","category":"anthropic-skill","format":"markdown","content":"---\nname: gui-onboarding-verification-skill\ndescription: GUI desktop app only. Launch two parallel Oz cloud agents with computer use to download and install the latest stable Linux Warp build, capture screenshots while walking through first-time onboarding in both logged-out and logged-in states, then selectively fan out follow-up cloud agents for distinct onboarding branches proposed by those initial explorers. Use this whenever the user asks to test, document, screenshot, or walk through the Warp first-time install/onboarding experience in a cloud Linux environment.\n---\n\n# Onboarding verification skill\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nUse this skill to verify the first-time Warp install and onboarding flow on Linux with broader branch coverage than a single linear walkthrough.\n\nThe parent agent should not perform the walkthrough locally. Launch two parallel Oz cloud agents with computer use. Both initial children install the latest stable Warp Linux package appropriate for their platform and capture screenshots at every visible onboarding step until Warp reaches a usable terminal session. One child verifies the login-free flow. The other child verifies the logged-in flow using the managed secret `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN`.\n\nThose two baseline explorers are also responsible for noticing meaningful alternate onboarding branches and returning concrete plans for follow-up cloud agents. The parent agent should synthesize those plans, deduplicate overlapping suggestions, and launch a bounded second wave of targeted follow-up agents to improve coverage of paths a real user might encounter.\n\n## Parent workflow\n\n1. Launch exactly two remote Oz cloud agents in a single parallel `run_agents` batch with computer use enabled.\n2. Use no environment-specific assumptions unless the user provided an environment. If no environment was provided, omit the environment ID and let Warp choose the default remote environment.\n3. Give both baseline child agents the shared child prompt below, plus the appropriate flow-specific prompt.\n4. Wait for both baseline agents' reports. Each report must include:\n   - The completed baseline walkthrough result and artifacts.\n   - A concise list of observed UI quality issues, suspected bugs, error states, or rough edges, with screenshots when visible.\n   - A prioritized follow-up coverage plan describing distinct onboarding paths worth exploring with additional cloud agents.\n5. Treat the authenticated baseline child as blocked if `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` is missing or does not authenticate successfully.\n6. Build a combined coverage map from the two baseline reports. Deduplicate suggestions that reach the same visible state or exercise the same decision surface.\n7. Launch a second `run_agents` batch with computer use enabled for the most valuable follow-up onboarding branches:\n   - Prefer branches that materially change visible UI, available controls, downstream screens, auth state, or setup outcomes.\n   - Favor paths likely to expose correctness, polish, layout, truncation, loading, or validation problems.\n   - Default to at most four follow-up agents total unless the user explicitly asked for exhaustive coverage or the baseline reports show more than four clearly distinct high-value branches.\n   - Do not launch speculative follow-ups when the baseline agents did not observe a concrete branch point; report that coverage stopped after the baseline pass instead.\n8. Give each follow-up child the shared child prompt, the follow-up flow prompt below, the logged-out or logged-in flow prompt that matches its assigned auth state, and one synthesized branch assignment from the baseline reports.\n9. Wait for all follow-up reports before summarizing coverage, issues, artifacts, and any still-unexplored branches worth a later run.\n\n## Managed FTUE auth secret\n\n- `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` is an internal-team managed secret for cloud agents, not a repo file or prompt literal.\n- The secret should authenticate as a dedicated non-employee, non-`warp.dev` FTUE test user.\n- Rotate the secret with `oz-dev secret update --team --value-file <private-token-file> ONBOARDING_AGENT_FTUE_REFRESH_TOKEN`.\n- Treat the private token file as local scratch material only. Do not read it into chat, print it, stage it, commit it, upload it, or include it in artifacts. Delete it after the managed secret is updated.\n- Children should receive the secret only through the managed environment variable injected into the remote run.\n\nUse the initial `run_agents` call shaped like this:\n\n```text\nsummary: Launching two baseline cloud agents with computer use to compare logged-out and logged-in Warp onboarding screenshots and propose follow-up coverage branches.\nremote.computer_use_enabled: true\nagent_run_configs:\n- name: \"warp-onboarding-logged-out\"\n  prompt: the logged-out flow prompt below\n- name: \"warp-onboarding-logged-in\"\n  prompt: the logged-in flow prompt below\nbase_prompt: the shared child prompt below\n```\n\nWhen the baseline reports identify concrete follow-up branches, use a second `run_agents` call shaped like this:\n\n```text\nsummary: Launching targeted cloud follow-up agents to explore distinct onboarding branches identified by the baseline onboarding explorers.\nremote.computer_use_enabled: true\nagent_run_configs:\n- name: \"warp-onboarding-followup-theme-choice\"\n  prompt: the follow-up flow prompt below, the logged-out flow prompt below, and one synthesized logged-out branch assignment\n- name: \"warp-onboarding-followup-model-choice\"\n  prompt: the follow-up flow prompt below, the logged-in flow prompt below, and one synthesized logged-in branch assignment\nbase_prompt: the shared child prompt below\n```\n\n## Shared child prompt\n\nGive both cloud agents these shared instructions:\n\n```text\nYou are verifying the first-time Warp install and onboarding experience on Linux.\n\nGoal:\n- Download and install the latest stable Warp Linux build appropriate for this cloud environment's distro and CPU architecture.\n- Launch Warp in a fresh first-run state.\n- Take a screenshot at every visible onboarding step.\n- Continue until Warp reaches a usable terminal session, or stop and report a blocker if the assigned flow cannot proceed.\n- Notice alternate onboarding decisions that lead to meaningfully different screens, states, or outcomes, and return concrete follow-up cloud-agent plans for the parent orchestrator.\n- Treat visual polish, missing assets, misalignment, overlapping content, clipped text, poor contrast, broken loading states, unexpected errors, and confusing controls as verification findings rather than ignoring them.\n\nInstall requirements:\n- Use official stable Warp downloads only.\n- Do not use Warp Preview, Alpha, source builds, or a repository development build.\n- Detect CPU architecture with `uname -m`.\n- Detect the package manager or distro before choosing the package format.\n- Prefer native packages over AppImage because they install dependencies and register the app normally.\n\nStable Linux package mapping:\n- Debian/Ubuntu with amd64 or x86_64: https://app.warp.dev/download?package=deb\n- Debian/Ubuntu with arm64 or aarch64: https://app.warp.dev/download?package=deb_arm64\n- Fedora/RHEL/CentOS/openSUSE with amd64 or x86_64: https://app.warp.dev/download?package=rpm\n- Fedora/RHEL/CentOS/openSUSE with arm64 or aarch64: https://app.warp.dev/download?package=rpm_arm64\n- Arch with amd64 or x86_64: https://app.warp.dev/download?package=pacman\n- Arch with arm64 or aarch64: https://app.warp.dev/download?package=pacman_arm64\n- If no native package path is available, use the AppImage fallback:\n  - amd64 or x86_64: https://app.warp.dev/download?package=appimage\n  - arm64 or aarch64: https://app.warp.dev/download?package=appimage_arm64\n\nBefore launch:\n- Create a flow-specific artifact directory such as `~/warp-onboarding-logged-out` or `~/warp-onboarding-logged-in`.\n- Ensure the run starts from a fresh Warp first-run state by removing only Warp-specific config/data/cache/state directories for the test user, such as `~/.config/warp-terminal`, `~/.local/share/warp-terminal`, `~/.local/state/warp-terminal`, and `~/.cache/warp-terminal` if they exist.\n- Do not delete unrelated user files or system directories.\n\nScreenshot workflow:\n- Take the first screenshot before interacting with the first visible Warp window.\n- Take one screenshot before every user action.\n- Take another screenshot after each action if the UI changes.\n- Use sequential filenames with a flow prefix, such as `01-logged-out-initial-window.png` or `01-logged-in-initial-window.png`.\n- If anything looks wrong, take an additional issue-focused screenshot that captures the problematic state as clearly as possible.\n- Maintain a manifest file in the artifact directory with, for each screenshot:\n  - filename\n  - timestamp\n  - what was visible\n  - what action was about to happen or just happened\n- For issue-focused screenshots, add the suspected issue category and the screen or step where it appeared.\n- Do not include secret values, refresh tokens, ID tokens, auth redirect URLs, or Authorization headers in the manifest, logs, shell history, screenshots, or final report.\n\nOnboarding behavior:\n- Baseline children choose the default or most conservative option at each step unless the flow-specific prompt says otherwise, while recording branch points that deserve separate follow-up coverage.\n- Follow-up children take the specifically assigned alternate branch, then use the default or most conservative option for unrelated decisions unless the branch assignment says otherwise.\n- If telemetry, shell, theme, editor-import, or agent integration choices appear, use the default path and document the choice in the manifest.\n- Continue until a normal terminal prompt is visible and usable.\n\nUI quality review:\n- Watch for screens that are visually broken, obviously unfinished, misaligned, truncated, clipped, crowded, low-contrast, unexpectedly blank, stuck loading, or inconsistent with adjacent steps.\n- Watch for actionable errors or validation states that appear during normal flow exploration, including auth failures, failed button transitions, controls that do not respond, duplicated overlays, missing images, or broken post-selection states.\n- For every suspicious state:\n  - Capture a screenshot.\n  - Record the screen, the action that led to it, what looked wrong, and whether it blocked progress.\n  - Describe the issue factually. If expected behavior is uncertain, say it appears suspicious rather than claiming a confirmed bug.\n\nTerminal verification:\n- Once a terminal session is visible, run a harmless flow-specific command:\n  - logged-out flow: `echo warp-onboarding-logged-out-ready`\n  - logged-in flow: `echo warp-onboarding-logged-in-ready`\n- Capture a final screenshot showing the usable terminal and command output.\n\nReport back:\n- Whether you were a baseline explorer or a follow-up branch explorer.\n- Which flow you ran: logged-out or logged-in.\n- OS and distro detected.\n- CPU architecture detected.\n- Package URL and install method used.\n- Launch command used.\n- Whether the walkthrough reached a usable terminal session.\n- Ordered screenshot list with short descriptions.\n- Artifact directory path.\n- Any built-in artifact IDs or attachment names if the harness supports artifact upload.\n- Any visual polish concern, suspected bug, error state, or unpolished/misaligned screen, including:\n  - screenshot filename\n  - screen or step\n  - action taken immediately before it appeared\n  - concise observed behavior\n  - whether it blocked progress\n- Any blocker, crash, missing dependency, display problem, auth failure, or step that required judgment.\n- For baseline explorers, include a `Follow-up coverage plan` section with zero or more proposed child-agent branches. Each proposal must include:\n  - suggested agent name\n  - logged-out or logged-in flow\n  - onboarding screen or decision point where the alternate branch begins\n  - exact alternate choice or action sequence to explore\n  - why it is materially distinct from the baseline path\n  - what user-visible state, setup outcome, or failure mode it could reveal\n  - any secret, auth, or environment dependency\n  - priority: high, medium, or low\n- For follow-up explorers, include whether the assigned branch was reachable and completed. If a new branch point appears while following the assigned path, record it as a later-run suggestion instead of recursively expanding the run yourself.\n\nDo not upload screenshots or logs to public external services. If the harness provides a built-in artifact or screenshot attachment mechanism, use that. Otherwise, leave the files in the artifact directory and report their paths.\n```\n\n## Logged-out flow prompt\n\nAppend this prompt to the shared child prompt for the logged-out child:\n\n```text\nYou own the logged-out onboarding flow.\n\nFlow-specific goal:\n- Do not create an account, log in, or use a real user identity.\n- Continue only through login-free or account-free paths until Warp reaches a usable terminal session.\n- Stop and report a blocker if the flow requires login or account creation with no skip/continue-without-account option.\n\nFlow-specific onboarding behavior:\n- If there is a skip, \"continue without account\", \"not now\", \"login later\", or equivalent option, use it.\n- Do not enter an email address, connect OAuth, paste an auth token, or create credentials.\n- Be especially alert for logged-out branch points around choosing terminal-only versus agentic experiences, customization/layout options, third-party integration toggles, and terminal theme selection. If they appear, propose follow-up branches that exercise materially different choices rather than trying all alternates inline.\n- Use the artifact directory `~/warp-onboarding-logged-out`.\n```\n\n## Logged-in flow prompt\n\nAppend this prompt to the shared child prompt for the logged-in child:\n\n```text\nYou own the logged-in onboarding flow.\n\nFlow-specific goal:\n- Use the managed secret environment variable `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` to authenticate as the dedicated non-employee, non-`warp.dev` FTUE test user.\n- Exercise onboarding screens that are available to an already-authenticated user.\n- Continue through the authenticated onboarding path until Warp reaches a usable terminal session.\n\nSecret handling requirements:\n- Before doing auth work, verify that `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` exists and is non-empty without printing it.\n- Never echo, log, screenshot, upload, or report the secret value.\n- Avoid shell tracing (`set -x`) and avoid writing commands that place the raw token in shell history or process lists.\n- Treat every auth redirect URL containing the refresh token as secret-bearing material, even after URL-encoding.\n- Do not pass a token-bearing redirect URL to a shell command, desktop URI handler, browser address bar, process argument, log, artifact, or report. In particular, do not use commands such as `xdg-open`, `gio open`, `open`, or equivalent with the redirect URL.\n- If you need to construct an auth redirect URL, keep it only in a clipboard value or a private temporary file with user-only permissions, paste it through Warp's visible Paste Auth Token flow, then delete the temporary file immediately after use.\n\nSecure Paste Auth Token process:\n1. Verify `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` exists and is non-empty without printing it.\n2. Start Warp's normal login flow and derive the current-run `state` from Warp's generated login URL.\n3. Normalize the managed secret privately:\n   - Trim surrounding whitespace and one pair of surrounding single or double quotes if present.\n   - If the secret parses as a URL with a `refresh_token` query parameter, extract that `refresh_token` value and ignore any stale `state` in the secret.\n   - Otherwise, treat the trimmed secret as the raw refresh token.\n4. URL-encode the extracted refresh token and current-run `state` separately as query parameter values.\n5. Construct the redirect URL only in a clipboard value or private temporary file with user-only permissions.\n6. Return to Warp and use the visible Paste Auth Token path:\n   - Click the `Click here to paste your token from the browser` link, `Paste Auth Token` button, or equivalent pasted-token control shown by Warp.\n   - Focus the auth token text input that appears.\n   - Paste the prepared redirect URL into that input and submit it through Warp's UI so Warp parses and validates it.\n7. Delete any private temporary files immediately after use and clear the clipboard if the environment supports doing so safely.\n8. If the Paste Auth Token UI cannot be reached or automated safely, stop and report an auth blocker instead of parsing the redirect in place of Warp, using a desktop URI handler, browser address bar, or shell command with the token-bearing URL.\n\nPreferred authenticated path:\n- Launch Warp in a fresh first-run state and choose the login/sign-in path from onboarding.\n- Use Warp's built-in Paste Auth Token flow rather than visiting real OAuth providers, invoking a desktop URI handler, or asking the agent to parse/validate the redirect URI itself.\n- Derive `<state>` from the login URL generated by Warp if the UI exposes a copied login URL or opens the browser. If the UI does not expose the state after reasonable effort, report that as an auth blocker rather than bypassing state validation.\n- Do not preflight the token with Firebase Secure Token before handing it to Warp. Warp's desktop redirect handler only requires `refresh_token` and `state`; `user_uid` is optional, and `deleted_anonymous_user=true` handles the anonymous-user override case.\n- Treat `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` as either of these secret shapes:\n  - a raw Firebase refresh token, or\n  - a complete Warp desktop auth redirect URL containing a `refresh_token` query parameter.\n- Normalize the secret into a current-run redirect URL without printing it:\n  - Trim surrounding whitespace and one pair of surrounding single or double quotes if present.\n  - If the secret parses as a URL with a `refresh_token` query parameter, extract that `refresh_token` value and ignore any stale `state` in the secret.\n  - Otherwise, treat the trimmed secret as the raw refresh token.\n  - URL-encode the extracted refresh token and the current-run `state` separately as query parameter values.\n  - Build `warp://auth/desktop_redirect?refresh_token=<url-encoded-normalized-refresh-token>&deleted_anonymous_user=true&state=<url-encoded-current-state>`.\n  - Do not include `user_uid` unless it is already present in a provided desktop redirect URL; it is not required for this flow.\n- Construct the normalized redirect URL in a clipboard value or private temporary file only, then hand it to Warp through the Paste Auth Token UI. Do not parse, validate, or route the redirect outside of Warp.\n- If the Paste Auth Token flow cannot be reached or automated safely, stop and report an auth blocker instead of using a desktop URI handler or any shell command that contains the token-bearing URL.\n\nFallback authenticated path:\n- If Warp rejects the normalized redirect, report the non-sensitive user-visible error and classify whether the secret appeared to be a raw token or a desktop redirect URL, without reporting any token contents.\n- If the Paste Auth Token flow is blocked by UI automation issues, report the blocker and include the exact non-sensitive step where automation failed.\n- Do not switch to a logged-out path for this child.\n\nFlow-specific onboarding behavior:\n- Choose login/sign-in rather than skip/login-later when presented with an auth choice.\n- After auth succeeds, continue through the remaining onboarding screens with default or conservative options.\n- Be especially alert for logged-in branch points around model selection, account-aware onboarding screens, AI/agent setup, workspace or project setup, and any decision that changes available product capability. If they appear, propose follow-up branches that exercise materially different choices rather than trying all alternates inline.\n- After the terminal verification succeeds, click the upper-right avatar/account control, open Settings from that menu, and capture an additional screenshot that clearly shows the logged-in user's email address in Warp settings or account/profile settings.\n- Include the account/settings email screenshot in the manifest and final report. The email address itself may be visible in the screenshot, but do not copy the email into logs, shell output, or the final text report unless the user explicitly asks for it.\n- Use the artifact directory `~/warp-onboarding-logged-in`.\n```\n\n## Follow-up flow prompt\n\nAppend this prompt to the shared child prompt for every second-wave child, followed by the matching logged-out or logged-in flow prompt and one branch assignment synthesized from the baseline reports:\n\n```text\nYou own one follow-up onboarding branch selected by the parent orchestrator from an earlier baseline exploration report.\n\nFollow-up branch behavior:\n- Start from a fresh first-run Warp state and install the same latest stable Linux build using the shared instructions.\n- Respect the assigned auth state: remain logged out for logged-out assignments, or use the managed authenticated flow for logged-in assignments.\n- Follow the exact alternate onboarding choice or action sequence in the branch assignment.\n- Capture screenshots before and after each assigned branch decision, then continue to a usable terminal session if the path allows it.\n- Apply the same UI quality review standard as the baseline explorers and call out anything that looks broken, rough, misaligned, confusing, or unexpectedly error-prone.\n- If the assigned branch is not reachable, capture the closest relevant screen, report why it was unreachable, and do not silently substitute a different branch.\n- If the assigned branch reveals another interesting alternate path, record it as a later-run suggestion rather than recursively launching more agents yourself.\n\nFinal report additions:\n- Repeat the exact branch assignment you attempted in concise non-sensitive terms.\n- State whether it was reachable, completed, blocked, or not applicable.\n- Compare the branch outcome against the likely baseline behavior when that comparison is visible from the UI.\n```\n\n## Success criteria\n\nThe run is successful when:\n\n- Warp stable was installed from an official Linux package or AppImage for the detected architecture.\n- Screenshots were captured for each onboarding screen and the final usable terminal.\n- The logged-out child reached a usable terminal without login, account creation, or a real user identity.\n- The logged-in child authenticated using `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` and reached a usable terminal in the authenticated FTUE path.\n- The logged-in child captured an additional post-login screenshot from the avatar/settings flow showing the logged-in user's email address.\n- Each terminal session was usable enough to run its flow-specific `echo` command.\n- Both baseline explorers returned either concrete follow-up coverage proposals or an explicit explanation that they did not observe meaningful additional branch points.\n- The parent orchestrator launched targeted second-wave agents for the highest-value concrete branch proposals, unless there were no such proposals or a prerequisite blocker made them infeasible.\n- Every reported visual polish concern, suspected bug, or error state includes a screenshot reference whenever the issue was visible on screen.\n\n## Common failure handling\n\n- If the package manager prompts for confirmation, use the non-interactive confirmation flag supported by that package manager.\n- If launching `warp-terminal` fails because of display setup, inspect the cloud environment's display variables and try launching from the desktop/app launcher if computer use provides one.\n- If the logged-out flow blocks on login with no skip path, stop at that screen, capture a screenshot, and report that as the terminal point for the logged-out flow.\n- If the logged-in flow cannot authenticate because the secret is missing, invalid, expired, revoked, or cannot be routed through Warp's auth redirect flow, stop at that screen, capture a screenshot, and report the non-sensitive blocker.\n- If the native package cannot be installed because dependencies are unavailable, fall back to the matching AppImage and clearly report the fallback.\n","frontmatter":{"name":"gui-onboarding-verification-skill","description":"GUI desktop app only. Launch two parallel Oz cloud agents with computer use to download and install the latest stable Linux Warp build, capture screenshots while walking through first-time onboarding in both logged-out and logged-in states, then selectively fan out follow-up cloud agents for distinct onboarding branches proposed by those initial explorers. Use this whenever the user asks to test, document, screenshot, or walk through the Warp first-time install/onboarding experience in a cloud Linux environment."},"isInternal":false,"tokens":4986,"sizeBytes":24885},{"name":"SKILL.md","path":".agents/skills/gui-reproduce-bug-report-local/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/gui-reproduce-bug-report-local/SKILL.md","title":"gui-reproduce-bug-report-local","category":"anthropic-skill","format":"markdown","content":"---\nname: gui-reproduce-bug-report-local\nspecializes: reproduce-bug-report\nspecializes_source: warpdotdev/common-skills:.agents/skills/reproduce-bug-report\ndescription: GUI desktop app only. Repo-specific bug reproduction guidance for Warp. Specializes the core reproduce-bug-report skill for logged-out Warp UI repros, exact reporter-version installs, and login-free onboarding.\n---\n\n# Repo-specific bug reproduction guidance for `warp`\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\n## Prerequisite: install the parent skill\n\nThis skill specializes the core `reproduce-bug-report` skill (named in the `specializes` frontmatter field) and is not functional on its own. Before applying its guidance, confirm the parent skill is installed and resolvable at `.agents/skills/reproduce-bug-report/SKILL.md`. If it is missing, install it first by copying the skill directory from the source declared in the `specializes_source` frontmatter field (`warpdotdev/common-skills:.agents/skills/reproduce-bug-report`). Then continue with the guidance below.\n\nThis file is a companion to the core `reproduce-bug-report` skill. It does not redefine the shared Oz computer-use orchestration, artifact handling, safety rules, or reporting format. It specializes scope and setup for Warp bug reports.\n\n## Scope\n\n- Use this workflow only for Warp bugs that can be exercised while the app remains logged out.\n- Apply it to UI-visible Warp bugs, interaction bugs, rendering/layout bugs, logged-out onboarding bugs, settings bugs, editor/display bugs, terminal-display bugs, and other visual or interactive issues where screenshots or recordings would help.\n- Do not use it for authenticated-user flows, account-specific state, cloud-synced state, logged-in onboarding, or AI behaviors that require login.\n- If a report requires authentication, account state, cloud sync, or another logged-in-only capability, do not launch a repro agent with this local specialization; report that it is out of scope for the current logged-out Warp workflow.\n\n## Warp version and install strategy\n\n- Prefer reproducing against the exact Warp version/build and channel reported by the user.\n- Do not build Warp from source by default. Install the matching Linux package or binary release for the reporter's version/channel instead.\n- If the bug report names a macOS or Windows build, use the corresponding Linux build from the same version/channel when a matching Linux artifact exists, and state that this is a Linux proxy for the reporter's platform.\n- Use the repository's or Warp release tooling/docs available in the environment to find and install the exact versioned Linux artifact. Do not silently substitute the latest stable build when an exact matching version can be installed.\n- If the exact version/build cannot be found or installed, report that clearly, explain what was attempted, and use the closest justified fallback only when it is useful for continuing the investigation.\n- Record the requested reporter Warp version, the installed Linux version, the source of the installed artifact, and any fallback decision in the manifest and final report.\n\n## Logged-out Warp baseline\n\n- Keep Warp logged out for the entire repro attempt. Do not create an account, sign in, paste auth tokens, or use real user credentials.\n- Launch Warp and complete the login-free / continue-without-account onboarding path until a normal logged-out terminal session is usable.\n- Capture a post-onboarding baseline screenshot before attempting the bug-specific reproduction.\n- If the assigned bug cannot be exercised after entering a normal logged-out Warp session, stop and report the blocker instead of improvising an authenticated flow.\n\n## Local prompt additions\n\nWhen applying the core skill to Warp, ensure the parent prompt and child prompts include:\n\n- Reporter Warp version/build/channel: the exact value from the report, or `unknown`.\n- Build/app target: the exact versioned Linux Warp package/binary to install, or the justified fallback if an exact artifact is unavailable.\n- Assigned Warp state: first-run logged-out state, completed logged-out onboarding, terminal/session/layout/settings state, or the targeted code-path hypothesis.\n- A reminder that Warp must remain logged out and that logged-in-only reports are blocked for this specialization.\n\n## Local reproduction priorities\n\n- Match the reporter's Warp version/build/channel before broadening the search space.\n- Follow the issue's exact steps first, then test at most two targeted variations supported by the issue or by a code-path hypothesis.\n- Prefer targeted hypotheses derived from Warp UI strings, settings names, feature names, telemetry names, route names, and relevant components over broad exploratory clicking.\n- In the final report, include:\n  - the reporter-requested Warp version/build/channel\n  - the installed Linux Warp version/build/channel\n  - the package or binary source\n  - whether a fallback was used\n  - whether the test was a Linux proxy for a macOS or Windows report\n","frontmatter":{"name":"gui-reproduce-bug-report-local","specializes":"reproduce-bug-report","specializes_source":"warpdotdev/common-skills:.agents/skills/reproduce-bug-report","description":"GUI desktop app only. Repo-specific bug reproduction guidance for Warp. Specializes the core reproduce-bug-report skill for logged-out Warp UI repros, exact reporter-version installs, and login-free onboarding."},"isInternal":false,"tokens":1110,"sizeBytes":5446},{"name":"SKILL.md","path":".agents/skills/gui-settings-ui/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/gui-settings-ui/SKILL.md","title":"gui-settings-ui","category":"anthropic-skill","format":"markdown","content":"---\nname: gui-settings-ui\ndescription: GUI desktop app only. How to build a Settings page in the Warp client (app/src/settings_view) so its widgets and settings search behave correctly — picking a PageType, deciding whether a heading belongs in the page-title slot or inside a widget, gating a widget, and scoping search_terms per widget. Use when adding or editing a settings page, a SettingsWidget, or anything that affects settings search.\n---\n\n# gui-settings-ui\n\n**Scope — GUI desktop app only.** This skill covers the settings modal in `app/src/settings_view/`, part of Warp's **GUI** desktop front-end. It does not apply to the headless TUI (`crates/warp_tui`). For general UI conventions see `gui-ui-guidelines`.\n\nSettings pages look simple, so they get written by pattern-matching the nearest neighbor — and the nearest neighbor is often wrong. The same two mistakes have produced five Linear tickets (APP-5060, APP-5058, APP-4910, APP-4922, APP-5059, one of which turned out to be a false positive). Read this before writing a settings page so the sixth doesn't happen.\n\n## The model\n\nA settings page is a `PageType` (`app/src/settings_view/settings_page.rs`). It holds **a list of searchable widgets** plus **an optional page title**:\n\n```rust\nPageType::new_uncategorized(widgets, Some(\"Knowledge\"))\nPageType::new_categorized(categories, None)\nPageType::new_monolith(widget, Some(\"Billing and Usage\"), /* is_dual_scrollable */ true)\n```\n\n- **`Uncategorized`** — a flat list of widgets. The common shape.\n- **`Categorized`** — widgets grouped under `Category`s, each with a subheader (rendered via `render_sub_header`) and an optional subtitle.\n- **`Monolith`** — the whole page is a single widget because its content can't be split for search (Keybindings, Teams, About, Environments).\n\nEach widget implements `SettingsWidget` (`settings_page.rs`):\n\n- `search_terms(&self) -> &str` — the terms this widget matches on.\n- `render(..)` — the widget's rows.\n- `should_render(&self, app) -> bool` — defaults to `true`. This is one of **two** ways to conditionally show a setting, and usually not the right one — see \"Two ways to gate a widget\" below.\n- `widget_id()` / `static_widget_id()` — default to `std::any::type_name::<Self>()`, used for scroll-to and deeplinks.\n\n**How search filters.** `PageType::update_filter` keeps a widget when\n`widget.should_render(app) && search_terms_match(widget.search_terms(), query)`.\n`search_terms_match` requires **every whitespace-delimited word** of the query to appear (case-insensitively, as a substring) somewhere in the widget's terms; an empty query matches everything. So filtering happens **per widget** — a widget is the smallest unit that can survive or disappear.\n\n**Whether the page title survives a search depends on the variant.** `PageType::render_page` treats the title differently for widget-list pages than for a monolith, and that difference is the easiest thing here to get wrong.\n\nFor **`Uncategorized` and `Categorized`**, the title is drawn **once, before the loop** over the filtered widget list, unconditionally:\n\n```rust\nlet mut page = Flex::column();\nif let Some(title) = title {\n    page.add_child(render_page_title(title, HEADER_FONT_SIZE, appearance));\n}\nfor widget in widgets { /* … only the widgets that matched … */ }\n```\n\nThe filter cannot reach it. That is what makes the title slot a fix for bug class 1: a title passed through `PageType` survives filtering on these two variants, while a title rendered inside a widget does not.\n\nFor **`Monolith`**, the title is **gated on the sole widget surviving the filter**:\n\n```rust\nFilteredPageType::Monolith { widget, title, .. } => {\n    let mut page = Empty::new().finish();\n    if let Some(widget) = widget          // None when the widget didn't match\n        && widget.should_render(app)\n    {\n        if let Some(title) = title { /* … title, then the widget … */ }\n    }\n    page                                  // otherwise nothing renders at all\n}\n```\n\n`get_filtered` sets that `widget` to `filter.then_some(..)`, so a non-matching query makes it `None` and the page renders empty — **title included**. On a monolith the title slot buys no search protection; the page is all-or-nothing, title and all.\n\nOne more thing worth knowing before you go looking for a title stranded over zero rows: you won't find one. `SettingsView::filtered_pages` (`app/src/settings_view/mod.rs`) drops any page whose `MatchData` is falsy from the sidebar and auto-selects the first page that still matches, so a fully non-matching page is never displayed. The state the title slot actually protects is the **partial** match — some widgets survive, some don't — and only widget-list pages can be in it.\n\n## Two ways to gate a widget\n\nThere are two mechanisms for conditionally showing a setting, and they are not interchangeable.\n\n**1. An `if` at page-build time — never create the widget.** This is how most gating in `AISettingsPageView::build_page` (`ai_page.rs`) is written:\n\n```rust\nif FeatureFlag::AIRules.is_enabled() {\n    widgets.extend(Self::knowledge_widgets());\n}\nif cfg!(feature = \"voice_input\")\n    && ai_settings.voice_input_enabled_internal.is_supported_on_current_platform()\n{\n    widgets.push(Box::new(VoiceWidget::default()));\n}\n```\n\n**2. `should_render(&self, app) -> bool` — create the widget and let it opt out per pass.**\n\n**Which one: can the value change while the app is running?**\n\n- **Fixed for the process** — a feature flag, a `cfg!` feature, a platform-support check. → **Use the build-time `if`.** This is the preferred default: the widget never exists, so there is nothing to filter, render, or reason about.\n- **Can change at runtime** — a setting the user toggles, auth state, an availability check that can flip mid-session. → **Use `should_render`.**\n\nThe reason is *when each is evaluated*. The build-time `if` runs once, when the page is constructed, and its result is frozen until something rebuilds the page (for AI/Code subpages, only switching subpages does — see below). `should_render` is re-evaluated on every filter and render pass, so it tracks a value that changes while the settings page is open. Gate a runtime-changing value with a build-time `if` and the page goes stale; gate a static flag with `should_render` and you carry a widget around for nothing.\n\nThe real `should_render` users are all the runtime kind: `SettingsSyncWidget` (`main_page.rs`) on auth state, `WarpDriveToggleWidget` (`warp_drive_page.rs`) on `WarpDriveSettings::is_warp_drive_available`, and the CLI-agent rich-input widgets (`ai_page.rs`) on the user-toggleable footer setting, via `should_render_cli_agent_rich_input`.\n\nEither mechanism keeps search honest: an uncreated widget isn't in the list, and `update_filter` already skips a widget whose `should_render` is false. What you must never do is hide rows inside `render` while `search_terms` still advertises them — that makes the page match a query and then show nothing for it.\n\n## The decision that matters: where does the heading live?\n\nFor any heading on a settings page, ask **what does it name?**\n\n- **It names the whole page** → it belongs in the `PageType` title slot. On `Uncategorized` / `Categorized` that is also what keeps it on screen while the page is filtered; on a `Monolith` it is a structural choice only (see above).\n- **It names one section among several** → it belongs inside the widget (or `Category`) that owns that section, so it disappears together with its own rows.\n\nGetting this wrong in the first direction is bug class 1 below. Getting it wrong in the second direction leaves a section header stranded above unrelated rows.\n\n### Page-shape taxonomy\n\nClassify the page before you write it:\n\n- **Single-topic page** — one heading names everything on it, but the content is still made of separately-matchable widgets. Knowledge, Third party CLI agents, Editor and Code Review, Account, Scripting. → **Title in the `PageType` slot.**\n- **Multi-section page** — several independent sections, each with its own heading. Warp Agent, Agent profiles, Appearance, Features. → **Per-section headings live in widgets/categories** and correctly disappear with their rows. (For `Categorized`, `get_filtered` drops categories whose widgets all filtered out, so their subheaders vanish automatically — that's the behavior you want.)\n- **Monolith page** — Keybindings, Teams, About, Environments, Codebase Indexing. There is no partial-match state: the sole widget either matches, and the whole page renders, or it doesn't, and the whole page renders empty and drops out of the sidebar. So a monolith can never strand an orphaned setting under a missing heading — it is **not** affected by bug class 1, but for that reason, *not* because its title is protected. Passing the title through the slot is still the tidier structure (Billing and Usage and Referrals do), just don't expect it to keep the title on screen during a non-matching search; on a monolith it will not. Codebase Indexing looks single-topic but is really one unfilterable widget covering the whole page (`CodePageWidget`, wrapped by `CodeIndexingPageWidget`) — building it as `Uncategorized` instead of `Monolith` made the sidebar show a permanent, misleading \"(1)\" on any match ([APP-5530]).\n\nThe two are not mutually exclusive: a page can name itself in the title slot **and** have per-section subheaders inside its widgets. Privacy does exactly that — `PageType::new_uncategorized(widgets, Some(\"Privacy\"))` plus `render_sub_header` calls inside individual widgets. The rule is per heading, not per page.\n\nWorked positive example: **Scripting** (`scripting_page.rs`) is a small page done right — two focused widgets (`WarpControlCliInstallWidget`, `LocalControlModeWidget`) with their own `search_terms`, and `PageType::new_uncategorized(widgets, Some(\"Scripting\"))`. A ticket was once filed claiming Scripting needed splitting; it was canceled because the premise was wrong. A small page is not automatically a mega-widget.\n\n## Bug class 1 — the vanishing title\n\n**Symptom:** on a single-topic `Uncategorized` or `Categorized` page, typing a search term that matches one row makes the page heading disappear along with the non-matching rows, leaving an unlabeled orphan setting. (A monolith can't reach this state — see the taxonomy above.)\n\n**Cause:** the only heading was rendered inside a widget — via `build_sub_header` / `render_page_title` in that widget's `render`, or via a header-only widget that exists just to draw a title. Filtering removes the widget, and the heading goes with it.\n\nCanonical fix — commit `ddadcee` ([APP-5060], #14519), Knowledge. Before, `AIFactWidget::render` opened with:\n\n```rust\nlet header = build_sub_header(appearance, \"Knowledge\", …).finish();\nlet mut column = Flex::column().with_child(header) /* … all the Knowledge rows … */;\n```\n\nAfter, the heading moved to page chrome and the rows became focused widgets:\n\n```rust\nlet title = match subpage {\n    AISubpage::Knowledge => Some(\"Knowledge\"),\n    AISubpage::ThirdPartyCLIAgents => Some(\"Third party CLI agents\"),\n    AISubpage::WarpAgent | AISubpage::Profiles => None,\n};\nPageType::new_uncategorized(widgets, title)\n```\n\n(The `ThirdPartyCLIAgents` arm and the exhaustive `match` came from #14524; note the deliberate absence of a `_` arm, so a new subpage forces this decision.)\n\nThe same commit (#14524) deleted `CodeSubpageHeaderWidget` from the then-combined Code page — a widget whose entire job was `build_sub_header(appearance, self.title, None)` — and replaced it with a title passed through `PageType`. Those two halves are now separate pages, `code_indexing_page.rs` and `code_editor_review_page.rs`, each passing its own `PAGE_TITLE` through the slot. **A header-only widget is always this bug.** If a widget renders nothing but a title, delete it and use the title slot.\n\n## Bug class 2 — the unfilterable mega-widget\n\n**Symptom:** searching a term that should isolate one row shows every row on the page, and the sidebar match count is 1 no matter how specific the query is.\n\n**Cause:** one widget renders many unrelated settings and declares one mega `search_terms()` blob covering all of them. The widget is the filter unit, so it's all-or-nothing.\n\nBefore (`CLIAgentWidget`, pre-#14524) — one widget, one blob, seven settings:\n\n```rust\nfn search_terms(&self) -> &str {\n    \"third party cli coding agent claude codex gemini toolbar footer layout chip chips \\\n     rearrange re-arrange bar command regex auto show rich input dismiss ctrl enter submit newline\"\n}\n```\n\nAfter — one widget per setting, each with terms scoped to just that setting:\n\n```rust\nfn cli_agent_widgets() -> Vec<Box<dyn SettingsWidget<View = AISettingsPageView>>> {\n    vec![\n        Box::new(CLIAgentWidget::default()),\n        Box::new(CLIAgentAutoToggleRichInputWidget::default()),\n        Box::new(CLIAgentAutoOpenRichInputWidget::default()),\n        Box::new(CLIAgentAutoDismissRichInputWidget::default()),\n        Box::new(CLIAgentSubmitRichInputWidget::default()),\n        Box::new(CLIAgentCommandsWidget),\n        Box::new(CLIAgentToolbarLayoutWidget),\n    ]\n}\n```\n\nRules of thumb when splitting:\n\n- **One widget ≈ one setting row** (or one tightly-coupled group that always shows and hides together).\n- **Scope `search_terms` to that widget only.** Keep enough shared context that a page-level query still matches (each CLI-agent widget above keeps `\"third party cli coding agent\"`), then add the terms unique to the row.\n- **Give each row the right gating mechanism** (see \"Two ways to gate a widget\"). A row that used to sit behind an `if … { … }` inside a mega-`render` becomes its own widget: if its condition is a static feature flag or platform check, gate it with an `if` in the page's build function and never create it; if it can change at runtime, give it `should_render`. The CLI-agent rich-input rows are the runtime case — they hang off the user-toggleable footer setting — so they use `should_render` and share the predicate through a small free function (`should_render_cli_agent_rich_input(app)`) rather than duplicating the condition.\n- **Move per-row state with the row.** Each `SwitchStateHandle` / `MouseStateHandle` moves to the widget that owns its control. Never create one inline while rendering (see `gui-ui-guidelines` and the AGENTS.md note on `MouseStateHandle`).\n- **Watch the widget ids.** `widget_id()` is `std::any::type_name::<Self>()`, so splitting a widget changes ids. `settings_widget_deeplink_target` in `app/src/settings_view/mod.rs` maps stable public slugs (`warp://settings?widget=<slug>`) onto them. The CLI-agent split deliberately kept `CLIAgentWidget` as the first widget so `cli_agent_settings_widget_id()` — the target of the `cli_agents` deeplink — stayed valid. If you rename or remove a widget that backs a deeplink, re-point the accessor.\n\n## Subpages rebuild their `PageType` — reapply the filter\n\nAI subpages rebuild their `PageType` when the active subpage changes (`AISettingsPageView::set_active_subpage` / `build_page`). A fresh `PageType` starts with **every** widget in its filter, so a live search query is silently dropped unless it's reapplied. `SettingsView::reapply_search_filter_to_active_subpage` in `app/src/settings_view/mod.rs` exists for exactly this ([APP-4922], #14116). If you add a code path that rebuilds a subpage's page while search may be active, call it.\nThe Code umbrella no longer works this way: `CodeIndexing` and `EditorAndCodeReview` are separate pages that each own their widgets outright, so nothing rebuilds and there is no filter to reapply. Prefer that shape for new umbrella children — a subpage that owns its own page needs none of this machinery.\n\n## Known anti-examples still in the tree\n\nUseful to read, not to copy:\n\n- **Warpify** (`warpify_page.rs`) — `PageType::new_categorized(categories, None)` where the first category is `Category::new(\"\", vec![Box::new(TitleWidget::default())])` and `TitleWidget::render` calls `render_page_title(\"Warpify\", …)`. Single-topic page, title inside a widget: bug class 1.\n- **Warp Drive** (`warp_drive_page.rs`) — `PageType::new_uncategorized([WarpDriveHeaderWidget, WarpDriveToggleWidget], None)` with no title slot at all. `WarpDriveHeaderWidget` is a conditional sign-up banner, so a signed-in user sees only a bare toggle row and no page heading.\n\n## How to verify a settings-page change\n\nVerification is cheap here; do both.\n\n1. **Unit-test the filter.** `app/src/settings_view/mod_tests.rs` has the harness — `StubWidget`, `stub_widgets_page`, and `visible_widget_count` (which reads `PageType::get_filtered`). Assert that a term unique to one widget yields exactly one visible widget, and that clearing the query restores all of them. Follow the `${filename}_tests.rs` convention from AGENTS.md; run with `cargo nextest run -p warp settings_view`.\n2. **Exercise the real search.** Open Settings, go to the page, and check three things:\n   - a term matching exactly one row leaves **only** that row,\n   - the **page title is still visible** while that filter is active — expect this on an `Uncategorized` / `Categorized` page; a monolith correctly disappears whole instead,\n   - clearing the search restores the full page.\n\n## Anti-patterns\n\n```rust\n// A widget whose whole job is to draw the page heading. Delete it and pass the\n// title through PageType instead.\nstruct FooSubpageHeaderWidget { title: &'static str }\n\n// A single-topic page with no page title, drawing its heading inside a widget.\n// Any non-matching search term erases the heading.\nPageType::new_uncategorized(widgets, None)   // and build_sub_header(\"Foo\", …) in a widget\n\n// One widget, one mega search_terms blob, many unrelated rows: nothing can be\n// filtered or attributed individually.\nfn search_terms(&self) -> &str { \"foo bar baz qux quux corge grault\" }\n\n// Conditional rows hidden inside render() while search_terms still advertises\n// them — the page matches a query and then renders nothing for it.\nfn render(&self, …) { if flag_enabled { /* the only rows */ } }\n// Gate the widget instead: an `if` at page-build time for a static flag, or\n// should_render() when the condition can change at runtime.\n```\n","frontmatter":{"name":"gui-settings-ui","description":"GUI desktop app only. How to build a Settings page in the Warp client (app/src/settings_view) so its widgets and settings search behave correctly — picking a PageType, deciding whether a heading belongs in the page-title slot or inside a widget, gating a widget, and scoping search_terms per widget. Use when adding or editing a settings page, a SettingsWidget, or anything that affects settings search."},"isInternal":false,"tokens":4284,"sizeBytes":18327},{"name":"SKILL.md","path":".agents/skills/gui-ui-guidelines/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/gui-ui-guidelines/SKILL.md","title":"gui-ui-guidelines","category":"anthropic-skill","format":"markdown","content":"---\nname: gui-ui-guidelines\ndescription: GUI desktop app only. Catalog of guidelines for writing UI code in the Warp client. Read whenever doing any UI work in this repo, up front before writing the change, so the relevant guidelines shape the implementation.\n---\n\n# gui-ui-guidelines\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nThis skill is a growing catalog of guidelines for working on Warp's UI code. Each guideline captures a lesson that would otherwise be re-learned through review — typically because an agent or contributor reinvented a component, drifted from the design system, or bypassed a shared abstraction.\n\n**How to use this skill:**\n\n- Read through the guidelines below once at the start of any UI task, then keep them in mind while implementing. The list is short enough to scan.\n- Each guideline is self-contained. Not every one will apply to every task — use judgment. But if a guideline *does* apply, follow it.\n- When in doubt, prefer reusing an existing abstraction over introducing a new one. The Warp UI has accumulated a well-factored set of shared components and themes; new one-offs almost always drift.\n\nFor work on the settings modal (`app/src/settings_view/`) — a settings page, a `SettingsWidget`, or anything that affects settings search — also read the `gui-settings-ui` skill, which covers the page-title, widget-gating, and widget-splitting rules specific to that surface.\n\nNew guidelines get added here over time. If you discover a recurring UI mistake that would have been caught by a written rule, add it.\n\n---\n\n## Guideline: Reuse button themes\n\nButton colors come from a shared set of `ActionButtonTheme` impls in `app/src/view_components/action_button.rs` (and the parallel `Theme` impls in `crates/ui_components/src/button/themes.rs`) — `PrimaryTheme`, `SecondaryTheme`, `NakedTheme`, `DangerPrimaryTheme`, etc. These encode the design system and keep button colors consistent across the app.\n\nWhen styling a button, **use one of the existing themes unchanged**. The shared themes are well-established and vetted; if one looks \"wrong\" for your use case, the most likely explanation is that you're reaching for the wrong theme, not that the theme is buggy.\n\nDo **not** modify a shared theme on your own initiative. Changing `PrimaryTheme`, `SecondaryTheme`, etc. affects every button in the app, and a tweak that fixes your screen can silently regress others. Only edit a shared theme when the user has explicitly confirmed that the design-system component itself needs to change.\n\nRed flags that you're about to make buttons inconsistent:\n\n- Writing a new `impl ActionButtonTheme for FooPrimaryTheme` that delegates to `PrimaryTheme` and only tweaks one method (usually `text_color`). Almost always the right move is to use `PrimaryTheme` directly and accept the result.\n- Hard-coding `ColorU::new(...)` instead of using `appearance.theme()` accessors (`accent`, `font_color(bg)`, `foreground`, etc.).\n- Setting `should_opt_out_of_contrast_adjustment` to `true` to force a specific label color.\n- Naming a theme after a feature or view (`FooPrimaryTheme`, `BarSubmitTheme`) rather than a design-system role.\n\nIf an existing theme genuinely doesn't fit and you think the shared theme should change, surface that to the user before editing it, rather than either editing it unilaterally or papering over it with a one-off.\n","frontmatter":{"name":"gui-ui-guidelines","description":"GUI desktop app only. Catalog of guidelines for writing UI code in the Warp client. Read whenever doing any UI work in this repo, up front before writing the change, so the relevant guidelines shape the implementation."},"isInternal":false,"tokens":841,"sizeBytes":3800},{"name":"SKILL.md","path":".agents/skills/logging-and-error-reporting/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/logging-and-error-reporting/SKILL.md","title":"logging-and-error-reporting","category":"anthropic-skill","format":"markdown","content":"# logging-and-error-reporting\n\nPath: `.agents/skills/logging-and-error-reporting/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/logging-and-error-reporting/SKILL.md)","tokens":300,"sizeBytes":228},{"name":"SKILL.md","path":".agents/skills/promote-feature/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/promote-feature/SKILL.md","title":"promote-feature","category":"anthropic-skill","format":"markdown","content":"# promote-feature\n\nPath: `.agents/skills/promote-feature/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/promote-feature/SKILL.md)","tokens":300,"sizeBytes":192},{"name":"SKILL.md","path":".agents/skills/remove-feature-flag/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/remove-feature-flag/SKILL.md","title":"remove-feature-flag","category":"anthropic-skill","format":"markdown","content":"# remove-feature-flag\n\nPath: `.agents/skills/remove-feature-flag/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/remove-feature-flag/SKILL.md)","tokens":300,"sizeBytes":204},{"name":"SKILL.md","path":".agents/skills/review-pr-local/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/review-pr-local/SKILL.md","title":"review-pr-local","category":"anthropic-skill","format":"markdown","content":"# review-pr-local\n\nPath: `.agents/skills/review-pr-local/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/review-pr-local/SKILL.md)","tokens":300,"sizeBytes":192},{"name":"SKILL.md","path":".agents/skills/rust-unit-tests/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/rust-unit-tests/SKILL.md","title":"rust-unit-tests","category":"anthropic-skill","format":"markdown","content":"# rust-unit-tests\n\nPath: `.agents/skills/rust-unit-tests/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/rust-unit-tests/SKILL.md)","tokens":300,"sizeBytes":192},{"name":"SKILL.md","path":".agents/skills/triage-issue-local/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/triage-issue-local/SKILL.md","title":"triage-issue-local","category":"anthropic-skill","format":"markdown","content":"# triage-issue-local\n\nPath: `.agents/skills/triage-issue-local/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/triage-issue-local/SKILL.md)","tokens":300,"sizeBytes":201},{"name":"SKILL.md","path":".agents/skills/tui-testing/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-testing/SKILL.md","title":"tui-testing","category":"anthropic-skill","format":"markdown","content":"# tui-testing\n\nPath: `.agents/skills/tui-testing/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-testing/SKILL.md)","tokens":300,"sizeBytes":180},{"name":"SKILL.md","path":".agents/skills/tui-ui-guidelines/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-ui-guidelines/SKILL.md","title":"tui-ui-guidelines","category":"anthropic-skill","format":"markdown","content":"# tui-ui-guidelines\n\nPath: `.agents/skills/tui-ui-guidelines/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-ui-guidelines/SKILL.md)","tokens":300,"sizeBytes":198},{"name":"SKILL.md","path":".agents/skills/tui-verify-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-verify-change/SKILL.md","title":"tui-verify-change","category":"anthropic-skill","format":"markdown","content":"# tui-verify-change\n\nPath: `.agents/skills/tui-verify-change/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-verify-change/SKILL.md)","tokens":300,"sizeBytes":198},{"name":"SKILL.md","path":".warp/skills/gui-integration-test-video/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.warp/skills/gui-integration-test-video/SKILL.md","title":"gui-integration-test-video","category":"anthropic-skill","format":"markdown","content":"# gui-integration-test-video\n\nPath: `.warp/skills/gui-integration-test-video/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.warp/skills/gui-integration-test-video/SKILL.md)","tokens":300,"sizeBytes":221},{"name":"SKILL.md","path":"resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md","title":"oz-child-agent-orchestration","category":"anthropic-skill","format":"markdown","content":"# oz-child-agent-orchestration\n\nPath: `resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md)","tokens":300,"sizeBytes":267},{"name":"SKILL.md","path":"resources/bundled/harness_skills/oz-finish-task/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-finish-task/SKILL.md","title":"oz-finish-task","category":"anthropic-skill","format":"markdown","content":"# oz-finish-task\n\nPath: `resources/bundled/harness_skills/oz-finish-task/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-finish-task/SKILL.md)","tokens":300,"sizeBytes":225},{"name":"SKILL.md","path":"resources/bundled/harness_skills/oz-notify-user/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-notify-user/SKILL.md","title":"oz-notify-user","category":"anthropic-skill","format":"markdown","content":"# oz-notify-user\n\nPath: `resources/bundled/harness_skills/oz-notify-user/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-notify-user/SKILL.md)","tokens":300,"sizeBytes":225},{"name":"SKILL.md","path":"resources/bundled/harness_skills/oz-report-pr/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-report-pr/SKILL.md","title":"oz-report-pr","category":"anthropic-skill","format":"markdown","content":"# oz-report-pr\n\nPath: `resources/bundled/harness_skills/oz-report-pr/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-report-pr/SKILL.md)","tokens":300,"sizeBytes":219},{"name":"SKILL.md","path":"resources/bundled/harness_skills/oz-upload-file/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-upload-file/SKILL.md","title":"oz-upload-file","category":"anthropic-skill","format":"markdown","content":"# oz-upload-file\n\nPath: `resources/bundled/harness_skills/oz-upload-file/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-upload-file/SKILL.md)","tokens":300,"sizeBytes":225},{"name":"SKILL.md","path":"resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md","title":"edit-figma-design","category":"anthropic-skill","format":"markdown","content":"# edit-figma-design\n\nPath: `resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md)","tokens":300,"sizeBytes":238},{"name":"SKILL.md","path":"resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md","title":"figma-code-connect-components","category":"anthropic-skill","format":"markdown","content":"# figma-code-connect-components\n\nPath: `resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-code-connect-components/SKILL.md)","tokens":300,"sizeBytes":274},{"name":"SKILL.md","path":"resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md","title":"figma-create-design-system-rules","category":"anthropic-skill","format":"markdown","content":"# figma-create-design-system-rules\n\nPath: `resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-create-design-system-rules/SKILL.md)","tokens":300,"sizeBytes":283},{"name":"SKILL.md","path":"resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md","title":"figma-create-new-file","category":"anthropic-skill","format":"markdown","content":"# figma-create-new-file\n\nPath: `resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-create-new-file/SKILL.md)","tokens":300,"sizeBytes":250},{"name":"SKILL.md","path":"resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md","title":"figma-generate-design","category":"anthropic-skill","format":"markdown","content":"# figma-generate-design\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-design/SKILL.md)","tokens":300,"sizeBytes":250},{"name":"code-connect-setup.md","path":"resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/code-connect-setup.md)","tokens":300,"sizeBytes":295},{"name":"component-creation.md","path":"resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/component-creation.md)","tokens":300,"sizeBytes":295},{"name":"discovery-phase.md","path":"resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md)","tokens":300,"sizeBytes":289},{"name":"documentation-creation.md","path":"resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/documentation-creation.md)","tokens":300,"sizeBytes":303},{"name":"error-recovery.md","path":"resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/error-recovery.md)","tokens":300,"sizeBytes":287},{"name":"naming-conventions.md","path":"resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/naming-conventions.md)","tokens":300,"sizeBytes":295},{"name":"token-creation.md","path":"resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/references/token-creation.md)","tokens":300,"sizeBytes":287},{"name":"SKILL.md","path":"resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md","title":"figma-generate-library","category":"anthropic-skill","format":"markdown","content":"# figma-generate-library\n\nPath: `resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-generate-library/SKILL.md)","tokens":300,"sizeBytes":253},{"name":"SKILL.md","path":"resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md","title":"figma-implement-design","category":"anthropic-skill","format":"markdown","content":"# figma-implement-design\n\nPath: `resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-implement-design/SKILL.md)","tokens":300,"sizeBytes":253},{"name":"api-reference.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/api-reference.md)","tokens":300,"sizeBytes":259},{"name":"common-patterns.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/common-patterns.md)","tokens":300,"sizeBytes":263},{"name":"component-patterns.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/component-patterns.md)","tokens":300,"sizeBytes":269},{"name":"effect-style-patterns.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/effect-style-patterns.md)","tokens":300,"sizeBytes":275},{"name":"gotchas.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/gotchas.md)","tokens":300,"sizeBytes":247},{"name":"plugin-api-patterns.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-patterns.md)","tokens":300,"sizeBytes":271},{"name":"plugin-api-standalone.index.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/plugin-api-standalone.index.md)","tokens":300,"sizeBytes":287},{"name":"text-style-patterns.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/text-style-patterns.md)","tokens":300,"sizeBytes":271},{"name":"validation-and-recovery.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/validation-and-recovery.md)","tokens":300,"sizeBytes":279},{"name":"variable-patterns.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/variable-patterns.md)","tokens":300,"sizeBytes":267},{"name":"wwds-components--creating.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md","title":"Working-with-design-systems Skill","category":"anthropic-skill","format":"markdown","content":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--creating.md)","tokens":300,"sizeBytes":356},{"name":"wwds-components--using.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md","title":"Working-with-design-systems Skill","category":"anthropic-skill","format":"markdown","content":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components--using.md)","tokens":300,"sizeBytes":350},{"name":"wwds-components.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md","title":"Working-with-design-systems Skill","category":"anthropic-skill","format":"markdown","content":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-components.md)","tokens":300,"sizeBytes":336},{"name":"wwds-effect-styles.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md","title":"Working-with-design-systems Skill","category":"anthropic-skill","format":"markdown","content":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-effect-styles.md)","tokens":300,"sizeBytes":342},{"name":"wwds-text-styles.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md","title":"Working-with-design-systems Skill","category":"anthropic-skill","format":"markdown","content":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-text-styles.md)","tokens":300,"sizeBytes":338},{"name":"wwds-variables--creating.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md","title":"Working-with-design-systems Skill","category":"anthropic-skill","format":"markdown","content":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--creating.md)","tokens":300,"sizeBytes":354},{"name":"wwds-variables--using.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md","title":"Working-with-design-systems Skill","category":"anthropic-skill","format":"markdown","content":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables--using.md)","tokens":300,"sizeBytes":348},{"name":"wwds-variables.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md","title":"Working-with-design-systems Skill","category":"anthropic-skill","format":"markdown","content":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds-variables.md)","tokens":300,"sizeBytes":334},{"name":"wwds.md","path":"resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md","title":"Working-with-design-systems Skill","category":"anthropic-skill","format":"markdown","content":"# Working-with-design-systems Skill\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/references/working-with-design-systems/wwds.md)","tokens":300,"sizeBytes":314},{"name":"SKILL.md","path":"resources/bundled/mcp_skills/figma/figma-use/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/SKILL.md","title":"figma-use","category":"anthropic-skill","format":"markdown","content":"# figma-use\n\nPath: `resources/bundled/mcp_skills/figma/figma-use/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/figma-use/SKILL.md)","tokens":300,"sizeBytes":214},{"name":"SKILL.md","path":"resources/bundled/skills/add-mcp-server/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/add-mcp-server/SKILL.md","title":"add-mcp-server","category":"anthropic-skill","format":"markdown","content":"# add-mcp-server\n\nPath: `resources/bundled/skills/add-mcp-server/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/add-mcp-server/SKILL.md)","tokens":300,"sizeBytes":209},{"name":"SKILL.md","path":"resources/bundled/skills/change-keybinding/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/change-keybinding/SKILL.md","title":"change-keybinding","category":"anthropic-skill","format":"markdown","content":"# change-keybinding\n\nPath: `resources/bundled/skills/change-keybinding/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/change-keybinding/SKILL.md)","tokens":300,"sizeBytes":218},{"name":"claude-api.md","path":"resources/bundled/skills/claude-api/csharp/claude-api.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/csharp/claude-api.md","title":"Csharp Skill","category":"anthropic-skill","format":"markdown","content":"# Csharp Skill\n\nPath: `resources/bundled/skills/claude-api/csharp/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/csharp/claude-api.md)","tokens":300,"sizeBytes":223},{"name":"examples.md","path":"resources/bundled/skills/claude-api/curl/examples.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/curl/examples.md","title":"Curl Skill","category":"anthropic-skill","format":"markdown","content":"# Curl Skill\n\nPath: `resources/bundled/skills/claude-api/curl/examples.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/curl/examples.md)","tokens":300,"sizeBytes":213},{"name":"managed-agents.md","path":"resources/bundled/skills/claude-api/curl/managed-agents.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/curl/managed-agents.md","title":"Curl Skill","category":"anthropic-skill","format":"markdown","content":"# Curl Skill\n\nPath: `resources/bundled/skills/claude-api/curl/managed-agents.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/curl/managed-agents.md)","tokens":300,"sizeBytes":225},{"name":"claude-api.md","path":"resources/bundled/skills/claude-api/go/claude-api.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/go/claude-api.md","title":"Go Skill","category":"anthropic-skill","format":"markdown","content":"# Go Skill\n\nPath: `resources/bundled/skills/claude-api/go/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/go/claude-api.md)","tokens":300,"sizeBytes":211},{"name":"README.md","path":"resources/bundled/skills/claude-api/go/managed-agents/README.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/go/managed-agents/README.md","title":"Managed-agents Skill","category":"anthropic-skill","format":"markdown","content":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/go/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/go/managed-agents/README.md)","tokens":300,"sizeBytes":245},{"name":"claude-api.md","path":"resources/bundled/skills/claude-api/java/claude-api.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/java/claude-api.md","title":"Java Skill","category":"anthropic-skill","format":"markdown","content":"# Java Skill\n\nPath: `resources/bundled/skills/claude-api/java/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/java/claude-api.md)","tokens":300,"sizeBytes":217},{"name":"README.md","path":"resources/bundled/skills/claude-api/java/managed-agents/README.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/java/managed-agents/README.md","title":"Managed-agents Skill","category":"anthropic-skill","format":"markdown","content":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/java/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/java/managed-agents/README.md)","tokens":300,"sizeBytes":249},{"name":"claude-api.md","path":"resources/bundled/skills/claude-api/php/claude-api.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/php/claude-api.md","title":"Php Skill","category":"anthropic-skill","format":"markdown","content":"# Php Skill\n\nPath: `resources/bundled/skills/claude-api/php/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/php/claude-api.md)","tokens":300,"sizeBytes":214},{"name":"README.md","path":"resources/bundled/skills/claude-api/php/managed-agents/README.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/php/managed-agents/README.md","title":"Managed-agents Skill","category":"anthropic-skill","format":"markdown","content":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/php/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/php/managed-agents/README.md)","tokens":300,"sizeBytes":247},{"name":"batches.md","path":"resources/bundled/skills/claude-api/python/claude-api/batches.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/batches.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/batches.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/batches.md)","tokens":300,"sizeBytes":243},{"name":"files-api.md","path":"resources/bundled/skills/claude-api/python/claude-api/files-api.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/files-api.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/files-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/files-api.md)","tokens":300,"sizeBytes":247},{"name":"README.md","path":"resources/bundled/skills/claude-api/python/claude-api/README.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/README.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/README.md)","tokens":300,"sizeBytes":241},{"name":"streaming.md","path":"resources/bundled/skills/claude-api/python/claude-api/streaming.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/streaming.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/streaming.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/streaming.md)","tokens":300,"sizeBytes":247},{"name":"tool-use.md","path":"resources/bundled/skills/claude-api/python/claude-api/tool-use.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/tool-use.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/python/claude-api/tool-use.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/claude-api/tool-use.md)","tokens":300,"sizeBytes":245},{"name":"README.md","path":"resources/bundled/skills/claude-api/python/managed-agents/README.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/managed-agents/README.md","title":"Managed-agents Skill","category":"anthropic-skill","format":"markdown","content":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/python/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/python/managed-agents/README.md)","tokens":300,"sizeBytes":253},{"name":"claude-api.md","path":"resources/bundled/skills/claude-api/ruby/claude-api.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/ruby/claude-api.md","title":"Ruby Skill","category":"anthropic-skill","format":"markdown","content":"# Ruby Skill\n\nPath: `resources/bundled/skills/claude-api/ruby/claude-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/ruby/claude-api.md)","tokens":300,"sizeBytes":217},{"name":"README.md","path":"resources/bundled/skills/claude-api/ruby/managed-agents/README.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/ruby/managed-agents/README.md","title":"Managed-agents Skill","category":"anthropic-skill","format":"markdown","content":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/ruby/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/ruby/managed-agents/README.md)","tokens":300,"sizeBytes":249},{"name":"agent-design.md","path":"resources/bundled/skills/claude-api/shared/agent-design.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/agent-design.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/agent-design.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/agent-design.md)","tokens":300,"sizeBytes":227},{"name":"error-codes.md","path":"resources/bundled/skills/claude-api/shared/error-codes.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/error-codes.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/error-codes.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/error-codes.md)","tokens":300,"sizeBytes":225},{"name":"live-sources.md","path":"resources/bundled/skills/claude-api/shared/live-sources.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/live-sources.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/live-sources.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/live-sources.md)","tokens":300,"sizeBytes":227},{"name":"managed-agents-api-reference.md","path":"resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-api-reference.md)","tokens":300,"sizeBytes":259},{"name":"managed-agents-client-patterns.md","path":"resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-client-patterns.md)","tokens":300,"sizeBytes":263},{"name":"managed-agents-core.md","path":"resources/bundled/skills/claude-api/shared/managed-agents-core.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-core.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-core.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-core.md)","tokens":300,"sizeBytes":241},{"name":"managed-agents-environments.md","path":"resources/bundled/skills/claude-api/shared/managed-agents-environments.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-environments.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-environments.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-environments.md)","tokens":300,"sizeBytes":257},{"name":"managed-agents-events.md","path":"resources/bundled/skills/claude-api/shared/managed-agents-events.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-events.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-events.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-events.md)","tokens":300,"sizeBytes":245},{"name":"managed-agents-onboarding.md","path":"resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-onboarding.md)","tokens":300,"sizeBytes":253},{"name":"managed-agents-overview.md","path":"resources/bundled/skills/claude-api/shared/managed-agents-overview.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-overview.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-overview.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-overview.md)","tokens":300,"sizeBytes":249},{"name":"managed-agents-tools.md","path":"resources/bundled/skills/claude-api/shared/managed-agents-tools.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-tools.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/managed-agents-tools.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/managed-agents-tools.md)","tokens":300,"sizeBytes":243},{"name":"model-migration.md","path":"resources/bundled/skills/claude-api/shared/model-migration.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/model-migration.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/model-migration.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/model-migration.md)","tokens":300,"sizeBytes":233},{"name":"models.md","path":"resources/bundled/skills/claude-api/shared/models.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/models.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/models.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/models.md)","tokens":300,"sizeBytes":215},{"name":"prompt-caching.md","path":"resources/bundled/skills/claude-api/shared/prompt-caching.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/prompt-caching.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/prompt-caching.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/prompt-caching.md)","tokens":300,"sizeBytes":231},{"name":"tool-use-concepts.md","path":"resources/bundled/skills/claude-api/shared/tool-use-concepts.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/tool-use-concepts.md","title":"Shared Skill","category":"anthropic-skill","format":"markdown","content":"# Shared Skill\n\nPath: `resources/bundled/skills/claude-api/shared/tool-use-concepts.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/shared/tool-use-concepts.md)","tokens":300,"sizeBytes":237},{"name":"SKILL.md","path":"resources/bundled/skills/claude-api/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/SKILL.md","title":"claude-api","category":"anthropic-skill","format":"markdown","content":"# claude-api\n\nPath: `resources/bundled/skills/claude-api/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/SKILL.md)","tokens":300,"sizeBytes":197},{"name":"batches.md","path":"resources/bundled/skills/claude-api/typescript/claude-api/batches.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/batches.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/batches.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/batches.md)","tokens":300,"sizeBytes":251},{"name":"files-api.md","path":"resources/bundled/skills/claude-api/typescript/claude-api/files-api.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/files-api.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/files-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/files-api.md)","tokens":300,"sizeBytes":255},{"name":"README.md","path":"resources/bundled/skills/claude-api/typescript/claude-api/README.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/README.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/README.md)","tokens":300,"sizeBytes":249},{"name":"streaming.md","path":"resources/bundled/skills/claude-api/typescript/claude-api/streaming.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/streaming.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/streaming.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/streaming.md)","tokens":300,"sizeBytes":255},{"name":"tool-use.md","path":"resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md","title":"Claude-api Skill","category":"anthropic-skill","format":"markdown","content":"# Claude-api Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/claude-api/tool-use.md)","tokens":300,"sizeBytes":253},{"name":"README.md","path":"resources/bundled/skills/claude-api/typescript/managed-agents/README.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/managed-agents/README.md","title":"Managed-agents Skill","category":"anthropic-skill","format":"markdown","content":"# Managed-agents Skill\n\nPath: `resources/bundled/skills/claude-api/typescript/managed-agents/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/claude-api/typescript/managed-agents/README.md)","tokens":300,"sizeBytes":261},{"name":"schemas.md","path":"resources/bundled/skills/create-skill/references/schemas.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/references/schemas.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/skills/create-skill/references/schemas.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/references/schemas.md)","tokens":300,"sizeBytes":233},{"name":"SKILL.md","path":"resources/bundled/skills/create-skill/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/SKILL.md","title":"create-skill","category":"anthropic-skill","format":"markdown","content":"# create-skill\n\nPath: `resources/bundled/skills/create-skill/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/SKILL.md)","tokens":300,"sizeBytes":203},{"name":"SKILL.md","path":"resources/bundled/skills/create-tab-config/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-tab-config/SKILL.md","title":"create-tab-config","category":"anthropic-skill","format":"markdown","content":"# create-tab-config\n\nPath: `resources/bundled/skills/create-tab-config/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-tab-config/SKILL.md)","tokens":300,"sizeBytes":218},{"name":"SKILL.md","path":"resources/bundled/skills/factory-deferred-repositories/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-deferred-repositories/SKILL.md","title":"factory-deferred-repositories","category":"anthropic-skill","format":"markdown","content":"# factory-deferred-repositories\n\nPath: `resources/bundled/skills/factory-deferred-repositories/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-deferred-repositories/SKILL.md)","tokens":300,"sizeBytes":254},{"name":"examples.md","path":"resources/bundled/skills/factory-files/references/examples.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/examples.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/skills/factory-files/references/examples.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/examples.md)","tokens":300,"sizeBytes":237},{"name":"scorers.md","path":"resources/bundled/skills/factory-files/references/scorers.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/scorers.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/skills/factory-files/references/scorers.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/scorers.md)","tokens":300,"sizeBytes":235},{"name":"validation.md","path":"resources/bundled/skills/factory-files/references/validation.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/validation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/skills/factory-files/references/validation.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/references/validation.md)","tokens":300,"sizeBytes":241},{"name":"SKILL.md","path":"resources/bundled/skills/factory-files/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/SKILL.md","title":"factory-files","category":"anthropic-skill","format":"markdown","content":"# factory-files\n\nPath: `resources/bundled/skills/factory-files/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-files/SKILL.md)","tokens":300,"sizeBytes":206},{"name":"SKILL.md","path":"resources/bundled/skills/factory-mcp/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-mcp/SKILL.md","title":"factory-mcp","category":"anthropic-skill","format":"markdown","content":"# factory-mcp\n\nPath: `resources/bundled/skills/factory-mcp/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/factory-mcp/SKILL.md)","tokens":300,"sizeBytes":200},{"name":"SKILL.md","path":"resources/bundled/skills/modify-settings/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/modify-settings/SKILL.md","title":"modify-settings","category":"anthropic-skill","format":"markdown","content":"# modify-settings\n\nPath: `resources/bundled/skills/modify-settings/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/modify-settings/SKILL.md)","tokens":300,"sizeBytes":212},{"name":"third-party-clis.md","path":"resources/bundled/skills/oz-platform/references/third-party-clis.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/oz-platform/references/third-party-clis.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/skills/oz-platform/references/third-party-clis.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/oz-platform/references/third-party-clis.md)","tokens":300,"sizeBytes":249},{"name":"SKILL.md","path":"resources/bundled/skills/oz-platform/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/oz-platform/SKILL.md","title":"oz-platform","category":"anthropic-skill","format":"markdown","content":"# oz-platform\n\nPath: `resources/bundled/skills/oz-platform/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/oz-platform/SKILL.md)","tokens":300,"sizeBytes":200},{"name":"SKILL.md","path":"resources/bundled/skills/pr-comments/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/pr-comments/SKILL.md","title":"pr-comments","category":"anthropic-skill","format":"markdown","content":"# pr-comments\n\nPath: `resources/bundled/skills/pr-comments/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/pr-comments/SKILL.md)","tokens":300,"sizeBytes":200},{"name":"SKILL.md","path":"resources/bundled/skills/tab-configs/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tab-configs/SKILL.md","title":"tab-configs","category":"anthropic-skill","format":"markdown","content":"# tab-configs\n\nPath: `resources/bundled/skills/tab-configs/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tab-configs/SKILL.md)","tokens":300,"sizeBytes":200},{"name":"migration-matrix.md","path":"resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/references/migration-matrix.md)","tokens":300,"sizeBytes":261},{"name":"SKILL.md","path":"resources/bundled/skills/tui-migrate-setup/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/SKILL.md","title":"tui-migrate-setup","category":"anthropic-skill","format":"markdown","content":"# tui-migrate-setup\n\nPath: `resources/bundled/skills/tui-migrate-setup/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/SKILL.md)","tokens":300,"sizeBytes":218},{"name":"destination_mcp.json","path":"resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json","title":"Fixtures Skill","category":"anthropic-skill","format":"json","content":"# Fixtures Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/destination_mcp.json)","tokens":300,"sizeBytes":269},{"name":"settings_schema.json","path":"resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json","title":"Fixtures Skill","category":"anthropic-skill","format":"json","content":"# Fixtures Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/settings_schema.json)","tokens":300,"sizeBytes":269},{"name":"source_mcp.json","path":"resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json","title":"Fixtures Skill","category":"anthropic-skill","format":"json","content":"# Fixtures Skill\n\nPath: `resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/tui-migrate-setup/tests/fixtures/source_mcp.json)","tokens":300,"sizeBytes":259},{"name":"SKILL.md","path":"resources/bundled/skills/update-tab-config/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/update-tab-config/SKILL.md","title":"update-tab-config","category":"anthropic-skill","format":"markdown","content":"# update-tab-config\n\nPath: `resources/bundled/skills/update-tab-config/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/update-tab-config/SKILL.md)","tokens":300,"sizeBytes":218},{"name":"SKILL.md","path":"resources/bundled/skills/warpctrl/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/warpctrl/SKILL.md","title":"warpctrl","category":"anthropic-skill","format":"markdown","content":"# warpctrl\n\nPath: `resources/bundled/skills/warpctrl/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/warpctrl/SKILL.md)","tokens":300,"sizeBytes":191},{"name":"SKILL.md","path":"resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md","title":"test-warp-ui","category":"anthropic-skill","format":"markdown","content":"# test-warp-ui\n\nPath: `resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/test-warp-ui/SKILL.md)","tokens":300,"sizeBytes":231},{"name":"SKILL.md","path":"resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md","title":"triage-vulnerabilities","category":"anthropic-skill","format":"markdown","content":"# triage-vulnerabilities\n\nPath: `resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/triage-vulnerabilities/SKILL.md)","tokens":300,"sizeBytes":261},{"name":"SKILL.md","path":"resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md","title":"verify-ui-change-in-cloud","category":"anthropic-skill","format":"markdown","content":"# verify-ui-change-in-cloud\n\nPath: `resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/channel-gated-skills/dogfood/verify-ui-change-in-cloud/SKILL.md)","tokens":300,"sizeBytes":270},{"name":"APP-4878 - fix TUI copy to use native clipboard.md","path":"agents/specs/APP-4878 - fix TUI copy to use native clipboard.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4878 - fix TUI copy to use native clipboard.md","title":"Subagent: app-4878 - fix tui copy to use native clipboard","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4878 - fix tui copy to use native clipboard\n\nPath: `agents/specs/APP-4878 - fix TUI copy to use native clipboard.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4878 - fix TUI copy to use native clipboard.md)","tokens":300,"sizeBytes":282},{"name":"APP-4880 - TUI view-logs slash command.md","path":"agents/specs/APP-4880 - TUI view-logs slash command.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4880 - TUI view-logs slash command.md","title":"Subagent: app-4880 - tui view-logs slash command","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4880 - tui view-logs slash command\n\nPath: `agents/specs/APP-4880 - TUI view-logs slash command.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4880 - TUI view-logs slash command.md)","tokens":300,"sizeBytes":255},{"name":"APP-4881-tui-starting-shell-indicator.md","path":"agents/specs/APP-4881-tui-starting-shell-indicator.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4881-tui-starting-shell-indicator.md","title":"Subagent: app-4881-tui-starting-shell-indicator","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4881-tui-starting-shell-indicator\n\nPath: `agents/specs/APP-4881-tui-starting-shell-indicator.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4881-tui-starting-shell-indicator.md)","tokens":300,"sizeBytes":252},{"name":"APP-4882 - fix misleading oversized-file read error.md","path":"agents/specs/APP-4882 - fix misleading oversized-file read error.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4882 - fix misleading oversized-file read error.md","title":"Subagent: app-4882 - fix misleading oversized-file read error","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4882 - fix misleading oversized-file read error\n\nPath: `agents/specs/APP-4882 - fix misleading oversized-file read error.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4882 - fix misleading oversized-file read error.md)","tokens":300,"sizeBytes":294},{"name":"APP-4889 - fixed-height expandable toasts with 3-active cap.md","path":"agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md","title":"Subagent: app-4889 - fixed-height expandable toasts with 3-active cap","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4889 - fixed-height expandable toasts with 3-active cap\n\nPath: `agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4889 - fixed-height expandable toasts with 3-active cap.md)","tokens":300,"sizeBytes":318},{"name":"APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md","path":"agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md","title":"Subagent: app-4896 - forward ctrl+shift+c to pty in fullscreen tui","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4896 - forward ctrl+shift+c to pty in fullscreen tui\n\nPath: `agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4896 - forward Ctrl+Shift+C to PTY in fullscreen TUI.md)","tokens":300,"sizeBytes":309},{"name":"APP-4901-add-fast-forward-slash-command.md","path":"agents/specs/APP-4901-add-fast-forward-slash-command.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4901-add-fast-forward-slash-command.md","title":"Subagent: app-4901-add-fast-forward-slash-command","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4901-add-fast-forward-slash-command\n\nPath: `agents/specs/APP-4901-add-fast-forward-slash-command.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4901-add-fast-forward-slash-command.md)","tokens":300,"sizeBytes":258},{"name":"APP-4902 - window-scoped background focus suppression.md","path":"agents/specs/APP-4902 - window-scoped background focus suppression.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4902 - window-scoped background focus suppression.md","title":"Subagent: app-4902 - window-scoped background focus suppression","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4902 - window-scoped background focus suppression\n\nPath: `agents/specs/APP-4902 - window-scoped background focus suppression.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4902 - window-scoped background focus suppression.md)","tokens":300,"sizeBytes":300},{"name":"APP-4903 improve TUI tab bar keybindings.md","path":"agents/specs/APP-4903 improve TUI tab bar keybindings.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4903 improve TUI tab bar keybindings.md","title":"Subagent: app-4903 improve tui tab bar keybindings","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4903 improve tui tab bar keybindings\n\nPath: `agents/specs/APP-4903 improve TUI tab bar keybindings.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4903 improve TUI tab bar keybindings.md)","tokens":300,"sizeBytes":261},{"name":"APP-4907 - TUI input focus during agent drafting.md","path":"agents/specs/APP-4907 - TUI input focus during agent drafting.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4907 - TUI input focus during agent drafting.md","title":"Subagent: app-4907 - tui input focus during agent drafting","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4907 - tui input focus during agent drafting\n\nPath: `agents/specs/APP-4907 - TUI input focus during agent drafting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4907 - TUI input focus during agent drafting.md)","tokens":300,"sizeBytes":285},{"name":"APP-4913-tui-input-prompt-prefix.md","path":"agents/specs/APP-4913-tui-input-prompt-prefix.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4913-tui-input-prompt-prefix.md","title":"Subagent: app-4913-tui-input-prompt-prefix","category":"subagent-persona","format":"markdown","content":"# Subagent: app-4913-tui-input-prompt-prefix\n\nPath: `agents/specs/APP-4913-tui-input-prompt-prefix.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/APP-4913-tui-input-prompt-prefix.md)","tokens":300,"sizeBytes":237},{"name":"CODE-1884-voice-input-slash-command.md","path":"agents/specs/CODE-1884-voice-input-slash-command.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1884-voice-input-slash-command.md","title":"Subagent: code-1884-voice-input-slash-command","category":"subagent-persona","format":"markdown","content":"# Subagent: code-1884-voice-input-slash-command\n\nPath: `agents/specs/CODE-1884-voice-input-slash-command.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1884-voice-input-slash-command.md)","tokens":300,"sizeBytes":246},{"name":"CODE-1890-status-line-v2.md","path":"agents/specs/CODE-1890-status-line-v2.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1890-status-line-v2.md","title":"Subagent: code-1890-status-line-v2","category":"subagent-persona","format":"markdown","content":"# Subagent: code-1890-status-line-v2\n\nPath: `agents/specs/CODE-1890-status-line-v2.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1890-status-line-v2.md)","tokens":300,"sizeBytes":213},{"name":"CODE-1895-tui-lrc-typeahead-wiring.md","path":"agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md","title":"Subagent: code-1895-tui-lrc-typeahead-wiring","category":"subagent-persona","format":"markdown","content":"# Subagent: code-1895-tui-lrc-typeahead-wiring\n\nPath: `agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1895-tui-lrc-typeahead-wiring.md)","tokens":300,"sizeBytes":243},{"name":"CODE-1902 change TUI log storage.md","path":"agents/specs/CODE-1902 change TUI log storage.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1902 change TUI log storage.md","title":"Subagent: code-1902 change tui log storage","category":"subagent-persona","format":"markdown","content":"# Subagent: code-1902 change tui log storage\n\nPath: `agents/specs/CODE-1902 change TUI log storage.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1902 change TUI log storage.md)","tokens":300,"sizeBytes":237},{"name":"CODE-1908 TUI default profile uses AgentDecides.md","path":"agents/specs/CODE-1908 TUI default profile uses AgentDecides.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1908 TUI default profile uses AgentDecides.md","title":"Subagent: code-1908 tui default profile uses agentdecides","category":"subagent-persona","format":"markdown","content":"# Subagent: code-1908 tui default profile uses agentdecides\n\nPath: `agents/specs/CODE-1908 TUI default profile uses AgentDecides.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/CODE-1908 TUI default profile uses AgentDecides.md)","tokens":300,"sizeBytes":282},{"name":"QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md","path":"agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md","title":"Subagent: quality-1112-smart-variable-speedcut-for-computer-use-recordings","category":"subagent-persona","format":"markdown","content":"# Subagent: quality-1112-smart-variable-speedcut-for-computer-use-recordings\n\nPath: `agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/QUALITY-1112-smart-variable-speedcut-for-computer-use-recordings.md)","tokens":300,"sizeBytes":333},{"name":"QUALITY-1169-richer-click-drag-annotations.md","path":"agents/specs/QUALITY-1169-richer-click-drag-annotations.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/QUALITY-1169-richer-click-drag-annotations.md","title":"Subagent: quality-1169-richer-click-drag-annotations","category":"subagent-persona","format":"markdown","content":"# Subagent: quality-1169-richer-click-drag-annotations\n\nPath: `agents/specs/QUALITY-1169-richer-click-drag-annotations.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/QUALITY-1169-richer-click-drag-annotations.md)","tokens":300,"sizeBytes":267},{"name":"REMOTE-2160 - macOS video recording for computer use.md","path":"agents/specs/REMOTE-2160 - macOS video recording for computer use.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/REMOTE-2160 - macOS video recording for computer use.md","title":"Subagent: remote-2160 - macos video recording for computer use","category":"subagent-persona","format":"markdown","content":"# Subagent: remote-2160 - macos video recording for computer use\n\nPath: `agents/specs/REMOTE-2160 - macOS video recording for computer use.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/agents/specs/REMOTE-2160 - macOS video recording for computer use.md)","tokens":300,"sizeBytes":297},{"name":"analyzer.md","path":"resources/bundled/skills/create-skill/agents/analyzer.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/analyzer.md","title":"Subagent: analyzer","category":"subagent-persona","format":"markdown","content":"# Subagent: analyzer\n\nPath: `resources/bundled/skills/create-skill/agents/analyzer.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/analyzer.md)","tokens":300,"sizeBytes":229},{"name":"comparator.md","path":"resources/bundled/skills/create-skill/agents/comparator.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/comparator.md","title":"Subagent: comparator","category":"subagent-persona","format":"markdown","content":"# Subagent: comparator\n\nPath: `resources/bundled/skills/create-skill/agents/comparator.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/comparator.md)","tokens":300,"sizeBytes":235},{"name":"grader.md","path":"resources/bundled/skills/create-skill/agents/grader.md","rawUrl":"https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/grader.md","title":"Subagent: grader","category":"subagent-persona","format":"markdown","content":"# Subagent: grader\n\nPath: `resources/bundled/skills/create-skill/agents/grader.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/skills/create-skill/agents/grader.md)","tokens":300,"sizeBytes":223}],"systemPromptSnippet":"<yakaai_skills repo=\"warpdotdev/warp\">\n<!-- File: AGENTS.md (Tokens: ~300 | Category: root-instruction) -->\n# AI Agent Protocol & Instructions\n\nPath: `AGENTS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/AGENTS.md)\n\n<!-- File: .agents/skills/add-feature-flag/SKILL.md (Tokens: ~848 | Category: anthropic-skill) -->\n---\nname: add-feature-flag\ndescription: Add a new feature flag to gate code changes in the Warp codebase.\n---\n\n# add-feature-flag\n\nAdd a new feature flag to gate code changes in the Warp codebase.\n\n## Overview\n\nFeature flags in Warp are compile-time flags that allow features to be selectively enabled for different channels (e.g.: Dev, Stable). They use a small runtime plumbing layer that checks if a flag is enabled.\n\n## TUI note\nThe `FeatureFlag` enum (`warp_core/src/features.rs`) and the runtime `FeatureFlag::X.is_enabled()` check are SHARED by both front-ends: the GUI desktop app (`app/`) and the headless TUI (`crates/warp_tui`). The Cargo-feature steps in this skill (`app/Cargo.toml`, `app/src/lib.rs`) are GUI-app-specific. Prefer runtime `is_enabled()` checks so a flag works in both front-ends with no per-binary wiring. Only if you truly need a compile-time Cargo feature in the TUI, wire it into `crates/warp_tui/Cargo.toml` as well. Test the TUI with `./script/run-tui`.\n\n## Steps\n\n### 1. Add to Cargo.toml\nAdd the feature to `app/Cargo.toml` under the `[features]` section, but **NOT** under the `default` nested stanza:\n\n```toml\n[features]\nyour_feature_name = []\n```\n\n### 2. Add to FeatureFlag enum\nAdd a new variant to the `FeatureFlag` enum in `warp_core/src/features.rs`:\n\n```rust\n#[derive(Sequence)]\npub enum FeatureFlag {\n    YourFeatureName,\n}\n```\n\n### 3. Add conditional compilation directive\nAdd the feature to `app/src/lib.rs` with a corresponding `#[cfg(feature = \"...\")]` attribute to ensure it's only included when enabled:\n\n```rust\n#[cfg(feature = \"your_feature_name\")]\nYourFeatureName,\n```\n\n### 4. Gate code with runtime checks\nIn your code, use the runtime check to conditionally execute feature-gated code:\n\n```rust\nif FeatureFlag::YourFeatureName.is_enabled() {\n    // feature-gated behavior\n}\n```\n\n### 5. (Optional) Enable for dogfood builds\nTo enable the feature by default for Dev/dogfood builds, add it to the `DOGFOOD_FLAGS` array in `features.rs`:\n\n```rust\npub const DOGFOOD_FLAGS: &[FeatureFlag] = &[\n    FeatureFlag::YourFeatureName,\n];\n```\n\n### 6. Running with feature flags\nTo test locally with the feature enabled:\n\n```bash\ncargo run --features your_feature_name\n\n# Multiple features:\ncargo run --features your_feature_name,another_feature\n```\n\n## Keybindings with Feature Flags\n\nIf adding an `EditableBinding` or `FixedBinding` that's part of a gated feature, include an enabled predicate that checks the feature flag. This prevents the keybinding from appearing in keyboard settings when the feature is disabled.\n\nExample:\n```rust\nEditableBinding::new(\n    \"action:name\",\n    \"Action description\",\n    YourAction::Variant\n)\n.with_enabled(|| FeatureFlag::YourFeatureName.is_enabled())\n.with_key_binding(\"cmdorctrl-key\")\n```\n\n## Rolling Out to Stable\n\nWhen ready to enable the feature for all Warp Stable users, add it to the `default` array in `app/Cargo.toml`:\n\n```toml\n[features]\ndefault = [\n    \"your_feature_name\",\n    # other default features...\n]\n```\n\n## Best Practices\n\n- **Prefer runtime checks over cfg directives**: Use `FeatureFlag::YourFeatureName.is_enabled()` instead of `#[cfg(...)]` when possible, so flags can be toggled without recompilation and are easier to clean up later\n- Use `#[cfg(...)]` only when code cannot compile without the flag (e.g., platform-specific code or missing dependencies)\n- Keep flags high-level and product-focused rather than per-call-site\n- Remove flags and dead branches after launch has stabilized\n\n<!-- File: .agents/skills/add-telemetry/SKILL.md (Tokens: ~1166 | Category: anthropic-skill) -->\n---\nname: add-telemetry\ndescription: Add telemetry events to track user behavior or system events in the Warp codebase. Use when instrumenting new features, debugging issues, or measuring product metrics.\n---\n\n# add-telemetry\n\n## Overview\n\nWarp uses a trait-based telemetry system where feature-specific enums implement the `TelemetryEvent` trait. This approach keeps telemetry events organized by domain rather than in one giant enum.\n\n**Important**: Before implementing telemetry, collaborate with the user to:\n- Define what events should be tracked and when\n- Determine what data should be included in each event\n- Clarify the purpose and expected usage of the telemetry\n\nAdding telemetry code is straightforward, but designing meaningful instrumentation requires careful thought.\n\n## Steps\n\n### 1. Identify or create a telemetry module\n\nFind an existing feature-specific telemetry file (e.g., `app/src/antivirus/telemetry.rs`) or create a new one for your feature area.\n\n### 2. Define the telemetry event enum\n\nAdd a new variant to an enum that implements `TelemetryEvent`, or create a new enum:\n\n```rust\nuse serde_json::{json, Value};\nuse strum_macros::{EnumDiscriminants, EnumIter};\nuse warp_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};\n\n#[derive(Debug, EnumDiscriminants)]\n#[strum_discriminants(derive(EnumIter))]\npub enum YourFeatureTelemetryEvent {\n    ActionStarted {\n        duration_ms: u64,\n    },\n    ActionCompleted {\n        success: bool,\n        error: Option<String>,\n    },\n}\n```\n\n### 3. Implement the TelemetryEvent trait\n\n`EnablementState` allows you to control when events are sent:\n\n- `EnablementState::Always` - Always send the event\n- `EnablementState::Flag(FeatureFlag::YourFeature)` - Only send when the feature flag is enabled\n- `EnablementState::Channel(Channel::Dev)` - Only send in specific build channels\n\n```rust\nimpl TelemetryEvent for YourFeatureTelemetryEvent {\n    fn name(&self) -> &'static str {\n        YourFeatureTelemetryEventDiscriminants::from(self).name()\n    }\n\n    fn payload(&self) -> Option<Value> {\n        match self {\n            Self::ActionStarted { duration_ms } => Some(json!({\n                \"duration_ms\": duration_ms,\n            })),\n            Self::ActionCompleted { success, error } => Some(json!({\n                \"success\": success,\n                \"error\": error,\n            })),\n        }\n    }\n\n    fn description(&self) -> &'static str {\n        YourFeatureTelemetryEventDiscriminants::from(self).description()\n    }\n\n    fn enablement_state(&self) -> EnablementState {\n        YourFeatureTelemetryEventDiscriminants::from(self).enablement_state()\n    }\n\n    fn contains_ugc(&self) -> bool {\n        match self {\n            Self::ActionStarted { .. } => false,\n            Self::ActionCompleted { .. } => false,\n        }\n    }\n\n    fn event_descs() -> impl Iterator<Item = Box<dyn TelemetryEventDesc>> {\n        warp_core::telemetry::enum_events::<Self>()\n    }\n}\n```\n\n### 4. Implement TelemetryEventDesc for the discriminants\n\n```rust\nimpl TelemetryEventDesc for YourFeatureTelemetryEventDiscriminants {\n    fn name(&self) -> &'static str {\n        match self {\n            Self::ActionStarted => \"YourFeature.Action.Started\",\n            Self::ActionCompleted => \"YourFeature.Action.Completed\",\n        }\n    }\n\n    fn description(&self) -> &'static str {\n        match self {\n            Self::ActionStarted => \"User started the action\",\n            Self::ActionCompleted => \"User completed the action\",\n        }\n    }\n\n    fn enablement_state(&self) -> EnablementState {\n        match self {\n            Self::ActionStarted | Self::ActionCompleted => EnablementState::Always,\n            // Or gate behind a feature flag:\n            // EnablementState::Flag(FeatureFlag::YourFeature)\n        }\n    }\n}\n```\n\n### 5. Register the telemetry event\n\nAt the end of your telemetry module, register the event:\n\n```rust\nwarp_core::register_telemetry_event!(YourFeatureTelemetryEvent);\n```\n\n### 6. Send telemetry events from your code\n\nUse `send_telemetry_from_ctx!` in views or models with a `ViewContext` or `ModelContext`:\n\n```rust\nuse warp_core::send_telemetry_from_ctx;\n\n// In a view update or model method\nsend_telemetry_from_ctx!(\n    YourFeatureTelemetryEvent::ActionStarted {\n        duration_ms: 150,\n    },\n    ctx\n);\n```\n\nFor code with only `AppContext`, use `send_telemetry_from_app_ctx!` instead.\n\n### 7. Test locally\n\nRun Warp with the `log_named_telemetry_events` feature flag to see telemetry events logged to the console:\n\n```bash\ncargo run --features log_named_telemetry_events\n```\n\n## Best Practices\n\n- Keep telemetry enums feature-specific rather than adding to a global enum\n- Set `contains_ugc()` to `true` if the payload includes user-generated content\n- Use descriptive event names following the pattern `Feature.Action.Result`\n- Include only necessary data in payloads to minimize bandwidth and storage\n- Consider privacy implications when deciding what data to include\n- Avoid exhaustive matching with wildcards; handle all variants explicitly\n\n## Example Reference\n\nSee `app/src/antivirus/telemetry.rs` for a complete example of a feature-specific telemetry implementation.\n\n<!-- File: .agents/skills/changelog-draft/examples/changelog-draft-example.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Examples Skill\n\nPath: `.agents/skills/changelog-draft/examples/changelog-draft-example.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/changelog-draft/examples/changelog-draft-example.md)\n\n<!-- File: .agents/skills/changelog-draft/SKILL.md (Tokens: ~3263 | Category: anthropic-skill) -->\n---\nname: changelog-draft\ndescription: Generate a reviewable changelog draft from PRs merged in a release range. Extracts explicit CHANGELOG markers, classifies unmarked PRs, adds external contributor attribution, and outputs markdown + JSON artifacts. Does NOT mutate channel_versions.json.\n---\n\n# Changelog Draft Generator\n\n## Inputs\n\n| Parameter | Required | Description |\n|-----------|----------|-------------|\n| `channel` | yes | Release channel: `stable`, `preview`, or `dev` |\n| `release_tag` | yes | The release tag to generate the changelog for (e.g. `v0.2026.05.06.09.12.stable_00`) |\n| `output_dir` | no | Directory to write output files. Defaults to `$RUNNER_TEMP` or `/tmp/changelog-draft` |\n| `attribution` | no | Attribution mode: `external-only` (default), `all`, or `none` |\n\n## Workflow\n\n### Step 1 — Determine the release range\n\nInfer the previous release **cut** for comparison. Release tags follow the pattern `v0.YYYY.MM.DD.HH.MM.<channel>_NN`, where `_NN` is the RC/hotfix number within that release cut. Multiple tags can share the same date prefix (e.g. `_00`, `_01`, `_02` are all part of one release cut).\n\nThe base tag must be the `_00` tag of the **previous** release cut (i.e. a different date), not just the previous tag. For example, if generating a changelog for `v0.2026.04.29.08.57.stable_01`, the base should be `v0.2026.04.22.08.57.stable_00`, not `v0.2026.04.29.08.57.stable_00`.\n\n```bash\n# 1. Extract the date prefix from the release_tag (everything before _NN)\nrelease_date_prefix=\"${release_tag%_*}\"\n\n# 2. List all _00 tags for the channel (these are release cut points), sorted descending\ngit tag --list \"v0.*.${channel}_00\" --sort=-version:refname\n\n# 3. Pick the first _00 tag whose date prefix differs from release_date_prefix\n```\n\nRecord the range as `previous_cut_tag..release_tag`.\n\n### Step 2 — Fetch PR data\n\nRun the `fetch_prs.py` script to collect all public-release PRs merged in the release range and extract explicit changelog markers. Pass the repository that the workflow checked out, not necessarily the public repository. Release workflows run from `warpdotdev/warp-internal`, and the script deterministically resolves `warp-repo-sync[bot]` PRs back to their original public `warpdotdev/warp` PR metadata before emitting JSON. When running from `warpdotdev/warp-internal`, the script intentionally omits PRs that were not authored by the repo-sync bot, because those are private internal changes that must not be exposed to the changelog agent or generated artifacts.\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/fetch_prs.py \\\n  --repo \"${GITHUB_REPOSITORY:-warpdotdev/warp}\" \\\n  --base-ref <previous_tag> \\\n  --head-ref <release_tag>\n```\n\nThe script outputs JSON to stdout with this structure:\n```json\n{\n  \"range\": { \"base\": \"<previous_tag>\", \"head\": \"<release_tag>\" },\n  \"prs\": [\n    {\n      \"number\": 1234,\n      \"url\": \"https://github.com/warpdotdev/warp/pull/1234\",\n      \"title\": \"...\",\n      \"commit_subject\": \"... (#1234)\",\n      \"author\": \"username\",\n      \"body\": \"...\",\n      \"labels\": [\"...\"],\n      \"merged_at\": \"2026-05-01T...\",\n      \"explicit_entries\": [\n        { \"category\": \"NEW-FEATURE\", \"text\": \"Added dark mode\" }\n      ],\n      \"linked_issues\": [5678],\n      \"changed_files\": [\"app/src/ai/agent.rs\", \"crates/warp_features/src/lib.rs\"],\n      \"source_repo\": \"warpdotdev/warp\",\n      \"internal_pr\": {\n        \"number\": 25712,\n        \"url\": \"https://github.com/warpdotdev/warp-internal/pull/25712\",\n        \"author\": \"warp-repo-sync[bot]\",\n        \"title\": \"...\",\n        \"repo\": \"warpdotdev/warp-internal\"\n      }\n    }\n  ]\n}\n```\n\nUse the top-level `number`, `url`, `commit_subject`, `author`, `body`, `labels`, `changed_files`, and `source_repo` fields as the source of truth. `internal_pr` is audit-only and must never be used for contributor attribution or user-facing changelog links. If `url` is empty, omit the PR link from user-facing markdown rather than synthesizing one.\n\n### Step 3 — Classify contributors\n\nRun the `classify_contributors.py` script with the unique author logins from Step 2:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/classify_contributors.py \\\n  --org warpdotdev \\\n  --authors author1,author2,author3\n```\n\nOutput JSON:\n```json\n{\n  \"internal\": [\"author1\"],\n  \"external\": [\"author3\"],\n  \"bot\": [\"author2\"],\n  \"unknown\": []\n}\n```\n\n### Step 4 — Extract feature flags\n\nRun the `extract_feature_flags.py` script to get the current flag gate lists:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/extract_feature_flags.py \\\n  --file crates/warp_features/src/lib.rs\n```\n\nOutput JSON:\n```json\n{\n  \"release_flags\": [\"Autoupdate\", \"Changelog\", ...],\n  \"preview_flags\": [\"Orchestration\", ...],\n  \"dogfood_flags\": [\"LogExpensiveFramesInSentry\", ...]\n}\n```\n\n### Step 5 — Fetch issue reporters\n\nCollect all unique `linked_issues` from Step 2 and fetch the original reporter for each. Pass `--org` so the script checks org membership and filters out internal reporters automatically:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/fetch_issue_reporters.py \\\n  --repo warpdotdev/warp \\\n  --org warpdotdev \\\n  --issues 5678,9012\n```\n\nOutput JSON (only external reporters are included):\n```json\n{\n  \"issue_reporters\": [\n    {\n      \"issue_number\": 5678,\n      \"title\": \"Crash when opening large file\",\n      \"reporter\": \"community-user\",\n      \"reporter_url\": \"https://github.com/community-user\",\n      \"url\": \"https://github.com/warpdotdev/warp/issues/5678\"\n    }\n  ]\n}\n```\n\nThe `--org` flag checks each reporter's org membership via the GitHub API, filtering out internal members so they aren't misattributed as external community reporters. These reporters will be credited in the \"Community\" section of the changelog.\nWhenever the markdown draft credits a PR author, contributor, or issue reporter, render the username as a GitHub profile link such as `[@username](https://github.com/username)`.\n\n### Step 6 — Classify unmarked PRs\nDetermine TUI impact independently from the regular changelog category for every PR. Explicit `CHANGELOG-TUI` and `CHANGELOG-OZ` markers are authoritative entries and may coexist with New Feature, Improvement, or Bug Fix entries. When a PR has an explicit TUI entry, preserve its other explicit entries. Otherwise, if `commit_subject` contains `TUI` as a standalone case-insensitive token, route any explicit New Feature, Improvement, or Bug Fix text to `TUI` instead of its regular category. This keeps clearly TUI-labeled changes out of the desktop changelog while allowing explicitly marked shared changes to appear on both surfaces.\n\nFor each PR that has no explicit `CHANGELOG-*` entries, decide whether to include it and under which category.\n\nFollow the classification guidance in `.agents/skills/classify-changelog-pr/SKILL.md`.\n\nFor each unmarked PR, produce a classification:\n```json\n{\n  \"pr_number\": 1234,\n  \"include\": true,\n  \"category\": \"IMPROVEMENT\",\n  \"text\": \"Proposed changelog line\",\n  \"impacts_tui\": true,\n  \"confidence\": \"high\",\n  \"rationale\": \"...\",\n  \"feature_flag\": null,\n  \"needs_review\": false\n}\n```\n\n**Key rules:**\n- PRs that only touch CI, tests, docs, or internal tooling → `include: false`\n- PRs behind dogfood-only feature flags → `include: false` for stable channel\n- PRs behind preview flags → `include: false` for stable, `include: true` for preview\n- Set `impacts_tui: true` when Warp Agent CLI users observe the change, including shared Agent capabilities such as tool-call or edit-file behavior\n- When in doubt, set `needs_review: true` and `confidence: \"low\"`\n- Bot PRs (dependabot, renovate, etc.) → `include: false`\n\n**Feature-flag detection:** Use the `changed_files` list from Step 2 to check if any PR touches `crates/warp_features/src/lib.rs` or references a `FeatureFlag` variant in its title/body. Cross-reference with the flag lists from Step 4 to determine channel visibility.\n\n**Unknown contributors:** Authors in the `unknown` bucket (org membership check failed due to auth) should be treated conservatively — do not attribute them as external. Note them in the output for manual verification.\n\n### Step 7 — Assemble the draft\n\nCombine explicit entries (Step 2) and inferred entries (Step 6) into the final report. Group by category in this order:\n\n1. `NEW-FEATURE` — New Features\n2. `IMPROVEMENT` — Improvements\n3. `BUG-FIX` — Bug Fixes\n4. `TUI` — TUI Updates\n5. `OZ` — Oz Updates\n\nPRs marked with `CHANGELOG-NONE` are explicitly opted out and must never appear in the changelog markdown.\n\nPreserve every explicit entry independently. For an inferred regular entry with `impacts_tui: true`, also create a `TUI` entry with the same user-facing text and PR metadata. Do not duplicate an inferred entry whose category is already `TUI`.\n\nWhen creating entries, copy `pr_number`, `url`, `author`, `source_repo`, and `internal_pr` from the normalized PR record. The release JSON converter uses `url` directly; do not invent public PR URLs from PR numbers.\n\n### Step 8 — Write output files\n\nWrite two files to `output_dir`:\n\n**`changelog-draft.md`** — Human-reviewable markdown, ready for Slack/Notion:\n\n```markdown\n# Changelog Draft\n**Channel:** stable\n**Range:** v0.2026.05.01... → v0.2026.05.06...\n**Generated:** 2026-05-06T15:00:00Z\n\n## New Features\n- Added dark mode ([#1234](https://github.com/warpdotdev/warp/pull/1234)) — [@external-contributor](https://github.com/external-contributor) ✨\n\n## Improvements\n- Faster tab switching ([#1235](https://github.com/warpdotdev/warp/pull/1235))\n\n## Bug Fixes\n- Fixed crash on startup ([#1236](https://github.com/warpdotdev/warp/pull/1236))\n\n## TUI Updates\n- Added inline command menus to Warp Agent CLI ([#1238](https://github.com/warpdotdev/warp/pull/1238))\n\n## Oz Updates\n- Improved agent memory ([#1237](https://github.com/warpdotdev/warp/pull/1237))\n\n## Community\n### Contributors\n- [@contributor1](https://github.com/contributor1) — [#1234](https://github.com/warpdotdev/warp/pull/1234)  ✨\n\n### Issue Reporters\nThanks to the community members who reported issues fixed in this release:\n- [@reporter1](https://github.com/reporter1) — [#5678](https://github.com/warpdotdev/warp/issues/5678) \"Crash when opening large file\"\n```\n\nThe markdown draft must **not** include \"Needs Review\" or \"Skipped PRs\" sections — those are internal details that belong only in the JSON audit artifact.\n\n**`changelog-draft.json`** — Machine-readable audit artifact (internal only):\n\n```json\n{\n  \"channel\": \"stable\",\n  \"range\": { \"base\": \"v0...\", \"head\": \"v0...\" },\n  \"generated_at\": \"2026-05-06T15:00:00Z\",\n  \"entries\": [\n    {\n      \"pr_number\": 1234,\n      \"url\": \"https://github.com/warpdotdev/warp/pull/1234\",\n      \"category\": \"NEW-FEATURE\",\n      \"text\": \"Added dark mode\",\n      \"source\": \"explicit\",\n      \"author\": \"external-contributor\",\n      \"is_external\": true,\n      \"confidence\": \"high\",\n      \"rationale\": null,\n      \"feature_flag\": null,\n      \"source_repo\": \"warpdotdev/warp\",\n      \"internal_pr\": null\n    }\n  ],\n  \"skipped\": [...],\n  \"needs_review\": [...],\n  \"issue_reporters\": [...]\n}\n```\n\nThe JSON artifact retains `skipped`, `needs_review`, and `issue_reporters` for audit purposes — every PR in the range must appear in either `entries`, `skipped`, or `needs_review`.\n\n### Step 9 — Generate release-pipeline JSON\n\nRun the conversion script to deterministically produce `changelog-release.json` from the audit artifact:\n\n```bash\npython3 .agents/skills/changelog-draft/scripts/convert_to_release_json.py \\\n  --input <output_dir>/changelog-draft.json \\\n  --output <output_dir>/changelog-release.json\n```\n\nThis produces the flat JSON structure consumed by the `create_release` workflow for Slack and the in-app \"What's New\" dialog. Do **not** generate this file manually — always use the script so the output is deterministic and consistent.\n\n## Constraints\n\n- **Never** write to `channel_versions.json` or any production config file.\n- **Never** push commits, create branches, or open PRs.\n- All output goes to `output_dir` only.\n- The markdown draft should be copy-pasteable into Slack or Notion for review.\n- Keep the JSON artifact complete enough for audit: every PR in the range should appear in either `entries`, `skipped`, or `needs_review`.\n\n## Validation\n\nAfter generating output, verify:\n1. Every PR in the range is accounted for (entries + skipped + needs_review = total PRs).\n2. Explicit marker entries match what `fetch_prs.py` extracted (no dropped markers).\n3. No duplicate PR numbers across sections.\n4. The markdown renders cleanly (no broken links or formatting).\n\n<!-- File: .agents/skills/classify-changelog-pr/SKILL.md (Tokens: ~985 | Category: anthropic-skill) -->\n---\nname: classify-changelog-pr\ndescription: Reference guidance for classifying whether an unmarked PR should appear in the changelog and under which category. Used inline by the changelog-draft skill — not dispatched as a separate agent.\n---\n\n# Classify Changelog PR\n\nThis document provides classification rules for PRs that lack explicit `CHANGELOG-*` markers. The changelog-draft agent follows these rules inline when deciding whether to include an unmarked PR.\n\n## Categories\n\n- **NEW-FEATURE** — A substantial new user-facing capability. Reserve for features that would warrant docs, marketing, or social media attention.\n- **IMPROVEMENT** — Enhances an existing feature in a way users would notice (performance, UX, new options).\n- **BUG-FIX** — Fixes a user-visible bug or regression.\n- **OZ** — Changes to Oz / AI agent capabilities. At most 4 per release in the stable changelog.\n- **TUI** — User-visible changes that impact the Warp Agent CLI/headless TUI, including shared Agent capabilities.\n- **NONE** — Explicitly opt out of changelog inclusion. Handled upstream by `fetch_prs.py` marker extraction.\n\n## Decision rules\n\n### Always exclude\n- PRs with an explicit `CHANGELOG-NONE` marker (contributor opted out)\n- PRs authored by known bots (dependabot, renovate, github-actions, codecov)\n- PRs that exclusively modify CI workflows (`.github/workflows/`), test files, or dev tooling\n- PRs that only update internal docs, comments, or README files\n- Dependency bumps with no user-facing behavior change\n- Refactors with no observable behavior change (code moves, renames, formatting)\n\n### Always include\n- PRs with explicit `CHANGELOG-*` markers (handled before this guidance applies)\n- PRs that fix a crash, data loss, or security issue — even without a marker\n\n### TUI surface classification\n- Treat explicit `CHANGELOG-TUI` and `CHANGELOG-OZ` markers as authoritative entries. They are independent and may coexist with each other or with regular changelog categories.\n- Determine TUI impact independently from the regular New Feature, Improvement, or Bug Fix category. A shared Agent capability may belong in both its regular category and `TUI`.\n- Classify a user-visible entry as impacting `TUI` when `commit_subject` contains `TUI` as a standalone token.\n- For stable releases, also use the PR title/body and TUI-owned paths such as `crates/warp_tui` or `crates/warpui_core/src/elements/tui`. Shared capabilities such as Agent tool-call or edit-file behavior also impact TUI when Warp Agent CLI users observe the change. If metadata is ambiguous, inspect the commit diff.\n- Do not infer TUI impact from file paths alone; confirm that Warp Agent CLI users observe the changed behavior.\n- A TUI-only entry belongs only in the TUI category. An entry that impacts both TUI and another surface should appear in both `TUI` and its regular category.\n\n### Conditional on channel\n- **Stable channel:** Only include changes that are live for all users. Exclude PRs gated behind `DOGFOOD_FLAGS` or `PREVIEW_FLAGS`.\n- **Preview channel:** Include PRs gated behind `PREVIEW_FLAGS`. Still exclude `DOGFOOD_FLAGS`-only changes.\n- **Dev channel:** Include everything that's user-visible, regardless of flag gates.\n\n### Feature-flagged PRs\nIf a PR mentions a `FeatureFlag` variant in its diff or title:\n1. Check which flag list it belongs to (`RELEASE_FLAGS`, `PREVIEW_FLAGS`, `DOGFOOD_FLAGS`).\n2. Apply the channel rules above.\n3. If the flag is in `RELEASE_FLAGS` or enabled by default in `app/Cargo.toml`, treat it as live.\n4. Set `feature_flag` in the classification output to the flag name.\n\n### Confidence levels\n- **high** — Clear user-visible change with obvious category.\n- **medium** — Likely user-visible but category or scope is somewhat ambiguous.\n- **low** — Unclear whether users would notice; or the PR touches both internal and user-facing code. Set `needs_review: true`.\n\n## Writing changelog text\n\n- Write from the user's perspective: \"Added X\", \"Fixed Y\", \"Improved Z\".\n- Keep it to one sentence, ≤ 120 characters.\n- Don't reference internal implementation details, file paths, or function names.\n- Don't start with \"PR\" or the PR number — those are added as metadata.\n- Use active voice and present tense for new features (\"Adds dark mode\"), past tense for fixes (\"Fixed crash on startup\").\n\n<!-- File: .agents/skills/cross-platform-cloud-verification/SKILL.md (Tokens: ~2108 | Category: anthropic-skill) -->\n---\nname: cross-platform-cloud-verification\ndescription: Orchestrates cost-conscious cloud verification across available operating-system and architecture runners after cheaper checks pass. Use whenever a code change, bug fix, build, packaging flow, native dependency, UI behavior, filesystem behavior, or test has material cross-platform implications, even if the user only asks to verify or test the change. Consult domain verification skills for what to test; use this skill to choose and access the smallest relevant set of platforms.\ncompatibility: Requires the Oz CLI (`oz-dev`) for runner discovery and `run_agents` support for `remote.runner_id`.\n---\n\n# Cross-platform cloud verification\n\nVerify a change on the smallest useful set of cloud platforms. This skill owns\nrunner discovery, platform selection, child orchestration, and result\naggregation. It does not define the product-specific verification procedure.\n\nCross-platform execution consumes remote compute and credits, so use it as the\nlast verification layer rather than as an exploratory first step.\n\n## Workflow\n\n### 1. Establish the local verification gate\n\nFinish the cheaper feedback loops first:\n\n1. Inspect the change and identify the behavior being verified.\n2. Run the relevant local build, focused tests, lint, type checks, or manual\n   verification.\n3. Fix deterministic local failures before launching cloud runs.\n4. Confirm the exact commit or branch containing the change can be checked out\n   by cloud agents. Do not test the default branch when the intended change is\n   only local.\n\nDo not commit or push changes merely to satisfy this workflow unless the user\nhas authorized that action. If the change is not remotely reachable, report the\nprecondition and ask for the minimum action needed.\n\nSkip cloud fan-out when local verification already shows the change is broken.\nDo not use remote platforms as a substitute for diagnosing an ordinary local\nfailure.\n\n### 2. Delegate test design to verification skills\n\nInspect the available skill descriptions and read every skill that materially\ndefines how to verify the affected surface. Examples include GUI computer-use\nverification, TUI live verification, integration testing, unit testing, CI\ndiagnosis, packaging, or repository-specific validation.\n\nExtract from those skills:\n\n- setup and authentication requirements\n- commands or interactions to perform\n- expected observations and pass criteria\n- required screenshots, recordings, logs, or text captures\n- platform-specific caveats\n\nBuild one shared verification procedure from that guidance. Tell each child\nwhich verification skill to read when it is available in the child environment,\nand include the essential procedure directly so the run remains actionable if\nthe skill is unavailable there.\n\nThis skill decides where to run that procedure, not what the procedure should\nbe.\n\n### 3. Discover runners at execution time\n\nList runners immediately before selecting platforms:\n\n```sh\noz-dev runner list --output-format json\n```\n\nUse the returned runner metadata rather than hard-coding names or IDs. Relevant\nfields include `uid`, `name`, `os`, `arch`, compute capacity, image or macOS\nversion, and setup commands.\n\nIf runner discovery fails because of authentication, permissions, or service\navailability, report the blocker instead of inventing a runner matrix.\n\n### 4. Infer the affected platform dimensions\n\nDetermine which dimensions the change can plausibly affect from the diff,\nrepository configuration, reported bug, and verification guidance.\n\nTreat the change as **OS-sensitive** when it touches or depends on items such as:\n\n- OS-gated code or platform modules\n- windowing, desktop integration, installers, packaging, or signing\n- shells, process creation, permissions, filesystems, paths, or line endings\n- OS-specific APIs, toolchains, or user-facing behavior\n\nTreat the change as **architecture-sensitive** only with evidence such as:\n\n- architecture gates or assembly\n- unsafe code, FFI, ABI, alignment, endianness, or binary serialization\n- SIMD, native libraries, architecture-specific dependencies, or packaging\n- an architecture-specific bug report or explicit user requirement\n\nDo not infer architecture sensitivity merely because both x86-64 and AArch64\nrunners exist.\n\n### 5. Select the minimal runner matrix\n\nFilter discovered runners to those that can execute the verification procedure,\nthen apply these defaults:\n\n1. Select only affected operating systems.\n2. Select one representative runner per affected OS.\n3. Add another architecture for an OS only when the change is\n   architecture-sensitive.\n4. Do not add an unaffected control platform by default.\n5. Deduplicate equivalent OS/architecture candidates.\n\nWhen several runners cover the same platform, prefer in order:\n\n1. the exact OS/version/architecture from the bug report or target\n2. the repository's normal CI or release platform\n3. a runner whose image, setup, capacity, and tools match the procedure\n4. the lower-setup-cost candidate\n\nFor architecture-insensitive Linux changes, prefer the repository's primary\nLinux architecture; if the repository gives no signal, use x86-64 as the single\nrepresentative.\n\nBefore launching, record a concise selection rationale and explicitly list\nrelevant platforms omitted because no runner is available. Missing runners do\nnot block useful verification on available relevant platforms.\n\nState each omission once. After the matrix is decided, do not keep repeating\nthat an irrelevant or redundant platform was not selected in child prompts,\nresult rows, evidence, and the conclusion.\n\n### 6. Construct verification-only child prompts\n\nEvery child prompt should contain:\n\n- repository, branch, and exact commit to check out\n- selected runner name, OS, and architecture\n- verification skill names and the distilled procedure\n- setup and state preconditions\n- exact commands or interactions\n- expected results and required evidence\n- a prohibition on fixing or pushing code\n\nTrust orchestration to route the child to the requested runner. Do not make every\nchild re-confirm its OS and architecture. Ask for platform introspection only\nwhen the verification depends on an exact OS version or capability, or when\nthere is evidence of a routing problem.\n\nAsk each child to return:\n\n```text\nPlatform: <runner name; OS; architecture; version if relevant>\nCommit: <tested SHA>\nStatus: <passed | failed | blocked>\nChecks:\n- <command or interaction>: <result>\nEvidence:\n- <artifact, screenshot, log, or concise observation>\nDeviations:\n- <difference from the requested procedure, or none>\n```\n\nChildren may make temporary, uncommitted setup adjustments required by the\nverification skill, but they must report them and must not push source changes.\n\n### 7. Launch with `run_agents`\n\nUse `run_agents` so child IDs, messages, lifecycle events, and artifacts remain\npart of the parent orchestration flow. Use the same repository environment for\nevery selected platform and set `remote.runner_id` to the discovered runner UID.\n\n```text\nsummary: Verifying the change on <OS/architecture>.\nbase_prompt: <shared verification-only instructions>\nremote:\n  environment_id: <repository environment ID>\n  runner_id: <selected runner UID>\n  computer_use_enabled: <true only when the verification procedure needs it>\nagent_run_configs:\n- name: <short platform-specific name>\n  prompt: <platform-specific procedure and expected evidence>\n```\n\nIf no suitable repository environment is already known, inspect\n`oz-dev environment list` and choose one that checks out the target repository.\nDo not silently create or mutate an environment.\n\n`remote.runner_id` is run-wide, so runners with different UIDs require separate\n`run_agents` calls. Use one single-child batch per selected runner, or group\nmultiple independently useful children only when they share the same runner and\nrun-wide configuration. Distinct runner IDs are a legitimate reason for\nseparate batches; do not place children for different platforms in one batch.\n\nOmit `model_id` and `remote.harness` unless the user requested them. Attach\nrelevant verification skills when the children need them. If an approved\norchestration config is active, ensure its resolved runner matches the selected\nrunner because config resolution takes precedence over call fields.\n\nCapture each trusted `agent_id` from the launched result. Coordinate through\npushed child messages and lifecycle events; do not poll `oz-dev run get` or\n`list_messages_from_agents`. Read notified messages, intervene only for a\nfailure or actionable block, and use `wait_for_events` when no other work can\nproceed. Allow at most one retry for a clearly transient infrastructure\nfailure. A product failure is evidence, not a reason to spend more credits\nrepeating the same check.\n\n### 8. Aggregate without hiding gaps\n\nWait for every launched child, then report:\n\n```text\nCross-platform verification: <passed | failed | incomplete>\nLocal gate: <checks completed before cloud launch>\nResults:\n- <OS/arch; runner>: <why selected> - <passed/failed/blocked>\nUnverified:\n- <relevant OS/arch>: unverified because no suitable runner was available\n\nEvidence:\n- <platform>: <commands, observations, and artifact/run links>\n\nConclusion:\n- <what the results establish and what remains unverified>\n```\n\nUse `passed` only when every selected platform passed and no required platform\nis unavailable. Use `failed` when any verification check failed. Use\n`incomplete` when runs were blocked or a relevant platform lacked a runner.\n\nKeep dry-run proposals and final summaries compact. Include the local gate, the\nselected matrix, one concise omission rationale when it is material, the\nverification procedure, and verdict rules. Do not repeat setup mechanics or\nplatform exclusions in multiple sections. A dry-run proposal should usually fit\nin roughly 300-500 words; expand only when the domain verification procedure\ngenuinely requires more detail.\n\n## Cost and scope guardrails\n\n- Run this workflow after local verification, not during implementation.\n- Choose platforms from evidence in the change, not from runner availability.\n- Keep one child per selected platform unless distinct state variants are\n  independently necessary.\n- State runner-selection and omission decisions once.\n- Preserve negative results and platform gaps; do not broaden the matrix merely\n  to obtain a passing result.\n- Do not let children implement fixes. Return failures to the parent workflow,\n  fix locally, rerun cheap checks, and only then decide whether another cloud\n  verification pass is justified.\n\n<!-- File: .agents/skills/dedupe-issue-local/SKILL.md (Tokens: ~534 | Category: anthropic-skill) -->\n---\nname: dedupe-issue-local\nspecializes: dedupe-issue\nspecializes_source: warpdotdev/oz-for-oss:.agents/skills/dedupe-issue\ndescription: Repo-specific dedupe guidance for warp. Only the categories declared overridable by the core dedupe-issue skill may be specialized here.\n---\n\n# Repo-specific dedupe guidance for `warp`\n\n## Prerequisite: install the parent skill\n\nThis skill specializes the core `dedupe-issue` skill (named in the `specializes` frontmatter field) and is not functional on its own. Before applying its guidance, confirm the parent skill is installed and resolvable at `.agents/skills/dedupe-issue/SKILL.md`. If it is missing, install it first by copying the skill directory from the source declared in the `specializes_source` frontmatter field (`warpdotdev/oz-for-oss:.agents/skills/dedupe-issue`). Then continue with the guidance below.\n\nThis file is a companion to the core `dedupe-issue` skill. It does not\nredefine the duplicate-detection algorithm, the similarity thresholds,\nor the output contract. It only specializes the override categories the\ncore skill marks as overridable.\n\n## Repo-specific normalizations\n\n- Strip low-signal title prefixes such as `Bug:`, `Feature:`, `Request:`, `[Bug]`, `[Feature]`, `Warp:`, and platform tags like `[macOS]`, `[Linux]`, or `[Windows]` before comparing titles.\n- Treat app channel/version, OS version, and shell name as supporting evidence, not as duplicate blockers, when the core symptom and reproduction path are otherwise the same.\n- Do not collapse distinct Warp surfaces just because they share a word like \"agent\", \"terminal\", \"MCP\", \"settings\", \"search\", or \"sync\". Require overlap in the actual failing behavior or requested capability.\n- For terminal issues, compare shell/session context, command output behavior, prompt rendering, input behavior, and remote/tmux involvement before treating two reports as duplicates.\n- For agent or MCP issues, compare the trigger path, local vs cloud execution, MCP server/tool, visible error, and expected workflow before treating two reports as duplicates.\n- For UI/rendering issues, compare the affected surface and visible symptom. Similar screenshots or recordings are strong duplicate evidence when the title is vague.\n\n## Known-duplicate clusters\n\nNo known-duplicate clusters have been captured for this repository yet. The weekly `update-dedupe` loop will propose additions here over time when maintainers repeatedly close issues as duplicates of the same canonical thread.\n\n<!-- File: .agents/skills/gui-create-launch-modal/SKILL.md (Tokens: ~3169 | Category: anthropic-skill) -->\n---\nname: gui-create-launch-modal\ndescription: GUI desktop app only. Create a one-time launch modal in the Warp client (feature announcement, onboarding, etc.). Use when adding a new modal that should appear exactly once per user on startup, gated by a feature flag, with colors sourced from Warp theme tokens and terminal theme colors.\n---\n\n# gui-create-launch-modal\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nCreate a one-time launch modal — the feature-announcement design used for launches like \"Orchestrate any agent, anywhere\" or \"Warp is now open-source.\"\n\n## Reference implementation\n\n`app/src/workspace/view/orchestration_launch_modal/` — the canonical, most up-to-date example of this pattern.\n\n## Checklist\n\n- [ ] Feature flag in `warp_features/src/lib.rs`\n- [ ] Settings field in `app/src/settings/ai.rs`\n- [ ] Trigger logic in `app/src/workspace/one_time_modal_model.rs`\n- [ ] View files under `app/src/workspace/view/<name>_launch_modal/`\n- [ ] Workspace wiring in `app/src/workspace/view.rs` and `app/src/workspace/mod.rs`\n- [ ] Debug actions in `app/src/workspace/action.rs`\n- [ ] Hero image at `app/assets/async/png/onboarding/<name>_launch_banner.png`\n- [ ] Any custom icons added to `crates/warp_core/src/ui/icons.rs` + SVG in `app/assets/bundled/svg/`\n\n---\n\n## Step 0 – Custom icons (if needed)\n\nIf the modal uses icons not yet in the `Icon` enum, add them before writing the view.\n\nIn `crates/warp_core/src/ui/icons.rs`:\n\n```rust\n// Add to enum\nYourIconName,\n\n// Add to From<Icon> for &'static str match\nIcon::YourIconName => \"bundled/svg/your-icon-name.svg\",\n```\n\nDrop the SVG file at `app/assets/bundled/svg/your-icon-name.svg`. Use the same 24×24 viewBox format as existing icons.\n\n---\n\n## Step 1 – Feature flag\n\nAdd to `crates/warp_features/src/lib.rs`:\n\n```rust\n/// Enables the <name> launch modal.\n<YourModalName>LaunchModal,\n```\n\nEnable for dogfood:\n\n```rust\npub const DOGFOOD_FLAGS: &[FeatureFlag] = &[\n    FeatureFlag::<YourModalName>LaunchModal,\n    // ...\n];\n```\n\n---\n\n## Step 2 – Settings field\n\nAdd to `app/src/settings/ai.rs` inside `define_settings_group!(AISettings, ...)`.\nPattern: one boolean field per modal, globally synced (not respecting user sync), private.\n\n```rust\n// This is not a user-visible setting - it's merely a one-time flag to track if the\n// <name> launch modal has been shown to the user.\n//\n// We model it as a setting so it's only shown once to a given user regardless of the number of\n// devices they use.\ndid_check_to_trigger_<name>_launch_modal: DidShow<Name>LaunchModal {\n    type: bool,\n    default: false,\n    supported_platforms: SupportedPlatforms::ALL,\n    sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::No),\n    private: true,\n}\n```\n\n---\n\n## Step 3 – OneTimeModalModel\n\nFile: `app/src/workspace/one_time_modal_model.rs`\n\n### 3a. Add field to struct\n\n```rust\nis_<name>_launch_modal_open: bool,\n```\n\n### 3b. Initialize to false in `new()`\n\n```rust\nis_<name>_launch_modal_open: false,\n```\n\n### 3c. Pre-dismiss for new users (critical)\n\nIn the `AuthComplete` → `!is_existing_user` branch, add to the `AISettings::handle` update block alongside the other pre-dismissals. **Without this, new users see the modal on their second startup after onboarding.**\n\n```rust\nif let Err(e) = settings\n    .did_check_to_trigger_<name>_launch_modal\n    .set_value(true, ctx)\n{\n    log::warn!(\"Failed to mark <name> launch modal as dismissed: {e}\");\n}\n```\n\n### 3d. Public API methods\n\n```rust\npub fn is_<name>_launch_modal_open(&self) -> bool {\n    self.is_<name>_launch_modal_open && self.target_window_id.is_some()\n}\n\npub fn mark_<name>_launch_modal_dismissed(&mut self, ctx: &mut ModelContext<Self>) {\n    self.set_<name>_launch_modal_open(false, ctx);\n}\n\n#[cfg(debug_assertions)]\npub fn force_open_<name>_launch_modal(&mut self, ctx: &mut ModelContext<Self>) {\n    self.set_<name>_launch_modal_open(true, ctx);\n}\n```\n\n### 3e. Private setter\n\n```rust\nfn set_<name>_launch_modal_open(&mut self, is_open: bool, ctx: &mut ModelContext<Self>) -> bool {\n    if self.is_<name>_launch_modal_open != is_open {\n        self.is_<name>_launch_modal_open = is_open;\n        ctx.emit(OneTimeModalEvent::VisibilityChanged { is_open });\n        return true;\n    }\n    false\n}\n```\n\n### 3f. Add to `is_any_modal_open`\n\n```rust\n|| self.is_<name>_launch_modal_open\n```\n\n### 3g. Trigger function\n\n```rust\nfn check_and_trigger_<name>_launch_modal(&mut self, ctx: &mut ModelContext<Self>) -> bool {\n    if !FeatureFlag::<Name>LaunchModal.is_enabled() {\n        return false;\n    }\n\n    let ai_settings = AISettings::as_ref(ctx);\n    if *ai_settings.did_check_to_trigger_<name>_launch_modal {\n        return false;\n    }\n\n    AISettings::handle(ctx).update(ctx, |settings, ctx| {\n        if let Err(e) = settings\n            .did_check_to_trigger_<name>_launch_modal\n            .set_value(true, ctx)\n        {\n            log::warn!(\"Failed to mark <name> launch modal as dismissed: {e}\");\n        }\n    });\n\n    let should_show = !matches!(ChannelState::channel(), Channel::Integration);\n    self.set_<name>_launch_modal_open(should_show, ctx);\n    should_show\n}\n```\n\n### 3h. Call from `check_and_trigger_all_modals`\n\nInsert before `check_and_trigger_hoa_onboarding`:\n\n```rust\nif self.check_and_trigger_<name>_launch_modal(ctx) {\n    return;\n}\n```\n\n---\n\n## Step 4 – View\n\nCreate `app/src/workspace/view/<name>_launch_modal/mod.rs`:\n\n```rust\nmod view;\npub use view::{init, <Name>LaunchModal, <Name>LaunchModalEvent};\n```\n\nCreate `app/src/workspace/view/<name>_launch_modal/view.rs`. Copy from `orchestration_launch_modal/view.rs` and adapt. Key details:\n\n### Color sources (important)\n\n- Prefer Warp theme tokens for modal backgrounds, text, overlays, and borders:\n  - background surfaces: `appearance.theme().surface_3()` (or another `surface_*` token when needed)\n  - primary/subtext: `appearance.theme().main_text_color(...)` and `appearance.theme().sub_text_color(...)`\n  - overlays/hover fills: `appearance.theme().surface_overlay_1()` / `surface_overlay_2()`\n  - subtle borders: `appearance.theme().outline()`\n- Use terminal theme colors for terminal-color accents (for example, magenta launch badge accents):\n  - `appearance.theme().terminal_colors().normal.magenta`\n  - `appearance.theme().ansi_overlay_1(magenta)` for low-alpha backgrounds\n- Avoid hardcoded hex colors.\n\n### Hero image\n\n- Store at `app/assets/async/png/onboarding/<name>_launch_banner.png`\n- **Aspect ratio matters**: if the image is wider than `MODAL_WIDTH/HERO_HEIGHT` (420/92 ≈ 4.57), wrap the hero `ConstrainedBox` in `Clipped::new(...)` to prevent horizontal bleed when `cover()` scales it\n- Images pre-sized to exactly 420×92 need no `Clipped`; images only taller (aspect ratio < 4.57) are fine without it\n\n```rust\nconst MODAL_WIDTH: f32 = 420.;\nconst HERO_HEIGHT: f32 = 92.;\nconst HERO_IMAGE_PATH: &str = \"async/png/onboarding/<name>_launch_banner.png\";\n\nfn render_hero(&self) -> Box<dyn Element> {\n    let hero = Clipped::new(          // only needed if image ratio > 4.57\n        ConstrainedBox::new(\n            Image::new(AssetSource::Bundled { path: HERO_IMAGE_PATH }, CacheOption::Original)\n                .with_corner_radius(CornerRadius::with_top(Radius::Pixels(8.)))\n                .cover()\n                .top_aligned()\n                .finish(),\n        )\n        .with_width(MODAL_WIDTH)\n        .with_height(HERO_HEIGHT)\n        .finish(),\n    )\n    .finish();\n    // ... close button overlay via Stack + add_positioned_child\n}\n```\n\n### \"New\" badge\n\nUse the standard badge — 24 px tall, 8 px horizontal padding, 14 px font, pill corners, with magenta sourced from terminal theme colors:\n\n```rust\nfn render_badge(appearance: &Appearance) -> Box<dyn Element> {\n    let magenta = appearance.theme().terminal_colors().normal.magenta;\n    let text = Text::new_inline(\"New\".to_string(), appearance.ui_font_family(), 14.)\n        .with_color(magenta.into())\n        .finish();\n    ConstrainedBox::new(\n        Container::new(\n            Flex::row()\n                .with_cross_axis_alignment(CrossAxisAlignment::Center)\n                .with_main_axis_size(MainAxisSize::Min)\n                .with_child(text)\n                .finish(),\n        )\n        .with_horizontal_padding(8.)\n        .with_background(Fill::Solid(appearance.theme().ansi_overlay_1(magenta)))\n        .with_corner_radius(CornerRadius::with_all(Radius::Percentage(50.)))\n        .finish(),\n    )\n    .with_height(24.)\n    .finish()\n}\n```\n\n### URLs\n\nAlways use `https://`, not `http://`:\n\n```rust\nconst LEARN_MORE_URL: &str = \"https://warp.dev/your-blog-link\";\n```\n\n---\n\n## Step 5 – Workspace wiring\n\n### `app/src/workspace/view.rs`\n\n```rust\n// Module declaration (top)\npub(crate) mod <name>_launch_modal;\n\n// Import\nuse crate::workspace::view::<name>_launch_modal::{<Name>LaunchModal, <Name>LaunchModalEvent};\n\n// Struct field\n<name>_launch_modal: ViewHandle<<Name>LaunchModal>,\n\n// In Workspace::new()\nlet <name>_launch_view = ctx.add_typed_action_view(<Name>LaunchModal::new);\nctx.subscribe_to_view(&<name>_launch_view, |me, _, event, ctx| {\n    me.handle_<name>_launch_modal_event(event, ctx);\n});\n\n// In struct initialization\n<name>_launch_modal: <name>_launch_view,\n\n// In OneTimeModalModel subscription handler\n} else if model_ref.is_<name>_launch_modal_open() {\n    me.focus_<name>_launch_modal(ctx);\n\n// In View::render (inside the should_show_modal block)\nif should_show_modal && one_time_modal_model.is_<name>_launch_modal_open() {\n    stack.add_child(ChildView::new(&self.<name>_launch_modal).finish());\n}\n```\n\nAdd event handler and focus helper:\n\n```rust\nfn handle_<name>_launch_modal_event(&mut self, event: &<Name>LaunchModalEvent, ctx: &mut ViewContext<Self>) {\n    match event {\n        <Name>LaunchModalEvent::Close => {\n            OneTimeModalModel::handle(ctx).update(ctx, |model, ctx| {\n                model.mark_<name>_launch_modal_dismissed(ctx);\n            });\n            self.focus_active_tab(ctx);\n            ctx.notify();\n        }\n    }\n}\n\nfn focus_<name>_launch_modal(&mut self, ctx: &mut ViewContext<Self>) {\n    ctx.focus(&self.<name>_launch_modal);\n}\n```\n\n### `app/src/workspace/mod.rs`\n\n```rust\n// In pub fn init()\nview::<name>_launch_modal::init(app);\n\n// In debug bindings block\nEditableBinding::new(\n    \"workspace:open_<name>_launch_modal\",\n    \"[Debug] Open <Name> Launch Modal\",\n    WorkspaceAction::Open<Name>LaunchModal,\n)\n.with_context_predicate(id!(\"Workspace\")),\nEditableBinding::new(\n    \"workspace:reset_<name>_launch_modal_state\",\n    \"[Debug] Reset <Name> Launch Modal State\",\n    WorkspaceAction::Reset<Name>LaunchModalState,\n)\n.with_context_predicate(id!(\"Workspace\")),\n```\n\n---\n\n## Step 6 – Debug actions\n\nIn `app/src/workspace/action.rs`:\n\n```rust\n/// Open the <Name> Launch Modal (for debugging)\n#[cfg(debug_assertions)]\nOpen<Name>LaunchModal,\n/// Reset the <name> launch modal dismissed state (for debugging)\n#[cfg(debug_assertions)]\nReset<Name>LaunchModalState,\n```\n\nAdd both variants to the `is_visible_in_command_palette` `false` arm.\n\nIn `app/src/workspace/view.rs` `TypedActionView::handle_action`:\n\n```rust\n#[cfg(debug_assertions)]\nOpen<Name>LaunchModal => {\n    OneTimeModalModel::handle(ctx).update(ctx, |model, ctx| {\n        model.force_open_<name>_launch_modal(ctx);\n    });\n    ctx.notify();\n}\n#[cfg(debug_assertions)]\nReset<Name>LaunchModalState => {\n    AISettings::handle(ctx).update(ctx, |settings, ctx| {\n        if let Err(e) = settings\n            .did_check_to_trigger_<name>_launch_modal\n            .set_value(false, ctx)\n        {\n            log::warn!(\"Failed to reset <name> launch modal state: {e}\");\n        }\n    });\n}\n```\n\n---\n\n## Behavior summary\n\n| User type | Sees modal? |\n|---|---|\n| New signup | No — pre-dismissed in `AuthComplete` new-user branch |\n| Not signed in | No — trigger never fires without `AuthComplete` |\n| Existing user, flag enabled | Yes — on first startup after cloud prefs load |\n| Integration channel | No — suppressed by `Channel::Integration` check |\n| Already seen it | No — setting persists globally across devices |\n\n<!-- File: .agents/skills/gui-integration-test/SKILL.md (Tokens: ~4463 | Category: anthropic-skill) -->\n---\nname: gui-integration-test\ndescription: GUI desktop app only. Writes, runs, and debugs Warp integration tests using the custom Builder/TestStep framework in `crates/integration`. Use when adding a new integration test, fixing a failing integration test, wiring a test into the manual runner or nextest suite, or verifying end-to-end UI and terminal behavior in Warp.\n---\n\n# Warp Integration Tests\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nUse this skill for Rust integration tests in Warp's custom framework under `crates/integration/`.\n\nThese are not ordinary unit tests. They boot a real Warp app instance, give it an isolated test home directory, drive it with synthetic UI and terminal events, and poll assertions until success or timeout.\n\n## When an integration test is the right tool\n\nIntegration tests are the most expensive tests in the repo. They boot the app, they are orders of magnitude slower than a unit test, and they are the most likely to go flaky. Write one when the risk you are covering genuinely lives *between* components:\n\n- Real terminal/PTY and shell-integration behavior, especially across bash, zsh, and fish.\n- Wiring and configuration: settings, keybindings, and user preferences actually taking effect end to end.\n- Cross-component flows a unit test cannot see — command palette into editor into terminal, focus and pane management, tab/window lifecycle.\n- Input and rendering paths that only exist once a real app (and sometimes a real display) is running.\n- Regressions for user-visible bugs that escaped unit tests.\n\nDo **not** reach for an integration test when:\n\n- The logic is deterministic and reachable in-process. Push it into a unit test (`rust-unit-tests`); it will run in milliseconds and point straight at the failure.\n- You are re-covering branch logic a unit test already covers. This harness is for the seams between components, not for re-testing conditionals at full app-boot cost.\n- You only need TUI rendering. That is `tui-testing` — this harness does not drive the TUI at all.\n- What you actually want is a screenshot or a manual look. Use `computer_use` or the `gui-integration-test-video` skill instead of attaching weak assertions to a full app boot.\n\nIf a behavior is hard to reach from a unit test *only* because of how the code is structured, prefer fixing the structure over writing a slow test around it.\n\n## Framework map\n\nThe core pieces are:\n\n- `crates/integration/src/bin/integration.rs`\n  - Manual integration test runner binary.\n  - Registers test names to `Builder` factories.\n  - Runs exactly one named test per invocation.\n- `crates/integration/tests/common/mod.rs`\n  - The outer Rust test harness used by `cargo test` and `cargo nextest`.\n  - Shells out to the integration binary.\n  - Forwards a limited set of env vars (`PATH`, `RUST_*`, `WARP_*`, `WARPUI_*`, `WGPU_*`, display-related vars).\n  - Re-runs tests up to 10 times when the integration binary exits with the special rerun code.\n- `crates/integration/src/test.rs`\n  - Module hub for integration tests.\n  - Add new test modules here and `pub use` their functions so the runner can see them.\n- `crates/integration/tests/integration/ui_tests.rs`\n  - List of UI-oriented integration tests that nextest should run.\n- `crates/integration/tests/integration/shell_integration_tests.rs`\n  - List of tests that must run against every shell or a specific shell matrix.\n- `crates/integration/src/builder.rs`\n  - Warp-specific wrapper around the lower-level WarpUI integration builder.\n  - Sets default timeout, hermetic home directory, shell rc files, user prefs, and real-display mode when requested.\n- `crates/warpui_core/src/integration/driver.rs`\n  - Executes steps, handles retries, precondition reruns, screenshots, video capture, artifact export, and `on_finish`.\n- `crates/warpui_core/src/integration/step.rs`\n  - Defines `TestStep`, input/event APIs, assertion polling, step-to-step data passing, and screenshot/recording hooks.\n- `app/src/integration_testing/`\n  - High-level helpers and assertions for common Warp behaviors.\n  - Prefer these helpers over raw low-level event plumbing whenever they fit.\n\n## How the framework actually runs a test\n\n1. A Rust test from `crates/integration/tests/integration/*.rs` calls `run_integration_test(\"test_name\")`.\n2. That harness launches the `integration` binary with the test name.\n3. The binary in `crates/integration/src/bin/integration.rs` looks up the name in `register_tests()`, builds the `Builder`, and turns it into a `TestDriver`.\n4. `Builder::build(...)` creates an isolated temp directory, points `HOME` at it, writes minimal rc files, and initializes file-backed user preferences.\n5. The driver runs each `TestStep` in order:\n   - setup callbacks\n   - synthetic events\n   - actions\n   - assertion polling until success or timeout\n6. If an assertion returns `PreconditionFailed`, the binary exits with the rerun code and the outer harness retries the whole test.\n7. On success, failure, or cancellation, the driver can run `on_finish` and export artifacts/runtime tags.\n\nThis means integration tests should be written for a hermetic environment. Do not rely on the developer's real shell dotfiles, home directory contents, or persisted Warp settings.\n\n## Where to put a new test\n\nAdd the actual test function in a module under `crates/integration/src/test/`.\n\nUse these heuristics:\n\n- Put the test in an existing module when it matches that feature area.\n- Create a new module when the feature does not fit an existing one cleanly.\n- Add the test to `crates/integration/tests/integration/ui_tests.rs` if it is primarily a UI/app behavior test.\n- Add the test to `crates/integration/tests/integration/shell_integration_tests.rs` if it needs to run against every shell, or depends on a specific shell/set of shells.\n\nBeing present in `crates/integration/src/test/*.rs` is not enough. For a test to run under `cargo nextest`, it also needs to be listed in one of the macro files in `crates/integration/tests/integration/`.\n\n## Authoring checklist for a new test\n\nWhen adding a new integration test, do all of the following:\n\n1. Implement `pub fn test_name() -> Builder` in a module under `crates/integration/src/test/`.\n2. Add the module to `crates/integration/src/test.rs`.\n3. `pub use` the new module's exports from `crates/integration/src/test.rs`.\n4. Add `register_test!(test_name);` in `crates/integration/src/bin/integration.rs`.\n5. Add `test_name` to either:\n   - `crates/integration/tests/integration/ui_tests.rs`, or\n   - `crates/integration/tests/integration/shell_integration_tests.rs`\n6. Default to making the test run in CI once it is added to one of those macro lists. Only mark it `#[ignore]` when the task explicitly calls for manual-only coverage or there is a concrete, documented reason it cannot run reliably in CI.\n7. Run the test manually first, then through nextest once it is stable enough for the suite you chose.\n\n## Writing the test body\n\nThe normal shape is:\n\n```rust\nuse crate::Builder;\nuse warp::integration_testing::step::new_step_with_default_assertions;\nuse warp::integration_testing::terminal::{\n    clear_blocklist_to_remove_bootstrapped_blocks,\n    execute_command_for_single_terminal_in_tab,\n    wait_until_bootstrapped_single_pane_for_tab,\n    util::ExpectedExitStatus,\n};\n\npub fn test_example() -> Builder {\n    Builder::new()\n        .with_step(wait_until_bootstrapped_single_pane_for_tab(0))\n        .with_step(clear_blocklist_to_remove_bootstrapped_blocks())\n        .with_step(execute_command_for_single_terminal_in_tab(\n            0,\n            \"echo hello\".to_string(),\n            ExpectedExitStatus::Success,\n            \"hello\".to_string(),\n        ))\n        .with_step(\n            new_step_with_default_assertions(\"Assert some UI state\")\n                .add_named_assertion(\"specific assertion name\", |app, window_id| {\n                    // inspect app state and return AssertionOutcome\n                    warpui::integration::AssertionOutcome::Success\n                }),\n        )\n}\n```\n\nPrefer a small number of focused steps with descriptive names over a huge monolithic test.\n\n## Builder guidance\n\n### `Builder::new()`\n\nStart here almost every time.\n\nWarp's wrapper automatically gives you:\n\n- a per-test root directory\n- isolated `HOME`\n- generated rc files for Bash, Zsh, and Fish\n- file-backed user preferences\n- a default 2-minute hard timeout\n- real-display support if `WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS` is present\n\n### `with_setup(...)`\n\nUse this for filesystem or environment setup before the app runs.\n\nCommon patterns:\n\n- `utils.set_env(\"NAME\", Some(value))`\n- creating files under `utils.test_dir()`\n- writing fixture config files\n\nPrefer this over reaching into the real filesystem.\n\n### `with_user_defaults(...)`\n\nUse this to set persisted Warp preferences before the test starts.\n\nThis is the right tool for settings backed by user preferences rather than environment variables.\n\n### `set_should_run_test(...)`\n\nUse this to gate tests on shell/platform/runtime capabilities when the test genuinely cannot run everywhere.\n\n### `with_on_finish(...)`\n\nUse this for final verification or artifact inspection that should happen after all steps complete, such as checking that screenshots or recordings were written.\n\n### `with_real_display()`\n\nUse this explicitly when the test needs a real display for frame capture or visual workflows. Video/screenshot tests should normally be manual or ignored in CI unless there is a stable real-display path.\n\n## `TestStep` guidance\n\n`TestStep` is the unit of execution. Each step can have:\n\n- setup callbacks\n- input events\n- actions\n- assertions\n- a timeout\n- retry count\n- failure handling\n\n### Start from helper constructors\n\nPrefer:\n\n- `wait_until_bootstrapped_single_pane_for_tab(0)`\n- `new_step_with_default_assertions(\"...\")`\n- `new_step_with_default_assertions_for_pane(\"...\", tab, pane)`\n\nThe default step helpers already assert:\n\n- no pending model events\n- no block executing\n\nThese are good baseline invariants for most UI interactions.\n\n### Prefer helper APIs over raw event plumbing\n\nUse high-level helpers from `app/src/integration_testing/` whenever possible:\n\n- terminal command execution helpers\n- block list helpers\n- command palette helpers\n- navigation helpers\n- settings helpers\n- workflow/file tree/notebook helpers\n\nDrop to raw `with_event(...)`, `with_event_fn(...)`, or saved-position mouse events only when there is no suitable helper.\n\n### Use named assertions\n\nPrefer `add_named_assertion(...)` over unnamed assertions. Named assertions make failure output and runtime tags much easier to interpret.\n\n### Use polling assertions instead of sleeps\n\nAssertions are polled until success or timeout. Lean on that model instead of hardcoding sleeps.\n\nGood pattern:\n\n- trigger an event or action\n- assert on the eventual UI/model state\n\nAvoid brittle timing assumptions.\n\n### Use step data when one step computes something for the next\n\nIf a later step needs data from an earlier one, use:\n\n- `add_named_assertion_with_data_from_prior_step(...)`\n- `StepDataMap`\n\nThis is useful for saving measured positions, counts, IDs, or other values from prior frames.\n\n### Use retries sparingly\n\n`set_retries(...)` can help for a legitimately retryable step, but do not use it to hide deterministic failures. Prefer making the step more robust first.\n\n### Use `PreconditionFailed` for genuinely environmental flakes\n\nIf the environment reaches a state where the rest of the test is invalid, return `AssertionOutcome::PreconditionFailed(...)` instead of failing hard. The outer harness can rerun the entire test up to 10 times. The existing bootstrap helper is a good model for this.\n\nUse this deliberately. The rerun mechanism exists for conditions the test genuinely cannot control, such as bootstrap racing or shell startup timing. It is not a way to turn an intermittently failing test green. A real bug that reproduces one run in five will pass under rerun and ship to users.\n\nBefore reaching for `PreconditionFailed`, confirm the failure is actually environmental by looking at the failure rate and the failure mode:\n\n```bash\nfor i in {0..50}; do\n  RUST_BACKTRACE=full cargo run -p integration --bin integration -- test_name || break\ndone\n```\n\nIf the same assertion fails in different ways, or fails while the environment looks fine, it is a product or test bug. Fix it rather than absorbing it into a rerun.\n\n## Designing a good integration test\n\n### Keep the scope as small as the behavior allows\n\nThe scope of a test follows the scope of what you boot and drive, so cover one user journey per test and let separate tests cover separate journeys. When a flow is long, prefer several shorter tests that each verify one hop over a single test that walks the whole path. A long test is slower, harder to diagnose, and fails for many unrelated reasons.\n\n### Assert on behavior a user could observe\n\nThe framework can see internal state, which makes it tempting to assert on it. Anchor each test on the user-observable outcome:\n\n- output visible in the terminal\n- focus moved where expected\n- UI element opened or closed\n- selection changed\n- settings applied\n\nInternal state assertions are still useful, but they should support the visible behavior rather than replace it. Assertions that mirror internal call sequences become change detectors: they break on every refactor and catch no bugs.\n\n### Make the failure diagnosable by someone else\n\nAn integration test spans processes, so a stack trace tells the next engineer almost nothing. The failure output has to carry the context instead:\n\n- Name every assertion with what it expects, not what it touches: `\"terminal shows command output\"`, not `\"check blocks\"`.\n- Give steps descriptive names; they are the breadcrumb trail through the run.\n- Put expected vs. actual into the assertion output rather than returning a bare failure.\n\nAssume the person reading the failure has never seen this test and does not own the code it covers.\n\n### Keep the test hermetic\n\n`Builder::new()` already gives you an isolated `HOME`, generated rc files, and file-backed preferences. Preserve that. Set up everything you depend on inside the test via `with_setup(...)` and `with_user_defaults(...)`, and never rely on the developer's dotfiles, real settings, network access, or state left behind by another test. A test that assumes a resource is already in the right state will fail for the wrong reason on someone else's machine or in CI.\n\n### Own the test\n\nLarger tests span components, so ownership is ambiguous by default and unowned tests rot. If you add one, you are the person who fixes it when it breaks. Put it in the module matching the feature area it covers so the next person can find the right owner.\n\n## Common test-writing patterns\n\n### 1. Wait for bootstrap first\n\nFor most terminal-facing tests, the first real step should be:\n\n- `wait_until_bootstrapped_single_pane_for_tab(0)`\n\nDo not start asserting on terminal UI before bootstrap completes.\n\n### 2. Clear the bootstrapped blocks if block indices matter\n\nIf the test relies on saved positions like `block_index:0`, clear the block list after bootstrap:\n\n- `clear_blocklist_to_remove_bootstrapped_blocks()`\n\nOtherwise the first user-generated block index depends on bootstrap output and the active shell.\n\n### 3. Use helper command runners\n\nPrefer helpers like:\n\n- `execute_command_for_single_terminal_in_tab(...)`\n- `execute_echo(...)`\n- `execute_echo_str(...)`\n- `execute_long_running_command(...)`\n\nThese helpers already handle a lot of correctness and output validation.\n\n## Running tests\n\n### Run one test directly through the integration binary\n\nUse this first while authoring:\n\n```bash\ncargo run -p integration --bin integration -- test_name\n```\n\nThis is the fastest way to iterate on a specific test because it bypasses the outer Rust test wrapper and runs the named test directly.\n\n### Run one test through nextest\n\nOnce it is wired into one of the `tests/integration/*.rs` macro lists, run it with nextest:\n\n```bash\ncargo nextest run --no-fail-fast --workspace test_name\n```\n\n### Run with a real display when needed\n\nFor screenshot/video or other real-display flows:\n\n```bash\nWARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo run -p integration --bin integration -- test_name\n```\n\nOr with nextest:\n\n```bash\nWARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo nextest run --no-fail-fast --workspace test_name\n```\n\n## Debugging and investigation\n\n### Get a backtrace on failures\n\n```bash\nRUST_BACKTRACE=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Pause on failure\n\nThis is useful when running locally and you want to inspect the failed UI state:\n\n```bash\nWARPUI_PAUSE_INTEGRATION_TEST_ON_FAILURE=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Pause after every step\n\nUseful for understanding exactly what the test is doing:\n\n```bash\nWARPUI_PAUSE_INTEGRATION_TEST_AT_EVERY_STEP=1 cargo run -p integration --bin integration -- test_name\n```\n\n### Video and screenshots\n\nIf the task is specifically about recording a test, collecting screenshots, or validating overlay/video artifacts, also use the `gui-integration-test-video` skill (located at `.warp/skills/gui-integration-test-video/SKILL.md`).\n\n### Environment variable gotcha\n\n`utils.set_env(...)` affects runtime environment lookups such as `std::env::var(...)`.\n\nIt does not affect compile-time lookups like `option_env!(...)`. If the product code uses `option_env!`, changing the env var inside the test will not change that behavior without rebuilding.\n\n## Verification checklist\n\nBefore considering a new integration test done, verify all of the following:\n\n- The test function lives under `crates/integration/src/test/`.\n- The module is added and re-exported in `crates/integration/src/test.rs`.\n- The test is registered in `crates/integration/src/bin/integration.rs`.\n- The test is listed in the correct nextest macro file and will run in CI by default, unless it was explicitly made manual-only with a documented reason.\n- The test passes when run directly through the integration binary.\n- The test passes through nextest if it is meant to be part of the automated suite.\n- An integration test is the right level for this behavior; it is not re-covering logic a unit test could reach.\n- The assertions check the intended user-visible behavior.\n- Every assertion is named, and the failure output would be actionable to someone who has never seen the test.\n- The test does not depend on the developer's real home directory, shell config, or machine state.\n- If the test uses screenshots/video, the produced artifacts were actually inspected rather than only assuming they exist.\n\n## Anti-patterns to avoid\n\n- Writing a test only in `src/test/*.rs` and forgetting the nextest macro list.\n- Asserting on bootstrap-sensitive block indices without clearing the bootstrapped blocks first.\n- Using raw events everywhere when a helper already exists.\n- Adding sleeps instead of assertion polling.\n- Making the test depend on personal dotfiles, real settings, or non-hermetic filesystem state.\n- Using retries or `PreconditionFailed` to paper over a deterministic bug.\n- Re-testing branch logic that a unit test already covers, at full app-boot cost.\n- Asserting on internal call sequences instead of user-visible behavior.\n- Unnamed assertions that produce failure output nobody can act on.\n- One long test that walks an entire user journey instead of several focused ones.\n- Leaving a real-display/manual test enabled in CI without a stable path.\n\n## Good workflow for agents\n\nWhen asked to add or fix an integration test:\n\n1. Find the closest existing integration test module for the feature.\n2. Reuse helper assertions and step constructors before inventing new low-level plumbing.\n3. Register the test in all required places, not just the implementation file.\n4. Run the test manually first.\n5. If it belongs in automation, run it with nextest too.\n6. If the test exercises visual behavior, verify the resulting UI behavior or artifacts directly.\n\n<!-- File: .agents/skills/gui-onboarding-verification-skill/SKILL.md (Tokens: ~4986 | Category: anthropic-skill) -->\n---\nname: gui-onboarding-verification-skill\ndescription: GUI desktop app only. Launch two parallel Oz cloud agents with computer use to download and install the latest stable Linux Warp build, capture screenshots while walking through first-time onboarding in both logged-out and logged-in states, then selectively fan out follow-up cloud agents for distinct onboarding branches proposed by those initial explorers. Use this whenever the user asks to test, document, screenshot, or walk through the Warp first-time install/onboarding experience in a cloud Linux environment.\n---\n\n# Onboarding verification skill\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nUse this skill to verify the first-time Warp install and onboarding flow on Linux with broader branch coverage than a single linear walkthrough.\n\nThe parent agent should not perform the walkthrough locally. Launch two parallel Oz cloud agents with computer use. Both initial children install the latest stable Warp Linux package appropriate for their platform and capture screenshots at every visible onboarding step until Warp reaches a usable terminal session. One child verifies the login-free flow. The other child verifies the logged-in flow using the managed secret `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN`.\n\nThose two baseline explorers are also responsible for noticing meaningful alternate onboarding branches and returning concrete plans for follow-up cloud agents. The parent agent should synthesize those plans, deduplicate overlapping suggestions, and launch a bounded second wave of targeted follow-up agents to improve coverage of paths a real user might encounter.\n\n## Parent workflow\n\n1. Launch exactly two remote Oz cloud agents in a single parallel `run_agents` batch with computer use enabled.\n2. Use no environment-specific assumptions unless the user provided an environment. If no environment was provided, omit the environment ID and let Warp choose the default remote environment.\n3. Give both baseline child agents the shared child prompt below, plus the appropriate flow-specific prompt.\n4. Wait for both baseline agents' reports. Each report must include:\n   - The completed baseline walkthrough result and artifacts.\n   - A concise list of observed UI quality issues, suspected bugs, error states, or rough edges, with screenshots when visible.\n   - A prioritized follow-up coverage plan describing distinct onboarding paths worth exploring with additional cloud agents.\n5. Treat the authenticated baseline child as blocked if `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` is missing or does not authenticate successfully.\n6. Build a combined coverage map from the two baseline reports. Deduplicate suggestions that reach the same visible state or exercise the same decision surface.\n7. Launch a second `run_agents` batch with computer use enabled for the most valuable follow-up onboarding branches:\n   - Prefer branches that materially change visible UI, available controls, downstream screens, auth state, or setup outcomes.\n   - Favor paths likely to expose correctness, polish, layout, truncation, loading, or validation problems.\n   - Default to at most four follow-up agents total unless the user explicitly asked for exhaustive coverage or the baseline reports show more than four clearly distinct high-value branches.\n   - Do not launch speculative follow-ups when the baseline agents did not observe a concrete branch point; report that coverage stopped after the baseline pass instead.\n8. Give each follow-up child the shared child prompt, the follow-up flow prompt below, the logged-out or logged-in flow prompt that matches its assigned auth state, and one synthesized branch assignment from the baseline reports.\n9. Wait for all follow-up reports before summarizing coverage, issues, artifacts, and any still-unexplored branches worth a later run.\n\n## Managed FTUE auth secret\n\n- `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` is an internal-team managed secret for cloud agents, not a repo file or prompt literal.\n- The secret should authenticate as a dedicated non-employee, non-`warp.dev` FTUE test user.\n- Rotate the secret with `oz-dev secret update --team --value-file <private-token-file> ONBOARDING_AGENT_FTUE_REFRESH_TOKEN`.\n- Treat the private token file as local scratch material only. Do not read it into chat, print it, stage it, commit it, upload it, or include it in artifacts. Delete it after the managed secret is updated.\n- Children should receive the secret only through the managed environment variable injected into the remote run.\n\nUse the initial `run_agents` call shaped like this:\n\n```text\nsummary: Launching two baseline cloud agents with computer use to compare logged-out and logged-in Warp onboarding screenshots and propose follow-up coverage branches.\nremote.computer_use_enabled: true\nagent_run_configs:\n- name: \"warp-onboarding-logged-out\"\n  prompt: the logged-out flow prompt below\n- name: \"warp-onboarding-logged-in\"\n  prompt: the logged-in flow prompt below\nbase_prompt: the shared child prompt below\n```\n\nWhen the baseline reports identify concrete follow-up branches, use a second `run_agents` call shaped like this:\n\n```text\nsummary: Launching targeted cloud follow-up agents to explore distinct onboarding branches identified by the baseline onboarding explorers.\nremote.computer_use_enabled: true\nagent_run_configs:\n- name: \"warp-onboarding-followup-theme-choice\"\n  prompt: the follow-up flow prompt below, the logged-out flow prompt below, and one synthesized logged-out branch assignment\n- name: \"warp-onboarding-followup-model-choice\"\n  prompt: the follow-up flow prompt below, the logged-in flow prompt below, and one synthesized logged-in branch assignment\nbase_prompt: the shared child prompt below\n```\n\n## Shared child prompt\n\nGive both cloud agents these shared instructions:\n\n```text\nYou are verifying the first-time Warp install and onboarding experience on Linux.\n\nGoal:\n- Download and install the latest stable Warp Linux build appropriate for this cloud environment's distro and CPU architecture.\n- Launch Warp in a fresh first-run state.\n- Take a screenshot at every visible onboarding step.\n- Continue until Warp reaches a usable terminal session, or stop and report a blocker if the assigned flow cannot proceed.\n- Notice alternate onboarding decisions that lead to meaningfully different screens, states, or outcomes, and return concrete follow-up cloud-agent plans for the parent orchestrator.\n- Treat visual polish, missing assets, misalignment, overlapping content, clipped text, poor contrast, broken loading states, unexpected errors, and confusing controls as verification findings rather than ignoring them.\n\nInstall requirements:\n- Use official stable Warp downloads only.\n- Do not use Warp Preview, Alpha, source builds, or a repository development build.\n- Detect CPU architecture with `uname -m`.\n- Detect the package manager or distro before choosing the package format.\n- Prefer native packages over AppImage because they install dependencies and register the app normally.\n\nStable Linux package mapping:\n- Debian/Ubuntu with amd64 or x86_64: https://app.warp.dev/download?package=deb\n- Debian/Ubuntu with arm64 or aarch64: https://app.warp.dev/download?package=deb_arm64\n- Fedora/RHEL/CentOS/openSUSE with amd64 or x86_64: https://app.warp.dev/download?package=rpm\n- Fedora/RHEL/CentOS/openSUSE with arm64 or aarch64: https://app.warp.dev/download?package=rpm_arm64\n- Arch with amd64 or x86_64: https://app.warp.dev/download?package=pacman\n- Arch with arm64 or aarch64: https://app.warp.dev/download?package=pacman_arm64\n- If no native package path is available, use the AppImage fallback:\n  - amd64 or x86_64: https://app.warp.dev/download?package=appimage\n  - arm64 or aarch64: https://app.warp.dev/download?package=appimage_arm64\n\nBefore launch:\n- Create a flow-specific artifact directory such as `~/warp-onboarding-logged-out` or `~/warp-onboarding-logged-in`.\n- Ensure the run starts from a fresh Warp first-run state by removing only Warp-specific config/data/cache/state directories for the test user, such as `~/.config/warp-terminal`, `~/.local/share/warp-terminal`, `~/.local/state/warp-terminal`, and `~/.cache/warp-terminal` if they exist.\n- Do not delete unrelated user files or system directories.\n\nScreenshot workflow:\n- Take the first screenshot before interacting with the first visible Warp window.\n- Take one screenshot before every user action.\n- Take another screenshot after each action if the UI changes.\n- Use sequential filenames with a flow prefix, such as `01-logged-out-initial-window.png` or `01-logged-in-initial-window.png`.\n- If anything looks wrong, take an additional issue-focused screenshot that captures the problematic state as clearly as possible.\n- Maintain a manifest file in the artifact directory with, for each screenshot:\n  - filename\n  - timestamp\n  - what was visible\n  - what action was about to happen or just happened\n- For issue-focused screenshots, add the suspected issue category and the screen or step where it appeared.\n- Do not include secret values, refresh tokens, ID tokens, auth redirect URLs, or Authorization headers in the manifest, logs, shell history, screenshots, or final report.\n\nOnboarding behavior:\n- Baseline children choose the default or most conservative option at each step unless the flow-specific prompt says otherwise, while recording branch points that deserve separate follow-up coverage.\n- Follow-up children take the specifically assigned alternate branch, then use the default or most conservative option for unrelated decisions unless the branch assignment says otherwise.\n- If telemetry, shell, theme, editor-import, or agent integration choices appear, use the default path and document the choice in the manifest.\n- Continue until a normal terminal prompt is visible and usable.\n\nUI quality review:\n- Watch for screens that are visually broken, obviously unfinished, misaligned, truncated, clipped, crowded, low-contrast, unexpectedly blank, stuck loading, or inconsistent with adjacent steps.\n- Watch for actionable errors or validation states that appear during normal flow exploration, including auth failures, failed button transitions, controls that do not respond, duplicated overlays, missing images, or broken post-selection states.\n- For every suspicious state:\n  - Capture a screenshot.\n  - Record the screen, the action that led to it, what looked wrong, and whether it blocked progress.\n  - Describe the issue factually. If expected behavior is uncertain, say it appears suspicious rather than claiming a confirmed bug.\n\nTerminal verification:\n- Once a terminal session is visible, run a harmless flow-specific command:\n  - logged-out flow: `echo warp-onboarding-logged-out-ready`\n  - logged-in flow: `echo warp-onboarding-logged-in-ready`\n- Capture a final screenshot showing the usable terminal and command output.\n\nReport back:\n- Whether you were a baseline explorer or a follow-up branch explorer.\n- Which flow you ran: logged-out or logged-in.\n- OS and distro detected.\n- CPU architecture detected.\n- Package URL and install method used.\n- Launch command used.\n- Whether the walkthrough reached a usable terminal session.\n- Ordered screenshot list with short descriptions.\n- Artifact directory path.\n- Any built-in artifact IDs or attachment names if the harness supports artifact upload.\n- Any visual polish concern, suspected bug, error state, or unpolished/misaligned screen, including:\n  - screenshot filename\n  - screen or step\n  - action taken immediately before it appeared\n  - concise observed behavior\n  - whether it blocked progress\n- Any blocker, crash, missing dependency, display problem, auth failure, or step that required judgment.\n- For baseline explorers, include a `Follow-up coverage plan` section with zero or more proposed child-agent branches. Each proposal must include:\n  - suggested agent name\n  - logged-out or logged-in flow\n  - onboarding screen or decision point where the alternate branch begins\n  - exact alternate choice or action sequence to explore\n  - why it is materially distinct from the baseline path\n  - what user-visible state, setup outcome, or failure mode it could reveal\n  - any secret, auth, or environment dependency\n  - priority: high, medium, or low\n- For follow-up explorers, include whether the assigned branch was reachable and completed. If a new branch point appears while following the assigned path, record it as a later-run suggestion instead of recursively expanding the run yourself.\n\nDo not upload screenshots or logs to public external services. If the harness provides a built-in artifact or screenshot attachment mechanism, use that. Otherwise, leave the files in the artifact directory and report their paths.\n```\n\n## Logged-out flow prompt\n\nAppend this prompt to the shared child prompt for the logged-out child:\n\n```text\nYou own the logged-out onboarding flow.\n\nFlow-specific goal:\n- Do not create an account, log in, or use a real user identity.\n- Continue only through login-free or account-free paths until Warp reaches a usable terminal session.\n- Stop and report a blocker if the flow requires login or account creation with no skip/continue-without-account option.\n\nFlow-specific onboarding behavior:\n- If there is a skip, \"continue without account\", \"not now\", \"login later\", or equivalent option, use it.\n- Do not enter an email address, connect OAuth, paste an auth token, or create credentials.\n- Be especially alert for logged-out branch points around choosing terminal-only versus agentic experiences, customization/layout options, third-party integration toggles, and terminal theme selection. If they appear, propose follow-up branches that exercise materially different choices rather than trying all alternates inline.\n- Use the artifact directory `~/warp-onboarding-logged-out`.\n```\n\n## Logged-in flow prompt\n\nAppend this prompt to the shared child prompt for the logged-in child:\n\n```text\nYou own the logged-in onboarding flow.\n\nFlow-specific goal:\n- Use the managed secret environment variable `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` to authenticate as the dedicated non-employee, non-`warp.dev` FTUE test user.\n- Exercise onboarding screens that are available to an already-authenticated user.\n- Continue through the authenticated onboarding path until Warp reaches a usable terminal session.\n\nSecret handling requirements:\n- Before doing auth work, verify that `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` exists and is non-empty without printing it.\n- Never echo, log, screenshot, upload, or report the secret value.\n- Avoid shell tracing (`set -x`) and avoid writing commands that place the raw token in shell history or process lists.\n- Treat every auth redirect URL containing the refresh token as secret-bearing material, even after URL-encoding.\n- Do not pass a token-bearing redirect URL to a shell command, desktop URI handler, browser address bar, process argument, log, artifact, or report. In particular, do not use commands such as `xdg-open`, `gio open`, `open`, or equivalent with the redirect URL.\n- If you need to construct an auth redirect URL, keep it only in a clipboard value or a private temporary file with user-only permissions, paste it through Warp's visible Paste Auth Token flow, then delete the temporary file immediately after use.\n\nSecure Paste Auth Token process:\n1. Verify `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` exists and is non-empty without printing it.\n2. Start Warp's normal login flow and derive the current-run `state` from Warp's generated login URL.\n3. Normalize the managed secret privately:\n   - Trim surrounding whitespace and one pair of surrounding single or double quotes if present.\n   - If the secret parses as a URL with a `refresh_token` query parameter, extract that `refresh_token` value and ignore any stale `state` in the secret.\n   - Otherwise, treat the trimmed secret as the raw refresh token.\n4. URL-encode the extracted refresh token and current-run `state` separately as query parameter values.\n5. Construct the redirect URL only in a clipboard value or private temporary file with user-only permissions.\n6. Return to Warp and use the visible Paste Auth Token path:\n   - Click the `Click here to paste your token from the browser` link, `Paste Auth Token` button, or equivalent pasted-token control shown by Warp.\n   - Focus the auth token text input that appears.\n   - Paste the prepared redirect URL into that input and submit it through Warp's UI so Warp parses and validates it.\n7. Delete any private temporary files immediately after use and clear the clipboard if the environment supports doing so safely.\n8. If the Paste Auth Token UI cannot be reached or automated safely, stop and report an auth blocker instead of parsing the redirect in place of Warp, using a desktop URI handler, browser address bar, or shell command with the token-bearing URL.\n\nPreferred authenticated path:\n- Launch Warp in a fresh first-run state and choose the login/sign-in path from onboarding.\n- Use Warp's built-in Paste Auth Token flow rather than visiting real OAuth providers, invoking a desktop URI handler, or asking the agent to parse/validate the redirect URI itself.\n- Derive `<state>` from the login URL generated by Warp if the UI exposes a copied login URL or opens the browser. If the UI does not expose the state after reasonable effort, report that as an auth blocker rather than bypassing state validation.\n- Do not preflight the token with Firebase Secure Token before handing it to Warp. Warp's desktop redirect handler only requires `refresh_token` and `state`; `user_uid` is optional, and `deleted_anonymous_user=true` handles the anonymous-user override case.\n- Treat `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` as either of these secret shapes:\n  - a raw Firebase refresh token, or\n  - a complete Warp desktop auth redirect URL containing a `refresh_token` query parameter.\n- Normalize the secret into a current-run redirect URL without printing it:\n  - Trim surrounding whitespace and one pair of surrounding single or double quotes if present.\n  - If the secret parses as a URL with a `refresh_token` query parameter, extract that `refresh_token` value and ignore any stale `state` in the secret.\n  - Otherwise, treat the trimmed secret as the raw refresh token.\n  - URL-encode the extracted refresh token and the current-run `state` separately as query parameter values.\n  - Build `warp://auth/desktop_redirect?refresh_token=<url-encoded-normalized-refresh-token>&deleted_anonymous_user=true&state=<url-encoded-current-state>`.\n  - Do not include `user_uid` unless it is already present in a provided desktop redirect URL; it is not required for this flow.\n- Construct the normalized redirect URL in a clipboard value or private temporary file only, then hand it to Warp through the Paste Auth Token UI. Do not parse, validate, or route the redirect outside of Warp.\n- If the Paste Auth Token flow cannot be reached or automated safely, stop and report an auth blocker instead of using a desktop URI handler or any shell command that contains the token-bearing URL.\n\nFallback authenticated path:\n- If Warp rejects the normalized redirect, report the non-sensitive user-visible error and classify whether the secret appeared to be a raw token or a desktop redirect URL, without reporting any token contents.\n- If the Paste Auth Token flow is blocked by UI automation issues, report the blocker and include the exact non-sensitive step where automation failed.\n- Do not switch to a logged-out path for this child.\n\nFlow-specific onboarding behavior:\n- Choose login/sign-in rather than skip/login-later when presented with an auth choice.\n- After auth succeeds, continue through the remaining onboarding screens with default or conservative options.\n- Be especially alert for logged-in branch points around model selection, account-aware onboarding screens, AI/agent setup, workspace or project setup, and any decision that changes available product capability. If they appear, propose follow-up branches that exercise materially different choices rather than trying all alternates inline.\n- After the terminal verification succeeds, click the upper-right avatar/account control, open Settings from that menu, and capture an additional screenshot that clearly shows the logged-in user's email address in Warp settings or account/profile settings.\n- Include the account/settings email screenshot in the manifest and final report. The email address itself may be visible in the screenshot, but do not copy the email into logs, shell output, or the final text report unless the user explicitly asks for it.\n- Use the artifact directory `~/warp-onboarding-logged-in`.\n```\n\n## Follow-up flow prompt\n\nAppend this prompt to the shared child prompt for every second-wave child, followed by the matching logged-out or logged-in flow prompt and one branch assignment synthesized from the baseline reports:\n\n```text\nYou own one follow-up onboarding branch selected by the parent orchestrator from an earlier baseline exploration report.\n\nFollow-up branch behavior:\n- Start from a fresh first-run Warp state and install the same latest stable Linux build using the shared instructions.\n- Respect the assigned auth state: remain logged out for logged-out assignments, or use the managed authenticated flow for logged-in assignments.\n- Follow the exact alternate onboarding choice or action sequence in the branch assignment.\n- Capture screenshots before and after each assigned branch decision, then continue to a usable terminal session if the path allows it.\n- Apply the same UI quality review standard as the baseline explorers and call out anything that looks broken, rough, misaligned, confusing, or unexpectedly error-prone.\n- If the assigned branch is not reachable, capture the closest relevant screen, report why it was unreachable, and do not silently substitute a different branch.\n- If the assigned branch reveals another interesting alternate path, record it as a later-run suggestion rather than recursively launching more agents yourself.\n\nFinal report additions:\n- Repeat the exact branch assignment you attempted in concise non-sensitive terms.\n- State whether it was reachable, completed, blocked, or not applicable.\n- Compare the branch outcome against the likely baseline behavior when that comparison is visible from the UI.\n```\n\n## Success criteria\n\nThe run is successful when:\n\n- Warp stable was installed from an official Linux package or AppImage for the detected architecture.\n- Screenshots were captured for each onboarding screen and the final usable terminal.\n- The logged-out child reached a usable terminal without login, account creation, or a real user identity.\n- The logged-in child authenticated using `ONBOARDING_AGENT_FTUE_REFRESH_TOKEN` and reached a usable terminal in the authenticated FTUE path.\n- The logged-in child captured an additional post-login screenshot from the avatar/settings flow showing the logged-in user's email address.\n- Each terminal session was usable enough to run its flow-specific `echo` command.\n- Both baseline explorers returned either concrete follow-up coverage proposals or an explicit explanation that they did not observe meaningful additional branch points.\n- The parent orchestrator launched targeted second-wave agents for the highest-value concrete branch proposals, unless there were no such proposals or a prerequisite blocker made them infeasible.\n- Every reported visual polish concern, suspected bug, or error state includes a screenshot reference whenever the issue was visible on screen.\n\n## Common failure handling\n\n- If the package manager prompts for confirmation, use the non-interactive confirmation flag supported by that package manager.\n- If launching `warp-terminal` fails because of display setup, inspect the cloud environment's display variables and try launching from the desktop/app launcher if computer use provides one.\n- If the logged-out flow blocks on login with no skip path, stop at that screen, capture a screenshot, and report that as the terminal point for the logged-out flow.\n- If the logged-in flow cannot authenticate because the secret is missing, invalid, expired, revoked, or cannot be routed through Warp's auth redirect flow, stop at that screen, capture a screenshot, and report the non-sensitive blocker.\n- If the native package cannot be installed because dependencies are unavailable, fall back to the matching AppImage and clearly report the fallback.\n\n<!-- File: .agents/skills/gui-reproduce-bug-report-local/SKILL.md (Tokens: ~1110 | Category: anthropic-skill) -->\n---\nname: gui-reproduce-bug-report-local\nspecializes: reproduce-bug-report\nspecializes_source: warpdotdev/common-skills:.agents/skills/reproduce-bug-report\ndescription: GUI desktop app only. Repo-specific bug reproduction guidance for Warp. Specializes the core reproduce-bug-report skill for logged-out Warp UI repros, exact reporter-version installs, and login-free onboarding.\n---\n\n# Repo-specific bug reproduction guidance for `warp`\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\n## Prerequisite: install the parent skill\n\nThis skill specializes the core `reproduce-bug-report` skill (named in the `specializes` frontmatter field) and is not functional on its own. Before applying its guidance, confirm the parent skill is installed and resolvable at `.agents/skills/reproduce-bug-report/SKILL.md`. If it is missing, install it first by copying the skill directory from the source declared in the `specializes_source` frontmatter field (`warpdotdev/common-skills:.agents/skills/reproduce-bug-report`). Then continue with the guidance below.\n\nThis file is a companion to the core `reproduce-bug-report` skill. It does not redefine the shared Oz computer-use orchestration, artifact handling, safety rules, or reporting format. It specializes scope and setup for Warp bug reports.\n\n## Scope\n\n- Use this workflow only for Warp bugs that can be exercised while the app remains logged out.\n- Apply it to UI-visible Warp bugs, interaction bugs, rendering/layout bugs, logged-out onboarding bugs, settings bugs, editor/display bugs, terminal-display bugs, and other visual or interactive issues where screenshots or recordings would help.\n- Do not use it for authenticated-user flows, account-specific state, cloud-synced state, logged-in onboarding, or AI behaviors that require login.\n- If a report requires authentication, account state, cloud sync, or another logged-in-only capability, do not launch a repro agent with this local specialization; report that it is out of scope for the current logged-out Warp workflow.\n\n## Warp version and install strategy\n\n- Prefer reproducing against the exact Warp version/build and channel reported by the user.\n- Do not build Warp from source by default. Install the matching Linux package or binary release for the reporter's version/channel instead.\n- If the bug report names a macOS or Windows build, use the corresponding Linux build from the same version/channel when a matching Linux artifact exists, and state that this is a Linux proxy for the reporter's platform.\n- Use the repository's or Warp release tooling/docs available in the environment to find and install the exact versioned Linux artifact. Do not silently substitute the latest stable build when an exact matching version can be installed.\n- If the exact version/build cannot be found or installed, report that clearly, explain what was attempted, and use the closest justified fallback only when it is useful for continuing the investigation.\n- Record the requested reporter Warp version, the installed Linux version, the source of the installed artifact, and any fallback decision in the manifest and final report.\n\n## Logged-out Warp baseline\n\n- Keep Warp logged out for the entire repro attempt. Do not create an account, sign in, paste auth tokens, or use real user credentials.\n- Launch Warp and complete the login-free / continue-without-account onboarding path until a normal logged-out terminal session is usable.\n- Capture a post-onboarding baseline screenshot before attempting the bug-specific reproduction.\n- If the assigned bug cannot be exercised after entering a normal logged-out Warp session, stop and report the blocker instead of improvising an authenticated flow.\n\n## Local prompt additions\n\nWhen applying the core skill to Warp, ensure the parent prompt and child prompts include:\n\n- Reporter Warp version/build/channel: the exact value from the report, or `unknown`.\n- Build/app target: the exact versioned Linux Warp package/binary to install, or the justified fallback if an exact artifact is unavailable.\n- Assigned Warp state: first-run logged-out state, completed logged-out onboarding, terminal/session/layout/settings state, or the targeted code-path hypothesis.\n- A reminder that Warp must remain logged out and that logged-in-only reports are blocked for this specialization.\n\n## Local reproduction priorities\n\n- Match the reporter's Warp version/build/channel before broadening the search space.\n- Follow the issue's exact steps first, then test at most two targeted variations supported by the issue or by a code-path hypothesis.\n- Prefer targeted hypotheses derived from Warp UI strings, settings names, feature names, telemetry names, route names, and relevant components over broad exploratory clicking.\n- In the final report, include:\n  - the reporter-requested Warp version/build/channel\n  - the installed Linux Warp version/build/channel\n  - the package or binary source\n  - whether a fallback was used\n  - whether the test was a Linux proxy for a macOS or Windows report\n\n<!-- File: .agents/skills/gui-settings-ui/SKILL.md (Tokens: ~4284 | Category: anthropic-skill) -->\n---\nname: gui-settings-ui\ndescription: GUI desktop app only. How to build a Settings page in the Warp client (app/src/settings_view) so its widgets and settings search behave correctly — picking a PageType, deciding whether a heading belongs in the page-title slot or inside a widget, gating a widget, and scoping search_terms per widget. Use when adding or editing a settings page, a SettingsWidget, or anything that affects settings search.\n---\n\n# gui-settings-ui\n\n**Scope — GUI desktop app only.** This skill covers the settings modal in `app/src/settings_view/`, part of Warp's **GUI** desktop front-end. It does not apply to the headless TUI (`crates/warp_tui`). For general UI conventions see `gui-ui-guidelines`.\n\nSettings pages look simple, so they get written by pattern-matching the nearest neighbor — and the nearest neighbor is often wrong. The same two mistakes have produced five Linear tickets (APP-5060, APP-5058, APP-4910, APP-4922, APP-5059, one of which turned out to be a false positive). Read this before writing a settings page so the sixth doesn't happen.\n\n## The model\n\nA settings page is a `PageType` (`app/src/settings_view/settings_page.rs`). It holds **a list of searchable widgets** plus **an optional page title**:\n\n```rust\nPageType::new_uncategorized(widgets, Some(\"Knowledge\"))\nPageType::new_categorized(categories, None)\nPageType::new_monolith(widget, Some(\"Billing and Usage\"), /* is_dual_scrollable */ true)\n```\n\n- **`Uncategorized`** — a flat list of widgets. The common shape.\n- **`Categorized`** — widgets grouped under `Category`s, each with a subheader (rendered via `render_sub_header`) and an optional subtitle.\n- **`Monolith`** — the whole page is a single widget because its content can't be split for search (Keybindings, Teams, About, Environments).\n\nEach widget implements `SettingsWidget` (`settings_page.rs`):\n\n- `search_terms(&self) -> &str` — the terms this widget matches on.\n- `render(..)` — the widget's rows.\n- `should_render(&self, app) -> bool` — defaults to `true`. This is one of **two** ways to conditionally show a setting, and usually not the right one — see \"Two ways to gate a widget\" below.\n- `widget_id()` / `static_widget_id()` — default to `std::any::type_name::<Self>()`, used for scroll-to and deeplinks.\n\n**How search filters.** `PageType::update_filter` keeps a widget when\n`widget.should_render(app) && search_terms_match(widget.search_terms(), query)`.\n`search_terms_match` requires **every whitespace-delimited word** of the query to appear (case-insensitively, as a substring) somewhere in the widget's terms; an empty query matches everything. So filtering happens **per widget** — a widget is the smallest unit that can survive or disappear.\n\n**Whether the page title survives a search depends on the variant.** `PageType::render_page` treats the title differently for widget-list pages than for a monolith, and that difference is the easiest thing here to get wrong.\n\nFor **`Uncategorized` and `Categorized`**, the title is drawn **once, before the loop** over the filtered widget list, unconditionally:\n\n```rust\nlet mut page = Flex::column();\nif let Some(title) = title {\n    page.add_child(render_page_title(title, HEADER_FONT_SIZE, appearance));\n}\nfor widget in widgets { /* … only the widgets that matched … */ }\n```\n\nThe filter cannot reach it. That is what makes the title slot a fix for bug class 1: a title passed through `PageType` survives filtering on these two variants, while a title rendered inside a widget does not.\n\nFor **`Monolith`**, the title is **gated on the sole widget surviving the filter**:\n\n```rust\nFilteredPageType::Monolith { widget, title, .. } => {\n    let mut page = Empty::new().finish();\n    if let Some(widget) = widget          // None when the widget didn't match\n        && widget.should_render(app)\n    {\n        if let Some(title) = title { /* … title, then the widget … */ }\n    }\n    page                                  // otherwise nothing renders at all\n}\n```\n\n`get_filtered` sets that `widget` to `filter.then_some(..)`, so a non-matching query makes it `None` and the page renders empty — **title included**. On a monolith the title slot buys no search protection; the page is all-or-nothing, title and all.\n\nOne more thing worth knowing before you go looking for a title stranded over zero rows: you won't find one. `SettingsView::filtered_pages` (`app/src/settings_view/mod.rs`) drops any page whose `MatchData` is falsy from the sidebar and auto-selects the first page that still matches, so a fully non-matching page is never displayed. The state the title slot actually protects is the **partial** match — some widgets survive, some don't — and only widget-list pages can be in it.\n\n## Two ways to gate a widget\n\nThere are two mechanisms for conditionally showing a setting, and they are not interchangeable.\n\n**1. An `if` at page-build time — never create the widget.** This is how most gating in `AISettingsPageView::build_page` (`ai_page.rs`) is written:\n\n```rust\nif FeatureFlag::AIRules.is_enabled() {\n    widgets.extend(Self::knowledge_widgets());\n}\nif cfg!(feature = \"voice_input\")\n    && ai_settings.voice_input_enabled_internal.is_supported_on_current_platform()\n{\n    widgets.push(Box::new(VoiceWidget::default()));\n}\n```\n\n**2. `should_render(&self, app) -> bool` — create the widget and let it opt out per pass.**\n\n**Which one: can the value change while the app is running?**\n\n- **Fixed for the process** — a feature flag, a `cfg!` feature, a platform-support check. → **Use the build-time `if`.** This is the preferred default: the widget never exists, so there is nothing to filter, render, or reason about.\n- **Can change at runtime** — a setting the user toggles, auth state, an availability check that can flip mid-session. → **Use `should_render`.**\n\nThe reason is *when each is evaluated*. The build-time `if` runs once, when the page is constructed, and its result is frozen until something rebuilds the page (for AI/Code subpages, only switching subpages does — see below). `should_render` is re-evaluated on every filter and render pass, so it tracks a value that changes while the settings page is open. Gate a runtime-changing value with a build-time `if` and the page goes stale; gate a static flag with `should_render` and you carry a widget around for nothing.\n\nThe real `should_render` users are all the runtime kind: `SettingsSyncWidget` (`main_page.rs`) on auth state, `WarpDriveToggleWidget` (`warp_drive_page.rs`) on `WarpDriveSettings::is_warp_drive_available`, and the CLI-agent rich-input widgets (`ai_page.rs`) on the user-toggleable footer setting, via `should_render_cli_agent_rich_input`.\n\nEither mechanism keeps search honest: an uncreated widget isn't in the list, and `update_filter` already skips a widget whose `should_render` is false. What you must never do is hide rows inside `render` while `search_terms` still advertises them — that makes the page match a query and then show nothing for it.\n\n## The decision that matters: where does the heading live?\n\nFor any heading on a settings page, ask **what does it name?**\n\n- **It names the whole page** → it belongs in the `PageType` title slot. On `Uncategorized` / `Categorized` that is also what keeps it on screen while the page is filtered; on a `Monolith` it is a structural choice only (see above).\n- **It names one section among several** → it belongs inside the widget (or `Category`) that owns that section, so it disappears together with its own rows.\n\nGetting this wrong in the first direction is bug class 1 below. Getting it wrong in the second direction leaves a section header stranded above unrelated rows.\n\n### Page-shape taxonomy\n\nClassify the page before you write it:\n\n- **Single-topic page** — one heading names everything on it, but the content is still made of separately-matchable widgets. Knowledge, Third party CLI agents, Editor and Code Review, Account, Scripting. → **Title in the `PageType` slot.**\n- **Multi-section page** — several independent sections, each with its own heading. Warp Agent, Agent profiles, Appearance, Features. → **Per-section headings live in widgets/categories** and correctly disappear with their rows. (For `Categorized`, `get_filtered` drops categories whose widgets all filtered out, so their subheaders vanish automatically — that's the behavior you want.)\n- **Monolith page** — Keybindings, Teams, About, Environments, Codebase Indexing. There is no partial-match state: the sole widget either matches, and the whole page renders, or it doesn't, and the whole page renders empty and drops out of the sidebar. So a monolith can never strand an orphaned setting under a missing heading — it is **not** affected by bug class 1, but for that reason, *not* because its title is protected. Passing the title through the slot is still the tidier structure (Billing and Usage and Referrals do), just don't expect it to keep the title on screen during a non-matching search; on a monolith it will not. Codebase Indexing looks single-topic but is really one unfilterable widget covering the whole page (`CodePageWidget`, wrapped by `CodeIndexingPageWidget`) — building it as `Uncategorized` instead of `Monolith` made the sidebar show a permanent, misleading \"(1)\" on any match ([APP-5530]).\n\nThe two are not mutually exclusive: a page can name itself in the title slot **and** have per-section subheaders inside its widgets. Privacy does exactly that — `PageType::new_uncategorized(widgets, Some(\"Privacy\"))` plus `render_sub_header` calls inside individual widgets. The rule is per heading, not per page.\n\nWorked positive example: **Scripting** (`scripting_page.rs`) is a small page done right — two focused widgets (`WarpControlCliInstallWidget`, `LocalControlModeWidget`) with their own `search_terms`, and `PageType::new_uncategorized(widgets, Some(\"Scripting\"))`. A ticket was once filed claiming Scripting needed splitting; it was canceled because the premise was wrong. A small page is not automatically a mega-widget.\n\n## Bug class 1 — the vanishing title\n\n**Symptom:** on a single-topic `Uncategorized` or `Categorized` page, typing a search term that matches one row makes the page heading disappear along with the non-matching rows, leaving an unlabeled orphan setting. (A monolith can't reach this state — see the taxonomy above.)\n\n**Cause:** the only heading was rendered inside a widget — via `build_sub_header` / `render_page_title` in that widget's `render`, or via a header-only widget that exists just to draw a title. Filtering removes the widget, and the heading goes with it.\n\nCanonical fix — commit `ddadcee` ([APP-5060], #14519), Knowledge. Before, `AIFactWidget::render` opened with:\n\n```rust\nlet header = build_sub_header(appearance, \"Knowledge\", …).finish();\nlet mut column = Flex::column().with_child(header) /* … all the Knowledge rows … */;\n```\n\nAfter, the heading moved to page chrome and the rows became focused widgets:\n\n```rust\nlet title = match subpage {\n    AISubpage::Knowledge => Some(\"Knowledge\"),\n    AISubpage::ThirdPartyCLIAgents => Some(\"Third party CLI agents\"),\n    AISubpage::WarpAgent | AISubpage::Profiles => None,\n};\nPageType::new_uncategorized(widgets, title)\n```\n\n(The `ThirdPartyCLIAgents` arm and the exhaustive `match` came from #14524; note the deliberate absence of a `_` arm, so a new subpage forces this decision.)\n\nThe same commit (#14524) deleted `CodeSubpageHeaderWidget` from the then-combined Code page — a widget whose entire job was `build_sub_header(appearance, self.title, None)` — and replaced it with a title passed through `PageType`. Those two halves are now separate pages, `code_indexing_page.rs` and `code_editor_review_page.rs`, each passing its own `PAGE_TITLE` through the slot. **A header-only widget is always this bug.** If a widget renders nothing but a title, delete it and use the title slot.\n\n## Bug class 2 — the unfilterable mega-widget\n\n**Symptom:** searching a term that should isolate one row shows every row on the page, and the sidebar match count is 1 no matter how specific the query is.\n\n**Cause:** one widget renders many unrelated settings and declares one mega `search_terms()` blob covering all of them. The widget is the filter unit, so it's all-or-nothing.\n\nBefore (`CLIAgentWidget`, pre-#14524) — one widget, one blob, seven settings:\n\n```rust\nfn search_terms(&self) -> &str {\n    \"third party cli coding agent claude codex gemini toolbar footer layout chip chips \\\n     rearrange re-arrange bar command regex auto show rich input dismiss ctrl enter submit newline\"\n}\n```\n\nAfter — one widget per setting, each with terms scoped to just that setting:\n\n```rust\nfn cli_agent_widgets() -> Vec<Box<dyn SettingsWidget<View = AISettingsPageView>>> {\n    vec![\n        Box::new(CLIAgentWidget::default()),\n        Box::new(CLIAgentAutoToggleRichInputWidget::default()),\n        Box::new(CLIAgentAutoOpenRichInputWidget::default()),\n        Box::new(CLIAgentAutoDismissRichInputWidget::default()),\n        Box::new(CLIAgentSubmitRichInputWidget::default()),\n        Box::new(CLIAgentCommandsWidget),\n        Box::new(CLIAgentToolbarLayoutWidget),\n    ]\n}\n```\n\nRules of thumb when splitting:\n\n- **One widget ≈ one setting row** (or one tightly-coupled group that always shows and hides together).\n- **Scope `search_terms` to that widget only.** Keep enough shared context that a page-level query still matches (each CLI-agent widget above keeps `\"third party cli coding agent\"`), then add the terms unique to the row.\n- **Give each row the right gating mechanism** (see \"Two ways to gate a widget\"). A row that used to sit behind an `if … { … }` inside a mega-`render` becomes its own widget: if its condition is a static feature flag or platform check, gate it with an `if` in the page's build function and never create it; if it can change at runtime, give it `should_render`. The CLI-agent rich-input rows are the runtime case — they hang off the user-toggleable footer setting — so they use `should_render` and share the predicate through a small free function (`should_render_cli_agent_rich_input(app)`) rather than duplicating the condition.\n- **Move per-row state with the row.** Each `SwitchStateHandle` / `MouseStateHandle` moves to the widget that owns its control. Never create one inline while rendering (see `gui-ui-guidelines` and the AGENTS.md note on `MouseStateHandle`).\n- **Watch the widget ids.** `widget_id()` is `std::any::type_name::<Self>()`, so splitting a widget changes ids. `settings_widget_deeplink_target` in `app/src/settings_view/mod.rs` maps stable public slugs (`warp://settings?widget=<slug>`) onto them. The CLI-agent split deliberately kept `CLIAgentWidget` as the first widget so `cli_agent_settings_widget_id()` — the target of the `cli_agents` deeplink — stayed valid. If you rename or remove a widget that backs a deeplink, re-point the accessor.\n\n## Subpages rebuild their `PageType` — reapply the filter\n\nAI subpages rebuild their `PageType` when the active subpage changes (`AISettingsPageView::set_active_subpage` / `build_page`). A fresh `PageType` starts with **every** widget in its filter, so a live search query is silently dropped unless it's reapplied. `SettingsView::reapply_search_filter_to_active_subpage` in `app/src/settings_view/mod.rs` exists for exactly this ([APP-4922], #14116). If you add a code path that rebuilds a subpage's page while search may be active, call it.\nThe Code umbrella no longer works this way: `CodeIndexing` and `EditorAndCodeReview` are separate pages that each own their widgets outright, so nothing rebuilds and there is no filter to reapply. Prefer that shape for new umbrella children — a subpage that owns its own page needs none of this machinery.\n\n## Known anti-examples still in the tree\n\nUseful to read, not to copy:\n\n- **Warpify** (`warpify_page.rs`) — `PageType::new_categorized(categories, None)` where the first category is `Category::new(\"\", vec![Box::new(TitleWidget::default())])` and `TitleWidget::render` calls `render_page_title(\"Warpify\", …)`. Single-topic page, title inside a widget: bug class 1.\n- **Warp Drive** (`warp_drive_page.rs`) — `PageType::new_uncategorized([WarpDriveHeaderWidget, WarpDriveToggleWidget], None)` with no title slot at all. `WarpDriveHeaderWidget` is a conditional sign-up banner, so a signed-in user sees only a bare toggle row and no page heading.\n\n## How to verify a settings-page change\n\nVerification is cheap here; do both.\n\n1. **Unit-test the filter.** `app/src/settings_view/mod_tests.rs` has the harness — `StubWidget`, `stub_widgets_page`, and `visible_widget_count` (which reads `PageType::get_filtered`). Assert that a term unique to one widget yields exactly one visible widget, and that clearing the query restores all of them. Follow the `${filename}_tests.rs` convention from AGENTS.md; run with `cargo nextest run -p warp settings_view`.\n2. **Exercise the real search.** Open Settings, go to the page, and check three things:\n   - a term matching exactly one row leaves **only** that row,\n   - the **page title is still visible** while that filter is active — expect this on an `Uncategorized` / `Categorized` page; a monolith correctly disappears whole instead,\n   - clearing the search restores the full page.\n\n## Anti-patterns\n\n```rust\n// A widget whose whole job is to draw the page heading. Delete it and pass the\n// title through PageType instead.\nstruct FooSubpageHeaderWidget { title: &'static str }\n\n// A single-topic page with no page title, drawing its heading inside a widget.\n// Any non-matching search term erases the heading.\nPageType::new_uncategorized(widgets, None)   // and build_sub_header(\"Foo\", …) in a widget\n\n// One widget, one mega search_terms blob, many unrelated rows: nothing can be\n// filtered or attributed individually.\nfn search_terms(&self) -> &str { \"foo bar baz qux quux corge grault\" }\n\n// Conditional rows hidden inside render() while search_terms still advertises\n// them — the page matches a query and then renders nothing for it.\nfn render(&self, …) { if flag_enabled { /* the only rows */ } }\n// Gate the widget instead: an `if` at page-build time for a static flag, or\n// should_render() when the condition can change at runtime.\n```\n\n<!-- File: .agents/skills/gui-ui-guidelines/SKILL.md (Tokens: ~841 | Category: anthropic-skill) -->\n---\nname: gui-ui-guidelines\ndescription: GUI desktop app only. Catalog of guidelines for writing UI code in the Warp client. Read whenever doing any UI work in this repo, up front before writing the change, so the relevant guidelines shape the implementation.\n---\n\n# gui-ui-guidelines\n\n**Scope — GUI desktop app only.** This skill applies to Warp's **GUI** desktop front-end (the `app/` crate on the WarpUI pixel/GPU framework). It does **not** apply to the headless **TUI** front-end (`crates/warp_tui`; cell-grid `TuiElement` library under `crates/warpui_core/src/elements/tui`), which has its own components, tests, and change-verification workflow. For TUI work, see the `tui-ui-guidelines`, `tui-testing`, and `tui-verify-change` skills instead.\n\nThis skill is a growing catalog of guidelines for working on Warp's UI code. Each guideline captures a lesson that would otherwise be re-learned through review — typically because an agent or contributor reinvented a component, drifted from the design system, or bypassed a shared abstraction.\n\n**How to use this skill:**\n\n- Read through the guidelines below once at the start of any UI task, then keep them in mind while implementing. The list is short enough to scan.\n- Each guideline is self-contained. Not every one will apply to every task — use judgment. But if a guideline *does* apply, follow it.\n- When in doubt, prefer reusing an existing abstraction over introducing a new one. The Warp UI has accumulated a well-factored set of shared components and themes; new one-offs almost always drift.\n\nFor work on the settings modal (`app/src/settings_view/`) — a settings page, a `SettingsWidget`, or anything that affects settings search — also read the `gui-settings-ui` skill, which covers the page-title, widget-gating, and widget-splitting rules specific to that surface.\n\nNew guidelines get added here over time. If you discover a recurring UI mistake that would have been caught by a written rule, add it.\n\n---\n\n## Guideline: Reuse button themes\n\nButton colors come from a shared set of `ActionButtonTheme` impls in `app/src/view_components/action_button.rs` (and the parallel `Theme` impls in `crates/ui_components/src/button/themes.rs`) — `PrimaryTheme`, `SecondaryTheme`, `NakedTheme`, `DangerPrimaryTheme`, etc. These encode the design system and keep button colors consistent across the app.\n\nWhen styling a button, **use one of the existing themes unchanged**. The shared themes are well-established and vetted; if one looks \"wrong\" for your use case, the most likely explanation is that you're reaching for the wrong theme, not that the theme is buggy.\n\nDo **not** modify a shared theme on your own initiative. Changing `PrimaryTheme`, `SecondaryTheme`, etc. affects every button in the app, and a tweak that fixes your screen can silently regress others. Only edit a shared theme when the user has explicitly confirmed that the design-system component itself needs to change.\n\nRed flags that you're about to make buttons inconsistent:\n\n- Writing a new `impl ActionButtonTheme for FooPrimaryTheme` that delegates to `PrimaryTheme` and only tweaks one method (usually `text_color`). Almost always the right move is to use `PrimaryTheme` directly and accept the result.\n- Hard-coding `ColorU::new(...)` instead of using `appearance.theme()` accessors (`accent`, `font_color(bg)`, `foreground`, etc.).\n- Setting `should_opt_out_of_contrast_adjustment` to `true` to force a specific label color.\n- Naming a theme after a feature or view (`FooPrimaryTheme`, `BarSubmitTheme`) rather than a design-system role.\n\nIf an existing theme genuinely doesn't fit and you think the shared theme should change, surface that to the user before editing it, rather than either editing it unilaterally or papering over it with a one-off.\n\n<!-- File: .agents/skills/logging-and-error-reporting/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# logging-and-error-reporting\n\nPath: `.agents/skills/logging-and-error-reporting/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/logging-and-error-reporting/SKILL.md)\n\n<!-- File: .agents/skills/promote-feature/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# promote-feature\n\nPath: `.agents/skills/promote-feature/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/promote-feature/SKILL.md)\n\n<!-- File: .agents/skills/remove-feature-flag/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# remove-feature-flag\n\nPath: `.agents/skills/remove-feature-flag/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/remove-feature-flag/SKILL.md)\n\n<!-- File: .agents/skills/review-pr-local/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# review-pr-local\n\nPath: `.agents/skills/review-pr-local/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/review-pr-local/SKILL.md)\n\n<!-- File: .agents/skills/rust-unit-tests/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# rust-unit-tests\n\nPath: `.agents/skills/rust-unit-tests/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/rust-unit-tests/SKILL.md)\n\n<!-- File: .agents/skills/triage-issue-local/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# triage-issue-local\n\nPath: `.agents/skills/triage-issue-local/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/triage-issue-local/SKILL.md)\n\n<!-- File: .agents/skills/tui-testing/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# tui-testing\n\nPath: `.agents/skills/tui-testing/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-testing/SKILL.md)\n\n<!-- File: .agents/skills/tui-ui-guidelines/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# tui-ui-guidelines\n\nPath: `.agents/skills/tui-ui-guidelines/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-ui-guidelines/SKILL.md)\n\n<!-- File: .agents/skills/tui-verify-change/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# tui-verify-change\n\nPath: `.agents/skills/tui-verify-change/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.agents/skills/tui-verify-change/SKILL.md)\n\n<!-- File: .warp/skills/gui-integration-test-video/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# gui-integration-test-video\n\nPath: `.warp/skills/gui-integration-test-video/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/.warp/skills/gui-integration-test-video/SKILL.md)\n\n<!-- File: resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# oz-child-agent-orchestration\n\nPath: `resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-child-agent-orchestration/SKILL.md)\n\n<!-- File: resources/bundled/harness_skills/oz-finish-task/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# oz-finish-task\n\nPath: `resources/bundled/harness_skills/oz-finish-task/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-finish-task/SKILL.md)\n\n<!-- File: resources/bundled/harness_skills/oz-notify-user/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# oz-notify-user\n\nPath: `resources/bundled/harness_skills/oz-notify-user/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-notify-user/SKILL.md)\n\n<!-- File: resources/bundled/harness_skills/oz-report-pr/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# oz-report-pr\n\nPath: `resources/bundled/harness_skills/oz-report-pr/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-report-pr/SKILL.md)\n\n<!-- File: resources/bundled/harness_skills/oz-upload-file/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# oz-upload-file\n\nPath: `resources/bundled/harness_skills/oz-upload-file/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/harness_skills/oz-upload-file/SKILL.md)\n\n<!-- File: resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# edit-figma-design\n\nPath: `resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/warpdotdev/warp/HEAD/resources/bundled/mcp_skills/figma/edit-figma-design/SKILL.md)\n\n<!-- ... +120 more modular skills available via /api/skills/warpdotdev/warp?path={path} -->\n</yakaai_skills>"}