diff --git a/plugins/agent-browser/.agents/skills/agent-browser/SKILL.md b/plugins/agent-browser/.agents/skills/agent-browser/SKILL.md index 8485a1f4..dc9bb54a 100644 --- a/plugins/agent-browser/.agents/skills/agent-browser/SKILL.md +++ b/plugins/agent-browser/.agents/skills/agent-browser/SKILL.md @@ -32,6 +32,7 @@ agent-browser skills get slack # Slack workspace automation agent-browser skills get dogfood # Exploratory testing / QA / bug hunts agent-browser skills get derive-client # Record a HAR, derive a standalone API client for a site agent-browser skills get vercel-sandbox # agent-browser inside Vercel Sandbox microVMs +agent-browser skills get protected-vercel-deployments # Access protected Vercel deployments agent-browser skills get agentcore # AWS Bedrock AgentCore cloud browsers ``` diff --git a/plugins/agent-browser/agent/skills/agent-browser/SKILL.md b/plugins/agent-browser/agent/skills/agent-browser/SKILL.md index 7a423df7..475e0410 100644 --- a/plugins/agent-browser/agent/skills/agent-browser/SKILL.md +++ b/plugins/agent-browser/agent/skills/agent-browser/SKILL.md @@ -28,6 +28,7 @@ agent-browser skills get slack # Slack workspace automation agent-browser skills get dogfood # Exploratory testing / QA / bug hunts agent-browser skills get derive-client # Record a HAR, derive a standalone API client for a site agent-browser skills get vercel-sandbox # agent-browser inside Vercel Sandbox microVMs +agent-browser skills get protected-vercel-deployments # Access protected Vercel deployments agent-browser skills get agentcore # AWS Bedrock AgentCore cloud browsers ``` diff --git a/plugins/agent-browser/skills-lock.json b/plugins/agent-browser/skills-lock.json index 7b474434..2a981b7e 100644 --- a/plugins/agent-browser/skills-lock.json +++ b/plugins/agent-browser/skills-lock.json @@ -5,7 +5,7 @@ "source": "vercel-labs/agent-browser", "sourceType": "github", "skillPath": "skills/agent-browser/SKILL.md", - "computedHash": "a674b7d81066e3cc471a7512ddb4ae724418cbfefa75cbb050b0dc430e4d57a0" + "computedHash": "80161e6836b3b40f4e83730a290deb40ac14d877d38c97ad5f6d71be37ce74c3" }, "dogfood": { "source": "vercel-labs/agent-browser", diff --git a/plugins/better-auth/.agents/skills/better-auth-best-practices/SKILL.md b/plugins/better-auth/.agents/skills/better-auth-best-practices/SKILL.md index ddeedc81..74195ce3 100644 --- a/plugins/better-auth/.agents/skills/better-auth-best-practices/SKILL.md +++ b/plugins/better-auth/.agents/skills/better-auth-best-practices/SKILL.md @@ -5,7 +5,17 @@ description: Configure Better Auth server and client, set up database adapters, # Better Auth Integration Guide -**Always consult [better-auth.com/docs](https://better-auth.com/docs) for code examples and latest API.** +## Documentation Version + +Use documentation that matches the Better Auth version installed in the project. APIs and plugin names can differ across maintained release lines. + +1. Prefer a version explicitly named by the user. +2. Otherwise, inspect the resolved `better-auth` version in the lockfile, falling back to the package manifest when no lockfile is available. +3. When the Better Auth MCP is available, call `get_doc` with `/llms.txt` to resolve that package version to a documentation identifier. Pass the identifier to every `search_docs` call and pass result paths to `get_doc` unchanged. +4. Without MCP, start at [better-auth.com/llms.txt](https://better-auth.com/llms.txt) and follow the matching version index. +5. Use the latest documentation only when the project version cannot be determined or the user explicitly asks about the latest release or an upgrade. + +When planning an upgrade, separate guidance for the currently installed version from guidance for the target version. --- @@ -16,9 +26,9 @@ description: Configure Better Auth server and client, set up database adapters, 3. Create `auth.ts` with database + config 4. Create route handler for your framework 5. Run migrations: - - **Built-in adapter:** `npx @better-auth/cli@latest migrate` - - **Drizzle:** `npx @better-auth/cli@latest generate --output src/db/auth-schema.ts` then `npx drizzle-kit push` (dev) or `npx drizzle-kit generate && npx drizzle-kit migrate` (prod) - - **Prisma:** `npx @better-auth/cli@latest generate --output prisma/schema.prisma` then `npx prisma migrate dev` + - **Built-in adapter:** `npx auth@latest migrate` + - **Drizzle:** `npx auth@latest generate --output src/db/auth-schema.ts` then `npx drizzle-kit push` (dev) or `npx drizzle-kit generate && npx drizzle-kit migrate` (prod) + - **Prisma:** `npx auth@latest generate --output prisma/schema.prisma` then `npx prisma migrate dev` 6. Verify: call `GET /api/auth/ok` — should return `{ status: "ok" }` --- @@ -35,9 +45,9 @@ Only define `baseURL`/`secret` in config if env vars are NOT set. CLI looks for `auth.ts` in: `./`, `./lib`, `./utils`, or under `./src`. Use `--config` for custom path. ### CLI Commands -- `npx @better-auth/cli@latest migrate` - Apply schema (built-in adapter) -- `npx @better-auth/cli@latest generate` - Generate schema for Prisma/Drizzle -- `npx @better-auth/cli mcp --cursor` - Add MCP to AI tools +- `npx auth@latest migrate` - Apply schema (built-in adapter) +- `npx auth@latest generate` - Generate schema for Prisma/Drizzle +- `npx auth@latest mcp --cursor` - Add MCP to AI tools **Re-run after adding/changing plugins.** @@ -179,4 +189,4 @@ For separate client/server projects: `createAuthClient()`. - [Options Reference](https://better-auth.com/docs/reference/options) - [LLMs.txt](https://better-auth.com/llms.txt) - [GitHub](https://github.com/better-auth/better-auth) -- [Init Options Source](https://github.com/better-auth/better-auth/blob/main/packages/core/src/types/init-options.ts) \ No newline at end of file +- [Init Options Source](https://github.com/better-auth/better-auth/blob/main/packages/core/src/types/init-options.ts) diff --git a/plugins/better-auth/.agents/skills/email-and-password-best-practices/SKILL.md b/plugins/better-auth/.agents/skills/email-and-password-best-practices/SKILL.md index 537c0108..3b7b7f16 100644 --- a/plugins/better-auth/.agents/skills/email-and-password-best-practices/SKILL.md +++ b/plugins/better-auth/.agents/skills/email-and-password-best-practices/SKILL.md @@ -8,7 +8,7 @@ description: Configure email verification, implement password reset flows, set p 1. Enable email/password: `emailAndPassword: { enabled: true }` 2. Configure `emailVerification.sendVerificationEmail` 3. Add `sendResetPassword` for password reset flows -4. Run `npx @better-auth/cli@latest migrate` +4. Run `npx auth@latest migrate` 5. Verify: attempt sign-up and confirm verification email triggers --- diff --git a/plugins/better-auth/.agents/skills/organization-best-practices/SKILL.md b/plugins/better-auth/.agents/skills/organization-best-practices/SKILL.md index 6f2f6447..38633d08 100644 --- a/plugins/better-auth/.agents/skills/organization-best-practices/SKILL.md +++ b/plugins/better-auth/.agents/skills/organization-best-practices/SKILL.md @@ -7,7 +7,7 @@ description: Configure multi-tenant organizations, manage members and invitation 1. Add `organization()` plugin to server config 2. Add `organizationClient()` plugin to client config -3. Run `npx @better-auth/cli@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma +3. Run `npx auth@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma 4. Verify: check that organization, member, invitation tables exist in your database ```ts diff --git a/plugins/better-auth/.agents/skills/two-factor-authentication-best-practices/SKILL.md b/plugins/better-auth/.agents/skills/two-factor-authentication-best-practices/SKILL.md index 74acac84..ec1653b7 100644 --- a/plugins/better-auth/.agents/skills/two-factor-authentication-best-practices/SKILL.md +++ b/plugins/better-auth/.agents/skills/two-factor-authentication-best-practices/SKILL.md @@ -7,7 +7,7 @@ description: Configure TOTP authenticator apps, send OTP codes via email/SMS, ma 1. Add `twoFactor()` plugin to server config with `issuer` 2. Add `twoFactorClient()` plugin to client config -3. Run `npx @better-auth/cli@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma +3. Run `npx auth@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma 4. Verify: check that `twoFactorSecret` column exists on user table ```ts diff --git a/plugins/better-auth/agent/skills/better-auth-best-practices/SKILL.md b/plugins/better-auth/agent/skills/better-auth-best-practices/SKILL.md index 3a77bc34..2e7b3a80 100644 --- a/plugins/better-auth/agent/skills/better-auth-best-practices/SKILL.md +++ b/plugins/better-auth/agent/skills/better-auth-best-practices/SKILL.md @@ -3,7 +3,17 @@ description: "Configure Better Auth server and client, set up database adapters, --- # Better Auth Integration Guide -**Always consult [better-auth.com/docs](https://better-auth.com/docs) for code examples and latest API.** +## Documentation Version + +Use documentation that matches the Better Auth version installed in the project. APIs and plugin names can differ across maintained release lines. + +1. Prefer a version explicitly named by the user. +2. Otherwise, inspect the resolved `better-auth` version in the lockfile, falling back to the package manifest when no lockfile is available. +3. When the Better Auth MCP is available, call `get_doc` with `/llms.txt` to resolve that package version to a documentation identifier. Pass the identifier to every `search_docs` call and pass result paths to `get_doc` unchanged. +4. Without MCP, start at [better-auth.com/llms.txt](https://better-auth.com/llms.txt) and follow the matching version index. +5. Use the latest documentation only when the project version cannot be determined or the user explicitly asks about the latest release or an upgrade. + +When planning an upgrade, separate guidance for the currently installed version from guidance for the target version. --- @@ -14,9 +24,9 @@ description: "Configure Better Auth server and client, set up database adapters, 3. Create `auth.ts` with database + config 4. Create route handler for your framework 5. Run migrations: - - **Built-in adapter:** `npx @better-auth/cli@latest migrate` - - **Drizzle:** `npx @better-auth/cli@latest generate --output src/db/auth-schema.ts` then `npx drizzle-kit push` (dev) or `npx drizzle-kit generate && npx drizzle-kit migrate` (prod) - - **Prisma:** `npx @better-auth/cli@latest generate --output prisma/schema.prisma` then `npx prisma migrate dev` + - **Built-in adapter:** `npx auth@latest migrate` + - **Drizzle:** `npx auth@latest generate --output src/db/auth-schema.ts` then `npx drizzle-kit push` (dev) or `npx drizzle-kit generate && npx drizzle-kit migrate` (prod) + - **Prisma:** `npx auth@latest generate --output prisma/schema.prisma` then `npx prisma migrate dev` 6. Verify: call `GET /api/auth/ok` — should return `{ status: "ok" }` --- @@ -33,9 +43,9 @@ Only define `baseURL`/`secret` in config if env vars are NOT set. CLI looks for `auth.ts` in: `./`, `./lib`, `./utils`, or under `./src`. Use `--config` for custom path. ### CLI Commands -- `npx @better-auth/cli@latest migrate` - Apply schema (built-in adapter) -- `npx @better-auth/cli@latest generate` - Generate schema for Prisma/Drizzle -- `npx @better-auth/cli mcp --cursor` - Add MCP to AI tools +- `npx auth@latest migrate` - Apply schema (built-in adapter) +- `npx auth@latest generate` - Generate schema for Prisma/Drizzle +- `npx auth@latest mcp --cursor` - Add MCP to AI tools **Re-run after adding/changing plugins.** @@ -177,4 +187,4 @@ For separate client/server projects: `createAuthClient()`. - [Options Reference](https://better-auth.com/docs/reference/options) - [LLMs.txt](https://better-auth.com/llms.txt) - [GitHub](https://github.com/better-auth/better-auth) -- [Init Options Source](https://github.com/better-auth/better-auth/blob/main/packages/core/src/types/init-options.ts) \ No newline at end of file +- [Init Options Source](https://github.com/better-auth/better-auth/blob/main/packages/core/src/types/init-options.ts) diff --git a/plugins/better-auth/agent/skills/email-and-password-best-practices/SKILL.md b/plugins/better-auth/agent/skills/email-and-password-best-practices/SKILL.md index d9284553..939f99a4 100644 --- a/plugins/better-auth/agent/skills/email-and-password-best-practices/SKILL.md +++ b/plugins/better-auth/agent/skills/email-and-password-best-practices/SKILL.md @@ -6,7 +6,7 @@ description: "Configure email verification, implement password reset flows, set 1. Enable email/password: `emailAndPassword: { enabled: true }` 2. Configure `emailVerification.sendVerificationEmail` 3. Add `sendResetPassword` for password reset flows -4. Run `npx @better-auth/cli@latest migrate` +4. Run `npx auth@latest migrate` 5. Verify: attempt sign-up and confirm verification email triggers --- diff --git a/plugins/better-auth/agent/skills/organization-best-practices/SKILL.md b/plugins/better-auth/agent/skills/organization-best-practices/SKILL.md index 819089a9..15ef6e40 100644 --- a/plugins/better-auth/agent/skills/organization-best-practices/SKILL.md +++ b/plugins/better-auth/agent/skills/organization-best-practices/SKILL.md @@ -5,7 +5,7 @@ description: "Configure multi-tenant organizations, manage members and invitatio 1. Add `organization()` plugin to server config 2. Add `organizationClient()` plugin to client config -3. Run `npx @better-auth/cli@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma +3. Run `npx auth@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma 4. Verify: check that organization, member, invitation tables exist in your database ```ts diff --git a/plugins/better-auth/agent/skills/two-factor-authentication-best-practices/SKILL.md b/plugins/better-auth/agent/skills/two-factor-authentication-best-practices/SKILL.md index 6f366f3c..06d34ba7 100644 --- a/plugins/better-auth/agent/skills/two-factor-authentication-best-practices/SKILL.md +++ b/plugins/better-auth/agent/skills/two-factor-authentication-best-practices/SKILL.md @@ -5,7 +5,7 @@ description: "Configure TOTP authenticator apps, send OTP codes via email/SMS, m 1. Add `twoFactor()` plugin to server config with `issuer` 2. Add `twoFactorClient()` plugin to client config -3. Run `npx @better-auth/cli@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma +3. Run `npx auth@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma 4. Verify: check that `twoFactorSecret` column exists on user table ```ts diff --git a/plugins/better-auth/skills-lock.json b/plugins/better-auth/skills-lock.json index 052584f7..0a208379 100644 --- a/plugins/better-auth/skills-lock.json +++ b/plugins/better-auth/skills-lock.json @@ -5,7 +5,7 @@ "source": "better-auth/skills", "sourceType": "github", "skillPath": "better-auth/best-practices/SKILL.md", - "computedHash": "61ba0ef64ed2e7c424401cc848ca33dd6d790a720c44727717dc0c5cba5fc122" + "computedHash": "0c30be0e118c2b7a0c4c57bd9e63bf110c7e420a79ec6ce7997153f3b1e066ee" }, "create-auth-skill": { "source": "better-auth/skills", @@ -17,19 +17,19 @@ "source": "better-auth/skills", "sourceType": "github", "skillPath": "better-auth/emailAndPassword/SKILL.md", - "computedHash": "747d7f73bd9e2ec5317b8ef88dc70f97df716d00e3d02de4051feebaa48b062d" + "computedHash": "863886759f729081e265941f4f72c503835d55a1f34f6d63cebe6571159fff4a" }, "organization-best-practices": { "source": "better-auth/skills", "sourceType": "github", "skillPath": "better-auth/organization/SKILL.md", - "computedHash": "27627eb3a13bd44eff3a5d890f96e7db052b623db90e01684f759abe3e7fe56b" + "computedHash": "8c85e683dabef4532f1e8b67bbed5c453d39e0e3d5aeedd69d16b568f6e6d44f" }, "two-factor-authentication-best-practices": { "source": "better-auth/skills", "sourceType": "github", "skillPath": "better-auth/twoFactor/SKILL.md", - "computedHash": "a6f720042e5a090909e0d519a6a50a7eb8179553cd5315f0bbad852a6d20dac8" + "computedHash": "5bc3051aef5060db013f0b3cbe812c40dbae5c22d6ba3ddde4b941158362a197" } } } diff --git a/plugins/dev3000/.agents/skills/d3k/SKILL.md b/plugins/dev3000/.agents/skills/d3k/SKILL.md index 1439e9ad..fffa5d6c 100644 --- a/plugins/dev3000/.agents/skills/d3k/SKILL.md +++ b/plugins/dev3000/.agents/skills/d3k/SKILL.md @@ -19,15 +19,29 @@ When this skill triggers, operate d3k. Do not merely tell the user how to run it Run from the project root. -1. Check for an existing project runtime: +1. Check canonical Portless readiness before starting any app process: + +```bash +d3k portless status --json +``` + +If `"setupRequired": true`, run: + +```bash +d3k portless setup +``` + +On macOS this opens the system administrator authorization dialog. Wait for the user to approve it, then rerun `d3k portless status --json`. Do not start d3k until `"canonical": true`, `"serviceInstalled": true`, and `"setupRequired": false`. If authorization is declined or unavailable, stop and explain the blocker. Never silently substitute direct localhost. + +2. Check for an existing project runtime: ```bash d3k status --json ``` -If it reports `"running": true`, reuse it. Do not start a second dev server or browser. +Reuse it only if it reports `"running": true`, `"ready": true`, `"routing": "portless"`, `"browserConnected": true`, and a genuinely port-free `https://...localhost` `appUrl`. If an active session reports `"routing": "direct"`, stop that retained d3k session and restart after Portless is ready. Do not start a second dev server or browser. -2. If d3k is not installed, install it: +3. If d3k is not installed, install it: ```bash bun install -g dev3000 @@ -35,10 +49,10 @@ bun install -g dev3000 Use `npm install -g dev3000` only when Bun is unavailable. -3. Start d3k with the agent's shell/process tool as a retained background or yielded session: +4. Start d3k with the agent's shell/process tool as a retained background or yielded session (non-TUI by default): ```bash -d3k --no-agent --no-tui -t +d3k -t ``` Do not wait for this long-running command to exit. Keep its process/session handle so you can monitor or stop it later. Prefer the execution tool's background/session support over shelling with `&`. @@ -46,18 +60,18 @@ Do not wait for this long-running command to exit. Keep its process/session hand If the target URL is already known, pass it so the managed browser opens there: ```bash -d3k --no-agent --no-tui -t --app-url "" +d3k -t --app-url "" ``` Let d3k auto-detect the package manager, dev command, and port. Add `--command`, `--script`, or `--port` only when detection is wrong or the user specified them. -4. Poll until the runtime is ready: +5. Poll until the runtime is ready: ```bash d3k status --json ``` -A successful status response is the readiness boundary. Prefer the reported Portless `appUrl`; the underlying app port may change between runs. If startup fails, inspect the retained process output and `d3k logs --type server`; do not launch a separate dev server. +A successful status response is the readiness boundary only when it reports `"ready": true`, `"routing": "portless"`, `"browserConnected": true`, and a port-free `https://...localhost` `appUrl`. The underlying app port may change between runs. If startup fails, inspect the retained process output and `d3k logs --type server`; do not launch a separate dev server. ## User-Driven Testing @@ -125,4 +139,4 @@ Use `--headless` only for CI or when explicitly requested. Use `--servers-only` - Keep d3k alive across edits and retests. - Preserve the project-stable Chrome profile unless the user explicitly asks for a fresh profile. - Leave the runtime running when handing a headed browser to the user; stop it only when asked or when the task requires a clean restart. -- Portless routing is the default. Use `--no-portless` or `PORTLESS=0` only when direct localhost routing is explicitly required. +- Canonical Portless HTTPS routing is required by default. Never add `--no-portless` or set `PORTLESS=0` unless the user explicitly requests direct localhost routing. diff --git a/plugins/dev3000/agent/skills/d3k/SKILL.md b/plugins/dev3000/agent/skills/d3k/SKILL.md index 511c3ec0..43118331 100644 --- a/plugins/dev3000/agent/skills/d3k/SKILL.md +++ b/plugins/dev3000/agent/skills/d3k/SKILL.md @@ -17,15 +17,29 @@ When this skill triggers, operate d3k. Do not merely tell the user how to run it Run from the project root. -1. Check for an existing project runtime: +1. Check canonical Portless readiness before starting any app process: + +```bash +d3k portless status --json +``` + +If `"setupRequired": true`, run: + +```bash +d3k portless setup +``` + +On macOS this opens the system administrator authorization dialog. Wait for the user to approve it, then rerun `d3k portless status --json`. Do not start d3k until `"canonical": true`, `"serviceInstalled": true`, and `"setupRequired": false`. If authorization is declined or unavailable, stop and explain the blocker. Never silently substitute direct localhost. + +2. Check for an existing project runtime: ```bash d3k status --json ``` -If it reports `"running": true`, reuse it. Do not start a second dev server or browser. +Reuse it only if it reports `"running": true`, `"ready": true`, `"routing": "portless"`, `"browserConnected": true`, and a genuinely port-free `https://...localhost` `appUrl`. If an active session reports `"routing": "direct"`, stop that retained d3k session and restart after Portless is ready. Do not start a second dev server or browser. -2. If d3k is not installed, install it: +3. If d3k is not installed, install it: ```bash bun install -g dev3000 @@ -33,10 +47,10 @@ bun install -g dev3000 Use `npm install -g dev3000` only when Bun is unavailable. -3. Start d3k with the agent's shell/process tool as a retained background or yielded session: +4. Start d3k with the agent's shell/process tool as a retained background or yielded session (non-TUI by default): ```bash -d3k --no-agent --no-tui -t +d3k -t ``` Do not wait for this long-running command to exit. Keep its process/session handle so you can monitor or stop it later. Prefer the execution tool's background/session support over shelling with `&`. @@ -44,18 +58,18 @@ Do not wait for this long-running command to exit. Keep its process/session hand If the target URL is already known, pass it so the managed browser opens there: ```bash -d3k --no-agent --no-tui -t --app-url "" +d3k -t --app-url "" ``` Let d3k auto-detect the package manager, dev command, and port. Add `--command`, `--script`, or `--port` only when detection is wrong or the user specified them. -4. Poll until the runtime is ready: +5. Poll until the runtime is ready: ```bash d3k status --json ``` -A successful status response is the readiness boundary. Prefer the reported Portless `appUrl`; the underlying app port may change between runs. If startup fails, inspect the retained process output and `d3k logs --type server`; do not launch a separate dev server. +A successful status response is the readiness boundary only when it reports `"ready": true`, `"routing": "portless"`, `"browserConnected": true`, and a port-free `https://...localhost` `appUrl`. The underlying app port may change between runs. If startup fails, inspect the retained process output and `d3k logs --type server`; do not launch a separate dev server. ## User-Driven Testing @@ -123,4 +137,4 @@ Use `--headless` only for CI or when explicitly requested. Use `--servers-only` - Keep d3k alive across edits and retests. - Preserve the project-stable Chrome profile unless the user explicitly asks for a fresh profile. - Leave the runtime running when handing a headed browser to the user; stop it only when asked or when the task requires a clean restart. -- Portless routing is the default. Use `--no-portless` or `PORTLESS=0` only when direct localhost routing is explicitly required. +- Canonical Portless HTTPS routing is required by default. Never add `--no-portless` or set `PORTLESS=0` unless the user explicitly requests direct localhost routing. diff --git a/plugins/dev3000/skills-lock.json b/plugins/dev3000/skills-lock.json index f7d3f718..fda32d3b 100644 --- a/plugins/dev3000/skills-lock.json +++ b/plugins/dev3000/skills-lock.json @@ -5,7 +5,7 @@ "source": "vercel-labs/dev3000", "sourceType": "github", "skillPath": "skills/d3k/SKILL.md", - "computedHash": "6462ff3b28aaec3ba02f8419e4a6faace3ccb69165bf0da8f55a7da93393e2d3" + "computedHash": "b8604d8dcb3136f9d04a3a0ec02962380d0260c7b7ffc321ca90e8d5569e2b8d" } } } diff --git a/plugins/emulate/.agents/skills/emulate/SKILL.md b/plugins/emulate/.agents/skills/emulate/SKILL.md index 81a5e999..d6f1bde3 100644 --- a/plugins/emulate/.agents/skills/emulate/SKILL.md +++ b/plugins/emulate/.agents/skills/emulate/SKILL.md @@ -99,6 +99,8 @@ await github.close() await vercel.close() ``` +For GitHub App tests, inspect secret-free minted installation-token metadata at `GET /_emulate/installation-tokens`. + ### Options | Option | Default | Description | @@ -386,7 +388,7 @@ const kvAdapter: PersistenceAdapter = { } ``` -State is loaded on cold start and saved after every mutating request (POST, PUT, PATCH, DELETE). Saves are serialized to prevent race conditions. +State is loaded on cold start and saved after every mutating request (POST, PUT, PATCH, DELETE). Saves are serialized to prevent race conditions. Generated GitHub App identities require `initialize` to atomically create the initial value or return the value another instance created first. ## Architecture diff --git a/plugins/emulate/.agents/skills/github/SKILL.md b/plugins/emulate/.agents/skills/github/SKILL.md index 6a9322a6..bae6a9e2 100644 --- a/plugins/emulate/.agents/skills/github/SKILL.md +++ b/plugins/emulate/.agents/skills/github/SKILL.md @@ -53,6 +53,8 @@ const privateKey = github.generatedSecrets.find( The key remains stable across `github.reset()`. Explicit keys are not included in `generatedSecrets`. +The Next.js and Nuxt adapters also generate omitted keys. Retain the returned handler and call its server-only `generatedSecrets()` method. With persistence configured, the identity survives cold starts. Keep snapshots private because they contain the signing key, and require custom persistence backends to implement atomic `initialize()` semantics. + For the CLI, omit `private_key` only when requesting a private delivery file: ```bash @@ -608,8 +610,13 @@ curl -X POST $BASE/app/installations/100/access_tokens \ # 3. Use the installation token to call API endpoints curl $BASE/repos/my-org/org-repo \ -H "Authorization: Bearer ghs_..." + +# Inspect minted installation-token metadata without token values +curl $BASE/_emulate/installation-tokens ``` +The inspection route is emulator-specific. It lists App, installation, account, permissions, repository access, issuance, expiry, and lifecycle status without accepting or returning token values or token-derived identifiers. Expiry is informational and does not change authorization behavior. + ### OAuth Flow 1. Redirect user to `$GITHUB_EMULATOR_URL/login/oauth/authorize?client_id=...&redirect_uri=...&scope=user+repo&state=...` diff --git a/plugins/emulate/.agents/skills/linear/SKILL.md b/plugins/emulate/.agents/skills/linear/SKILL.md index 55660387..2511a752 100644 --- a/plugins/emulate/.agents/skills/linear/SKILL.md +++ b/plugins/emulate/.agents/skills/linear/SKILL.md @@ -103,6 +103,8 @@ Supported mutations: Connections use Relay-style cursors with `nodes`, `edges`, and `pageInfo`. +Issue selections expose both numeric `priority` and Linear's derived `priorityLabel` values: `No priority`, `Urgent`, `High`, `Medium`, and `Low`. + ## OAuth - `GET /oauth/authorize` - authorization endpoint with local user picker diff --git a/plugins/emulate/.agents/skills/next/SKILL.md b/plugins/emulate/.agents/skills/next/SKILL.md index ce6ac745..e7ce45da 100644 --- a/plugins/emulate/.agents/skills/next/SKILL.md +++ b/plugins/emulate/.agents/skills/next/SKILL.md @@ -50,6 +50,8 @@ This creates the following routes: - `/emulate/github/**` serves the GitHub emulator - `/emulate/google/**` serves the Google emulator +GitHub installation-token metadata is available server-side at `/emulate/github/_emulate/installation-tokens`. + ## Auth.js / NextAuth Configuration Point your provider at the emulator paths on the same origin: @@ -123,6 +125,8 @@ import { filePersistence } from '@emulators/core' persistence: filePersistence('.emulate/state.json'), ``` +GitHub App seeds may omit `private_key`. Retain the handler and call server-only `generatedSecrets()`; explicit keys are excluded. Keep persisted snapshots private and implement `initialize` atomically. + ### How Persistence Works - **Cold start**: The adapter loads state from the persistence adapter. If found, it restores the full Store and token map (skipping seed). If not found, it seeds from config and saves the initial state. @@ -140,7 +144,7 @@ persistence: filePersistence('.emulate/state.json'), ## Limitations - Requires the Node.js runtime (not Edge) since emulators use `crypto.randomBytes` -- Concurrent serverless instances writing to the same persistence adapter use last-write-wins semantics (acceptable for dev/preview traffic) +- Concurrent mutations use last-write-wins semantics. Generated identities require `initialize` to select the initial snapshot atomically across cold starts. ## Config Reference @@ -172,7 +176,8 @@ Wraps a Next.js config to include emulator font files in the serverless output t interface PersistenceAdapter { load(): Promise save(data: string): Promise + initialize?(data: string): Promise } ``` -The built-in `filePersistence(path)` from `@emulators/core` provides a file-based adapter for local development. +`initialize` must atomically create the initial value or return the value another instance created first. Implement it with compare-and-set semantics such as Redis `SET NX`. The built-in `filePersistence(path)` from `@emulators/core` provides this behavior for local development. diff --git a/plugins/emulate/agent/skills/emulate/SKILL.md b/plugins/emulate/agent/skills/emulate/SKILL.md index fb5b8ab5..4ca47466 100644 --- a/plugins/emulate/agent/skills/emulate/SKILL.md +++ b/plugins/emulate/agent/skills/emulate/SKILL.md @@ -96,6 +96,8 @@ await github.close() await vercel.close() ``` +For GitHub App tests, inspect secret-free minted installation-token metadata at `GET /_emulate/installation-tokens`. + ### Options | Option | Default | Description | @@ -383,7 +385,7 @@ const kvAdapter: PersistenceAdapter = { } ``` -State is loaded on cold start and saved after every mutating request (POST, PUT, PATCH, DELETE). Saves are serialized to prevent race conditions. +State is loaded on cold start and saved after every mutating request (POST, PUT, PATCH, DELETE). Saves are serialized to prevent race conditions. Generated GitHub App identities require `initialize` to atomically create the initial value or return the value another instance created first. ## Architecture diff --git a/plugins/emulate/agent/skills/github/SKILL.md b/plugins/emulate/agent/skills/github/SKILL.md index 2f789777..a6638f13 100644 --- a/plugins/emulate/agent/skills/github/SKILL.md +++ b/plugins/emulate/agent/skills/github/SKILL.md @@ -50,6 +50,8 @@ const privateKey = github.generatedSecrets.find( The key remains stable across `github.reset()`. Explicit keys are not included in `generatedSecrets`. +The Next.js and Nuxt adapters also generate omitted keys. Retain the returned handler and call its server-only `generatedSecrets()` method. With persistence configured, the identity survives cold starts. Keep snapshots private because they contain the signing key, and require custom persistence backends to implement atomic `initialize()` semantics. + For the CLI, omit `private_key` only when requesting a private delivery file: ```bash @@ -605,8 +607,13 @@ curl -X POST $BASE/app/installations/100/access_tokens \ # 3. Use the installation token to call API endpoints curl $BASE/repos/my-org/org-repo \ -H "Authorization: Bearer ghs_..." + +# Inspect minted installation-token metadata without token values +curl $BASE/_emulate/installation-tokens ``` +The inspection route is emulator-specific. It lists App, installation, account, permissions, repository access, issuance, expiry, and lifecycle status without accepting or returning token values or token-derived identifiers. Expiry is informational and does not change authorization behavior. + ### OAuth Flow 1. Redirect user to `$GITHUB_EMULATOR_URL/login/oauth/authorize?client_id=...&redirect_uri=...&scope=user+repo&state=...` diff --git a/plugins/emulate/agent/skills/linear/SKILL.md b/plugins/emulate/agent/skills/linear/SKILL.md index d52ddc5b..69a732b6 100644 --- a/plugins/emulate/agent/skills/linear/SKILL.md +++ b/plugins/emulate/agent/skills/linear/SKILL.md @@ -100,6 +100,8 @@ Supported mutations: Connections use Relay-style cursors with `nodes`, `edges`, and `pageInfo`. +Issue selections expose both numeric `priority` and Linear's derived `priorityLabel` values: `No priority`, `Urgent`, `High`, `Medium`, and `Low`. + ## OAuth - `GET /oauth/authorize` - authorization endpoint with local user picker diff --git a/plugins/emulate/agent/skills/next/SKILL.md b/plugins/emulate/agent/skills/next/SKILL.md index 87215212..4729fd5f 100644 --- a/plugins/emulate/agent/skills/next/SKILL.md +++ b/plugins/emulate/agent/skills/next/SKILL.md @@ -47,6 +47,8 @@ This creates the following routes: - `/emulate/github/**` serves the GitHub emulator - `/emulate/google/**` serves the Google emulator +GitHub installation-token metadata is available server-side at `/emulate/github/_emulate/installation-tokens`. + ## Auth.js / NextAuth Configuration Point your provider at the emulator paths on the same origin: @@ -120,6 +122,8 @@ import { filePersistence } from '@emulators/core' persistence: filePersistence('.emulate/state.json'), ``` +GitHub App seeds may omit `private_key`. Retain the handler and call server-only `generatedSecrets()`; explicit keys are excluded. Keep persisted snapshots private and implement `initialize` atomically. + ### How Persistence Works - **Cold start**: The adapter loads state from the persistence adapter. If found, it restores the full Store and token map (skipping seed). If not found, it seeds from config and saves the initial state. @@ -137,7 +141,7 @@ persistence: filePersistence('.emulate/state.json'), ## Limitations - Requires the Node.js runtime (not Edge) since emulators use `crypto.randomBytes` -- Concurrent serverless instances writing to the same persistence adapter use last-write-wins semantics (acceptable for dev/preview traffic) +- Concurrent mutations use last-write-wins semantics. Generated identities require `initialize` to select the initial snapshot atomically across cold starts. ## Config Reference @@ -169,7 +173,8 @@ Wraps a Next.js config to include emulator font files in the serverless output t interface PersistenceAdapter { load(): Promise save(data: string): Promise + initialize?(data: string): Promise } ``` -The built-in `filePersistence(path)` from `@emulators/core` provides a file-based adapter for local development. +`initialize` must atomically create the initial value or return the value another instance created first. Implement it with compare-and-set semantics such as Redis `SET NX`. The built-in `filePersistence(path)` from `@emulators/core` provides this behavior for local development. diff --git a/plugins/emulate/skills-lock.json b/plugins/emulate/skills-lock.json index 3fedfa86..9cd9b646 100644 --- a/plugins/emulate/skills-lock.json +++ b/plugins/emulate/skills-lock.json @@ -17,13 +17,13 @@ "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/emulate/SKILL.md", - "computedHash": "9f490bb96968516335972dc0df78fcd7b75025aab169a6bea24c2f9f4e63987e" + "computedHash": "613dbaaa41f1f5f3be1c7916d353a23137427a49640ca15b782c7bd642cdca44" }, "github": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/github/SKILL.md", - "computedHash": "ae3920ffad035acdd71b0764015bc9401223197788092efb0be6a5edb7210fa6" + "computedHash": "cdfd3d73237fffc766e92011f5e930b75a407dd5b274b82601a70141d5302dc9" }, "google": { "source": "vercel-labs/emulate", @@ -35,7 +35,7 @@ "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/linear/SKILL.md", - "computedHash": "74f2ae3e6d6d7749bfb25f538c7d678cd0bb13a22407df80bf4a2c288c45565e" + "computedHash": "0959a13f8b49f4a32bcf3910a57a0c271b4ee1a68de9d35e44e5850ddbb64cb6" }, "microsoft": { "source": "vercel-labs/emulate", @@ -47,7 +47,7 @@ "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/next/SKILL.md", - "computedHash": "105094c96ab29d6e081554c77bf30fe4393a81d91ddca385f215e09899af8a48" + "computedHash": "6d9b668e22b6559d7f324d2c479b383716640013b095aca144c891669a00f519" }, "resend": { "source": "vercel-labs/emulate", diff --git a/plugins/eve/.claude/skills/eve b/plugins/eve/.claude/skills/eve new file mode 120000 index 00000000..c1d54684 --- /dev/null +++ b/plugins/eve/.claude/skills/eve @@ -0,0 +1 @@ +../../.agents/skills/eve \ No newline at end of file diff --git a/plugins/eve/skills-lock.json b/plugins/eve/skills-lock.json index 09d01e1a..79f6134d 100644 --- a/plugins/eve/skills-lock.json +++ b/plugins/eve/skills-lock.json @@ -4,8 +4,8 @@ "eve": { "source": "vercel/eve", "sourceType": "github", - "skillPath": "SKILL.md", - "computedHash": "79a1e606ecf33eed33c1f3adc1cc266de391e19aa9237ca37e4f3aa3fba6b228" + "skillPath": "skills/eve/SKILL.md", + "computedHash": "4833349453c45c606f79202e495b7bbc42ea380f329f3beee36b9927763f19ef" } } } diff --git a/plugins/portless/.agents/skills/portless/SKILL.md b/plugins/portless/.agents/skills/portless/SKILL.md index 1e528ee0..a8aed422 100644 --- a/plugins/portless/.agents/skills/portless/SKILL.md +++ b/plugins/portless/.agents/skills/portless/SKILL.md @@ -155,6 +155,10 @@ Set `PORTLESS=0` to run the command directly without the proxy: PORTLESS=0 pnpm dev # Bypasses proxy, uses default port ``` +When a proxied command is stopped with Ctrl+C, portless waits for its process tree to exit. A second +Ctrl+C forwards another interrupt, and remaining descendants are terminated after a short grace +period. + ## How It Works 1. `portless proxy start` starts an HTTPS reverse proxy on port 443 as a background daemon. Auto-elevates with sudo on macOS/Linux; falls back to port 1355 if sudo is unavailable. Use `--no-tls` for plain HTTP on port 80. Configurable with `-p` / `--port` or the `PORTLESS_PORT` env var. The proxy also auto-starts when you run an app. @@ -411,7 +415,7 @@ portless hosts sync # Adds current routes to /etc/hosts portless hosts clean # Remove entries later ``` -Auto-syncs `/etc/hosts` for route hostnames by default. Set `PORTLESS_SYNC_HOSTS=0` to disable. +Auto-syncs `/etc/hosts` for route hostnames by default. Set `PORTLESS_SYNC_HOSTS=0` to disable. If a route hostname will not resolve, the command that registered it warns and points you to `portless hosts sync`. ### Browser shows certificate warning with --https diff --git a/plugins/portless/agent/skills/portless/SKILL.md b/plugins/portless/agent/skills/portless/SKILL.md index dda0dda7..2beba3d2 100644 --- a/plugins/portless/agent/skills/portless/SKILL.md +++ b/plugins/portless/agent/skills/portless/SKILL.md @@ -153,6 +153,10 @@ Set `PORTLESS=0` to run the command directly without the proxy: PORTLESS=0 pnpm dev # Bypasses proxy, uses default port ``` +When a proxied command is stopped with Ctrl+C, portless waits for its process tree to exit. A second +Ctrl+C forwards another interrupt, and remaining descendants are terminated after a short grace +period. + ## How It Works 1. `portless proxy start` starts an HTTPS reverse proxy on port 443 as a background daemon. Auto-elevates with sudo on macOS/Linux; falls back to port 1355 if sudo is unavailable. Use `--no-tls` for plain HTTP on port 80. Configurable with `-p` / `--port` or the `PORTLESS_PORT` env var. The proxy also auto-starts when you run an app. @@ -409,7 +413,7 @@ portless hosts sync # Adds current routes to /etc/hosts portless hosts clean # Remove entries later ``` -Auto-syncs `/etc/hosts` for route hostnames by default. Set `PORTLESS_SYNC_HOSTS=0` to disable. +Auto-syncs `/etc/hosts` for route hostnames by default. Set `PORTLESS_SYNC_HOSTS=0` to disable. If a route hostname will not resolve, the command that registered it warns and points you to `portless hosts sync`. ### Browser shows certificate warning with --https diff --git a/plugins/portless/skills-lock.json b/plugins/portless/skills-lock.json index 2f82146a..f6edef59 100644 --- a/plugins/portless/skills-lock.json +++ b/plugins/portless/skills-lock.json @@ -5,7 +5,7 @@ "source": "vercel-labs/portless", "sourceType": "github", "skillPath": "skills/portless/SKILL.md", - "computedHash": "4dd2cfba1475a35878d3936d4ce80047b800f7d7f65c9c6ddccb4638651d66a5" + "computedHash": "3554b1c4b77a327e87dfa1fafe8fec5136c19f5adfb9803a412aab7fd6c16e05" } } } diff --git a/plugins/react-native/.agents/skills/vercel-react-native-skills/metadata.json b/plugins/react-native/.agents/skills/vercel-react-native-skills/metadata.json deleted file mode 100644 index 600eb5bc..00000000 --- a/plugins/react-native/.agents/skills/vercel-react-native-skills/metadata.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "1.0.0", - "organization": "Engineering", - "date": "January 2026", - "abstract": "Comprehensive performance optimization guide for React Native applications, designed for AI agents and LLMs. Contains 35+ rules across 13 categories, prioritized by impact from critical (core rendering, list performance) to incremental (fonts, imports). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.", - "references": [ - "https://react.dev", - "https://reactnative.dev", - "https://docs.swmansion.com/react-native-reanimated", - "https://docs.swmansion.com/react-native-gesture-handler", - "https://docs.expo.dev", - "https://legendapp.com/open-source/legend-list", - "https://github.com/nandorojo/galeria", - "https://zeego.dev" - ] -} diff --git a/plugins/react-native/agent/skills/vercel-react-native-skills/metadata.json b/plugins/react-native/agent/skills/vercel-react-native-skills/metadata.json deleted file mode 100644 index 600eb5bc..00000000 --- a/plugins/react-native/agent/skills/vercel-react-native-skills/metadata.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "1.0.0", - "organization": "Engineering", - "date": "January 2026", - "abstract": "Comprehensive performance optimization guide for React Native applications, designed for AI agents and LLMs. Contains 35+ rules across 13 categories, prioritized by impact from critical (core rendering, list performance) to incremental (fonts, imports). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.", - "references": [ - "https://react.dev", - "https://reactnative.dev", - "https://docs.swmansion.com/react-native-reanimated", - "https://docs.swmansion.com/react-native-gesture-handler", - "https://docs.expo.dev", - "https://legendapp.com/open-source/legend-list", - "https://github.com/nandorojo/galeria", - "https://zeego.dev" - ] -} diff --git a/plugins/react-native/skills-lock.json b/plugins/react-native/skills-lock.json index 14674bd4..8545c572 100644 --- a/plugins/react-native/skills-lock.json +++ b/plugins/react-native/skills-lock.json @@ -5,7 +5,7 @@ "source": "vercel-labs/agent-skills", "sourceType": "github", "skillPath": "skills/react-native-skills/SKILL.md", - "computedHash": "41d24eafa7c3d82e270439808f7cfbc4d51aeb2d14f2809a2267c16275784d06" + "computedHash": "2e9088a7333666d8c2833b8ff58bd51b955501c42b4c7244f72b4cbf22dafcc4" } } } diff --git a/plugins/react/.agents/skills/vercel-composition-patterns/metadata.json b/plugins/react/.agents/skills/vercel-composition-patterns/metadata.json deleted file mode 100644 index 3470b744..00000000 --- a/plugins/react/.agents/skills/vercel-composition-patterns/metadata.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "1.0.0", - "organization": "Engineering", - "date": "January 2026", - "abstract": "Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.", - "references": [ - "https://react.dev", - "https://react.dev/learn/passing-data-deeply-with-context", - "https://react.dev/reference/react/use" - ] -} diff --git a/plugins/react/.agents/skills/vercel-react-best-practices/metadata.json b/plugins/react/.agents/skills/vercel-react-best-practices/metadata.json deleted file mode 100644 index 3bec38b1..00000000 --- a/plugins/react/.agents/skills/vercel-react-best-practices/metadata.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": "1.0.0", - "organization": "Vercel Engineering", - "date": "January 2026", - "abstract": "Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.", - "references": [ - "https://react.dev", - "https://nextjs.org", - "https://swr.vercel.app", - "https://github.com/shuding/better-all", - "https://github.com/isaacs/node-lru-cache", - "https://vercel.com/blog/how-we-optimized-package-imports-in-next-js", - "https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast" - ] -} diff --git a/plugins/react/.agents/skills/vercel-react-view-transitions/AGENTS.md b/plugins/react/.agents/skills/vercel-react-view-transitions/AGENTS.md index 8e213614..ad47782c 100644 --- a/plugins/react/.agents/skills/vercel-react-view-transitions/AGENTS.md +++ b/plugins/react/.agents/skills/vercel-react-view-transitions/AGENTS.md @@ -2,47 +2,11 @@ **Version 1.0.0** Vercel Engineering -March 2026 > **Note:** -> This document is mainly for agents and LLMs to follow when implementing -> view transitions in React applications. Humans may also find it useful, -> but guidance here is optimized for automation and consistency by -> AI-assisted workflows. - ---- - -## Abstract - -Guide for implementing smooth, native-feeling animations using React's View Transition API. Covers the `` component, `addTransitionType`, CSS view transition pseudo-elements, shared element transitions, Suspense reveals, list reorder, directional navigation, and Next.js integration. Includes a step-by-step implementation workflow, ready-to-use CSS animation recipes, and common mistake warnings. - ---- - -## Table of Contents - -1. [Core Reference](#when-to-animate) - - [When to Animate](#when-to-animate) - - [Availability](#availability) - - [Core Concepts](#core-concepts) - - [Styling with View Transition Classes](#styling-with-view-transition-classes) - - [Transition Types](#transition-types) - - [Shared Element Transitions](#shared-element-transitions) - - [Common Patterns](#common-patterns) - - [How Multiple VTs Interact](#how-multiple-vts-interact) - - [Next.js Integration](#nextjs-integration) - - [Accessibility](#accessibility) -2. [Implementation Workflow](#implementation-workflow) - - [Step 1: Audit the App](#step-1-audit-the-app) - - [Step 2: Add CSS Recipes](#step-2-add-css-recipes) - - [Step 3: Isolate Persistent Elements](#step-3-isolate-persistent-elements) - - [Step 4: Add Directional Page Transitions](#step-4-add-directional-page-transitions) - - [Step 5: Add Suspense Reveals](#step-5-add-suspense-reveals) - - [Step 6: Add Shared Element Transitions](#step-6-add-shared-element-transitions) - - [Step 7: Verify Each Navigation Path](#step-7-verify-each-navigation-path) - - [Common Mistakes](#common-mistakes) -3. [Patterns and Guidelines](#patterns-and-guidelines) -4. [CSS Animation Recipes](#css-animation-recipes) -5. [View Transitions in Next.js](#view-transitions-in-nextjs) +> This standalone guide is compiled from `SKILL.md` and its reference files +> for agents that consume a single `AGENTS.md` document. Edit the source +> files, not this compiled copy. --- @@ -60,7 +24,7 @@ Implement **all** applicable patterns from this list, in this order: | 2 | **Suspense reveal** | "Data loaded" | | 3 | **List identity** (per-item `key`) | "Same items, new arrangement" | | 4 | **State change** (`enter`/`exit`) | "Something appeared/disappeared" | -| 5 | **Route change** (layout-level) | "Going to a new place" | +| 5 | **Route change** (page-level) | "Going to a new place" | This is an implementation order, not a "pick one" list. Implement every pattern that fits the app. Only skip a pattern if the app has no use case for it. @@ -81,7 +45,13 @@ Reserve directional slides for hierarchical navigation (list → detail) and ord - **Next.js:** Do **not** install `react@canary` — the App Router already bundles React canary internally. `ViewTransition` works out of the box. `npm ls react` may show a stable-looking version; this is expected. - **Without Next.js:** Install `react@canary react-dom@canary` (`ViewTransition` is not in stable React). -- Browser support: Chromium 111+, Firefox 144+, Safari 18.2+. Graceful degradation. +- Browser support: Chromium 125+ (React needs the v2 object form of `startViewTransition`), Firefox 144+, Safari 18.2+. Graceful degradation on unsupported browsers. + +--- + +## Implementation Workflow + +When adding view transitions to an existing app, **follow [references/implementation.md](references/implementation.md) step by step.** Start with the audit — do not skip it. Use [references/css-recipes.md](references/css-recipes.md) for the applicable CSS and adapt it to the app. --- @@ -103,29 +73,37 @@ React auto-assigns a unique `view-transition-name` and calls `document.startView | Trigger | When it fires | |---------|--------------| -| **enter** | VT first inserted during a Transition | -| **exit** | VT first removed during a Transition | -| **update** | DOM mutations inside a VT. With nested VTs, mutation applies to the innermost one | -| **share** | Named VT unmounts and another with same `name` mounts in same Transition | +| **enter** | `` first inserted during a Transition | +| **exit** | `` first removed during a Transition | +| **update** | DOM mutations inside a ``, or the boundary itself changing size/position due to an immediate sibling. With nested VTs, mutation applies to the innermost one | +| **share** | Named VT unmounts and another with same `name` mounts in the same Transition | Only `startTransition`, `useDeferredValue`, or `Suspense` activate VTs. Regular `setState` does not animate. ### Critical Placement Rule -VT only activates enter/exit if it appears **before any DOM nodes**: +`` only activates enter/exit if it appears **before any DOM nodes**: ```jsx // Works -
Content
+ +
Content
+
-// Broken — div wraps the VT -
Content
+// Broken — div wraps the VT, suppressing enter/exit +
+ +
Content
+
+
``` --- ## Styling with View Transition Classes +### Props + Values: `"auto"` (browser cross-fade), `"none"` (disabled), `"class-name"` (custom CSS), or `{ [type]: value }` for type-specific animations. ```jsx @@ -141,11 +119,13 @@ If `default` is `"none"`, all triggers are off unless explicitly listed. - `::view-transition-group(.class)` — container - `::view-transition-image-pair(.class)` — old + new pair +See [references/css-recipes.md](references/css-recipes.md) for ready-to-use animation recipes. + --- ## Transition Types -Tag transitions with `addTransitionType` so VTs can pick different animations. Call it multiple times to stack types — different VTs in the tree react to different types: +Tag transitions with `addTransitionType` so VTs can pick different animations based on context. Call it multiple times to stack types — different VTs in the tree react to different types: ```jsx startTransition(() => { @@ -155,7 +135,7 @@ startTransition(() => { }); ``` -Map types to CSS classes. Works on `enter`, `exit`, **and** `share`: +Pass an object to map types to CSS classes. Works on `enter`, `exit`, **and** `share`: ```jsx ``` -**TypeScript:** `ViewTransitionClassPerType` requires a `default` key. +**TypeScript:** `ViewTransitionClassPerType` requires a `default` key in the object. + +For apps with multiple pages, extract the type-keyed VT into a reusable wrapper: + +```jsx +export function DirectionalTransition({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} +``` ### `router.back()` and Browser Back Button -`router.back()` and the browser's back/forward buttons do **not** trigger view transitions (`popstate` is synchronous, incompatible with `startViewTransition`). Use `router.push()` with an explicit URL instead. +`router.back()` and the browser's back/forward buttons carry **no transition types**, so type-keyed animations (directional slides) resolve to their `default` and don't play — untyped shared-element morphs still apply. For typed animations, use `router.push()` with an explicit URL. ### Types and Suspense Types are available during navigation but **not** during subsequent Suspense reveals (separate transitions, no type). Use type maps for page-level enter/exit; use simple string props for Suspense reveals. +### Shared Element Readiness + +A shared element transition can pair elements only when both the old and new views are rendered in the same Transition. If incoming content suspends, only its fallback exists for that update; the resolved content appears in a later Suspense transition and can be animated separately. + --- ## Shared Element Transitions @@ -199,15 +199,16 @@ Same `name` on two VTs — one unmounting, one mounting — creates a shared ele startTransition(() => onSelect())} /> -// Other view — same name +// On the other view — same name ``` -- Only one VT with a given `name` can be mounted at a time — use unique names. Watch for reusable components: if a component with a named VT is rendered in both a modal/popover *and* a page, both mount simultaneously and break the morph. Either make the name conditional (via a prop) or move the named VT out of the shared component into the specific consumer. -- `share` takes precedence over `enter`/`exit`. Think through each navigation path: when no pair forms, `enter`/`exit` fires instead. Consider whether the element needs a fallback animation for those paths. -- Never use fade-out exit on pages with shared morphs — use directional slide. +- Only one VT with a given `name` can be mounted at a time — use unique names (`photo-${id}`). Watch for reusable components: if a component with a named VT is rendered in both a modal/popover *and* a page, both mount simultaneously and break the morph. Either make the name conditional (via a prop) or move the named VT out of the shared component into the specific consumer. +- `share` takes precedence over `enter`/`exit`. Think through each navigation path: when no matching pair forms (e.g., the target page doesn't have the same name), `enter`/`exit` fires instead. Consider whether the element needs a fallback animation for those paths. +- Two ways a wired-up morph silently never fires: (1) `default="none"` with no explicit `share` prop — share resolves to none; (2) type-keyed `share` where the navigation never adds the type — a plain link click resolves the map's `default`. Every link that should morph must add the type (`transitionTypes` on `next/link`, or `addTransitionType`). +- Never use a fade-out exit on pages with shared morphs — use a directional slide instead. --- @@ -231,9 +232,13 @@ Same `name` on two VTs — one unmounting, one mounting — creates a shared ele Trigger inside `startTransition`. Avoid wrapper `
`s between list and VT. +### Layout Displacement Morph + +Only content inside an activated boundary animates position — everything else teleports to its new layout spot. Wrap the sibling content below a growing/shrinking list in a bare `` so it glides instead of jumping. See [Layout Displacement Morph](references/patterns.md#layout-displacement-morph). + ### Composing Shared Elements with List Identity -Shared elements and list identity are independent concerns — don't confuse one for the other. When a list item contains a shared element, use two nested `` boundaries: +Shared elements and list identity are independent concerns — don't confuse one for the other. When a list item contains a shared element (e.g., an image that morphs into a detail view), use two nested `` boundaries: ```jsx {items.map(item => ( @@ -248,7 +253,7 @@ Shared elements and list identity are independent concerns — don't confuse one ))} ``` -The outer VT handles list reorder/enter. The inner VT handles cross-route shared element morph. Missing either layer means that animation silently doesn't happen. +The outer VT handles list reorder/enter animations. The inner VT handles the cross-route shared element morph. Missing either layer means that animation silently doesn't happen. ### Force Re-Enter with `key` @@ -258,7 +263,7 @@ The outer VT handles list reorder/enter. The inner VT handles cross-route shared ``` -**Caution:** Wrapping `` with key remounts the boundary and refetches. +**Caution:** If wrapping ``, changing `key` remounts the boundary and refetches. ### Suspense Fallback to Content @@ -276,15 +281,19 @@ Directional reveal: ``` +For more patterns, see [references/patterns.md](references/patterns.md). + --- ## How Multiple VTs Interact -Every VT matching the trigger fires simultaneously in a single `document.startViewTransition`. VTs in **different** transitions don't compete. +Every VT matching the trigger fires simultaneously in a single `document.startViewTransition`. VTs in **different** transitions (navigation vs later Suspense resolve) don't compete. + +### Use `default="none"` Deliberately -### Use `default="none"` Liberally +Without it, every VT fires the browser cross-fade on **every** transition — Suspense resolves, `useDeferredValue` updates, background revalidations. Use `default="none"` on named/shared elements and type-keyed page VTs. -Without it, every VT fires the browser cross-fade on **every** transition. Always use `default="none"` and explicitly enable only desired triggers. +But it also turns off `update` (layout/reflow morphs) and `share` (a named pair with no explicit `share` prop never morphs). Keyed list items and displaced siblings *want* update — leave them bare or set `update="auto"`. ### Two Patterns Coexist @@ -295,47 +304,48 @@ They coexist because they fire at different moments. `default="none"` on both pr ### Nested VT Limitation -When a parent VT exits, nested VTs inside it do **not** fire their own enter/exit — only the outermost VT animates. Per-item staggered animations during page navigation are not possible today. See [react#36135](https://github.com/facebook/react/pull/36135) for an experimental opt-in fix. +When a parent VT mounts/unmounts **as one unit** with nested VTs inside it, the nested ones do not fire their own enter/exit — only the outermost VT animates. (A child VT mounted inside a *persistent* parent VT fires enter/exit normally.) Per-item staggered animations during page navigation are not currently available in Next.js; see [troubleshooting](references/troubleshooting.md) for the upstream experimental status. --- ## Next.js Integration -See the [View Transitions in Next.js](#view-transitions-in-nextjs) section below. +For Next.js integration (`transitionTypes` on `next/link` and `useRouter`, App Router patterns, Server Components), see [references/nextjs.md](references/nextjs.md). --- ## Accessibility -Always add reduced motion CSS to your global stylesheet: +Always add the reduced motion CSS from [references/css-recipes.md](references/css-recipes.md#reduced-motion) to your global stylesheet. -```css -@media (prefers-reduced-motion: reduce) { - ::view-transition-old(*), - ::view-transition-new(*), - ::view-transition-group(*) { - animation-duration: 0s !important; - animation-delay: 0s !important; - } -} -``` +--- + +## Reference Files + +- **[references/implementation.md](references/implementation.md)** — Step-by-step implementation workflow. +- **[references/patterns.md](references/patterns.md)** — Patterns, animation timing, and events API. +- **[references/troubleshooting.md](references/troubleshooting.md)** — Symptom-driven debugging and runtime limitations. +- **[references/css-recipes.md](references/css-recipes.md)** — Ready-to-use CSS animation recipes. +- **[references/nextjs.md](references/nextjs.md)** — Next.js App Router patterns and Server Component details. --- # Implementation Workflow -**Follow these steps in order.** Start with the audit — do not skip it. Copy the CSS recipes from the CSS Recipes section below — do not write your own animation CSS. +Follow these steps in order when adding view transitions to an app. Each step builds on the previous one. + +Use the official [React `` reference](https://react.dev/reference/react/ViewTransition) and [Next.js guide](https://nextjs.org/docs/app/guides/view-transitions) for API behavior. This file focuses on audit order, integration decisions, and verification. ## Step 1: Audit the App Before writing any code, scan the codebase thoroughly. Search for: -- **Every `` and `router.push`** — open every file that contains one -- **Every `` boundary** — check what its fallback renders -- **Every page/route component** — each needs a VT placement decision -- **Persistent elements** (headers, navbars, sidebars) — need `viewTransitionName` isolation -- **Shared visual elements** on both source and target views -- **Skeleton-to-content control pairs** — if a fallback renders a control that also exists in the real content, both need a matching `viewTransitionName` +- **Every `` and `router.push`** — these are your navigation triggers. Open every file that contains one. +- **Every `` boundary** — each one is a candidate for a reveal animation. Check what its fallback renders. +- **Every page/route component** — list them all. Each page needs a VT placement decision. +- **Persistent elements** — headers, navbars, sidebars, sticky controls that stay on screen across navigations. These need `viewTransitionName` isolation. +- **Shared visual elements** — images, cards, or avatars that appear on both a source and target view (e.g., a thumbnail in a list and the same image on a detail page). +- **Skeleton-to-content control pairs** — if a Suspense fallback renders a control (search input, tab bar) that also exists in the real content, both need a matching `viewTransitionName`. Then classify every navigation and produce a navigation map: @@ -349,29 +359,30 @@ Then classify every navigation and produce a navigation map: | (Suspense) | (content loads) | — | slide-up reveal | ``` -For each shared element (`name` prop), note where a pair forms and where it doesn't — this determines whether you need `enter`/`exit` as a fallback alongside `share`. +For each shared element (`name` prop), note every navigation where a pair forms and where it doesn't — this determines whether you need `enter`/`exit` as a fallback alongside `share`. ## Step 2: Add CSS Recipes -Copy the **complete** CSS recipe set from the CSS Animation Recipes section below into your global stylesheet. Don't write your own — the recipes handle staggered timing, motion blur, and reduced motion. +Choose the animation pattern from the audit and this skill's guidance, then copy only the applicable sections from [css-recipes.md](css-recipes.md). Always include reduced motion. Add live-root, persistent-element, backdrop, or floating-element rules only when the audit found those surfaces. + +Customize timing after the structure works. Keep ordinary crossfades opacity-only; scope blur to a specific shared morph when it is intentional. ## Step 3: Isolate Persistent Elements +For every persistent element identified in Step 1, add a `viewTransitionName` style to pull it out of the page content's transition snapshot: + ```jsx
...
``` -```css -::view-transition-group(site-header) { - animation: none; - z-index: 100; -} -``` +Then add the [Persistent Element Isolation](css-recipes.md#persistent-element-isolation) CSS (prevents the element from animating during page transitions). If the element uses `backdrop-blur` or `backdrop-filter`, use the [Backdrop-Blur Workaround](css-recipes.md#backdrop-blur-workaround) instead. -For `backdrop-blur`/`backdrop-filter`, use the backdrop-blur workaround instead. +If a Suspense fallback mirrors a persistent control (e.g., a skeleton search input), give both the real control and the skeleton the same `viewTransitionName` so they morph in place. ## Step 4: Add Directional Page Transitions +For hierarchical navigations identified in Step 1, tag the navigation direction using `addTransitionType` inside `startTransition`: + ```jsx startTransition(() => { addTransitionType('nav-forward'); @@ -379,19 +390,29 @@ startTransition(() => { }); ``` -Wrap each **page component** (not layout) in a type-keyed VT: +Then wrap each **page component** (not layout) in a type-keyed ``: ```jsx
...page content...
``` -Extract into a reusable component so every page doesn't repeat the type map: +The `nav-forward` and `nav-back` CSS classes from [Directional Navigation](css-recipes.md#directional-navigation) produce horizontal slides. For simpler apps where directional motion isn't needed, a bare `` wrapper with `enter="fade-in"` / `exit="fade-out"` works too. + +Extract this into a reusable component so every page doesn't repeat the verbose type map: ```jsx export function DirectionalTransition({ children }: { children: React.ReactNode }) { @@ -407,83 +428,89 @@ export function DirectionalTransition({ children }: { children: React.ReactNode } ``` -**Rules:** Always pair `enter` with `exit`. Always include `default: "none"`. Place in page components, not layouts. Only use directional slides for hierarchical navigation or ordered sequences (prev/next). +This also becomes the single place to adjust if you add new transition types later. + +**Rules:** +- Always pair `enter` with `exit` — without an exit animation, the old page disappears instantly while the new one animates in. +- Always include `default: "none"` in type map objects and `default="none"` on the component — otherwise it fires on every transition. +- Place the directional `` in each page component, not in a layout. Layouts persist across navigations and never trigger enter/exit. +- Only use directional slides for hierarchical navigation or ordered sequences (prev/next). Lateral/sibling navigation (tab-to-tab) should use a bare `` (cross-fade) or `default="none"`. ## Step 5: Add Suspense Reveals +For every `` boundary identified in Step 1, wrap the fallback and content in separate ``s: + ```jsx -}> - + + + + } +> + + +
``` -Use `default="none"` on content VT. Use simple string props (not type maps) — Suspense resolves have no type. +This example uses `slide-down` / `slide-up` for directional vertical motion. For a simpler reveal, a bare `` around the `` gives a cross-fade with zero configuration. Choose based on the spatial meaning described in the main skill. + +**Rules:** +- Always use `default="none"` on the content `` to prevent re-animation on revalidation or unrelated transitions. +- Use simple string props (not type maps) on Suspense ``s — Suspense resolves fire as separate transitions with no type, so type-keyed props won't match. +- A fallback/content `share` pair morphs between snapshots. Use it only when that interpolation is desired and does not distort layout or geometry. +- If the same element appears in **both** the fallback and the content (a title, a heading), it flickers on reveal — an opacity dip. Render it **outside** the `` boundary (or pin it), so it isn't in both. See [Suspense reveal flicker](patterns.md#suspense-reveal-flicker). ## Step 6: Add Shared Element Transitions +For every shared visual element identified in Step 1, add matching named `` wrappers on both the source and target views: + ```jsx -// Source view +// On the source view (e.g., list/grid page) -// Target view — same name +// On the target view (e.g., detail page) — same name ``` -When list items contain shared elements, compose both patterns — two independent layers: - -```jsx -{items.map(item => ( - {/* list identity */} - - {/* shared element */} - - - - -))} -``` +The `share="morph"` class uses the [Shared Element Morph](css-recipes.md#shared-element-morph) recipe (controlled duration + motion blur). For a simpler cross-fade, use `share="auto"` (browser default). -The outer VT handles list reorder/enter. The inner VT handles cross-route shared element morph. Missing either layer means that animation silently doesn't happen. +When list items contain shared elements, compose both patterns with two nested `` layers — an outer keyed VT for list identity and an inner named VT for the cross-route pair. See [Composing Shared Elements with List Identity](../SKILL.md#composing-shared-elements-with-list-identity). -**Rules:** Names must be globally unique. Add `default="none"` on list-side shared elements. +**Rules:** +- Names must be globally unique — use prefixes like `photo-${id}`. +- Add `default="none"` on list-side shared elements to prevent per-item cross-fades on filter/search updates. +- The target must be **in the DOM at navigation time** for the pair to form. If it's behind a Suspense fallback (not rendered yet), no pair forms and it won't morph. It works when the target is present at the snapshot — render it above the data boundary, or have its data **cached/prefetched** so it resolves in time. ## Step 7: Verify Each Navigation Path -Walk through every row in the navigation map from Step 1: +Walk through every row in the navigation map from Step 1 and confirm: -- Does the VT mount/unmount, or stay mounted (same-route)? +- Does the VT mount/unmount on this navigation, or does it stay mounted (same-route)? - For named VTs: does a shared pair form? If not, does `enter`/`exit` provide a fallback? - Does `default="none"` block an animation you actually want? -- Do persistent elements stay static? +- Do persistent elements stay static (not sliding with page content)? - Do Suspense reveals animate independently from directional navigations? ---- - -## Common Mistakes - -- **Bare VT without `default="none"`** — fires cross-fade on every transition -- **Directional VT in a layout** — layouts persist, enter/exit won't fire on route changes -- **Fade-out exit with shared morphs** — conflicts with morph, use directional slide -- **Writing custom animation CSS** — use the recipes -- **Missing `default: "none"` in type-keyed objects** — TypeScript requires it, fallback is `"auto"` -- **Type maps on Suspense reveals** — Suspense resolves have no type, use string props -- **Raw `viewTransitionName` CSS to trigger animations** — React only starts view transitions when `` components are in the tree. Bare `viewTransitionName` is for isolating elements, not triggering animations. -- **`update` trigger for same-route navigations** — nested VTs steal the mutation from the parent. Use `key` + `name` + `share` instead. -- **Named VT in a reusable component** — if a component with a named VT is rendered in both a modal/popover *and* a page, both mount simultaneously and break the morph. Make the name conditional or move it to the specific consumer. -- **`router.back()` for back navigation** — `router.back()` triggers synchronous `popstate`, incompatible with view transitions. Use `router.push()` with an explicit URL. +If any path produces no animation or competing animations, use the symptom-driven [troubleshooting guide](troubleshooting.md). -For Next.js-specific steps, see the Next.js section below. +For Next.js-specific implementation steps (`transitionTypes` on ``, prefetch behavior, and same-route dynamic segments), see [nextjs.md](nextjs.md). --- # Patterns and Guidelines +Use the official [React `` reference](https://react.dev/reference/react/ViewTransition) for API mechanics. This file collects reusable implementation patterns and failure modes from production apps. + ## Searchable Grid with `useDeferredValue` +`useDeferredValue` makes filter updates a transition, activating ``: + ```tsx 'use client'; @@ -506,10 +533,20 @@ export default function SearchableGrid({ itemsPromise }) { } ``` -Per-item named VTs in deferred lists trigger cross-fades on every keystroke. Fix with `default="none"`. +Per-item `` inside a deferred list triggers cross-fades on every keystroke. Fix with `default="none"`: + +```tsx +{filteredItems.map(item => ( + + + +))} +``` ## Card Expand/Collapse with `startTransition` +Toggle between grid and detail view with shared element morph: + ```tsx 'use client'; @@ -549,58 +586,168 @@ export default function ItemGrid({ items }) { } ``` +## Type-Safe Transition Helpers + +Use `as const` arrays and derived types to prevent ID clashes: + +```tsx +const transitionTypes = ['default', 'transition-to-detail', 'transition-to-list'] as const; +const animationTypes = ['auto', 'none', 'animate-slide-from-left', 'animate-slide-from-right'] as const; + +type TransitionType = (typeof transitionTypes)[number]; +type AnimationType = (typeof animationTypes)[number]; +type TransitionMap = { default: AnimationType } & Partial, AnimationType>>; + +export function HorizontalTransition({ children, enter, exit }: { + children: React.ReactNode; + enter: TransitionMap; + exit: TransitionMap; +}) { + return {children}; +} +``` + ## Cross-Fade Without Remount -Omit `key` to trigger update (cross-fade) instead of exit + enter. Avoids Suspense remount: +Omit `key` to trigger an update (cross-fade) instead of exit + enter. Avoids Suspense remount/refetch: ```jsx - + + + ``` +Use `key` when content identity changes (state resets). Omit for cross-fades (tabs, panels, carousel). + ## Isolate Elements from Parent Animations -Persistent elements get captured in page's transition snapshot. Fix with `viewTransitionName`: +Pull an element out of the animated `root` snapshot by giving it its own `view-transition-name`. **`view-transition-name: none` is a no-op** — it's the CSS default, so the element stays in `root` (a common flicker bug). Use a real, unique name, then neutralize with `` (no CSS) or CSS (needed for `z-index`/`display` control — see [css-recipes.md](css-recipes.md#persistent-element-isolation)). + +- **Persistent chrome** (nav, sidebar, player bar): `