diff --git a/plugins/agent-browser/.agents/skills/agent-browser/SKILL.md b/plugins/agent-browser/.agents/skills/agent-browser/SKILL.md index bdd73cc6..8485a1f4 100644 --- a/plugins/agent-browser/.agents/skills/agent-browser/SKILL.md +++ b/plugins/agent-browser/.agents/skills/agent-browser/SKILL.md @@ -30,6 +30,7 @@ Load a specialized skill when the task falls outside browser web pages: agent-browser skills get electron # Electron desktop apps (VS Code, Slack, Discord, Figma, ...) 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 agentcore # AWS Bedrock AgentCore cloud browsers ``` diff --git a/plugins/agent-browser/.claude/skills/agent-browser b/plugins/agent-browser/.claude/skills/agent-browser new file mode 120000 index 00000000..e298b7be --- /dev/null +++ b/plugins/agent-browser/.claude/skills/agent-browser @@ -0,0 +1 @@ +../../.agents/skills/agent-browser \ No newline at end of file diff --git a/plugins/agent-browser/agent/skills/agent-browser/SKILL.md b/plugins/agent-browser/agent/skills/agent-browser/SKILL.md index baf88bcd..7a423df7 100644 --- a/plugins/agent-browser/agent/skills/agent-browser/SKILL.md +++ b/plugins/agent-browser/agent/skills/agent-browser/SKILL.md @@ -26,6 +26,7 @@ Load a specialized skill when the task falls outside browser web pages: agent-browser skills get electron # Electron desktop apps (VS Code, Slack, Discord, Figma, ...) 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 agentcore # AWS Bedrock AgentCore cloud browsers ``` diff --git a/plugins/agent-browser/skills-lock.json b/plugins/agent-browser/skills-lock.json index d7858725..7b474434 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": "ecc7641aea05f85ca3b11e7759d32aaf52fe05946ab4b63739c7bf78a41237a2" + "computedHash": "a674b7d81066e3cc471a7512ddb4ae724418cbfefa75cbb050b0dc430e4d57a0" }, "dogfood": { "source": "vercel-labs/agent-browser", diff --git a/plugins/ai-sdk/.claude/skills/ai-sdk b/plugins/ai-sdk/.claude/skills/ai-sdk new file mode 120000 index 00000000..ec2935fb --- /dev/null +++ b/plugins/ai-sdk/.claude/skills/ai-sdk @@ -0,0 +1 @@ +../../.agents/skills/ai-sdk \ No newline at end of file diff --git a/plugins/antfu/.claude/skills/antfu b/plugins/antfu/.claude/skills/antfu new file mode 120000 index 00000000..4fc00718 --- /dev/null +++ b/plugins/antfu/.claude/skills/antfu @@ -0,0 +1 @@ +../../.agents/skills/antfu \ No newline at end of file diff --git a/plugins/ast-grep/.claude/skills/ast-grep b/plugins/ast-grep/.claude/skills/ast-grep new file mode 120000 index 00000000..00582958 --- /dev/null +++ b/plugins/ast-grep/.claude/skills/ast-grep @@ -0,0 +1 @@ +../../.agents/skills/ast-grep \ No newline at end of file diff --git a/plugins/axi/.agents/skills/axi/SKILL.md b/plugins/axi/.agents/skills/axi/SKILL.md index b7c59803..b6e0819a 100644 --- a/plugins/axi/.agents/skills/axi/SKILL.md +++ b/plugins/axi/.agents/skills/axi/SKILL.md @@ -245,3 +245,29 @@ description: Manage project tasks in the current workspace ``` Every subcommand should support `--help` with a concise, complete reference: available flags with defaults, required arguments, and 2-3 usage examples. Keep it focused on the requested subcommand — don't dump the entire CLI's manual. + +### Identify yourself instantly: the `--version` fast path + +`-v`, `-V`, and `--version` must all print the bare version and exit 0. Agents and their harnesses probe `--version` constantly - to confirm a tool is installed, to check whether a fix has shipped, to decide whether to suggest `update`. That makes latency an ergonomics property, not just a perf tweak: a probe that takes 80 ms is 80 ms of every session start, paid before any useful work happens. + +The trap is ESM static imports. If `bin/.js` statically imports the module that builds the command graph, every dependency in that graph is fully evaluated _before_ the version check runs. One heavy import anywhere in the tree - an SDK, a server framework - is then paid on every `--version`. + +Answer the version before the graph loads: keep the version in a leaf module that imports only node builtins, and defer the real CLI to a dynamic `import()`. + +```js +#!/usr/bin/env node +import { tryFastPath } from "axi-sdk-js/fast-path"; +import { VERSION } from "../src/version.js"; // leaf module - node builtins only + +if (!tryFastPath(process.argv.slice(2), { version: VERSION })) { + const { main } = await import("../src/cli.js"); // heavy graph loads only here + await main(); +} +``` + +`axi-sdk-js/fast-path` is a dedicated subpath export that imports nothing at all, so pulling it in never drags in `runAxiCli` or its dependencies. `tryFastPath` handles only a bare, single-argument version flag and returns `false` for everything else, so all other argv - including version flags in trailing positions - falls through to `runAxiCli`, which stays the single owner of the general case. Its accepted flags and output are identical to the SDK's own version handling, so adopting it changes nothing an agent can observe except the latency. + +Two things keep this honest: + +- The version must come from a **leaf** module. If `VERSION` is defined inside `cli.ts`, importing it re-pulls the whole graph and the fast path buys nothing. +- Guard it with a test that measures the version path against the `node -e "console.log(1)"` floor measured in the same process, rather than an absolute millisecond budget that goes flaky across machines. diff --git a/plugins/axi/.claude/skills/axi b/plugins/axi/.claude/skills/axi new file mode 120000 index 00000000..51a5e828 --- /dev/null +++ b/plugins/axi/.claude/skills/axi @@ -0,0 +1 @@ +../../.agents/skills/axi \ No newline at end of file diff --git a/plugins/axi/agent/skills/axi/SKILL.md b/plugins/axi/agent/skills/axi/SKILL.md index f83de045..adbb4949 100644 --- a/plugins/axi/agent/skills/axi/SKILL.md +++ b/plugins/axi/agent/skills/axi/SKILL.md @@ -241,3 +241,29 @@ description: Manage project tasks in the current workspace ``` Every subcommand should support `--help` with a concise, complete reference: available flags with defaults, required arguments, and 2-3 usage examples. Keep it focused on the requested subcommand — don't dump the entire CLI's manual. + +### Identify yourself instantly: the `--version` fast path + +`-v`, `-V`, and `--version` must all print the bare version and exit 0. Agents and their harnesses probe `--version` constantly - to confirm a tool is installed, to check whether a fix has shipped, to decide whether to suggest `update`. That makes latency an ergonomics property, not just a perf tweak: a probe that takes 80 ms is 80 ms of every session start, paid before any useful work happens. + +The trap is ESM static imports. If `bin/.js` statically imports the module that builds the command graph, every dependency in that graph is fully evaluated _before_ the version check runs. One heavy import anywhere in the tree - an SDK, a server framework - is then paid on every `--version`. + +Answer the version before the graph loads: keep the version in a leaf module that imports only node builtins, and defer the real CLI to a dynamic `import()`. + +```js +#!/usr/bin/env node +import { tryFastPath } from "axi-sdk-js/fast-path"; +import { VERSION } from "../src/version.js"; // leaf module - node builtins only + +if (!tryFastPath(process.argv.slice(2), { version: VERSION })) { + const { main } = await import("../src/cli.js"); // heavy graph loads only here + await main(); +} +``` + +`axi-sdk-js/fast-path` is a dedicated subpath export that imports nothing at all, so pulling it in never drags in `runAxiCli` or its dependencies. `tryFastPath` handles only a bare, single-argument version flag and returns `false` for everything else, so all other argv - including version flags in trailing positions - falls through to `runAxiCli`, which stays the single owner of the general case. Its accepted flags and output are identical to the SDK's own version handling, so adopting it changes nothing an agent can observe except the latency. + +Two things keep this honest: + +- The version must come from a **leaf** module. If `VERSION` is defined inside `cli.ts`, importing it re-pulls the whole graph and the fast path buys nothing. +- Guard it with a test that measures the version path against the `node -e "console.log(1)"` floor measured in the same process, rather than an absolute millisecond budget that goes flaky across machines. diff --git a/plugins/axi/skills-lock.json b/plugins/axi/skills-lock.json index cf1e86d4..2f235a5d 100644 --- a/plugins/axi/skills-lock.json +++ b/plugins/axi/skills-lock.json @@ -5,7 +5,7 @@ "source": "kunchenguid/axi", "sourceType": "github", "skillPath": ".agents/skills/axi/SKILL.md", - "computedHash": "07a1364ca8cea05e5d264ee90c669531618e381a82d6976b17108095fcf90366" + "computedHash": "7de23a6b8171a06b7885712f3dd971e64d9301543d1b8c46f21660494303df95" } } } 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 3e6a4e19..ddeedc81 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 @@ -15,7 +15,10 @@ description: Configure Better Auth server and client, set up database adapters, 2. Set env vars: `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL` 3. Create `auth.ts` with database + config 4. Create route handler for your framework -5. Run `npx @better-auth/cli@latest migrate` +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` 6. Verify: call `GET /api/auth/ok` — should return `{ status: "ok" }` --- @@ -59,10 +62,12 @@ CLI looks for `auth.ts` in: `./`, `./lib`, `./utils`, or under `./src`. Use `--c ## Database -**Direct connections:** Pass `pg.Pool`, `mysql2` pool, `better-sqlite3`, or `bun:sqlite` instance. +**Direct connections:** Pass `pg.Pool`, `mysql2` pool, `better-sqlite3`, or `bun:sqlite` instance. For Postgres, also supports `postgres` (postgres.js) and `@neondatabase/serverless`. **ORM adapters:** Import from `better-auth/adapters/drizzle`, `better-auth/adapters/prisma`, `better-auth/adapters/mongodb`. +**Drizzle provider values:** `"pg"` (PostgreSQL), `"mysql"` (MySQL), `"sqlite"` (SQLite). Must match the driver used. + **Critical:** Better Auth uses adapter model names, NOT underlying table names. If Prisma model is `User` mapping to table `users`, use `modelName: "user"` (Prisma reference), not `"users"`. --- @@ -163,6 +168,8 @@ For separate client/server projects: `createAuthClient()`. 4. **Cookie cache** - Custom session fields NOT cached, always re-fetched 5. **Stateless mode** - No DB = session in cookie only, logout on cache expiry 6. **Change email flow** - Sends to current email first, then new email +7. **Drizzle: db not initialized** - `drizzleAdapter(db, ...)` requires a `db` instance from `drizzle()`. See `create-auth` skill for setup examples (node-postgres, postgres.js, Neon). +8. **Drizzle: missing drizzle.config.ts** - `drizzle-kit` commands require a `drizzle.config.ts` pointing to the generated schema file and DB credentials. --- 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 0e84a844..6f2f6447 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 migrate` +3. Run `npx @better-auth/cli@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 cf9c30bf..74acac84 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 migrate` +3. Run `npx @better-auth/cli@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/.claude/skills/better-auth-best-practices b/plugins/better-auth/.claude/skills/better-auth-best-practices new file mode 120000 index 00000000..d28d6bd7 --- /dev/null +++ b/plugins/better-auth/.claude/skills/better-auth-best-practices @@ -0,0 +1 @@ +../../.agents/skills/better-auth-best-practices \ No newline at end of file diff --git a/plugins/better-auth/.claude/skills/email-and-password-best-practices b/plugins/better-auth/.claude/skills/email-and-password-best-practices new file mode 120000 index 00000000..e78bcf65 --- /dev/null +++ b/plugins/better-auth/.claude/skills/email-and-password-best-practices @@ -0,0 +1 @@ +../../.agents/skills/email-and-password-best-practices \ No newline at end of file diff --git a/plugins/better-auth/.claude/skills/organization-best-practices b/plugins/better-auth/.claude/skills/organization-best-practices new file mode 120000 index 00000000..d43ca82a --- /dev/null +++ b/plugins/better-auth/.claude/skills/organization-best-practices @@ -0,0 +1 @@ +../../.agents/skills/organization-best-practices \ No newline at end of file diff --git a/plugins/better-auth/.claude/skills/two-factor-authentication-best-practices b/plugins/better-auth/.claude/skills/two-factor-authentication-best-practices new file mode 120000 index 00000000..0defcaa0 --- /dev/null +++ b/plugins/better-auth/.claude/skills/two-factor-authentication-best-practices @@ -0,0 +1 @@ +../../.agents/skills/two-factor-authentication-best-practices \ No newline at end of file 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 b380767c..3a77bc34 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 @@ -13,7 +13,10 @@ description: "Configure Better Auth server and client, set up database adapters, 2. Set env vars: `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL` 3. Create `auth.ts` with database + config 4. Create route handler for your framework -5. Run `npx @better-auth/cli@latest migrate` +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` 6. Verify: call `GET /api/auth/ok` — should return `{ status: "ok" }` --- @@ -57,10 +60,12 @@ CLI looks for `auth.ts` in: `./`, `./lib`, `./utils`, or under `./src`. Use `--c ## Database -**Direct connections:** Pass `pg.Pool`, `mysql2` pool, `better-sqlite3`, or `bun:sqlite` instance. +**Direct connections:** Pass `pg.Pool`, `mysql2` pool, `better-sqlite3`, or `bun:sqlite` instance. For Postgres, also supports `postgres` (postgres.js) and `@neondatabase/serverless`. **ORM adapters:** Import from `better-auth/adapters/drizzle`, `better-auth/adapters/prisma`, `better-auth/adapters/mongodb`. +**Drizzle provider values:** `"pg"` (PostgreSQL), `"mysql"` (MySQL), `"sqlite"` (SQLite). Must match the driver used. + **Critical:** Better Auth uses adapter model names, NOT underlying table names. If Prisma model is `User` mapping to table `users`, use `modelName: "user"` (Prisma reference), not `"users"`. --- @@ -161,6 +166,8 @@ For separate client/server projects: `createAuthClient()`. 4. **Cookie cache** - Custom session fields NOT cached, always re-fetched 5. **Stateless mode** - No DB = session in cookie only, logout on cache expiry 6. **Change email flow** - Sends to current email first, then new email +7. **Drizzle: db not initialized** - `drizzleAdapter(db, ...)` requires a `db` instance from `drizzle()`. See `create-auth` skill for setup examples (node-postgres, postgres.js, Neon). +8. **Drizzle: missing drizzle.config.ts** - `drizzle-kit` commands require a `drizzle.config.ts` pointing to the generated schema file and DB credentials. --- 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 4e07fbbd..819089a9 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 migrate` +3. Run `npx @better-auth/cli@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 c5bdf15a..6f366f3c 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 migrate` +3. Run `npx @better-auth/cli@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 11f6218b..052584f7 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": "a4c830509e85557b59339d8d93a4e243e9e59c686e7678854d39230e12c2a6dc" + "computedHash": "61ba0ef64ed2e7c424401cc848ca33dd6d790a720c44727717dc0c5cba5fc122" }, "create-auth-skill": { "source": "better-auth/skills", @@ -23,13 +23,13 @@ "source": "better-auth/skills", "sourceType": "github", "skillPath": "better-auth/organization/SKILL.md", - "computedHash": "79a5a85b43d10e9fe37582b3506b051a33a991817b938f349099baa5ddba21aa" + "computedHash": "27627eb3a13bd44eff3a5d890f96e7db052b623db90e01684f759abe3e7fe56b" }, "two-factor-authentication-best-practices": { "source": "better-auth/skills", "sourceType": "github", "skillPath": "better-auth/twoFactor/SKILL.md", - "computedHash": "7e297aaf887e11fdc03e52bdbf44974e161e8f7c393170ced7dc66cace4f6d46" + "computedHash": "a6f720042e5a090909e0d519a6a50a7eb8179553cd5315f0bbad852a6d20dac8" } } } diff --git a/plugins/chat-sdk/.claude/skills/chat-sdk b/plugins/chat-sdk/.claude/skills/chat-sdk new file mode 120000 index 00000000..77d22f1b --- /dev/null +++ b/plugins/chat-sdk/.claude/skills/chat-sdk @@ -0,0 +1 @@ +../../.agents/skills/chat-sdk \ No newline at end of file diff --git a/plugins/dev3000/.agents/skills/d3k/PUBLISH.md b/plugins/dev3000/.agents/skills/d3k/PUBLISH.md index 254bb3dd..512c50ee 100644 --- a/plugins/dev3000/.agents/skills/d3k/PUBLISH.md +++ b/plugins/dev3000/.agents/skills/d3k/PUBLISH.md @@ -13,15 +13,15 @@ ### Short description -`Bootstraps d3k runtime for standalone AI apps` +`Agent-owned local web debugging with a managed browser` ### Long description -`Installs/initializes dev3000 (d3k) for standalone agent shells (Codex, Cursor, Claude Code), starts d3k as the default runtime, and uses unified logs plus CDP browser control instead of raw npm/bun dev.` +`Starts or reuses d3k in a retained background agent session, opens the project-stable managed browser, and uses unified browser/server evidence instead of raw dev servers or separate automation browsers.` ### Default prompt -`Use $d3k to initialize d3k, start the correct runtime, and drive debugging with unified logs and CDP browser controls.` +`Use $d3k to let me test this project in its monitored browser, then inspect the captured evidence after I reproduce the issue.` ## Source URL diff --git a/plugins/dev3000/.agents/skills/d3k/SKILL.md b/plugins/dev3000/.agents/skills/d3k/SKILL.md index 7ae3c655..1439e9ad 100644 --- a/plugins/dev3000/.agents/skills/d3k/SKILL.md +++ b/plugins/dev3000/.agents/skills/d3k/SKILL.md @@ -1,199 +1,128 @@ --- name: "d3k" -description: "Bootstrap d3k in standalone AI apps (Codex, Cursor, Claude Code): detect/install dev3000, start d3k as the runtime, and use unified logs plus d3k-owned browser/session control instead of running npm/bun dev directly." +description: "Use when the user asks to use d3k, run/dev/test/debug a web project with d3k, or reproduce a browser issue. Own the runtime: reuse or background-start d3k non-interactively, wait for readiness, use its project-stable managed Chrome profile, and inspect unified browser/server evidence." --- -# d3k Standalone Bootstrap +# d3k Agent Runtime -Use this skill when working in a standalone AI app and you need reliable local web debugging with browser + server context. +d3k is the local web runtime for this task. It starts the dev server behind a stable Portless URL, owns a project-stable Chrome profile, and records server logs, browser console output, network activity, interactions, and screenshots in one timeline. -## Why d3k-first +When this skill triggers, operate d3k. Do not merely tell the user how to run it. -- `d3k` captures server logs, browser console, network events, and screenshots in one timeline. -- `d3k` owns the browser session so the agent can control the same browser being monitored. -- Running `npm run dev` or `bun run dev` directly omits this unified telemetry and usually leads to weaker diagnoses. +## Interpret the Request -## Auth-Sensitive Browser Rule +- "Let me test/dev my project with d3k": prepare the runtime and headed browser, confirm it is ready, then hand control to the user. Wait for them to reproduce the issue before inspecting evidence. +- "Test/debug/fix this with d3k": prepare the runtime, then drive the managed browser and investigate autonomously. +- If ambiguous, start the runtime and browser first. That action is safe and useful for either path. -For Google OAuth, Supabase auth, and any other auth-sensitive debugging, d3k must own browser startup. Start d3k normally so it launches the app and browser together, including `--app-url` when the target URL is known. +## Start or Reuse d3k -Do not use `d3k agent-browser --profile ... --headed open ...`, raw Chrome, Playwright, browser MCP sessions, manual CDP attachment, or any other separate automation browser for auth debugging unless the user explicitly asks for that path. Agent-browser-created/custom Chrome profiles can be rejected by Google with `This browser or app may not be secure`. +Run from the project root. + +1. Check for an existing project runtime: -After d3k has launched the browser, use the safe managed-browser path: ```bash -d3k agent-browser --require-d3k-browser open "" -d3k agent-browser snapshot -i -d3k agent-browser click @e1 -d3k errors --context +d3k status --json ``` -If this fails because no d3k-managed browser exists, restart d3k cleanly with its normal browser-owning flow. Do not fall back to creating a new agent-browser Chrome for auth. +If it reports `"running": true`, reuse it. Do not start a second dev server or browser. -## Bootstrap Workflow - -1. Confirm whether `d3k` is installed: -```bash -command -v d3k >/dev/null && d3k --version -``` +2. If d3k is not installed, install it: -2. If `d3k` is missing, install dev3000 globally (prefer Bun): ```bash bun install -g dev3000 ``` -Fallback if Bun is unavailable: -```bash -npm install -g dev3000 -``` -3. Start d3k as the runtime and let d3k own browser startup. When the app command, port, and target URL are known, use the normal app-debugging shape: -```bash -d3k --no-agent --command "" --port --startup-timeout --no-tui --app-url "" -``` +Use `npm install -g dev3000` only when Bun is unavailable. - For a repo-default shell with no target URL yet: -```bash -d3k --no-agent --no-tui -t -``` - -4. Keep d3k running while editing code. Do not start a second dev server with `npm/bun dev`. +3. Start d3k with the agent's shell/process tool as a retained background or yielded session: -5. Drive the page through d3k's active browser session: ```bash -d3k agent-browser snapshot -i -d3k agent-browser click @e1 -d3k errors --context +d3k --no-agent --no-tui -t ``` -## Required Browser/Session Default - -When a user asks to start or debug an app with d3k, prefer d3k's normal browser-owning flow, including `--app-url` when a target URL is known. +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 `&`. -Do not launch a separate raw Chrome, Playwright browser, browser MCP session, or manually attach to CDP unless the user explicitly asks for that path. Separate automation-only browser profiles can break OAuth flows, especially Google sign-in with `This browser or app may not be secure`. +If the target URL is already known, pass it so the managed browser opens there: -After d3k is running, drive the page through `d3k agent-browser ...` commands so interactions target d3k's active browser session. - -If profile or daemon state seems stale, first run: ```bash -d3k agent-browser close --all +d3k --no-agent --no-tui -t --app-url "" ``` -Then restart d3k cleanly with the normal browser-owning flow. +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: -For a normal app-debugging session, use: ```bash -d3k --no-agent --command "" --port --startup-timeout --no-tui --app-url "" -d3k agent-browser --require-d3k-browser open "" -d3k agent-browser snapshot -i -d3k agent-browser click @e1 -d3k errors --context +d3k status --json ``` -## Non-Auth Fresh Browser/Profile Startup - -Use this special-case workflow only for non-auth debugging when the user explicitly asks Codex to start d3k with a fresh browser/profile. Do not use this workflow for Google OAuth, Supabase auth, or any sign-in flow that may reject automation browsers. The default app-debugging workflow is to let d3k own browser startup and then interact through `d3k agent-browser`. +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. -1. Close any stale `agent-browser` daemon before launching with `--profile`. Otherwise `agent-browser` will reuse the existing daemon and print `--profile ignored`. - ```bash - d3k agent-browser close --all - ``` +## User-Driven Testing -2. Start the app through d3k in `servers-only` mode and keep that command running. In Codex, this is more reliable than asking d3k to launch the browser itself when a fresh profile is required. - ```bash - d3k --no-agent --no-skills --servers-only --command "npm run dev -- -H 127.0.0.1 -p 3000" --port 3000 --startup-timeout 90 --no-tui - ``` +When the user says "let me test": - Adjust the package-manager command and port for the project. Prefer `--command` over `--script` when passing framework flags. For npm scripts, put flags after `--`; otherwise tools like Next.js can interpret the port as a project directory. - -3. Verify the server before opening more browser windows: - ```bash - curl -I http://127.0.0.1:3000 - ``` - -4. Open the fresh profile as a separate browser step: - ```bash - d3k agent-browser --allow-new-browser --profile /tmp/d3k-fresh-profile --headed open http://127.0.0.1:3000 - ``` - -5. Sanity-check the opened page: - ```bash - d3k agent-browser get title - d3k agent-browser snapshot -i - d3k errors - ``` - -Practical rules: - -- Prefer `127.0.0.1` for this workflow. If `localhost` hangs or flips between IPv4/IPv6 behavior, do not keep retrying browser launches. -- If `curl -I` hangs, the server is wedged even if the port appears occupied; restart the d3k server process before opening a browser. -- In `servers-only` mode there is no d3k-managed browser. Use `--allow-new-browser` only for the explicit non-auth fresh-profile open step; do not use `d3k cdp-port`. -- In sandboxed agent environments, rerun local-network checks and `agent-browser` opens outside the sandbox when sandbox networking blocks access to `127.0.0.1`. - -## Debugging Commands - -Use these first before ad-hoc log scraping: +1. Confirm the status response includes the app URL and `"browserConnected": true`. +2. Tell the user the monitored browser is ready. +3. Keep the d3k process running and wait for the user to reproduce the behavior. +4. When they report that it happened, begin with: ```bash d3k errors --context d3k logs -n 200 -d3k logs --type browser -d3k logs --type server ``` -## Browser Interaction +Do not replace the headed browser with automation while the user is testing. + +## Agent-Driven Testing -Use the already-monitored d3k browser session instead of launching a separate automation browser. +Drive the exact browser d3k is monitoring: ```bash -d3k agent-browser --require-d3k-browser open http://localhost:3000 d3k agent-browser snapshot -i d3k agent-browser click @e2 -d3k agent-browser screenshot /tmp/d3k-current.png +d3k agent-browser fill @e3 "text" +d3k errors --context ``` -`d3k agent-browser` auto-connects to the active d3k session's browser. `--require-d3k-browser` fails instead of creating a new browser when no d3k-managed browser exists. Manual CDP attachment, `d3k agent-browser connect `, and `--allow-new-browser` are explicit opt-in paths for targeting or creating a different browser, not the default. +Use `--require-d3k-browser` when opening a URL so failure cannot silently create another browser: -## Browser Tool Choice +```bash +d3k agent-browser --require-d3k-browser open "" +``` + +After every reproduction or code change, replay the relevant interaction and check `d3k errors --context` again. -Use the browser tool that matches the task instead of treating them as interchangeable: +## Evidence Commands -- `agent-browser` - - Default choice. - - Best for generic web apps and for driving the exact headed browser session that d3k is already monitoring. - - Use it when you need `snapshot`, ref-based `click`, `fill`, or to reproduce what the user sees in the monitored tab. -- `next-browser` - - Next.js-specific tool. - - Best for React/Next introspection: `tree`, `errors`, `logs`, `routes`, `project`, PPR inspection, and related Next dev-server signals. - - It is not a drop-in replacement for `agent-browser`: no accessibility `snapshot`, no ref-based `click`, and no `fill`. - - It launches its own daemon/browser flow and does not use d3k's active browser session. +Prefer these over ad-hoc log scraping: -Practical rule: +```bash +d3k status --json +d3k errors --context +d3k logs -n 200 +d3k logs --type browser +d3k logs --type server +``` -- Need to drive the same browser d3k is monitoring: use `agent-browser`. -- Need Next.js component tree or Next-specific diagnostics: use `next-browser`. +Artifacts live under `~/.d3k//`, including `session.json`, logs, screenshots, and the Chrome profile. -Examples: +## Browser and Auth Safety -```bash -# Same monitored browser session -d3k agent-browser snapshot -i -d3k agent-browser click @e2 +d3k must own browser startup by default. Its per-project Chrome profile preserves login state, cookies, and local storage. -# Next.js-specific inspection -d3k next-browser open http://localhost:3000 -d3k next-browser tree -d3k next-browser errors -d3k next-browser logs -``` +For Google OAuth, Supabase auth, and other auth-sensitive flows, never substitute raw Chrome, Playwright, a browser MCP session, manual CDP attachment, or `agent-browser --profile`. Those paths use a different browser/profile and can trigger "This browser or app may not be secure." -## Artifacts to Read +If the managed browser is unavailable, stop or interrupt the retained d3k process and restart d3k cleanly. Do not work around it by creating another browser. -- `~/.d3k/{project}/d3k.log` -- `~/.d3k/{project}/logs/` -- `~/.d3k/{project}/screenshots/` -- `~/.d3k/{project}/session.json` +Use `--headless` only for CI or when explicitly requested. Use `--servers-only` only when browser monitoring is intentionally unwanted. ## Operating Rules -- Prefer headed mode for interactive debugging. -- Use `--headless` only for CI or when explicitly requested. -- Use `--servers-only` only when browser monitoring is intentionally disabled, and not for auth-sensitive debugging. +- Do not run `npm run dev`, `bun run dev`, or another dev server alongside d3k. +- Do not start a second d3k when `d3k status --json` reports an active one. +- 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. diff --git a/plugins/dev3000/.claude/skills/d3k b/plugins/dev3000/.claude/skills/d3k new file mode 120000 index 00000000..be13baf4 --- /dev/null +++ b/plugins/dev3000/.claude/skills/d3k @@ -0,0 +1 @@ +../../.agents/skills/d3k \ No newline at end of file diff --git a/plugins/dev3000/agent/skills/d3k/PUBLISH.md b/plugins/dev3000/agent/skills/d3k/PUBLISH.md index 254bb3dd..512c50ee 100644 --- a/plugins/dev3000/agent/skills/d3k/PUBLISH.md +++ b/plugins/dev3000/agent/skills/d3k/PUBLISH.md @@ -13,15 +13,15 @@ ### Short description -`Bootstraps d3k runtime for standalone AI apps` +`Agent-owned local web debugging with a managed browser` ### Long description -`Installs/initializes dev3000 (d3k) for standalone agent shells (Codex, Cursor, Claude Code), starts d3k as the default runtime, and uses unified logs plus CDP browser control instead of raw npm/bun dev.` +`Starts or reuses d3k in a retained background agent session, opens the project-stable managed browser, and uses unified browser/server evidence instead of raw dev servers or separate automation browsers.` ### Default prompt -`Use $d3k to initialize d3k, start the correct runtime, and drive debugging with unified logs and CDP browser controls.` +`Use $d3k to let me test this project in its monitored browser, then inspect the captured evidence after I reproduce the issue.` ## Source URL diff --git a/plugins/dev3000/agent/skills/d3k/SKILL.md b/plugins/dev3000/agent/skills/d3k/SKILL.md index 9f6260fc..511c3ec0 100644 --- a/plugins/dev3000/agent/skills/d3k/SKILL.md +++ b/plugins/dev3000/agent/skills/d3k/SKILL.md @@ -1,197 +1,126 @@ --- -description: "Bootstrap d3k in standalone AI apps (Codex, Cursor, Claude Code): detect/install dev3000, start d3k as the runtime, and use unified logs plus d3k-owned browser/session control instead of running npm/bun dev directly." +description: "Use when the user asks to use d3k, run/dev/test/debug a web project with d3k, or reproduce a browser issue. Own the runtime: reuse or background-start d3k non-interactively, wait for readiness, use its project-stable managed Chrome profile, and inspect unified browser/server evidence." --- -# d3k Standalone Bootstrap +# d3k Agent Runtime -Use this skill when working in a standalone AI app and you need reliable local web debugging with browser + server context. +d3k is the local web runtime for this task. It starts the dev server behind a stable Portless URL, owns a project-stable Chrome profile, and records server logs, browser console output, network activity, interactions, and screenshots in one timeline. -## Why d3k-first +When this skill triggers, operate d3k. Do not merely tell the user how to run it. -- `d3k` captures server logs, browser console, network events, and screenshots in one timeline. -- `d3k` owns the browser session so the agent can control the same browser being monitored. -- Running `npm run dev` or `bun run dev` directly omits this unified telemetry and usually leads to weaker diagnoses. +## Interpret the Request -## Auth-Sensitive Browser Rule +- "Let me test/dev my project with d3k": prepare the runtime and headed browser, confirm it is ready, then hand control to the user. Wait for them to reproduce the issue before inspecting evidence. +- "Test/debug/fix this with d3k": prepare the runtime, then drive the managed browser and investigate autonomously. +- If ambiguous, start the runtime and browser first. That action is safe and useful for either path. -For Google OAuth, Supabase auth, and any other auth-sensitive debugging, d3k must own browser startup. Start d3k normally so it launches the app and browser together, including `--app-url` when the target URL is known. +## Start or Reuse d3k -Do not use `d3k agent-browser --profile ... --headed open ...`, raw Chrome, Playwright, browser MCP sessions, manual CDP attachment, or any other separate automation browser for auth debugging unless the user explicitly asks for that path. Agent-browser-created/custom Chrome profiles can be rejected by Google with `This browser or app may not be secure`. +Run from the project root. + +1. Check for an existing project runtime: -After d3k has launched the browser, use the safe managed-browser path: ```bash -d3k agent-browser --require-d3k-browser open "" -d3k agent-browser snapshot -i -d3k agent-browser click @e1 -d3k errors --context +d3k status --json ``` -If this fails because no d3k-managed browser exists, restart d3k cleanly with its normal browser-owning flow. Do not fall back to creating a new agent-browser Chrome for auth. +If it reports `"running": true`, reuse it. Do not start a second dev server or browser. -## Bootstrap Workflow - -1. Confirm whether `d3k` is installed: -```bash -command -v d3k >/dev/null && d3k --version -``` +2. If d3k is not installed, install it: -2. If `d3k` is missing, install dev3000 globally (prefer Bun): ```bash bun install -g dev3000 ``` -Fallback if Bun is unavailable: -```bash -npm install -g dev3000 -``` -3. Start d3k as the runtime and let d3k own browser startup. When the app command, port, and target URL are known, use the normal app-debugging shape: -```bash -d3k --no-agent --command "" --port --startup-timeout --no-tui --app-url "" -``` +Use `npm install -g dev3000` only when Bun is unavailable. - For a repo-default shell with no target URL yet: -```bash -d3k --no-agent --no-tui -t -``` - -4. Keep d3k running while editing code. Do not start a second dev server with `npm/bun dev`. +3. Start d3k with the agent's shell/process tool as a retained background or yielded session: -5. Drive the page through d3k's active browser session: ```bash -d3k agent-browser snapshot -i -d3k agent-browser click @e1 -d3k errors --context +d3k --no-agent --no-tui -t ``` -## Required Browser/Session Default - -When a user asks to start or debug an app with d3k, prefer d3k's normal browser-owning flow, including `--app-url` when a target URL is known. +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 `&`. -Do not launch a separate raw Chrome, Playwright browser, browser MCP session, or manually attach to CDP unless the user explicitly asks for that path. Separate automation-only browser profiles can break OAuth flows, especially Google sign-in with `This browser or app may not be secure`. +If the target URL is already known, pass it so the managed browser opens there: -After d3k is running, drive the page through `d3k agent-browser ...` commands so interactions target d3k's active browser session. - -If profile or daemon state seems stale, first run: ```bash -d3k agent-browser close --all +d3k --no-agent --no-tui -t --app-url "" ``` -Then restart d3k cleanly with the normal browser-owning flow. +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: -For a normal app-debugging session, use: ```bash -d3k --no-agent --command "" --port --startup-timeout --no-tui --app-url "" -d3k agent-browser --require-d3k-browser open "" -d3k agent-browser snapshot -i -d3k agent-browser click @e1 -d3k errors --context +d3k status --json ``` -## Non-Auth Fresh Browser/Profile Startup - -Use this special-case workflow only for non-auth debugging when the user explicitly asks Codex to start d3k with a fresh browser/profile. Do not use this workflow for Google OAuth, Supabase auth, or any sign-in flow that may reject automation browsers. The default app-debugging workflow is to let d3k own browser startup and then interact through `d3k agent-browser`. +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. -1. Close any stale `agent-browser` daemon before launching with `--profile`. Otherwise `agent-browser` will reuse the existing daemon and print `--profile ignored`. - ```bash - d3k agent-browser close --all - ``` +## User-Driven Testing -2. Start the app through d3k in `servers-only` mode and keep that command running. In Codex, this is more reliable than asking d3k to launch the browser itself when a fresh profile is required. - ```bash - d3k --no-agent --no-skills --servers-only --command "npm run dev -- -H 127.0.0.1 -p 3000" --port 3000 --startup-timeout 90 --no-tui - ``` +When the user says "let me test": - Adjust the package-manager command and port for the project. Prefer `--command` over `--script` when passing framework flags. For npm scripts, put flags after `--`; otherwise tools like Next.js can interpret the port as a project directory. - -3. Verify the server before opening more browser windows: - ```bash - curl -I http://127.0.0.1:3000 - ``` - -4. Open the fresh profile as a separate browser step: - ```bash - d3k agent-browser --allow-new-browser --profile /tmp/d3k-fresh-profile --headed open http://127.0.0.1:3000 - ``` - -5. Sanity-check the opened page: - ```bash - d3k agent-browser get title - d3k agent-browser snapshot -i - d3k errors - ``` - -Practical rules: - -- Prefer `127.0.0.1` for this workflow. If `localhost` hangs or flips between IPv4/IPv6 behavior, do not keep retrying browser launches. -- If `curl -I` hangs, the server is wedged even if the port appears occupied; restart the d3k server process before opening a browser. -- In `servers-only` mode there is no d3k-managed browser. Use `--allow-new-browser` only for the explicit non-auth fresh-profile open step; do not use `d3k cdp-port`. -- In sandboxed agent environments, rerun local-network checks and `agent-browser` opens outside the sandbox when sandbox networking blocks access to `127.0.0.1`. - -## Debugging Commands - -Use these first before ad-hoc log scraping: +1. Confirm the status response includes the app URL and `"browserConnected": true`. +2. Tell the user the monitored browser is ready. +3. Keep the d3k process running and wait for the user to reproduce the behavior. +4. When they report that it happened, begin with: ```bash d3k errors --context d3k logs -n 200 -d3k logs --type browser -d3k logs --type server ``` -## Browser Interaction +Do not replace the headed browser with automation while the user is testing. + +## Agent-Driven Testing -Use the already-monitored d3k browser session instead of launching a separate automation browser. +Drive the exact browser d3k is monitoring: ```bash -d3k agent-browser --require-d3k-browser open http://localhost:3000 d3k agent-browser snapshot -i d3k agent-browser click @e2 -d3k agent-browser screenshot /tmp/d3k-current.png +d3k agent-browser fill @e3 "text" +d3k errors --context ``` -`d3k agent-browser` auto-connects to the active d3k session's browser. `--require-d3k-browser` fails instead of creating a new browser when no d3k-managed browser exists. Manual CDP attachment, `d3k agent-browser connect `, and `--allow-new-browser` are explicit opt-in paths for targeting or creating a different browser, not the default. +Use `--require-d3k-browser` when opening a URL so failure cannot silently create another browser: -## Browser Tool Choice +```bash +d3k agent-browser --require-d3k-browser open "" +``` + +After every reproduction or code change, replay the relevant interaction and check `d3k errors --context` again. -Use the browser tool that matches the task instead of treating them as interchangeable: +## Evidence Commands -- `agent-browser` - - Default choice. - - Best for generic web apps and for driving the exact headed browser session that d3k is already monitoring. - - Use it when you need `snapshot`, ref-based `click`, `fill`, or to reproduce what the user sees in the monitored tab. -- `next-browser` - - Next.js-specific tool. - - Best for React/Next introspection: `tree`, `errors`, `logs`, `routes`, `project`, PPR inspection, and related Next dev-server signals. - - It is not a drop-in replacement for `agent-browser`: no accessibility `snapshot`, no ref-based `click`, and no `fill`. - - It launches its own daemon/browser flow and does not use d3k's active browser session. +Prefer these over ad-hoc log scraping: -Practical rule: +```bash +d3k status --json +d3k errors --context +d3k logs -n 200 +d3k logs --type browser +d3k logs --type server +``` -- Need to drive the same browser d3k is monitoring: use `agent-browser`. -- Need Next.js component tree or Next-specific diagnostics: use `next-browser`. +Artifacts live under `~/.d3k//`, including `session.json`, logs, screenshots, and the Chrome profile. -Examples: +## Browser and Auth Safety -```bash -# Same monitored browser session -d3k agent-browser snapshot -i -d3k agent-browser click @e2 +d3k must own browser startup by default. Its per-project Chrome profile preserves login state, cookies, and local storage. -# Next.js-specific inspection -d3k next-browser open http://localhost:3000 -d3k next-browser tree -d3k next-browser errors -d3k next-browser logs -``` +For Google OAuth, Supabase auth, and other auth-sensitive flows, never substitute raw Chrome, Playwright, a browser MCP session, manual CDP attachment, or `agent-browser --profile`. Those paths use a different browser/profile and can trigger "This browser or app may not be secure." -## Artifacts to Read +If the managed browser is unavailable, stop or interrupt the retained d3k process and restart d3k cleanly. Do not work around it by creating another browser. -- `~/.d3k/{project}/d3k.log` -- `~/.d3k/{project}/logs/` -- `~/.d3k/{project}/screenshots/` -- `~/.d3k/{project}/session.json` +Use `--headless` only for CI or when explicitly requested. Use `--servers-only` only when browser monitoring is intentionally unwanted. ## Operating Rules -- Prefer headed mode for interactive debugging. -- Use `--headless` only for CI or when explicitly requested. -- Use `--servers-only` only when browser monitoring is intentionally disabled, and not for auth-sensitive debugging. +- Do not run `npm run dev`, `bun run dev`, or another dev server alongside d3k. +- Do not start a second d3k when `d3k status --json` reports an active one. +- 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. diff --git a/plugins/dev3000/skills-lock.json b/plugins/dev3000/skills-lock.json index dbcced14..f7d3f718 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": "13d37628042043672f4331264634ac19956f6e1fc9d97e320af12be5243343aa" + "computedHash": "6462ff3b28aaec3ba02f8419e4a6faace3ccb69165bf0da8f55a7da93393e2d3" } } } diff --git a/plugins/docus/.claude/skills/create-docs b/plugins/docus/.claude/skills/create-docs new file mode 120000 index 00000000..dac07543 --- /dev/null +++ b/plugins/docus/.claude/skills/create-docs @@ -0,0 +1 @@ +../../.agents/skills/create-docs \ No newline at end of file diff --git a/plugins/docus/.claude/skills/review-docs b/plugins/docus/.claude/skills/review-docs new file mode 120000 index 00000000..8d8fe1a9 --- /dev/null +++ b/plugins/docus/.claude/skills/review-docs @@ -0,0 +1 @@ +../../.agents/skills/review-docs \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/apple b/plugins/emulate/.claude/skills/apple new file mode 120000 index 00000000..9a130dbc --- /dev/null +++ b/plugins/emulate/.claude/skills/apple @@ -0,0 +1 @@ +../../.agents/skills/apple \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/aws b/plugins/emulate/.claude/skills/aws new file mode 120000 index 00000000..8a8a0cc6 --- /dev/null +++ b/plugins/emulate/.claude/skills/aws @@ -0,0 +1 @@ +../../.agents/skills/aws \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/emulate b/plugins/emulate/.claude/skills/emulate new file mode 120000 index 00000000..810be77e --- /dev/null +++ b/plugins/emulate/.claude/skills/emulate @@ -0,0 +1 @@ +../../.agents/skills/emulate \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/github b/plugins/emulate/.claude/skills/github new file mode 120000 index 00000000..af893bfd --- /dev/null +++ b/plugins/emulate/.claude/skills/github @@ -0,0 +1 @@ +../../.agents/skills/github \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/google b/plugins/emulate/.claude/skills/google new file mode 120000 index 00000000..50f5353d --- /dev/null +++ b/plugins/emulate/.claude/skills/google @@ -0,0 +1 @@ +../../.agents/skills/google \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/linear b/plugins/emulate/.claude/skills/linear new file mode 120000 index 00000000..647ab93b --- /dev/null +++ b/plugins/emulate/.claude/skills/linear @@ -0,0 +1 @@ +../../.agents/skills/linear \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/microsoft b/plugins/emulate/.claude/skills/microsoft new file mode 120000 index 00000000..18651efb --- /dev/null +++ b/plugins/emulate/.claude/skills/microsoft @@ -0,0 +1 @@ +../../.agents/skills/microsoft \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/next b/plugins/emulate/.claude/skills/next new file mode 120000 index 00000000..55113dc1 --- /dev/null +++ b/plugins/emulate/.claude/skills/next @@ -0,0 +1 @@ +../../.agents/skills/next \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/resend b/plugins/emulate/.claude/skills/resend new file mode 120000 index 00000000..eddaea0b --- /dev/null +++ b/plugins/emulate/.claude/skills/resend @@ -0,0 +1 @@ +../../.agents/skills/resend \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/slack b/plugins/emulate/.claude/skills/slack new file mode 120000 index 00000000..1cf3af3f --- /dev/null +++ b/plugins/emulate/.claude/skills/slack @@ -0,0 +1 @@ +../../.agents/skills/slack \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/stripe b/plugins/emulate/.claude/skills/stripe new file mode 120000 index 00000000..7eb1ba12 --- /dev/null +++ b/plugins/emulate/.claude/skills/stripe @@ -0,0 +1 @@ +../../.agents/skills/stripe \ No newline at end of file diff --git a/plugins/emulate/.claude/skills/vercel b/plugins/emulate/.claude/skills/vercel new file mode 120000 index 00000000..5e1b3ce3 --- /dev/null +++ b/plugins/emulate/.claude/skills/vercel @@ -0,0 +1 @@ +../../.agents/skills/vercel \ No newline at end of file diff --git a/plugins/git-ai/.claude/skills/ask b/plugins/git-ai/.claude/skills/ask new file mode 120000 index 00000000..2db68559 --- /dev/null +++ b/plugins/git-ai/.claude/skills/ask @@ -0,0 +1 @@ +../../.agents/skills/ask \ No newline at end of file diff --git a/plugins/git-ai/.claude/skills/git-ai-search b/plugins/git-ai/.claude/skills/git-ai-search new file mode 120000 index 00000000..1635d65a --- /dev/null +++ b/plugins/git-ai/.claude/skills/git-ai-search @@ -0,0 +1 @@ +../../.agents/skills/git-ai-search \ No newline at end of file diff --git a/plugins/git-ai/.claude/skills/prompt-analysis b/plugins/git-ai/.claude/skills/prompt-analysis new file mode 120000 index 00000000..3b8bc272 --- /dev/null +++ b/plugins/git-ai/.claude/skills/prompt-analysis @@ -0,0 +1 @@ +../../.agents/skills/prompt-analysis \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-admin-reports b/plugins/google-workspace/.claude/skills/gws-admin-reports new file mode 120000 index 00000000..a0cbdf73 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-admin-reports @@ -0,0 +1 @@ +../../.agents/skills/gws-admin-reports \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-calendar b/plugins/google-workspace/.claude/skills/gws-calendar new file mode 120000 index 00000000..3dbc8165 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-calendar @@ -0,0 +1 @@ +../../.agents/skills/gws-calendar \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-calendar-agenda b/plugins/google-workspace/.claude/skills/gws-calendar-agenda new file mode 120000 index 00000000..5309b10e --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-calendar-agenda @@ -0,0 +1 @@ +../../.agents/skills/gws-calendar-agenda \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-calendar-insert b/plugins/google-workspace/.claude/skills/gws-calendar-insert new file mode 120000 index 00000000..9c87ae80 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-calendar-insert @@ -0,0 +1 @@ +../../.agents/skills/gws-calendar-insert \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-chat b/plugins/google-workspace/.claude/skills/gws-chat new file mode 120000 index 00000000..706de078 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-chat @@ -0,0 +1 @@ +../../.agents/skills/gws-chat \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-chat-send b/plugins/google-workspace/.claude/skills/gws-chat-send new file mode 120000 index 00000000..8d31876c --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-chat-send @@ -0,0 +1 @@ +../../.agents/skills/gws-chat-send \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-classroom b/plugins/google-workspace/.claude/skills/gws-classroom new file mode 120000 index 00000000..b9a8293a --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-classroom @@ -0,0 +1 @@ +../../.agents/skills/gws-classroom \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-docs b/plugins/google-workspace/.claude/skills/gws-docs new file mode 120000 index 00000000..7e0b2bd7 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-docs @@ -0,0 +1 @@ +../../.agents/skills/gws-docs \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-docs-write b/plugins/google-workspace/.claude/skills/gws-docs-write new file mode 120000 index 00000000..9ec51409 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-docs-write @@ -0,0 +1 @@ +../../.agents/skills/gws-docs-write \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-drive b/plugins/google-workspace/.claude/skills/gws-drive new file mode 120000 index 00000000..ed118107 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-drive @@ -0,0 +1 @@ +../../.agents/skills/gws-drive \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-drive-upload b/plugins/google-workspace/.claude/skills/gws-drive-upload new file mode 120000 index 00000000..7b3256cc --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-drive-upload @@ -0,0 +1 @@ +../../.agents/skills/gws-drive-upload \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-events b/plugins/google-workspace/.claude/skills/gws-events new file mode 120000 index 00000000..849df899 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-events @@ -0,0 +1 @@ +../../.agents/skills/gws-events \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-events-renew b/plugins/google-workspace/.claude/skills/gws-events-renew new file mode 120000 index 00000000..cbaff727 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-events-renew @@ -0,0 +1 @@ +../../.agents/skills/gws-events-renew \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-events-subscribe b/plugins/google-workspace/.claude/skills/gws-events-subscribe new file mode 120000 index 00000000..bb8df6e1 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-events-subscribe @@ -0,0 +1 @@ +../../.agents/skills/gws-events-subscribe \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-forms b/plugins/google-workspace/.claude/skills/gws-forms new file mode 120000 index 00000000..cbaf837f --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-forms @@ -0,0 +1 @@ +../../.agents/skills/gws-forms \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-gmail b/plugins/google-workspace/.claude/skills/gws-gmail new file mode 120000 index 00000000..539fd358 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-gmail @@ -0,0 +1 @@ +../../.agents/skills/gws-gmail \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-gmail-forward b/plugins/google-workspace/.claude/skills/gws-gmail-forward new file mode 120000 index 00000000..cfbf67c3 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-gmail-forward @@ -0,0 +1 @@ +../../.agents/skills/gws-gmail-forward \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-gmail-read b/plugins/google-workspace/.claude/skills/gws-gmail-read new file mode 120000 index 00000000..c5b6c147 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-gmail-read @@ -0,0 +1 @@ +../../.agents/skills/gws-gmail-read \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-gmail-reply b/plugins/google-workspace/.claude/skills/gws-gmail-reply new file mode 120000 index 00000000..bf46a8b6 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-gmail-reply @@ -0,0 +1 @@ +../../.agents/skills/gws-gmail-reply \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-gmail-reply-all b/plugins/google-workspace/.claude/skills/gws-gmail-reply-all new file mode 120000 index 00000000..a91ece4a --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-gmail-reply-all @@ -0,0 +1 @@ +../../.agents/skills/gws-gmail-reply-all \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-gmail-send b/plugins/google-workspace/.claude/skills/gws-gmail-send new file mode 120000 index 00000000..8cfd6b73 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-gmail-send @@ -0,0 +1 @@ +../../.agents/skills/gws-gmail-send \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-gmail-triage b/plugins/google-workspace/.claude/skills/gws-gmail-triage new file mode 120000 index 00000000..6275cd8d --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-gmail-triage @@ -0,0 +1 @@ +../../.agents/skills/gws-gmail-triage \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-gmail-watch b/plugins/google-workspace/.claude/skills/gws-gmail-watch new file mode 120000 index 00000000..42ff1bc9 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-gmail-watch @@ -0,0 +1 @@ +../../.agents/skills/gws-gmail-watch \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-keep b/plugins/google-workspace/.claude/skills/gws-keep new file mode 120000 index 00000000..9c985289 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-keep @@ -0,0 +1 @@ +../../.agents/skills/gws-keep \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-meet b/plugins/google-workspace/.claude/skills/gws-meet new file mode 120000 index 00000000..275f80c4 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-meet @@ -0,0 +1 @@ +../../.agents/skills/gws-meet \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-modelarmor b/plugins/google-workspace/.claude/skills/gws-modelarmor new file mode 120000 index 00000000..e0d87302 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-modelarmor @@ -0,0 +1 @@ +../../.agents/skills/gws-modelarmor \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-modelarmor-create-template b/plugins/google-workspace/.claude/skills/gws-modelarmor-create-template new file mode 120000 index 00000000..7beb6f26 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-modelarmor-create-template @@ -0,0 +1 @@ +../../.agents/skills/gws-modelarmor-create-template \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-modelarmor-sanitize-prompt b/plugins/google-workspace/.claude/skills/gws-modelarmor-sanitize-prompt new file mode 120000 index 00000000..2c2e075a --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-modelarmor-sanitize-prompt @@ -0,0 +1 @@ +../../.agents/skills/gws-modelarmor-sanitize-prompt \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-modelarmor-sanitize-response b/plugins/google-workspace/.claude/skills/gws-modelarmor-sanitize-response new file mode 120000 index 00000000..6ef3a328 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-modelarmor-sanitize-response @@ -0,0 +1 @@ +../../.agents/skills/gws-modelarmor-sanitize-response \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-people b/plugins/google-workspace/.claude/skills/gws-people new file mode 120000 index 00000000..1c1d6bef --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-people @@ -0,0 +1 @@ +../../.agents/skills/gws-people \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-script b/plugins/google-workspace/.claude/skills/gws-script new file mode 120000 index 00000000..571c9498 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-script @@ -0,0 +1 @@ +../../.agents/skills/gws-script \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-script-push b/plugins/google-workspace/.claude/skills/gws-script-push new file mode 120000 index 00000000..a690bf67 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-script-push @@ -0,0 +1 @@ +../../.agents/skills/gws-script-push \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-shared b/plugins/google-workspace/.claude/skills/gws-shared new file mode 120000 index 00000000..55b3ceda --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-shared @@ -0,0 +1 @@ +../../.agents/skills/gws-shared \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-sheets b/plugins/google-workspace/.claude/skills/gws-sheets new file mode 120000 index 00000000..89c52b60 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-sheets @@ -0,0 +1 @@ +../../.agents/skills/gws-sheets \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-sheets-append b/plugins/google-workspace/.claude/skills/gws-sheets-append new file mode 120000 index 00000000..63e73807 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-sheets-append @@ -0,0 +1 @@ +../../.agents/skills/gws-sheets-append \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-sheets-read b/plugins/google-workspace/.claude/skills/gws-sheets-read new file mode 120000 index 00000000..d652ba4b --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-sheets-read @@ -0,0 +1 @@ +../../.agents/skills/gws-sheets-read \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-slides b/plugins/google-workspace/.claude/skills/gws-slides new file mode 120000 index 00000000..6bdd6344 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-slides @@ -0,0 +1 @@ +../../.agents/skills/gws-slides \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-tasks b/plugins/google-workspace/.claude/skills/gws-tasks new file mode 120000 index 00000000..ce75ec88 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-tasks @@ -0,0 +1 @@ +../../.agents/skills/gws-tasks \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-workflow b/plugins/google-workspace/.claude/skills/gws-workflow new file mode 120000 index 00000000..44ee741e --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-workflow @@ -0,0 +1 @@ +../../.agents/skills/gws-workflow \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-workflow-email-to-task b/plugins/google-workspace/.claude/skills/gws-workflow-email-to-task new file mode 120000 index 00000000..a48618f9 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-workflow-email-to-task @@ -0,0 +1 @@ +../../.agents/skills/gws-workflow-email-to-task \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-workflow-file-announce b/plugins/google-workspace/.claude/skills/gws-workflow-file-announce new file mode 120000 index 00000000..7b32e249 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-workflow-file-announce @@ -0,0 +1 @@ +../../.agents/skills/gws-workflow-file-announce \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-workflow-meeting-prep b/plugins/google-workspace/.claude/skills/gws-workflow-meeting-prep new file mode 120000 index 00000000..e58774cd --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-workflow-meeting-prep @@ -0,0 +1 @@ +../../.agents/skills/gws-workflow-meeting-prep \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-workflow-standup-report b/plugins/google-workspace/.claude/skills/gws-workflow-standup-report new file mode 120000 index 00000000..89398327 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-workflow-standup-report @@ -0,0 +1 @@ +../../.agents/skills/gws-workflow-standup-report \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/gws-workflow-weekly-digest b/plugins/google-workspace/.claude/skills/gws-workflow-weekly-digest new file mode 120000 index 00000000..62bf9d0c --- /dev/null +++ b/plugins/google-workspace/.claude/skills/gws-workflow-weekly-digest @@ -0,0 +1 @@ +../../.agents/skills/gws-workflow-weekly-digest \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-content-creator b/plugins/google-workspace/.claude/skills/persona-content-creator new file mode 120000 index 00000000..75d3653f --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-content-creator @@ -0,0 +1 @@ +../../.agents/skills/persona-content-creator \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-customer-support b/plugins/google-workspace/.claude/skills/persona-customer-support new file mode 120000 index 00000000..7046bd1e --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-customer-support @@ -0,0 +1 @@ +../../.agents/skills/persona-customer-support \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-event-coordinator b/plugins/google-workspace/.claude/skills/persona-event-coordinator new file mode 120000 index 00000000..796a032f --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-event-coordinator @@ -0,0 +1 @@ +../../.agents/skills/persona-event-coordinator \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-exec-assistant b/plugins/google-workspace/.claude/skills/persona-exec-assistant new file mode 120000 index 00000000..b6fa265f --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-exec-assistant @@ -0,0 +1 @@ +../../.agents/skills/persona-exec-assistant \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-hr-coordinator b/plugins/google-workspace/.claude/skills/persona-hr-coordinator new file mode 120000 index 00000000..67b4f8d1 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-hr-coordinator @@ -0,0 +1 @@ +../../.agents/skills/persona-hr-coordinator \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-it-admin b/plugins/google-workspace/.claude/skills/persona-it-admin new file mode 120000 index 00000000..7c227ef4 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-it-admin @@ -0,0 +1 @@ +../../.agents/skills/persona-it-admin \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-project-manager b/plugins/google-workspace/.claude/skills/persona-project-manager new file mode 120000 index 00000000..40533673 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-project-manager @@ -0,0 +1 @@ +../../.agents/skills/persona-project-manager \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-researcher b/plugins/google-workspace/.claude/skills/persona-researcher new file mode 120000 index 00000000..037318f2 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-researcher @@ -0,0 +1 @@ +../../.agents/skills/persona-researcher \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-sales-ops b/plugins/google-workspace/.claude/skills/persona-sales-ops new file mode 120000 index 00000000..dab84f62 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-sales-ops @@ -0,0 +1 @@ +../../.agents/skills/persona-sales-ops \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/persona-team-lead b/plugins/google-workspace/.claude/skills/persona-team-lead new file mode 120000 index 00000000..ba21e938 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/persona-team-lead @@ -0,0 +1 @@ +../../.agents/skills/persona-team-lead \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-backup-sheet-as-csv b/plugins/google-workspace/.claude/skills/recipe-backup-sheet-as-csv new file mode 120000 index 00000000..d2cfc075 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-backup-sheet-as-csv @@ -0,0 +1 @@ +../../.agents/skills/recipe-backup-sheet-as-csv \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-batch-invite-to-event b/plugins/google-workspace/.claude/skills/recipe-batch-invite-to-event new file mode 120000 index 00000000..17a380bc --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-batch-invite-to-event @@ -0,0 +1 @@ +../../.agents/skills/recipe-batch-invite-to-event \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-block-focus-time b/plugins/google-workspace/.claude/skills/recipe-block-focus-time new file mode 120000 index 00000000..9e23d515 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-block-focus-time @@ -0,0 +1 @@ +../../.agents/skills/recipe-block-focus-time \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-bulk-download-folder b/plugins/google-workspace/.claude/skills/recipe-bulk-download-folder new file mode 120000 index 00000000..e046300e --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-bulk-download-folder @@ -0,0 +1 @@ +../../.agents/skills/recipe-bulk-download-folder \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-collect-form-responses b/plugins/google-workspace/.claude/skills/recipe-collect-form-responses new file mode 120000 index 00000000..cbe6307a --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-collect-form-responses @@ -0,0 +1 @@ +../../.agents/skills/recipe-collect-form-responses \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-compare-sheet-tabs b/plugins/google-workspace/.claude/skills/recipe-compare-sheet-tabs new file mode 120000 index 00000000..2d8dc0ad --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-compare-sheet-tabs @@ -0,0 +1 @@ +../../.agents/skills/recipe-compare-sheet-tabs \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-copy-sheet-for-new-month b/plugins/google-workspace/.claude/skills/recipe-copy-sheet-for-new-month new file mode 120000 index 00000000..41226253 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-copy-sheet-for-new-month @@ -0,0 +1 @@ +../../.agents/skills/recipe-copy-sheet-for-new-month \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-classroom-course b/plugins/google-workspace/.claude/skills/recipe-create-classroom-course new file mode 120000 index 00000000..5899da45 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-classroom-course @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-classroom-course \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-doc-from-template b/plugins/google-workspace/.claude/skills/recipe-create-doc-from-template new file mode 120000 index 00000000..78d8afd7 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-doc-from-template @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-doc-from-template \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-events-from-sheet b/plugins/google-workspace/.claude/skills/recipe-create-events-from-sheet new file mode 120000 index 00000000..b6422472 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-events-from-sheet @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-events-from-sheet \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-expense-tracker b/plugins/google-workspace/.claude/skills/recipe-create-expense-tracker new file mode 120000 index 00000000..dc100069 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-expense-tracker @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-expense-tracker \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-feedback-form b/plugins/google-workspace/.claude/skills/recipe-create-feedback-form new file mode 120000 index 00000000..0264f187 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-feedback-form @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-feedback-form \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-gmail-filter b/plugins/google-workspace/.claude/skills/recipe-create-gmail-filter new file mode 120000 index 00000000..aae6809d --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-gmail-filter @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-gmail-filter \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-meet-space b/plugins/google-workspace/.claude/skills/recipe-create-meet-space new file mode 120000 index 00000000..8118cff1 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-meet-space @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-meet-space \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-presentation b/plugins/google-workspace/.claude/skills/recipe-create-presentation new file mode 120000 index 00000000..479e35ae --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-presentation @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-presentation \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-shared-drive b/plugins/google-workspace/.claude/skills/recipe-create-shared-drive new file mode 120000 index 00000000..7e1aa709 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-shared-drive @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-shared-drive \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-task-list b/plugins/google-workspace/.claude/skills/recipe-create-task-list new file mode 120000 index 00000000..1b5455cb --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-task-list @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-task-list \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-create-vacation-responder b/plugins/google-workspace/.claude/skills/recipe-create-vacation-responder new file mode 120000 index 00000000..6f714712 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-create-vacation-responder @@ -0,0 +1 @@ +../../.agents/skills/recipe-create-vacation-responder \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-draft-email-from-doc b/plugins/google-workspace/.claude/skills/recipe-draft-email-from-doc new file mode 120000 index 00000000..18bcfce2 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-draft-email-from-doc @@ -0,0 +1 @@ +../../.agents/skills/recipe-draft-email-from-doc \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-email-drive-link b/plugins/google-workspace/.claude/skills/recipe-email-drive-link new file mode 120000 index 00000000..ee653c46 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-email-drive-link @@ -0,0 +1 @@ +../../.agents/skills/recipe-email-drive-link \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-find-free-time b/plugins/google-workspace/.claude/skills/recipe-find-free-time new file mode 120000 index 00000000..3e27bf1e --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-find-free-time @@ -0,0 +1 @@ +../../.agents/skills/recipe-find-free-time \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-find-large-files b/plugins/google-workspace/.claude/skills/recipe-find-large-files new file mode 120000 index 00000000..2a83c5d7 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-find-large-files @@ -0,0 +1 @@ +../../.agents/skills/recipe-find-large-files \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-forward-labeled-emails b/plugins/google-workspace/.claude/skills/recipe-forward-labeled-emails new file mode 120000 index 00000000..cb1302fb --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-forward-labeled-emails @@ -0,0 +1 @@ +../../.agents/skills/recipe-forward-labeled-emails \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-generate-report-from-sheet b/plugins/google-workspace/.claude/skills/recipe-generate-report-from-sheet new file mode 120000 index 00000000..475eab4e --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-generate-report-from-sheet @@ -0,0 +1 @@ +../../.agents/skills/recipe-generate-report-from-sheet \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-label-and-archive-emails b/plugins/google-workspace/.claude/skills/recipe-label-and-archive-emails new file mode 120000 index 00000000..628ba559 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-label-and-archive-emails @@ -0,0 +1 @@ +../../.agents/skills/recipe-label-and-archive-emails \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-log-deal-update b/plugins/google-workspace/.claude/skills/recipe-log-deal-update new file mode 120000 index 00000000..47f06c92 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-log-deal-update @@ -0,0 +1 @@ +../../.agents/skills/recipe-log-deal-update \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-organize-drive-folder b/plugins/google-workspace/.claude/skills/recipe-organize-drive-folder new file mode 120000 index 00000000..26628af1 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-organize-drive-folder @@ -0,0 +1 @@ +../../.agents/skills/recipe-organize-drive-folder \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-plan-weekly-schedule b/plugins/google-workspace/.claude/skills/recipe-plan-weekly-schedule new file mode 120000 index 00000000..37095348 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-plan-weekly-schedule @@ -0,0 +1 @@ +../../.agents/skills/recipe-plan-weekly-schedule \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-post-mortem-setup b/plugins/google-workspace/.claude/skills/recipe-post-mortem-setup new file mode 120000 index 00000000..661eb6f8 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-post-mortem-setup @@ -0,0 +1 @@ +../../.agents/skills/recipe-post-mortem-setup \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-reschedule-meeting b/plugins/google-workspace/.claude/skills/recipe-reschedule-meeting new file mode 120000 index 00000000..db48b554 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-reschedule-meeting @@ -0,0 +1 @@ +../../.agents/skills/recipe-reschedule-meeting \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-review-meet-participants b/plugins/google-workspace/.claude/skills/recipe-review-meet-participants new file mode 120000 index 00000000..61b3124c --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-review-meet-participants @@ -0,0 +1 @@ +../../.agents/skills/recipe-review-meet-participants \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-review-overdue-tasks b/plugins/google-workspace/.claude/skills/recipe-review-overdue-tasks new file mode 120000 index 00000000..dde6965c --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-review-overdue-tasks @@ -0,0 +1 @@ +../../.agents/skills/recipe-review-overdue-tasks \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-save-email-attachments b/plugins/google-workspace/.claude/skills/recipe-save-email-attachments new file mode 120000 index 00000000..72f9ba75 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-save-email-attachments @@ -0,0 +1 @@ +../../.agents/skills/recipe-save-email-attachments \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-save-email-to-doc b/plugins/google-workspace/.claude/skills/recipe-save-email-to-doc new file mode 120000 index 00000000..00568e8d --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-save-email-to-doc @@ -0,0 +1 @@ +../../.agents/skills/recipe-save-email-to-doc \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-schedule-recurring-event b/plugins/google-workspace/.claude/skills/recipe-schedule-recurring-event new file mode 120000 index 00000000..ea6ead0d --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-schedule-recurring-event @@ -0,0 +1 @@ +../../.agents/skills/recipe-schedule-recurring-event \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-send-team-announcement b/plugins/google-workspace/.claude/skills/recipe-send-team-announcement new file mode 120000 index 00000000..e59007b4 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-send-team-announcement @@ -0,0 +1 @@ +../../.agents/skills/recipe-send-team-announcement \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-share-doc-and-notify b/plugins/google-workspace/.claude/skills/recipe-share-doc-and-notify new file mode 120000 index 00000000..625ac380 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-share-doc-and-notify @@ -0,0 +1 @@ +../../.agents/skills/recipe-share-doc-and-notify \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-share-event-materials b/plugins/google-workspace/.claude/skills/recipe-share-event-materials new file mode 120000 index 00000000..696b79ad --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-share-event-materials @@ -0,0 +1 @@ +../../.agents/skills/recipe-share-event-materials \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-share-folder-with-team b/plugins/google-workspace/.claude/skills/recipe-share-folder-with-team new file mode 120000 index 00000000..d7691960 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-share-folder-with-team @@ -0,0 +1 @@ +../../.agents/skills/recipe-share-folder-with-team \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-sync-contacts-to-sheet b/plugins/google-workspace/.claude/skills/recipe-sync-contacts-to-sheet new file mode 120000 index 00000000..43f300f9 --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-sync-contacts-to-sheet @@ -0,0 +1 @@ +../../.agents/skills/recipe-sync-contacts-to-sheet \ No newline at end of file diff --git a/plugins/google-workspace/.claude/skills/recipe-watch-drive-changes b/plugins/google-workspace/.claude/skills/recipe-watch-drive-changes new file mode 120000 index 00000000..b3fa5bdb --- /dev/null +++ b/plugins/google-workspace/.claude/skills/recipe-watch-drive-changes @@ -0,0 +1 @@ +../../.agents/skills/recipe-watch-drive-changes \ No newline at end of file diff --git a/plugins/greptile/.agents/skills/greploop/SKILL.md b/plugins/greptile/.agents/skills/greploop/SKILL.md index 43ddb608..43a91fd6 100644 --- a/plugins/greptile/.agents/skills/greploop/SKILL.md +++ b/plugins/greptile/.agents/skills/greploop/SKILL.md @@ -116,14 +116,23 @@ Then poll for the Greptile check run to complete: ```bash HEAD_SHA=$(gh pr view --json headRefOid -q .headRefOid) +ATTEMPTS=0 +MAX_ATTEMPTS=60 +POLL_INTERVAL_SECONDS=10 while true; do + ATTEMPTS=$((ATTEMPTS + 1)) + if [ "$ATTEMPTS" -gt "$MAX_ATTEMPTS" ]; then + echo "Timed out waiting for the Greptile check run after approximately 10 minutes." >&2 + exit 1 + fi + GREPTILE_CHECK=$(gh api "repos/{owner}/{repo}/commits/$HEAD_SHA/check-runs" \ --jq '.check_runs[] | select(.name | test("greptile"; "i"))' 2>/dev/null) if [ -z "$GREPTILE_CHECK" ]; then echo "Waiting for Greptile check to appear..." - sleep 5 + sleep "$POLL_INTERVAL_SECONDS" continue fi @@ -140,10 +149,12 @@ while true; do fi echo "Waiting for Greptile... (status: $STATUS)" - sleep 10 + sleep "$POLL_INTERVAL_SECONDS" done ``` +If polling times out, stop the greploop workflow and report the timeout. Do not continue with stale or missing review results. + **GitLab** — check if Greptile is already running before posting a trigger comment: ```bash @@ -165,8 +176,17 @@ Then poll for the Greptile pipeline job to complete (see [GitLab API reference]( ```bash HEAD_SHA=$(glab mr view --output json | jq -r '.sha') +ATTEMPTS=0 +MAX_ATTEMPTS=60 +POLL_INTERVAL_SECONDS=10 while true; do + ATTEMPTS=$((ATTEMPTS + 1)) + if [ "$ATTEMPTS" -gt "$MAX_ATTEMPTS" ]; then + echo "Timed out waiting for the Greptile pipeline job after approximately 10 minutes." >&2 + exit 1 + fi + PIPELINES=$(glab api "projects/:fullpath/merge_requests//pipelines") # Find the most recent pipeline for this SHA PIPELINE_ID=$(echo "$PIPELINES" | jq -r --arg sha "$HEAD_SHA" \ @@ -174,7 +194,7 @@ while true; do if [ -z "$PIPELINE_ID" ]; then echo "Waiting for Greptile pipeline to appear..." - sleep 5 + sleep "$POLL_INTERVAL_SECONDS" continue fi @@ -183,7 +203,7 @@ while true; do if [ -z "$GREPTILE_JOB" ]; then echo "Waiting for Greptile job to appear..." - sleep 5 + sleep "$POLL_INTERVAL_SECONDS" continue fi @@ -195,10 +215,12 @@ while true; do fi echo "Waiting for Greptile... (status: $JOB_STATUS)" - sleep 10 + sleep "$POLL_INTERVAL_SECONDS" done ``` +If polling times out, stop the greploop workflow and report the timeout. Do not continue with stale or missing review results. + #### B. Fetch Greptile review results Greptile may surface its score in several places — check **all** of the relevant sources: diff --git a/plugins/greptile/.claude/skills/check-pr b/plugins/greptile/.claude/skills/check-pr new file mode 120000 index 00000000..b5085f31 --- /dev/null +++ b/plugins/greptile/.claude/skills/check-pr @@ -0,0 +1 @@ +../../.agents/skills/check-pr \ No newline at end of file diff --git a/plugins/greptile/.claude/skills/cli-review b/plugins/greptile/.claude/skills/cli-review new file mode 120000 index 00000000..f7f9386b --- /dev/null +++ b/plugins/greptile/.claude/skills/cli-review @@ -0,0 +1 @@ +../../.agents/skills/cli-review \ No newline at end of file diff --git a/plugins/greptile/.claude/skills/greploop b/plugins/greptile/.claude/skills/greploop new file mode 120000 index 00000000..f7b25640 --- /dev/null +++ b/plugins/greptile/.claude/skills/greploop @@ -0,0 +1 @@ +../../.agents/skills/greploop \ No newline at end of file diff --git a/plugins/greptile/agent/skills/greploop/SKILL.md b/plugins/greptile/agent/skills/greploop/SKILL.md index dbbb2261..ac6a3870 100644 --- a/plugins/greptile/agent/skills/greploop/SKILL.md +++ b/plugins/greptile/agent/skills/greploop/SKILL.md @@ -106,14 +106,23 @@ Then poll for the Greptile check run to complete: ```bash HEAD_SHA=$(gh pr view --json headRefOid -q .headRefOid) +ATTEMPTS=0 +MAX_ATTEMPTS=60 +POLL_INTERVAL_SECONDS=10 while true; do + ATTEMPTS=$((ATTEMPTS + 1)) + if [ "$ATTEMPTS" -gt "$MAX_ATTEMPTS" ]; then + echo "Timed out waiting for the Greptile check run after approximately 10 minutes." >&2 + exit 1 + fi + GREPTILE_CHECK=$(gh api "repos/{owner}/{repo}/commits/$HEAD_SHA/check-runs" \ --jq '.check_runs[] | select(.name | test("greptile"; "i"))' 2>/dev/null) if [ -z "$GREPTILE_CHECK" ]; then echo "Waiting for Greptile check to appear..." - sleep 5 + sleep "$POLL_INTERVAL_SECONDS" continue fi @@ -130,10 +139,12 @@ while true; do fi echo "Waiting for Greptile... (status: $STATUS)" - sleep 10 + sleep "$POLL_INTERVAL_SECONDS" done ``` +If polling times out, stop the greploop workflow and report the timeout. Do not continue with stale or missing review results. + **GitLab** — check if Greptile is already running before posting a trigger comment: ```bash @@ -155,8 +166,17 @@ Then poll for the Greptile pipeline job to complete (see [GitLab API reference]( ```bash HEAD_SHA=$(glab mr view --output json | jq -r '.sha') +ATTEMPTS=0 +MAX_ATTEMPTS=60 +POLL_INTERVAL_SECONDS=10 while true; do + ATTEMPTS=$((ATTEMPTS + 1)) + if [ "$ATTEMPTS" -gt "$MAX_ATTEMPTS" ]; then + echo "Timed out waiting for the Greptile pipeline job after approximately 10 minutes." >&2 + exit 1 + fi + PIPELINES=$(glab api "projects/:fullpath/merge_requests//pipelines") # Find the most recent pipeline for this SHA PIPELINE_ID=$(echo "$PIPELINES" | jq -r --arg sha "$HEAD_SHA" \ @@ -164,7 +184,7 @@ while true; do if [ -z "$PIPELINE_ID" ]; then echo "Waiting for Greptile pipeline to appear..." - sleep 5 + sleep "$POLL_INTERVAL_SECONDS" continue fi @@ -173,7 +193,7 @@ while true; do if [ -z "$GREPTILE_JOB" ]; then echo "Waiting for Greptile job to appear..." - sleep 5 + sleep "$POLL_INTERVAL_SECONDS" continue fi @@ -185,10 +205,12 @@ while true; do fi echo "Waiting for Greptile... (status: $JOB_STATUS)" - sleep 10 + sleep "$POLL_INTERVAL_SECONDS" done ``` +If polling times out, stop the greploop workflow and report the timeout. Do not continue with stale or missing review results. + #### B. Fetch Greptile review results Greptile may surface its score in several places — check **all** of the relevant sources: diff --git a/plugins/greptile/skills-lock.json b/plugins/greptile/skills-lock.json index 9127be01..00e4fe07 100644 --- a/plugins/greptile/skills-lock.json +++ b/plugins/greptile/skills-lock.json @@ -17,7 +17,7 @@ "source": "greptileai/skills", "sourceType": "github", "skillPath": "greploop/SKILL.md", - "computedHash": "13849c1cfc853dd4ebdef9caa05e73ea322355dcee6114df64879fdf37ad6f19" + "computedHash": "d37000d0e461ccf46116abdf371ad9b0beaaf7d2ed7365fb91f10903bc51cd85" } } } diff --git a/plugins/lavish/.agents/skills/lavish/SKILL.md b/plugins/lavish/.agents/skills/lavish/SKILL.md index fac4ab56..fa5eef9c 100644 --- a/plugins/lavish/.agents/skills/lavish/SKILL.md +++ b/plugins/lavish/.agents/skills/lavish/SKILL.md @@ -1,12 +1,12 @@ --- name: lavish description: Turn complex or visual agent responses into rich, reviewable HTML artifacts the user can annotate and send feedback on, using the lavish-axi CLI. Use when about to give a plan, comparison, diagram, table, code diff, report, or anything easier to grasp visually than as prose. -argument-hint: -author: Kun Chen (kunchenguid) +license: MIT metadata: - hermes: - tags: [html, review, artifacts, visualization] - category: productivity + author: Kun Chen (kunchenguid) + argument-hint: + hermes-tags: html, review, artifacts, visualization + hermes-category: productivity --- # Lavish Editor @@ -15,6 +15,7 @@ Lavish Editor helps agents turn rich HTML artifacts into collaborative human rev You do not need lavish-axi installed globally - invoke it with `npx -y lavish-axi `. If lavish-axi output shows a follow-up command starting with `lavish-axi`, run it as `npx -y lavish-axi ...` instead. +In restricted subprocess sandboxes, CI, or agent harnesses where `npx -y` exits opaquely (for example with status 216), use an already-installed copy directly: `node "$(npm root)/lavish-axi/dist/cli.mjs" ` for a local install, `node "$(npm root -g)/lavish-axi/dist/cli.mjs" ` for a global install, or the bare `lavish-axi ` bin after installing once. ## Request @@ -31,13 +32,22 @@ Use lavish-axi when the user asks for a visual artifact, HTML explainer, interac 1. Create the HTML artifact (default location `.lavish/.html` in the working directory). 2. Run `npx -y lavish-axi ` to open or resume a review session in the browser. -3. Run `npx -y lavish-axi poll ` to long-poll for the user's annotations, queued prompts, and browser-reported `layout_warnings`. - The poll stays silent until the user acts or the real browser reports fresh layout warnings - leave it running, never kill it. - If your harness limits how long a foreground command may run, run the poll as a background task; if it gets killed or times out anyway, just re-run it - queued feedback is never lost. -4. If poll returns `layout_warnings`, follow the returned `next_step`: fix and re-check fresh error-severity findings, but proceed with a note instead of looping when every current warning is persistent or low-severity. -5. Apply human feedback, then poll again with `--agent-reply ""` to reply in the browser and keep the loop going. + If the output carries a `self_paint_warning`, fix the unpainted page surface and save before polling - Lavish live-reloads the artifact. +3. Run `npx -y lavish-axi poll ` to long-poll for the user's annotations and queued prompts. + On the first poll, prefer `--agent-reply ""` so the conversation panel opens with context. + Browser-detected layout issues are filed passively in the user's Layout issues inbox and arrive as an ordinary `layout-warnings` prompt only when the user selects and queues them. Never edit an issue the user has not queued. The only response that arrives without user action is `artifact_failures`, when the review surface itself is unusable. + The poll stays silent until the user acts or a fatal artifact failure makes the review surface unusable - leave it running, never kill it. + Cosmetic, intentional, transient, tiny, and uncertain observations remain silent. + Keep the poll in the foreground by default and let it return the feedback directly to the agent. + A background poll is allowed only through a harness-native tracked background-job facility whose completion result is guaranteed to resume or notify the same agent. + Never use `nohup`, shell `&`, `disown`, redirected fire-and-forget processes, or a detached terminal without an explicit verified callback merely to keep polling alive. + If the harness has no completion-aware background facility, use the foreground poll or first wire a verified wake callback into the surrounding supervisor. + Do not tell the user the artifact is being monitored until that wake path is live. + If the poll gets killed or times out anyway, just re-run it - queued feedback is never lost. +4. If poll returns feedback, apply the user's prompts. A `layout-warnings` prompt is an explicit repair request; apply every listed fix in one pass before saving, and let Lavish re-check it after a newer artifact load. +5. Apply human feedback, then poll again with `--agent-reply ""` to reply in the browser and keep the loop going under the same foreground-or-verified-wake-path rule. 6. Run `npx -y lavish-axi end ` when the review is finished. -7. If the user ends the session from the browser instead, `npx -y lavish-axi ` refuses to reopen it and says so - only pass `--reopen` when the user asks for further review or something genuinely important needs their visual attention. Otherwise deliver remaining updates directly in this conversation. +7. `Send & End` ends the session. Its final feedback is still delivered once. After that response, polling stops, and the agent must not reopen the session uninvited. Deliver any remaining updates directly in this conversation. ## Visual guidance @@ -51,7 +61,7 @@ Use lavish-axi when the user asks for a visual artifact, HTML explainer, interac Run `npx -y lavish-axi playbook ` for focused, detailed guidance on any of these. One artifact often combines several playbooks (for example a plan that includes a comparison and a diagram), so MUST open each matching playbook before writing HTML. -For flows, architecture, state, or sequence diagrams, do not hand-build boxes-and-arrows from div/flexbox; open the diagram playbook and use Mermaid unless SVG is needed for richly annotated nodes. +For flows, architecture, state, or sequence diagrams, do not hand-build boxes-and-arrows from div/flexbox; open the diagram playbook and use the theme-aware Mermaid snippet from `npx -y lavish-axi design` unless SVG is needed for richly annotated nodes. - `diagram` - Map relationships, flows, state, and architecture - `table` - Turn dense records into scan-friendly review surfaces @@ -66,11 +76,12 @@ For flows, architecture, state, or sequence diagrams, do not hand-build boxes-an - Run `npx -y lavish-axi ` to open or resume a Lavish Editor session. If the user explicitly ended the session from the browser, this refuses to reopen it and explains why instead of reopening uninvited - pass `--reopen` only when the user asks for further review or something important needs their visual attention - Unless the user specifies another location, create HTML artifacts in the current working directory under `.lavish/` - Lavish serves the html file through a local express.js server. If your html needs to reference other filesystem assets such as images, CSS, fonts, and local scripts, copy them into the same directory as the HTML file, then reference them with relative paths from that directory. Never prepend `/` to those asset paths - root paths won't work -- Run `npx -y lavish-axi poll ` to wait for user feedback or browser-reported layout_warnings. It long-polls and stays silent until the user sends feedback, ends the session, or the real browser reports fresh layout_warnings, so leave it running - never kill it. Fix and re-check fresh error-severity layout_warnings before involving the human; if the poll says every current warning is persistent or low-severity, proceed with a note instead of looping. If your harness limits how long a foreground command may run, run the poll as a background task; if it gets killed or times out anyway, just re-run it - queued feedback is never lost. When it reports the session ended, stop polling and do not reopen it uninvited - deliver remaining updates in this conversation instead +- Run `npx -y lavish-axi poll ` to wait for user feedback. It long-polls and stays silent until the user sends feedback or ends the session, so leave it running - never kill it. Detected layout issues never return this poll: the browser files them in the user's Layout issues inbox in the Lavish top bar, and they arrive as an ordinary tag "layout-warnings" prompt only when the user selects them and queues the fixes. Never edit the artifact to chase a layout issue the user has not queued. The only exception is a fatal artifact_failures response, which means the review surface itself could not be used. Keep the poll in the foreground by default and let it return the feedback directly to the agent. A background poll is allowed only through a harness-native tracked background-job facility whose completion result is guaranteed to resume or notify the same agent. Never use `nohup`, shell `&`, `disown`, redirected fire-and-forget processes, or a detached terminal without an explicit verified callback merely to keep polling alive. If the harness has no completion-aware background facility, use the foreground poll or first wire a verified wake callback into the surrounding supervisor. Do not tell the user the artifact is being monitored until that wake path is live. If the poll gets killed or times out anyway, just re-run it - queued feedback is never lost. `Send & End` ends the session. Its final feedback is still delivered once. After that response, polling stops, and the agent must not reopen the session uninvited. +- Rendered Mermaid diagrams in `.mermaid` containers become embedded, editable Excalidraw whiteboards in the browser (click a diagram to unlock editing; a Fullscreen action opens it over the whole viewport) - flowchart, sequence, class, ER, and state diagrams convert to editable shapes; other types embed as an image to draw on. Scenes autosave locally; when a reload detects a changed Mermaid source, the reviewer explicitly chooses to re-convert and discard saved edits or keep editing the saved scene. Standalone and exported copies still render plain Mermaid. Queue feedback adds a prompt to the Conversation panel; when the user sends it, poll returns a tag "whiteboard" prompt carrying a bounded edit summary plus local scenePath (.excalidraw JSON) and previewPath (PNG) files - read the summary first, open the files only when needed, then apply the edits by updating the Mermaid source in the artifact (never try to write the scene back) - Run `npx -y lavish-axi end ` to end a session as the agent - ending it this way still allows a plain reopen later. When the user ends it from the browser instead, a later `npx -y lavish-axi ` refuses to reopen it without `--reopen` - Run `npx -y lavish-axi export [--out ]` to write a portable copy of the artifact - one HTML file with its LOCAL assets inlined - so it opens with no Lavish server and no sibling files. Remote CDN/font references are left as links, so it needs network to render those. Users can also export from the browser chrome's overflow menu - Run `npx -y lavish-axi share [--password ] [--token ]` to publish the artifact on ht-ml.app (https://ht-ml.app), a third-party hosting service not part of Lavish, and get back a visitable URL. Shares are PUBLIC by default, so anyone with the link can open them. Pass --password to publish a PRIVATE password-protected page; viewers must supply the password to view. Local assets are inlined; remote refs load over the network. It returns the url plus a secret update_key for managing the page later. Use --token or LAVISH_AXI_HTML_APP_TOKEN only when you have an optional bearer token; it is never required. Users can also publish from the browser chrome's overflow menu - Run `npx -y lavish-axi stop` to shut down the background server (it also self-stops when idle or after the last session ends with nothing connected) - Run `npx -y lavish-axi playbook ` for focused artifact guidance. One artifact often combines several playbooks (for example a plan that includes a comparison and a diagram), so MUST open each matching playbook before writing HTML. -- Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML, decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's design system: Tailwind or theme config, shared CSS variables or design tokens, component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN - run `npx -y lavish-axi design` for a content-to-playbook router, a copy-pasteable CDN snippet, a Mermaid CDN snippet/init for diagrams, and the DaisyUI component reference, and prefer the Tailwind/DaisyUI CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. When you deliver the artifact, state which of the three design sources you used and why. +- Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML: Decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's design system: Tailwind or theme config, shared CSS variables or design tokens, component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN, and prefer that CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. Run `npx -y lavish-axi design` for a content-to-playbook router, a copy-pasteable CDN snippet, a Mermaid CDN snippet/init for diagrams, and the DaisyUI component reference. When you deliver the artifact, state which of the three design sources you used and why. - Use lavish-axi when the user asks for a visual artifact, HTML explainer, interactive prototype, review surface, product or technical plan, comparison, report, or browser-based feedback loop diff --git a/plugins/lavish/.claude/skills/lavish b/plugins/lavish/.claude/skills/lavish new file mode 120000 index 00000000..0fa00290 --- /dev/null +++ b/plugins/lavish/.claude/skills/lavish @@ -0,0 +1 @@ +../../.agents/skills/lavish \ No newline at end of file diff --git a/plugins/lavish/agent/skills/lavish/SKILL.md b/plugins/lavish/agent/skills/lavish/SKILL.md index 256f3495..06a399ce 100644 --- a/plugins/lavish/agent/skills/lavish/SKILL.md +++ b/plugins/lavish/agent/skills/lavish/SKILL.md @@ -1,5 +1,7 @@ --- description: "Turn complex or visual agent responses into rich, reviewable HTML artifacts the user can annotate and send feedback on, using the lavish-axi CLI. Use when about to give a plan, comparison, diagram, table, code diff, report, or anything easier to grasp visually than as prose." +license: "MIT" +metadata: {"author":"Kun Chen (kunchenguid)","argument-hint":"","hermes-tags":"html, review, artifacts, visualization","hermes-category":"productivity"} --- # Lavish Editor @@ -7,6 +9,7 @@ Lavish Editor helps agents turn rich HTML artifacts into collaborative human rev You do not need lavish-axi installed globally - invoke it with `npx -y lavish-axi `. If lavish-axi output shows a follow-up command starting with `lavish-axi`, run it as `npx -y lavish-axi ...` instead. +In restricted subprocess sandboxes, CI, or agent harnesses where `npx -y` exits opaquely (for example with status 216), use an already-installed copy directly: `node "$(npm root)/lavish-axi/dist/cli.mjs" ` for a local install, `node "$(npm root -g)/lavish-axi/dist/cli.mjs" ` for a global install, or the bare `lavish-axi ` bin after installing once. ## Request @@ -23,13 +26,22 @@ Use lavish-axi when the user asks for a visual artifact, HTML explainer, interac 1. Create the HTML artifact (default location `.lavish/.html` in the working directory). 2. Run `npx -y lavish-axi ` to open or resume a review session in the browser. -3. Run `npx -y lavish-axi poll ` to long-poll for the user's annotations, queued prompts, and browser-reported `layout_warnings`. - The poll stays silent until the user acts or the real browser reports fresh layout warnings - leave it running, never kill it. - If your harness limits how long a foreground command may run, run the poll as a background task; if it gets killed or times out anyway, just re-run it - queued feedback is never lost. -4. If poll returns `layout_warnings`, follow the returned `next_step`: fix and re-check fresh error-severity findings, but proceed with a note instead of looping when every current warning is persistent or low-severity. -5. Apply human feedback, then poll again with `--agent-reply ""` to reply in the browser and keep the loop going. + If the output carries a `self_paint_warning`, fix the unpainted page surface and save before polling - Lavish live-reloads the artifact. +3. Run `npx -y lavish-axi poll ` to long-poll for the user's annotations and queued prompts. + On the first poll, prefer `--agent-reply ""` so the conversation panel opens with context. + Browser-detected layout issues are filed passively in the user's Layout issues inbox and arrive as an ordinary `layout-warnings` prompt only when the user selects and queues them. Never edit an issue the user has not queued. The only response that arrives without user action is `artifact_failures`, when the review surface itself is unusable. + The poll stays silent until the user acts or a fatal artifact failure makes the review surface unusable - leave it running, never kill it. + Cosmetic, intentional, transient, tiny, and uncertain observations remain silent. + Keep the poll in the foreground by default and let it return the feedback directly to the agent. + A background poll is allowed only through a harness-native tracked background-job facility whose completion result is guaranteed to resume or notify the same agent. + Never use `nohup`, shell `&`, `disown`, redirected fire-and-forget processes, or a detached terminal without an explicit verified callback merely to keep polling alive. + If the harness has no completion-aware background facility, use the foreground poll or first wire a verified wake callback into the surrounding supervisor. + Do not tell the user the artifact is being monitored until that wake path is live. + If the poll gets killed or times out anyway, just re-run it - queued feedback is never lost. +4. If poll returns feedback, apply the user's prompts. A `layout-warnings` prompt is an explicit repair request; apply every listed fix in one pass before saving, and let Lavish re-check it after a newer artifact load. +5. Apply human feedback, then poll again with `--agent-reply ""` to reply in the browser and keep the loop going under the same foreground-or-verified-wake-path rule. 6. Run `npx -y lavish-axi end ` when the review is finished. -7. If the user ends the session from the browser instead, `npx -y lavish-axi ` refuses to reopen it and says so - only pass `--reopen` when the user asks for further review or something genuinely important needs their visual attention. Otherwise deliver remaining updates directly in this conversation. +7. `Send & End` ends the session. Its final feedback is still delivered once. After that response, polling stops, and the agent must not reopen the session uninvited. Deliver any remaining updates directly in this conversation. ## Visual guidance @@ -43,7 +55,7 @@ Use lavish-axi when the user asks for a visual artifact, HTML explainer, interac Run `npx -y lavish-axi playbook ` for focused, detailed guidance on any of these. One artifact often combines several playbooks (for example a plan that includes a comparison and a diagram), so MUST open each matching playbook before writing HTML. -For flows, architecture, state, or sequence diagrams, do not hand-build boxes-and-arrows from div/flexbox; open the diagram playbook and use Mermaid unless SVG is needed for richly annotated nodes. +For flows, architecture, state, or sequence diagrams, do not hand-build boxes-and-arrows from div/flexbox; open the diagram playbook and use the theme-aware Mermaid snippet from `npx -y lavish-axi design` unless SVG is needed for richly annotated nodes. - `diagram` - Map relationships, flows, state, and architecture - `table` - Turn dense records into scan-friendly review surfaces @@ -58,11 +70,12 @@ For flows, architecture, state, or sequence diagrams, do not hand-build boxes-an - Run `npx -y lavish-axi ` to open or resume a Lavish Editor session. If the user explicitly ended the session from the browser, this refuses to reopen it and explains why instead of reopening uninvited - pass `--reopen` only when the user asks for further review or something important needs their visual attention - Unless the user specifies another location, create HTML artifacts in the current working directory under `.lavish/` - Lavish serves the html file through a local express.js server. If your html needs to reference other filesystem assets such as images, CSS, fonts, and local scripts, copy them into the same directory as the HTML file, then reference them with relative paths from that directory. Never prepend `/` to those asset paths - root paths won't work -- Run `npx -y lavish-axi poll ` to wait for user feedback or browser-reported layout_warnings. It long-polls and stays silent until the user sends feedback, ends the session, or the real browser reports fresh layout_warnings, so leave it running - never kill it. Fix and re-check fresh error-severity layout_warnings before involving the human; if the poll says every current warning is persistent or low-severity, proceed with a note instead of looping. If your harness limits how long a foreground command may run, run the poll as a background task; if it gets killed or times out anyway, just re-run it - queued feedback is never lost. When it reports the session ended, stop polling and do not reopen it uninvited - deliver remaining updates in this conversation instead +- Run `npx -y lavish-axi poll ` to wait for user feedback. It long-polls and stays silent until the user sends feedback or ends the session, so leave it running - never kill it. Detected layout issues never return this poll: the browser files them in the user's Layout issues inbox in the Lavish top bar, and they arrive as an ordinary tag "layout-warnings" prompt only when the user selects them and queues the fixes. Never edit the artifact to chase a layout issue the user has not queued. The only exception is a fatal artifact_failures response, which means the review surface itself could not be used. Keep the poll in the foreground by default and let it return the feedback directly to the agent. A background poll is allowed only through a harness-native tracked background-job facility whose completion result is guaranteed to resume or notify the same agent. Never use `nohup`, shell `&`, `disown`, redirected fire-and-forget processes, or a detached terminal without an explicit verified callback merely to keep polling alive. If the harness has no completion-aware background facility, use the foreground poll or first wire a verified wake callback into the surrounding supervisor. Do not tell the user the artifact is being monitored until that wake path is live. If the poll gets killed or times out anyway, just re-run it - queued feedback is never lost. `Send & End` ends the session. Its final feedback is still delivered once. After that response, polling stops, and the agent must not reopen the session uninvited. +- Rendered Mermaid diagrams in `.mermaid` containers become embedded, editable Excalidraw whiteboards in the browser (click a diagram to unlock editing; a Fullscreen action opens it over the whole viewport) - flowchart, sequence, class, ER, and state diagrams convert to editable shapes; other types embed as an image to draw on. Scenes autosave locally; when a reload detects a changed Mermaid source, the reviewer explicitly chooses to re-convert and discard saved edits or keep editing the saved scene. Standalone and exported copies still render plain Mermaid. Queue feedback adds a prompt to the Conversation panel; when the user sends it, poll returns a tag "whiteboard" prompt carrying a bounded edit summary plus local scenePath (.excalidraw JSON) and previewPath (PNG) files - read the summary first, open the files only when needed, then apply the edits by updating the Mermaid source in the artifact (never try to write the scene back) - Run `npx -y lavish-axi end ` to end a session as the agent - ending it this way still allows a plain reopen later. When the user ends it from the browser instead, a later `npx -y lavish-axi ` refuses to reopen it without `--reopen` - Run `npx -y lavish-axi export [--out ]` to write a portable copy of the artifact - one HTML file with its LOCAL assets inlined - so it opens with no Lavish server and no sibling files. Remote CDN/font references are left as links, so it needs network to render those. Users can also export from the browser chrome's overflow menu - Run `npx -y lavish-axi share [--password ] [--token ]` to publish the artifact on ht-ml.app (https://ht-ml.app), a third-party hosting service not part of Lavish, and get back a visitable URL. Shares are PUBLIC by default, so anyone with the link can open them. Pass --password to publish a PRIVATE password-protected page; viewers must supply the password to view. Local assets are inlined; remote refs load over the network. It returns the url plus a secret update_key for managing the page later. Use --token or LAVISH_AXI_HTML_APP_TOKEN only when you have an optional bearer token; it is never required. Users can also publish from the browser chrome's overflow menu - Run `npx -y lavish-axi stop` to shut down the background server (it also self-stops when idle or after the last session ends with nothing connected) - Run `npx -y lavish-axi playbook ` for focused artifact guidance. One artifact often combines several playbooks (for example a plan that includes a comparison and a diagram), so MUST open each matching playbook before writing HTML. -- Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML, decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's design system: Tailwind or theme config, shared CSS variables or design tokens, component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN - run `npx -y lavish-axi design` for a content-to-playbook router, a copy-pasteable CDN snippet, a Mermaid CDN snippet/init for diagrams, and the DaisyUI component reference, and prefer the Tailwind/DaisyUI CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. When you deliver the artifact, state which of the three design sources you used and why. +- Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML: Decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's design system: Tailwind or theme config, shared CSS variables or design tokens, component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN, and prefer that CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. Run `npx -y lavish-axi design` for a content-to-playbook router, a copy-pasteable CDN snippet, a Mermaid CDN snippet/init for diagrams, and the DaisyUI component reference. When you deliver the artifact, state which of the three design sources you used and why. - Use lavish-axi when the user asks for a visual artifact, HTML explainer, interactive prototype, review surface, product or technical plan, comparison, report, or browser-based feedback loop diff --git a/plugins/lavish/skills-lock.json b/plugins/lavish/skills-lock.json index 0f5b5295..c259f312 100644 --- a/plugins/lavish/skills-lock.json +++ b/plugins/lavish/skills-lock.json @@ -5,7 +5,7 @@ "source": "kunchenguid/lavish-axi", "sourceType": "github", "skillPath": "skills/lavish/SKILL.md", - "computedHash": "c3a0299d820bc3b30613d7740718b194511201319047fa9d3075e0ab1e596bde" + "computedHash": "1516c3ea2b973555b7cc052118102996266366ee27020ce9ec40d93bab6f7ab8" } } } diff --git a/plugins/mastra/.agents/skills/mastra/SKILL.md b/plugins/mastra/.agents/skills/mastra/SKILL.md index e66994dc..eac88bf1 100644 --- a/plugins/mastra/.agents/skills/mastra/SKILL.md +++ b/plugins/mastra/.agents/skills/mastra/SKILL.md @@ -1,10 +1,10 @@ --- name: mastra -description: "Comprehensive Mastra framework guide for building agents, workflows, tools, memory, workspaces, and storage with current APIs. Use for documentation lookup, API verification, TypeScript setup, common errors, migrations, and `mastra api` CLI tasks: inspect or call resources on local, Mastra platform, or remote servers." +description: "Comprehensive Mastra framework guide for building agents, workflows, tools, memory, workspaces, and storage with current APIs. Use for documentation lookup, API verification, TypeScript setup, common errors, migrations, and `mastra api` CLI tasks: inspect or call resources on local, Mastra platform, Trace Intelligence, or remote servers." license: Apache-2.0 metadata: author: Mastra - version: "2.0.0" + version: "2.1.0" repository: https://github.com/mastra-ai/skills --- @@ -43,6 +43,7 @@ ls node_modules/@mastra/ | I'm getting an error... | [`references/common-errors.md`](references/common-errors.md) | Common errors and solutions | | Upgrade from v0.x to v1.x | [`references/migration-guide.md`](references/migration-guide.md) | Version upgrade workflows | | Inspect/call server resources via CLI | [`references/mastra-api.md`](references/mastra-api.md) | `mastra api` CLI for local, Mastra platform, or remote servers | +| Investigate agent health, recurring failures, or improvement opportunities | [`references/trace-intelligence.md`](references/trace-intelligence.md) | Start with aggregate Trace Intelligence themes, then inspect trace/log evidence | ### Scripts @@ -87,6 +88,10 @@ Then open `http://localhost:4111` in a browser to show Mastra Studio to your hum Use `mastra api` to inspect or call resources on local dev servers, Mastra platform deployments, or remote Mastra endpoints. It is useful for agent-readable state, execution, traces, logs, scores, threads, and workflow operations. See [`references/mastra-api.md`](references/mastra-api.md) for usage patterns. +## Trace Intelligence + +Trace Intelligence (private beta on the Mastra platform) clusters completed agent traces into recurring themes across four trace signals: goal, outcome, behavior, and sentiment. Use it first for aggregate agent-health questions: what users ask for, where outcomes fail or get blocked, which behaviors recur, how sentiment shifts, and where the agent can improve. Then use `mastra api trace`, `log`, `metric`, and `score` commands for concrete execution evidence from specific traces. Query Trace Intelligence with `mastra api learning` CLI commands, or over HTTP via the local dev server proxy or the platform endpoint. See [`references/trace-intelligence.md`](references/trace-intelligence.md) for the investigation workflow, CLI commands, and route reference. + ## Critical requirements ### TypeScript config diff --git a/plugins/mastra/.agents/skills/mastra/references/trace-intelligence.md b/plugins/mastra/.agents/skills/mastra/references/trace-intelligence.md new file mode 100644 index 00000000..d79dc919 --- /dev/null +++ b/plugins/mastra/.agents/skills/mastra/references/trace-intelligence.md @@ -0,0 +1,220 @@ +# Trace Intelligence Reference + +How to query Trace Intelligence (private beta) from the Mastra platform. Trace Intelligence analyzes completed agent traces and groups them into recurring themes across four trace signals: `goal`, `outcome`, `behavior`, and `sentiment`. + +Use this reference when the user asks to investigate agent health, find recurring failures or behavior issues, identify ways to improve an agent, understand what users ask for, inspect recurring goal/outcome/behavior/sentiment themes, or query Trace Intelligence data programmatically. + +## Concepts + +- **Trace signal**: one-sentence description generated per completed trace, per dimension (`goal`, `outcome`, `behavior`, `sentiment`). +- **Theme**: durable cluster of similar trace signals for one dimension, with a label and description. Theme IDs are stable across snapshots for one signal. +- **Snapshot**: a moving analysis window over recent traces. Identified by an opaque `snapshotId`. +- **Noise**: traces in a snapshot that did not cluster into any theme. Window-local, no durable identity. + +## Prerequisites + +- The project uses Mastra platform Observability and has completed traces. +- The project is enrolled in the Trace Intelligence private beta. Non-enrolled projects get `403` from direct project reads. + +Analysis is asynchronous: a project generally needs 100+ completed traces before themes exist. Empty responses usually mean not enough analyzed data yet, not an error. + +## Access paths + +All Trace Intelligence routes are read-only `GET` requests under `/api/learning/`. + +1. **`mastra api learning` CLI** (preferred): use the same credential model as hosted observability commands. No `--url` or `--header` is required if `MASTRA_PLATFORM_ACCESS_TOKEN` and `MASTRA_PROJECT_ID` are set, or if `.mastra-project.json` is present. The CLI also resolves `X-Mastra-Organization-Id` from `MASTRA_ORGANIZATION_ID` or `.mastra-project.json`. + +```bash +mastra api learning entities '{"entityType":"agent"}' +``` + +Pass `--url` and `--header` only when overriding the hosted Trace Intelligence target or credentials. + +2. **Local dev server proxy**: `mastra dev` proxies `GET http://localhost:4111/api/learning/*` to the platform using its normal platform credentials. Loopback only. + +```bash +curl -fsS "http://localhost:4111/api/learning/entities?entityType=agent" | jq +``` + +3. **Direct platform endpoint** (no CLI or dev server needed): call `https://output.signals.mastra.ai` with explicit auth, project, and organization headers. + +```bash +BASE="https://output.signals.mastra.ai" +AUTH=( + -H "Authorization: Bearer $MASTRA_PLATFORM_ACCESS_TOKEN" + -H "X-Mastra-Project-Id: $MASTRA_PROJECT_ID" + -H "X-Mastra-Organization-Id: $MASTRA_ORGANIZATION_ID" +) + +curl -fsS "${AUTH[@]}" "$BASE/api/learning/entities?entityType=agent" | jq +``` + +The curl examples below use `$BASE` and `"${AUTH[@]}"`; for the local proxy, replace `$BASE` with `http://localhost:4111` and drop the headers. + +## CLI commands + +Every route has a CLI command. Positional args carry `entityId`/`themeId`; the JSON input carries the query params from the [route summary](#route-summary). Pass `--schema` to any command to print its input schema, and `--pretty` for readable output. + +| Command | Route | +| --- | --- | +| `mastra api learning entities '{"entityType":"agent"}'` | `/api/learning/entities` | +| `mastra api learning snapshots ` | `.../theme-snapshots` | +| `mastra api learning flow ` | `.../theme-flow` | +| `mastra api learning paths ` | `.../theme-paths` | +| `mastra api learning theme list ` | `.../themes` | +| `mastra api learning theme get ` | `.../themes/:themeId` | +| `mastra api learning theme examples ` | `.../themes/:themeId/examples` | +| `mastra api learning theme history ` | `.../themes/:themeId/history` | +| `mastra api learning noise get ` | `.../noise` | +| `mastra api learning noise examples ` | `.../noise/examples` | + +Same workflow as the curl steps below: + +```bash +# 1. Discover entities and their available signals +mastra api learning entities '{"entityType":"agent"}' + +# 2. List snapshots (signalNames is ordered, comma-separated) +mastra api learning snapshots my-agent \ + '{"entityType":"agent","signalNames":"goal,outcome,behavior,sentiment","limit":10}' + +# 3. Themes for one signal in one snapshot (snapshotId from step 2) +mastra api learning theme list my-agent \ + '{"entityType":"agent","signalName":"goal","snapshotId":""}' + +# 4. Drill into one theme (numeric themeId from step 3) +mastra api learning theme examples my-agent 42 \ + '{"entityType":"agent","signalName":"goal","snapshotId":"","limit":10}' +mastra api learning theme history my-agent 42 \ + '{"entityType":"agent","signalName":"goal"}' +``` + +## Investigation workflow + +For broad agent-health or improvement questions, start with Trace Intelligence to find recurring patterns, then use trace/log/metric/score APIs for concrete evidence from specific runs. For a specific failed run or error, start with `mastra api trace` or `mastra api log`, then use Trace Intelligence to check whether the issue is recurring. + +Follow this order for aggregate Trace Intelligence analysis. Later calls need values returned by earlier calls. + +### 1. Discover entities + +Lists entities (agents) that have theme output, with which signals are available: + +```bash +curl -fsS "${AUTH[@]}" "$BASE/api/learning/entities?entityType=agent" \ + | jq '.entities[] | {entityId, availableSignals, latestWindow}' +``` + +Only request `signalNames` that appear in `availableSignals` in later calls. + +### 2. List snapshots + +`signalNames` is an ordered, comma-separated list (1-4 unique values). A snapshot is returned only when every requested signal has usable output for the window: + +```bash +ENTITY="my-agent" # TODO: entityId from step 1 +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/theme-snapshots?entityType=agent&signalNames=goal,outcome,behavior,sentiment&limit=10" \ + | jq '.snapshots[] | {snapshotId, ordinal, total, startedAt, endedAt, traceCount}' +``` + +Optional `from`/`to` (ISO timestamps with offset) bound the snapshot cutoffs; `cursor` paginates newest-first via `nextCursor`. + +### 3. Read themes or the cross-signal flow + +Use each trace signal for a different diagnostic angle: + +- `goal`: what users are trying to do. +- `outcome`: what completes, fails, gets blocked, or remains unresolved. +- `behavior`: how the agent behaves, including tool use, loops, refusals, recovery, or drift. +- `sentiment`: how user emotion changes across interactions. + +Themes for one signal in one snapshot: + +```bash +SNAPSHOT="..." # TODO: snapshotId from step 2 +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/themes?entityType=agent&signalName=goal&snapshotId=$SNAPSHOT" \ + | jq '{themes: [.themes[] | {themeId, label, state, traceCount, coverage, trend}], noise}' +``` + +Cross-signal flow (Sankey-style stages and links; counts are distinct traces): + +```bash +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/theme-flow?entityType=agent&signalNames=goal,outcome&snapshotId=$SNAPSHOT" \ + | jq '{stages: [.stages[] | {signalName, nodes: [.nodes[] | {label, kind, traceCount, stageShare}]}], links}' +``` + +### 4. Drill into one theme + +Use examples to move from aggregate themes to concrete traces. After identifying a suspicious theme, inspect its examples, then use the returned `traceId` with `mastra api trace`, logs, metrics, or scores when you need execution-level evidence. + +Detail, examples (raw trace signal texts), and history: + +```bash +THEME="42" # TODO: numeric themeId from step 3 +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/themes/$THEME?entityType=agent&signalName=goal&snapshotId=$SNAPSHOT" | jq '.theme' + +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/themes/$THEME/examples?entityType=agent&signalName=goal&snapshotId=$SNAPSHOT&limit=10" \ + | jq '.examples[] | {traceId, signalText}' + +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/themes/$THEME/history?entityType=agent&signalName=goal" \ + | jq '{points: [.points[] | {state, traceCount, coverage}], relationships}' +``` + +History does not take `snapshotId`; it returns the theme's lifecycle (`birth`, `continue`, `split`, `merge`, `death`, `resurrection`) across snapshots, plus split/merge relationships. + +### 5. Noise and per-trace paths + +Noise bucket and its examples (same query shape as themes, using `/noise` and `/noise/examples`): + +```bash +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/noise?entityType=agent&signalName=goal&snapshotId=$SNAPSHOT" | jq '.noise' +``` + +Per-trace assignments across the ordered signals (trace-level companion to `theme-flow`; paginate with `limit`/`offset` until `nextOffset` is absent): + +```bash +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/theme-paths?entityType=agent&signalNames=goal,outcome&snapshotId=$SNAPSHOT&limit=100" \ + | jq '{themes, paths: .paths[:5]}' +``` + +`paths[].assignments` maps each signal to a theme key (resolved in the `themes` dictionary) or `"noise"`. Use this to join themes back to concrete `traceId` values, then inspect those traces with `mastra api trace` (see [`mastra-api.md`](mastra-api.md)). + +## Route summary + +| Route | Required query params | Optional | +| --- | --- | --- | +| `GET /api/learning/entities` | `entityType` | `limit` | +| `GET .../:entityId/theme-snapshots` | `entityType`, `signalNames` | `limit`, `cursor`, `from`, `to` | +| `GET .../:entityId/theme-flow` | `entityType`, `signalNames`, `snapshotId` | `themeLimitPerStage` | +| `GET .../:entityId/theme-paths` | `entityType`, `signalNames`, `snapshotId` | `limit`, `offset` | +| `GET .../:entityId/themes` | `entityType`, `signalName`, `snapshotId` | — | +| `GET .../:entityId/themes/:themeId` | `entityType`, `signalName`, `snapshotId` | — | +| `GET .../:entityId/themes/:themeId/examples` | `entityType`, `signalName`, `snapshotId` | `limit`, `offset` | +| `GET .../:entityId/themes/:themeId/history` | `entityType`, `signalName` | `limit`, `cursor` | +| `GET .../:entityId/noise` | `entityType`, `signalName`, `snapshotId` | — | +| `GET .../:entityId/noise/examples` | `entityType`, `signalName`, `snapshotId` | `limit`, `offset` | + +`:themeId` is numeric. Flow/paths/snapshots take plural ordered `signalNames`; theme/noise routes take singular `signalName`. + +## Rules and caveats + +- **`snapshotId` is opaque.** Send it back unchanged, with the same entity and signal selection it came from. It is rejected for a different project, entity, or signal set. Never construct or reuse snapshot IDs across scopes. +- **Counts are distinct traces**, not assignment rows. `coverage`, `stageShare`, `sourceShare`, `targetShare` are fractions of the deduplicated counts. +- **`other` nodes in `theme-flow`** are lower-volume themes collapsed per stage. They have no `themeId` and cannot be drilled into; raise `themeLimitPerStage` to expand them. +- **Noise is window-local.** It has no durable ID, label, or trend, and differs between snapshots. +- **Results are AI-generated summaries.** Verify conclusions against theme examples and the underlying traces before acting on them. + +## Errors + +- `401`: bad or missing bearer token. Check `MASTRA_PLATFORM_ACCESS_TOKEN`. +- `403` mentioning `X-Mastra-Organization-Id`: the organization header is missing. Set `MASTRA_ORGANIZATION_ID` (direct curl) or run from a directory containing `.mastra-project.json` (CLI). +- `403`: project not enrolled in the private beta, or the local proxy was called from a non-loopback host. +- `503` from the local proxy: `MASTRA_PLATFORM_ACCESS_TOKEN` / `MASTRA_PROJECT_ID` missing from the dev server environment. +- Empty `entities` or `snapshots`: not enough analyzed traces yet, or the requested `signalNames` are not all available. Re-check `availableSignals` from the entities call. diff --git a/plugins/mastra/.claude/skills/mastra b/plugins/mastra/.claude/skills/mastra new file mode 120000 index 00000000..2d4ffc16 --- /dev/null +++ b/plugins/mastra/.claude/skills/mastra @@ -0,0 +1 @@ +../../.agents/skills/mastra \ No newline at end of file diff --git a/plugins/mastra/agent/skills/mastra/SKILL.md b/plugins/mastra/agent/skills/mastra/SKILL.md index 0188b009..a313495f 100644 --- a/plugins/mastra/agent/skills/mastra/SKILL.md +++ b/plugins/mastra/agent/skills/mastra/SKILL.md @@ -1,7 +1,7 @@ --- -description: "Comprehensive Mastra framework guide for building agents, workflows, tools, memory, workspaces, and storage with current APIs. Use for documentation lookup, API verification, TypeScript setup, common errors, migrations, and `mastra api` CLI tasks: inspect or call resources on local, Mastra platform, or remote servers." +description: "Comprehensive Mastra framework guide for building agents, workflows, tools, memory, workspaces, and storage with current APIs. Use for documentation lookup, API verification, TypeScript setup, common errors, migrations, and `mastra api` CLI tasks: inspect or call resources on local, Mastra platform, Trace Intelligence, or remote servers." license: "Apache-2.0" -metadata: {"author":"Mastra","version":"2.0.0","repository":"https://github.com/mastra-ai/skills"} +metadata: {"author":"Mastra","version":"2.1.0","repository":"https://github.com/mastra-ai/skills"} --- # Mastra Framework Guide @@ -38,6 +38,7 @@ ls node_modules/@mastra/ | I'm getting an error... | [`references/common-errors.md`](references/common-errors.md) | Common errors and solutions | | Upgrade from v0.x to v1.x | [`references/migration-guide.md`](references/migration-guide.md) | Version upgrade workflows | | Inspect/call server resources via CLI | [`references/mastra-api.md`](references/mastra-api.md) | `mastra api` CLI for local, Mastra platform, or remote servers | +| Investigate agent health, recurring failures, or improvement opportunities | [`references/trace-intelligence.md`](references/trace-intelligence.md) | Start with aggregate Trace Intelligence themes, then inspect trace/log evidence | ### Scripts @@ -82,6 +83,10 @@ Then open `http://localhost:4111` in a browser to show Mastra Studio to your hum Use `mastra api` to inspect or call resources on local dev servers, Mastra platform deployments, or remote Mastra endpoints. It is useful for agent-readable state, execution, traces, logs, scores, threads, and workflow operations. See [`references/mastra-api.md`](references/mastra-api.md) for usage patterns. +## Trace Intelligence + +Trace Intelligence (private beta on the Mastra platform) clusters completed agent traces into recurring themes across four trace signals: goal, outcome, behavior, and sentiment. Use it first for aggregate agent-health questions: what users ask for, where outcomes fail or get blocked, which behaviors recur, how sentiment shifts, and where the agent can improve. Then use `mastra api trace`, `log`, `metric`, and `score` commands for concrete execution evidence from specific traces. Query Trace Intelligence with `mastra api learning` CLI commands, or over HTTP via the local dev server proxy or the platform endpoint. See [`references/trace-intelligence.md`](references/trace-intelligence.md) for the investigation workflow, CLI commands, and route reference. + ## Critical requirements ### TypeScript config diff --git a/plugins/mastra/agent/skills/mastra/references/trace-intelligence.md b/plugins/mastra/agent/skills/mastra/references/trace-intelligence.md new file mode 100644 index 00000000..d79dc919 --- /dev/null +++ b/plugins/mastra/agent/skills/mastra/references/trace-intelligence.md @@ -0,0 +1,220 @@ +# Trace Intelligence Reference + +How to query Trace Intelligence (private beta) from the Mastra platform. Trace Intelligence analyzes completed agent traces and groups them into recurring themes across four trace signals: `goal`, `outcome`, `behavior`, and `sentiment`. + +Use this reference when the user asks to investigate agent health, find recurring failures or behavior issues, identify ways to improve an agent, understand what users ask for, inspect recurring goal/outcome/behavior/sentiment themes, or query Trace Intelligence data programmatically. + +## Concepts + +- **Trace signal**: one-sentence description generated per completed trace, per dimension (`goal`, `outcome`, `behavior`, `sentiment`). +- **Theme**: durable cluster of similar trace signals for one dimension, with a label and description. Theme IDs are stable across snapshots for one signal. +- **Snapshot**: a moving analysis window over recent traces. Identified by an opaque `snapshotId`. +- **Noise**: traces in a snapshot that did not cluster into any theme. Window-local, no durable identity. + +## Prerequisites + +- The project uses Mastra platform Observability and has completed traces. +- The project is enrolled in the Trace Intelligence private beta. Non-enrolled projects get `403` from direct project reads. + +Analysis is asynchronous: a project generally needs 100+ completed traces before themes exist. Empty responses usually mean not enough analyzed data yet, not an error. + +## Access paths + +All Trace Intelligence routes are read-only `GET` requests under `/api/learning/`. + +1. **`mastra api learning` CLI** (preferred): use the same credential model as hosted observability commands. No `--url` or `--header` is required if `MASTRA_PLATFORM_ACCESS_TOKEN` and `MASTRA_PROJECT_ID` are set, or if `.mastra-project.json` is present. The CLI also resolves `X-Mastra-Organization-Id` from `MASTRA_ORGANIZATION_ID` or `.mastra-project.json`. + +```bash +mastra api learning entities '{"entityType":"agent"}' +``` + +Pass `--url` and `--header` only when overriding the hosted Trace Intelligence target or credentials. + +2. **Local dev server proxy**: `mastra dev` proxies `GET http://localhost:4111/api/learning/*` to the platform using its normal platform credentials. Loopback only. + +```bash +curl -fsS "http://localhost:4111/api/learning/entities?entityType=agent" | jq +``` + +3. **Direct platform endpoint** (no CLI or dev server needed): call `https://output.signals.mastra.ai` with explicit auth, project, and organization headers. + +```bash +BASE="https://output.signals.mastra.ai" +AUTH=( + -H "Authorization: Bearer $MASTRA_PLATFORM_ACCESS_TOKEN" + -H "X-Mastra-Project-Id: $MASTRA_PROJECT_ID" + -H "X-Mastra-Organization-Id: $MASTRA_ORGANIZATION_ID" +) + +curl -fsS "${AUTH[@]}" "$BASE/api/learning/entities?entityType=agent" | jq +``` + +The curl examples below use `$BASE` and `"${AUTH[@]}"`; for the local proxy, replace `$BASE` with `http://localhost:4111` and drop the headers. + +## CLI commands + +Every route has a CLI command. Positional args carry `entityId`/`themeId`; the JSON input carries the query params from the [route summary](#route-summary). Pass `--schema` to any command to print its input schema, and `--pretty` for readable output. + +| Command | Route | +| --- | --- | +| `mastra api learning entities '{"entityType":"agent"}'` | `/api/learning/entities` | +| `mastra api learning snapshots ` | `.../theme-snapshots` | +| `mastra api learning flow ` | `.../theme-flow` | +| `mastra api learning paths ` | `.../theme-paths` | +| `mastra api learning theme list ` | `.../themes` | +| `mastra api learning theme get ` | `.../themes/:themeId` | +| `mastra api learning theme examples ` | `.../themes/:themeId/examples` | +| `mastra api learning theme history ` | `.../themes/:themeId/history` | +| `mastra api learning noise get ` | `.../noise` | +| `mastra api learning noise examples ` | `.../noise/examples` | + +Same workflow as the curl steps below: + +```bash +# 1. Discover entities and their available signals +mastra api learning entities '{"entityType":"agent"}' + +# 2. List snapshots (signalNames is ordered, comma-separated) +mastra api learning snapshots my-agent \ + '{"entityType":"agent","signalNames":"goal,outcome,behavior,sentiment","limit":10}' + +# 3. Themes for one signal in one snapshot (snapshotId from step 2) +mastra api learning theme list my-agent \ + '{"entityType":"agent","signalName":"goal","snapshotId":""}' + +# 4. Drill into one theme (numeric themeId from step 3) +mastra api learning theme examples my-agent 42 \ + '{"entityType":"agent","signalName":"goal","snapshotId":"","limit":10}' +mastra api learning theme history my-agent 42 \ + '{"entityType":"agent","signalName":"goal"}' +``` + +## Investigation workflow + +For broad agent-health or improvement questions, start with Trace Intelligence to find recurring patterns, then use trace/log/metric/score APIs for concrete evidence from specific runs. For a specific failed run or error, start with `mastra api trace` or `mastra api log`, then use Trace Intelligence to check whether the issue is recurring. + +Follow this order for aggregate Trace Intelligence analysis. Later calls need values returned by earlier calls. + +### 1. Discover entities + +Lists entities (agents) that have theme output, with which signals are available: + +```bash +curl -fsS "${AUTH[@]}" "$BASE/api/learning/entities?entityType=agent" \ + | jq '.entities[] | {entityId, availableSignals, latestWindow}' +``` + +Only request `signalNames` that appear in `availableSignals` in later calls. + +### 2. List snapshots + +`signalNames` is an ordered, comma-separated list (1-4 unique values). A snapshot is returned only when every requested signal has usable output for the window: + +```bash +ENTITY="my-agent" # TODO: entityId from step 1 +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/theme-snapshots?entityType=agent&signalNames=goal,outcome,behavior,sentiment&limit=10" \ + | jq '.snapshots[] | {snapshotId, ordinal, total, startedAt, endedAt, traceCount}' +``` + +Optional `from`/`to` (ISO timestamps with offset) bound the snapshot cutoffs; `cursor` paginates newest-first via `nextCursor`. + +### 3. Read themes or the cross-signal flow + +Use each trace signal for a different diagnostic angle: + +- `goal`: what users are trying to do. +- `outcome`: what completes, fails, gets blocked, or remains unresolved. +- `behavior`: how the agent behaves, including tool use, loops, refusals, recovery, or drift. +- `sentiment`: how user emotion changes across interactions. + +Themes for one signal in one snapshot: + +```bash +SNAPSHOT="..." # TODO: snapshotId from step 2 +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/themes?entityType=agent&signalName=goal&snapshotId=$SNAPSHOT" \ + | jq '{themes: [.themes[] | {themeId, label, state, traceCount, coverage, trend}], noise}' +``` + +Cross-signal flow (Sankey-style stages and links; counts are distinct traces): + +```bash +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/theme-flow?entityType=agent&signalNames=goal,outcome&snapshotId=$SNAPSHOT" \ + | jq '{stages: [.stages[] | {signalName, nodes: [.nodes[] | {label, kind, traceCount, stageShare}]}], links}' +``` + +### 4. Drill into one theme + +Use examples to move from aggregate themes to concrete traces. After identifying a suspicious theme, inspect its examples, then use the returned `traceId` with `mastra api trace`, logs, metrics, or scores when you need execution-level evidence. + +Detail, examples (raw trace signal texts), and history: + +```bash +THEME="42" # TODO: numeric themeId from step 3 +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/themes/$THEME?entityType=agent&signalName=goal&snapshotId=$SNAPSHOT" | jq '.theme' + +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/themes/$THEME/examples?entityType=agent&signalName=goal&snapshotId=$SNAPSHOT&limit=10" \ + | jq '.examples[] | {traceId, signalText}' + +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/themes/$THEME/history?entityType=agent&signalName=goal" \ + | jq '{points: [.points[] | {state, traceCount, coverage}], relationships}' +``` + +History does not take `snapshotId`; it returns the theme's lifecycle (`birth`, `continue`, `split`, `merge`, `death`, `resurrection`) across snapshots, plus split/merge relationships. + +### 5. Noise and per-trace paths + +Noise bucket and its examples (same query shape as themes, using `/noise` and `/noise/examples`): + +```bash +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/noise?entityType=agent&signalName=goal&snapshotId=$SNAPSHOT" | jq '.noise' +``` + +Per-trace assignments across the ordered signals (trace-level companion to `theme-flow`; paginate with `limit`/`offset` until `nextOffset` is absent): + +```bash +curl -fsS "${AUTH[@]}" \ + "$BASE/api/learning/entities/$ENTITY/theme-paths?entityType=agent&signalNames=goal,outcome&snapshotId=$SNAPSHOT&limit=100" \ + | jq '{themes, paths: .paths[:5]}' +``` + +`paths[].assignments` maps each signal to a theme key (resolved in the `themes` dictionary) or `"noise"`. Use this to join themes back to concrete `traceId` values, then inspect those traces with `mastra api trace` (see [`mastra-api.md`](mastra-api.md)). + +## Route summary + +| Route | Required query params | Optional | +| --- | --- | --- | +| `GET /api/learning/entities` | `entityType` | `limit` | +| `GET .../:entityId/theme-snapshots` | `entityType`, `signalNames` | `limit`, `cursor`, `from`, `to` | +| `GET .../:entityId/theme-flow` | `entityType`, `signalNames`, `snapshotId` | `themeLimitPerStage` | +| `GET .../:entityId/theme-paths` | `entityType`, `signalNames`, `snapshotId` | `limit`, `offset` | +| `GET .../:entityId/themes` | `entityType`, `signalName`, `snapshotId` | — | +| `GET .../:entityId/themes/:themeId` | `entityType`, `signalName`, `snapshotId` | — | +| `GET .../:entityId/themes/:themeId/examples` | `entityType`, `signalName`, `snapshotId` | `limit`, `offset` | +| `GET .../:entityId/themes/:themeId/history` | `entityType`, `signalName` | `limit`, `cursor` | +| `GET .../:entityId/noise` | `entityType`, `signalName`, `snapshotId` | — | +| `GET .../:entityId/noise/examples` | `entityType`, `signalName`, `snapshotId` | `limit`, `offset` | + +`:themeId` is numeric. Flow/paths/snapshots take plural ordered `signalNames`; theme/noise routes take singular `signalName`. + +## Rules and caveats + +- **`snapshotId` is opaque.** Send it back unchanged, with the same entity and signal selection it came from. It is rejected for a different project, entity, or signal set. Never construct or reuse snapshot IDs across scopes. +- **Counts are distinct traces**, not assignment rows. `coverage`, `stageShare`, `sourceShare`, `targetShare` are fractions of the deduplicated counts. +- **`other` nodes in `theme-flow`** are lower-volume themes collapsed per stage. They have no `themeId` and cannot be drilled into; raise `themeLimitPerStage` to expand them. +- **Noise is window-local.** It has no durable ID, label, or trend, and differs between snapshots. +- **Results are AI-generated summaries.** Verify conclusions against theme examples and the underlying traces before acting on them. + +## Errors + +- `401`: bad or missing bearer token. Check `MASTRA_PLATFORM_ACCESS_TOKEN`. +- `403` mentioning `X-Mastra-Organization-Id`: the organization header is missing. Set `MASTRA_ORGANIZATION_ID` (direct curl) or run from a directory containing `.mastra-project.json` (CLI). +- `403`: project not enrolled in the private beta, or the local proxy was called from a non-loopback host. +- `503` from the local proxy: `MASTRA_PLATFORM_ACCESS_TOKEN` / `MASTRA_PROJECT_ID` missing from the dev server environment. +- Empty `entities` or `snapshots`: not enough analyzed traces yet, or the requested `signalNames` are not all available. Re-check `availableSignals` from the entities call. diff --git a/plugins/mastra/skills-lock.json b/plugins/mastra/skills-lock.json index a77ed31f..940dd7a9 100644 --- a/plugins/mastra/skills-lock.json +++ b/plugins/mastra/skills-lock.json @@ -5,7 +5,7 @@ "source": "mastra-ai/skills", "sourceType": "github", "skillPath": "skills/mastra/SKILL.md", - "computedHash": "f0ca76d36d67a345064f471a9577e752beb2b20ab46acdf154ed223905e1d3a4" + "computedHash": "6482dad39fb72d1fe9d02078c15c3ee708774fd026a8557b4c7dc37fb554977a" } } } diff --git a/plugins/nostics/.agents/skills/nostics/SKILL.md b/plugins/nostics/.agents/skills/nostics/SKILL.md index c9c1b46e..23269443 100644 --- a/plugins/nostics/.agents/skills/nostics/SKILL.md +++ b/plugins/nostics/.agents/skills/nostics/SKILL.md @@ -142,7 +142,7 @@ export const diagnostics = }) ``` -The accessed code becomes the `message`, `docs` still derives from `docsBase`, no `why`/`fix` text ships. No `reporters` by default (so a surviving `throw` doesn't also log and then resurface as the uncaught error); pass `reporters` to keep prod telemetry. `nosticsStrip` tracks this ternary like a direct catalog export. +The accessed code becomes the instance `name`, `docs` still derives from `docsBase`, `why` points to the docs URL when one exists (empty otherwise), no `why`/`fix` text ships. No `reporters` by default (so a surviving `throw` doesn't also log and then resurface as the uncaught error); pass `reporters` to keep prod telemetry. `nosticsStrip` tracks this ternary like a direct catalog export. ## Conventions diff --git a/plugins/nostics/.claude/skills/add-diagnostic b/plugins/nostics/.claude/skills/add-diagnostic new file mode 120000 index 00000000..232e310b --- /dev/null +++ b/plugins/nostics/.claude/skills/add-diagnostic @@ -0,0 +1 @@ +../../.agents/skills/add-diagnostic \ No newline at end of file diff --git a/plugins/nostics/.claude/skills/nostics b/plugins/nostics/.claude/skills/nostics new file mode 120000 index 00000000..9f76d555 --- /dev/null +++ b/plugins/nostics/.claude/skills/nostics @@ -0,0 +1 @@ +../../.agents/skills/nostics \ No newline at end of file diff --git a/plugins/nostics/agent/skills/nostics/SKILL.md b/plugins/nostics/agent/skills/nostics/SKILL.md index d2daaee6..76179399 100644 --- a/plugins/nostics/agent/skills/nostics/SKILL.md +++ b/plugins/nostics/agent/skills/nostics/SKILL.md @@ -140,7 +140,7 @@ export const diagnostics = }) ``` -The accessed code becomes the `message`, `docs` still derives from `docsBase`, no `why`/`fix` text ships. No `reporters` by default (so a surviving `throw` doesn't also log and then resurface as the uncaught error); pass `reporters` to keep prod telemetry. `nosticsStrip` tracks this ternary like a direct catalog export. +The accessed code becomes the instance `name`, `docs` still derives from `docsBase`, `why` points to the docs URL when one exists (empty otherwise), no `why`/`fix` text ships. No `reporters` by default (so a surviving `throw` doesn't also log and then resurface as the uncaught error); pass `reporters` to keep prod telemetry. `nosticsStrip` tracks this ternary like a direct catalog export. ## Conventions diff --git a/plugins/nostics/skills-lock.json b/plugins/nostics/skills-lock.json index e8f71ee1..797422a3 100644 --- a/plugins/nostics/skills-lock.json +++ b/plugins/nostics/skills-lock.json @@ -5,13 +5,13 @@ "source": "vercel-labs/nostics", "sourceType": "github", "skillPath": "skills/add-diagnostic/SKILL.md", - "computedHash": "bffe858a73515c6dcd391644a6c5ac494f0ef56454fa713156f093f7241544f6" + "computedHash": "c7b8e4657722027d0ec9099406e05941d33103cc76b4915859e3cd60f94617a0" }, "nostics": { "source": "vercel-labs/nostics", "sourceType": "github", "skillPath": "skills/nostics/SKILL.md", - "computedHash": "e44672950987d49b46bc53be17bd11ed7e4270eb031c837417239cb34f2c95f8" + "computedHash": "7a39ad8833dd28e1209b7c269564b6a864a859cf86fb3b64a87790bf3b47c5cc" } } } diff --git a/plugins/nuxt-seo/.claude/skills/nuxt-seo b/plugins/nuxt-seo/.claude/skills/nuxt-seo new file mode 120000 index 00000000..fb7f6572 --- /dev/null +++ b/plugins/nuxt-seo/.claude/skills/nuxt-seo @@ -0,0 +1 @@ +../../.agents/skills/nuxt-seo \ No newline at end of file diff --git a/plugins/nuxt-ui/.claude/skills/nuxt-ui b/plugins/nuxt-ui/.claude/skills/nuxt-ui new file mode 120000 index 00000000..f7d99193 --- /dev/null +++ b/plugins/nuxt-ui/.claude/skills/nuxt-ui @@ -0,0 +1 @@ +../../.agents/skills/nuxt-ui \ No newline at end of file diff --git a/plugins/nuxt/.claude/skills/nuxt b/plugins/nuxt/.claude/skills/nuxt new file mode 120000 index 00000000..7bf43bbc --- /dev/null +++ b/plugins/nuxt/.claude/skills/nuxt @@ -0,0 +1 @@ +../../.agents/skills/nuxt \ No newline at end of file diff --git a/plugins/pinia/.claude/skills/pinia b/plugins/pinia/.claude/skills/pinia new file mode 120000 index 00000000..b29e1ea3 --- /dev/null +++ b/plugins/pinia/.claude/skills/pinia @@ -0,0 +1 @@ +../../.agents/skills/pinia \ No newline at end of file diff --git a/plugins/playwright-cli/.claude/skills/playwright-cli b/plugins/playwright-cli/.claude/skills/playwright-cli new file mode 120000 index 00000000..a5bb5229 --- /dev/null +++ b/plugins/playwright-cli/.claude/skills/playwright-cli @@ -0,0 +1 @@ +../../.agents/skills/playwright-cli \ No newline at end of file diff --git a/plugins/pnpm/.claude/skills/pnpm b/plugins/pnpm/.claude/skills/pnpm new file mode 120000 index 00000000..c3181c55 --- /dev/null +++ b/plugins/pnpm/.claude/skills/pnpm @@ -0,0 +1 @@ +../../.agents/skills/pnpm \ No newline at end of file diff --git a/plugins/portless/.agents/skills/portless/SKILL.md b/plugins/portless/.agents/skills/portless/SKILL.md index c810a582..d386f2ff 100644 --- a/plugins/portless/.agents/skills/portless/SKILL.md +++ b/plugins/portless/.agents/skills/portless/SKILL.md @@ -161,37 +161,41 @@ PORTLESS=0 pnpm dev # Bypasses proxy, uses default port 2. `portless ` assigns a random free port (4000-4999) via the `PORT` env var and registers the app with the proxy 3. The browser hits `https://.localhost`; the proxy forwards to the app's assigned port +Outside LAN mode, the proxy and its HTTP redirect listener bind only to the IPv4 and IPv6 loopback addresses, `127.0.0.1` and `::1`. They do not accept connections through LAN, VPN, or other network interfaces. + `.localhost` domains resolve to `127.0.0.1` natively in Chrome, Firefox, and Edge. Safari relies on the system DNS resolver, which may not handle `.localhost` subdomains on all configurations. Run `portless hosts sync` to add entries to `/etc/hosts` if needed. -Use `portless proxy start --tld localhost --tld test` to serve the same app names under multiple TLDs from one proxy. `PORTLESS_URL` uses the first configured TLD. `PORTLESS_TLD` accepts the same comma separated list format, e.g. `PORTLESS_TLD=localhost,test`. +Use `portless proxy start --tld localhost --tld test` to serve the same app names under multiple TLDs from one proxy. `PORTLESS_URL` uses the first configured TLD. When configured TLDs overlap (e.g. `example.com` and `dev.example.com`), hostnames are matched against the longest TLD first, regardless of configuration order. `PORTLESS_TLD` accepts the same comma separated list format, e.g. `PORTLESS_TLD=localhost,test`. + +TLDs can be multi-segment DNS names such as `dev.example.com`, so local URLs can mirror production structure (`myapp.dev.example.com`). Each label follows DNS rules: lowercase letters, digits, interior hyphens, 63 characters per label, 253 total. Strict OAuth providers that reject `.localhost` redirect URIs accept a real domain like `https://myapp.dev.example.com/api/auth/callback/google`. Most frameworks (Next.js, Express, Nuxt, etc.) respect the `PORT` env var automatically. For frameworks that ignore `PORT` (Vite, VitePlus, Astro, React Router, Angular, Expo, React Native), portless auto-injects the correct `--port` flag and, when needed, a matching `--host` CLI flag. ### State directory -Portless stores its state (routes, PID file, port file) in `~/.portless`. Override with the `PORTLESS_STATE_DIR` environment variable. +Portless stores its state (routes, PID file, port file) in `~/.portless`. When the proxy runs under sudo, this remains the invoking user's home directory so unprivileged apps and the proxy share route registrations. Override with the `PORTLESS_STATE_DIR` environment variable. ### Environment variables -| Variable | Description | -| --------------------- | --------------------------------------------------------------------------- | -| `PORTLESS_PORT` | Override the default proxy port (default: 443 with HTTPS, 80 without) | -| `PORTLESS_APP_PORT` | Use a fixed port for the app (skip auto-assignment) | -| `PORTLESS_HTTPS` | HTTPS on by default; set to `0` to disable (same as `--no-tls`) | -| `PORTLESS_LAN` | Set to `1` to always enable LAN mode (auto-detects LAN IP) | -| `PORTLESS_LAN_IP` | Pin a specific LAN IP for LAN mode | -| `PORTLESS_TLD` | Use one or more TLDs (e.g. localhost,test) | -| `PORTLESS_WILDCARD` | Set to `1` to allow unregistered subdomains to fall back to parent | -| `PORTLESS_SYNC_HOSTS` | Set to `0` to disable auto-sync of /etc/hosts (on by default) | -| `PORTLESS_TAILSCALE` | Set to `1` to share apps on your Tailscale network (same as `--tailscale`) | -| `PORTLESS_FUNNEL` | Set to `1` to share apps publicly via Tailscale Funnel (same as `--funnel`) | -| `PORTLESS_NGROK` | Set to `1` to share apps publicly via ngrok (same as `--ngrok`) | -| `PORTLESS_STATE_DIR` | Override the state directory | -| `PORTLESS=0` | Bypass the proxy, run the command directly | +| Variable | Description | +| --------------------- | ------------------------------------------------------------------------------ | +| `PORTLESS_PORT` | Override the default proxy port (default: 443 with HTTPS, 80 without) | +| `PORTLESS_APP_PORT` | Use a fixed port for the app (skip auto-assignment) | +| `PORTLESS_HTTPS` | HTTPS on by default; set to `0` to disable (same as `--no-tls`) | +| `PORTLESS_LAN` | Set to `1` to always enable LAN mode (auto-detects LAN IP) | +| `PORTLESS_LAN_IP` | Pin a specific LAN IP for LAN mode | +| `PORTLESS_TLD` | Use one or more TLDs, single or multi-segment (e.g. localhost,dev.example.com) | +| `PORTLESS_WILDCARD` | Set to `1` to allow unregistered subdomains to fall back to parent | +| `PORTLESS_SYNC_HOSTS` | Set to `0` to disable auto-sync of /etc/hosts (on by default) | +| `PORTLESS_TAILSCALE` | Set to `1` to share apps on your Tailscale network (same as `--tailscale`) | +| `PORTLESS_FUNNEL` | Set to `1` to share apps publicly via Tailscale Funnel (same as `--funnel`) | +| `PORTLESS_NGROK` | Set to `1` to share apps publicly via ngrok (same as `--ngrok`) | +| `PORTLESS_STATE_DIR` | Override the state directory | +| `PORTLESS=0` | Bypass the proxy, run the command directly | ### HTTP/2 + HTTPS -HTTPS with HTTP/2 is enabled by default (faster page loads for dev servers with many files). First run generates a local CA and adds it to the system trust store. After that, no prompts and no browser warnings. +HTTPS with HTTP/2 is enabled by default (faster page loads for dev servers with many files). WebSockets work over both HTTP/1.1 (Upgrade) and HTTP/2 (RFC 8441 extended CONNECT), so dev server HMR works through the proxy. First run generates a local CA and adds it to the system trust store. After that, no prompts and no browser warnings. ```bash portless proxy start --cert ./c.pem --key ./k.pem # Use custom certs @@ -199,7 +203,7 @@ portless proxy start --no-tls # Disable HTTPS (plain HTTP) portless trust # Add CA to trust store later ``` -On Linux, `portless trust` supports Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, and openSUSE (via `update-ca-certificates` or `update-ca-trust`). On Windows, it uses `certutil` to add the CA to the system trust store. +On Linux, `portless trust` supports Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, and openSUSE (via `update-ca-certificates` or `update-ca-trust`). On Windows, it uses `certutil` to add the CA to the system trust store. On WSL, it updates both the Linux trust store and the Windows current-user Root store so Windows browsers trust portless HTTPS certificates. ### LAN mode @@ -209,7 +213,7 @@ portless proxy start --lan --https portless proxy start --lan --ip 192.168.1.42 ``` -`--lan` advertises `.local` hostnames over mDNS so any device on the same Wi-Fi can reach your apps. Portless auto-detects your LAN IP and follows network changes automatically, but you can pin a specific address with `--ip
` or the `PORTLESS_LAN_IP` environment variable. Set `PORTLESS_LAN=1` to default to LAN mode every time the proxy starts. +`--lan` explicitly binds the proxy to the IPv4 and IPv6 unspecified addresses, `0.0.0.0` and `::`, and advertises `.local` hostnames over mDNS so devices on the same Wi-Fi can reach your apps. Portless auto-detects your LAN IP and follows network changes automatically, but you can pin a specific address with `--ip
` or the `PORTLESS_LAN_IP` environment variable. Set `PORTLESS_LAN=1` to default to LAN mode every time the proxy starts. Portless remembers LAN mode via `proxy.lan`, so if you stop a LAN proxy and start again, it stays in LAN mode. All proxy settings (port, TLS, TLDs, LAN) are persisted and reused on auto-start unless overridden by explicit flags or env vars. Use `PORTLESS_LAN=0` for one start to switch back to `.localhost` mode. If a proxy is already running with different explicit LAN/TLS/TLD settings, portless warns and asks you to stop it first. @@ -297,6 +301,7 @@ The chosen service configuration is written into launchd, systemd, or Task Sched | `portless proxy start -p ` | Start the proxy on a custom port | | `portless proxy start --tld test` | Use .test instead of .localhost | | `portless proxy start --tld localhost --tld test` | Serve both TLDs from one proxy | +| `portless proxy start --tld dev.example.com` | Use a multi-segment TLD for production-parity URLs | | `portless proxy start --foreground` | Start the proxy in foreground (for debugging) | | `portless proxy start --wildcard` | Allow unregistered subdomains to fall back to parent route | | `portless proxy stop` | Stop the proxy | @@ -424,7 +429,7 @@ This adds the portless local CA to your system trust store. After that, restart portless clean ``` -Stops the proxy if needed, removes the portless CA from the trust store (when portless added it), deletes known files under state directories, and removes the portless `/etc/hosts` block. May require `sudo` on macOS/Linux. +Stops the proxy if needed, removes the portless CA from the trust store (when portless added it), deletes known files under state directories, and removes the portless `/etc/hosts` block. May require `sudo` on macOS/Linux. If trust-store removal fails, portless retains its CA certificate and key so a later `portless clean` can safely retry. ### Proxy loop (508 Loop Detected) diff --git a/plugins/portless/.claude/skills/portless b/plugins/portless/.claude/skills/portless new file mode 120000 index 00000000..240f102d --- /dev/null +++ b/plugins/portless/.claude/skills/portless @@ -0,0 +1 @@ +../../.agents/skills/portless \ No newline at end of file diff --git a/plugins/portless/agent/skills/portless/SKILL.md b/plugins/portless/agent/skills/portless/SKILL.md index 0f5df541..3b9f2302 100644 --- a/plugins/portless/agent/skills/portless/SKILL.md +++ b/plugins/portless/agent/skills/portless/SKILL.md @@ -159,37 +159,41 @@ PORTLESS=0 pnpm dev # Bypasses proxy, uses default port 2. `portless ` assigns a random free port (4000-4999) via the `PORT` env var and registers the app with the proxy 3. The browser hits `https://.localhost`; the proxy forwards to the app's assigned port +Outside LAN mode, the proxy and its HTTP redirect listener bind only to the IPv4 and IPv6 loopback addresses, `127.0.0.1` and `::1`. They do not accept connections through LAN, VPN, or other network interfaces. + `.localhost` domains resolve to `127.0.0.1` natively in Chrome, Firefox, and Edge. Safari relies on the system DNS resolver, which may not handle `.localhost` subdomains on all configurations. Run `portless hosts sync` to add entries to `/etc/hosts` if needed. -Use `portless proxy start --tld localhost --tld test` to serve the same app names under multiple TLDs from one proxy. `PORTLESS_URL` uses the first configured TLD. `PORTLESS_TLD` accepts the same comma separated list format, e.g. `PORTLESS_TLD=localhost,test`. +Use `portless proxy start --tld localhost --tld test` to serve the same app names under multiple TLDs from one proxy. `PORTLESS_URL` uses the first configured TLD. When configured TLDs overlap (e.g. `example.com` and `dev.example.com`), hostnames are matched against the longest TLD first, regardless of configuration order. `PORTLESS_TLD` accepts the same comma separated list format, e.g. `PORTLESS_TLD=localhost,test`. + +TLDs can be multi-segment DNS names such as `dev.example.com`, so local URLs can mirror production structure (`myapp.dev.example.com`). Each label follows DNS rules: lowercase letters, digits, interior hyphens, 63 characters per label, 253 total. Strict OAuth providers that reject `.localhost` redirect URIs accept a real domain like `https://myapp.dev.example.com/api/auth/callback/google`. Most frameworks (Next.js, Express, Nuxt, etc.) respect the `PORT` env var automatically. For frameworks that ignore `PORT` (Vite, VitePlus, Astro, React Router, Angular, Expo, React Native), portless auto-injects the correct `--port` flag and, when needed, a matching `--host` CLI flag. ### State directory -Portless stores its state (routes, PID file, port file) in `~/.portless`. Override with the `PORTLESS_STATE_DIR` environment variable. +Portless stores its state (routes, PID file, port file) in `~/.portless`. When the proxy runs under sudo, this remains the invoking user's home directory so unprivileged apps and the proxy share route registrations. Override with the `PORTLESS_STATE_DIR` environment variable. ### Environment variables -| Variable | Description | -| --------------------- | --------------------------------------------------------------------------- | -| `PORTLESS_PORT` | Override the default proxy port (default: 443 with HTTPS, 80 without) | -| `PORTLESS_APP_PORT` | Use a fixed port for the app (skip auto-assignment) | -| `PORTLESS_HTTPS` | HTTPS on by default; set to `0` to disable (same as `--no-tls`) | -| `PORTLESS_LAN` | Set to `1` to always enable LAN mode (auto-detects LAN IP) | -| `PORTLESS_LAN_IP` | Pin a specific LAN IP for LAN mode | -| `PORTLESS_TLD` | Use one or more TLDs (e.g. localhost,test) | -| `PORTLESS_WILDCARD` | Set to `1` to allow unregistered subdomains to fall back to parent | -| `PORTLESS_SYNC_HOSTS` | Set to `0` to disable auto-sync of /etc/hosts (on by default) | -| `PORTLESS_TAILSCALE` | Set to `1` to share apps on your Tailscale network (same as `--tailscale`) | -| `PORTLESS_FUNNEL` | Set to `1` to share apps publicly via Tailscale Funnel (same as `--funnel`) | -| `PORTLESS_NGROK` | Set to `1` to share apps publicly via ngrok (same as `--ngrok`) | -| `PORTLESS_STATE_DIR` | Override the state directory | -| `PORTLESS=0` | Bypass the proxy, run the command directly | +| Variable | Description | +| --------------------- | ------------------------------------------------------------------------------ | +| `PORTLESS_PORT` | Override the default proxy port (default: 443 with HTTPS, 80 without) | +| `PORTLESS_APP_PORT` | Use a fixed port for the app (skip auto-assignment) | +| `PORTLESS_HTTPS` | HTTPS on by default; set to `0` to disable (same as `--no-tls`) | +| `PORTLESS_LAN` | Set to `1` to always enable LAN mode (auto-detects LAN IP) | +| `PORTLESS_LAN_IP` | Pin a specific LAN IP for LAN mode | +| `PORTLESS_TLD` | Use one or more TLDs, single or multi-segment (e.g. localhost,dev.example.com) | +| `PORTLESS_WILDCARD` | Set to `1` to allow unregistered subdomains to fall back to parent | +| `PORTLESS_SYNC_HOSTS` | Set to `0` to disable auto-sync of /etc/hosts (on by default) | +| `PORTLESS_TAILSCALE` | Set to `1` to share apps on your Tailscale network (same as `--tailscale`) | +| `PORTLESS_FUNNEL` | Set to `1` to share apps publicly via Tailscale Funnel (same as `--funnel`) | +| `PORTLESS_NGROK` | Set to `1` to share apps publicly via ngrok (same as `--ngrok`) | +| `PORTLESS_STATE_DIR` | Override the state directory | +| `PORTLESS=0` | Bypass the proxy, run the command directly | ### HTTP/2 + HTTPS -HTTPS with HTTP/2 is enabled by default (faster page loads for dev servers with many files). First run generates a local CA and adds it to the system trust store. After that, no prompts and no browser warnings. +HTTPS with HTTP/2 is enabled by default (faster page loads for dev servers with many files). WebSockets work over both HTTP/1.1 (Upgrade) and HTTP/2 (RFC 8441 extended CONNECT), so dev server HMR works through the proxy. First run generates a local CA and adds it to the system trust store. After that, no prompts and no browser warnings. ```bash portless proxy start --cert ./c.pem --key ./k.pem # Use custom certs @@ -197,7 +201,7 @@ portless proxy start --no-tls # Disable HTTPS (plain HTTP) portless trust # Add CA to trust store later ``` -On Linux, `portless trust` supports Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, and openSUSE (via `update-ca-certificates` or `update-ca-trust`). On Windows, it uses `certutil` to add the CA to the system trust store. +On Linux, `portless trust` supports Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, and openSUSE (via `update-ca-certificates` or `update-ca-trust`). On Windows, it uses `certutil` to add the CA to the system trust store. On WSL, it updates both the Linux trust store and the Windows current-user Root store so Windows browsers trust portless HTTPS certificates. ### LAN mode @@ -207,7 +211,7 @@ portless proxy start --lan --https portless proxy start --lan --ip 192.168.1.42 ``` -`--lan` advertises `.local` hostnames over mDNS so any device on the same Wi-Fi can reach your apps. Portless auto-detects your LAN IP and follows network changes automatically, but you can pin a specific address with `--ip
` or the `PORTLESS_LAN_IP` environment variable. Set `PORTLESS_LAN=1` to default to LAN mode every time the proxy starts. +`--lan` explicitly binds the proxy to the IPv4 and IPv6 unspecified addresses, `0.0.0.0` and `::`, and advertises `.local` hostnames over mDNS so devices on the same Wi-Fi can reach your apps. Portless auto-detects your LAN IP and follows network changes automatically, but you can pin a specific address with `--ip
` or the `PORTLESS_LAN_IP` environment variable. Set `PORTLESS_LAN=1` to default to LAN mode every time the proxy starts. Portless remembers LAN mode via `proxy.lan`, so if you stop a LAN proxy and start again, it stays in LAN mode. All proxy settings (port, TLS, TLDs, LAN) are persisted and reused on auto-start unless overridden by explicit flags or env vars. Use `PORTLESS_LAN=0` for one start to switch back to `.localhost` mode. If a proxy is already running with different explicit LAN/TLS/TLD settings, portless warns and asks you to stop it first. @@ -295,6 +299,7 @@ The chosen service configuration is written into launchd, systemd, or Task Sched | `portless proxy start -p ` | Start the proxy on a custom port | | `portless proxy start --tld test` | Use .test instead of .localhost | | `portless proxy start --tld localhost --tld test` | Serve both TLDs from one proxy | +| `portless proxy start --tld dev.example.com` | Use a multi-segment TLD for production-parity URLs | | `portless proxy start --foreground` | Start the proxy in foreground (for debugging) | | `portless proxy start --wildcard` | Allow unregistered subdomains to fall back to parent route | | `portless proxy stop` | Stop the proxy | @@ -422,7 +427,7 @@ This adds the portless local CA to your system trust store. After that, restart portless clean ``` -Stops the proxy if needed, removes the portless CA from the trust store (when portless added it), deletes known files under state directories, and removes the portless `/etc/hosts` block. May require `sudo` on macOS/Linux. +Stops the proxy if needed, removes the portless CA from the trust store (when portless added it), deletes known files under state directories, and removes the portless `/etc/hosts` block. May require `sudo` on macOS/Linux. If trust-store removal fails, portless retains its CA certificate and key so a later `portless clean` can safely retry. ### Proxy loop (508 Loop Detected) diff --git a/plugins/portless/skills-lock.json b/plugins/portless/skills-lock.json index 8a58bfcf..583f677b 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": "5f707167fa4e23761d1c74dc176649fbb2169dea0890c035e8ad72cbfdc0d56b" + "computedHash": "5b758ea66233a1ebd494554904db55d902f5ea8832c353188d771f5c6fed9d2b" } } } diff --git a/plugins/prisma/.agents/skills/prisma-cli/SKILL.md b/plugins/prisma/.agents/skills/prisma-cli/SKILL.md index 17595be1..b2b770ef 100644 --- a/plugins/prisma/.agents/skills/prisma-cli/SKILL.md +++ b/plugins/prisma/.agents/skills/prisma-cli/SKILL.md @@ -1,19 +1,19 @@ --- name: prisma-cli -description: Prisma ORM CLI commands reference covering init, generate, migrate, db, dev, studio, validate, format, debug, and mcp. Use for ORM/database CLI workflows, not Prisma Compute app deployment. For Prisma Compute, `@prisma/cli app deploy`, `compute:deploy`, `create-prisma --deploy`, apps, deployments, logs, or domains, use the `prisma-compute` skill instead. Triggers on "prisma init", "prisma generate", "prisma migrate", "prisma db", "prisma studio", "prisma mcp". +description: Prisma ORM CLI commands reference covering init, generate, migrate, db, dev, complete, studio, validate, format, debug, and mcp. Use for ORM/database CLI workflows, not the Prisma Platform CLI. Triggers on "prisma init", "prisma generate", "prisma migrate", "prisma db", "prisma complete", "prisma studio", "prisma mcp". license: MIT metadata: author: prisma - version: "7.6.0" + version: "7.9.1" --- # Prisma CLI Reference Reference for Prisma ORM CLI commands. This skill provides guidance on command usage, options, and best practices for current Prisma ORM releases. -## Boundary: Compute +## Boundary: Platform and Compute -Do not use this skill for Prisma Compute app deployment. Use `prisma-compute` for `@prisma/cli app deploy`, `compute:deploy`, `create-prisma --deploy`, Compute apps, deployments, logs, domains, and framework deploy readiness. +Do not confuse the stable ORM command (`prisma`) with the public-beta Platform package (`@prisma/cli`, binary `prisma-cli`). Use `prisma-compute` for Compute apps and workspace auth, and `prisma-postgres` for Platform projects and databases. ## When to Apply @@ -24,6 +24,7 @@ Reference this skill when: - Managing database state (`prisma db push/pull`) - Using local development database (`prisma dev`) - Debugging Prisma issues (`prisma debug`) +- Generating shell completions (`prisma complete`) ## Rule Categories by Priority @@ -34,19 +35,19 @@ Reference this skill when: | 3 | Development | HIGH | `dev` | | 4 | Database | HIGH | `db-` | | 5 | Migrations | CRITICAL | `migrate-` | -| 6 | Utility | MEDIUM | `studio`, `validate`, `format`, `debug`, `mcp` | +| 6 | Utility | MEDIUM | `complete`, `studio`, `validate`, `format`, `debug`, `mcp` | ## Command Categories | Category | Commands | Purpose | |----------|----------|---------| -| Setup | `init` | Bootstrap new Prisma project | +| Setup | `init` | Initialize a Prisma project | | Generation | `generate` | Generate Prisma Client | | Validation | `validate`, `format` | Schema validation and formatting | | Development | `dev` | Local Prisma Postgres for development | | Database | `db pull`, `db push`, `db seed`, `db execute` | Direct database operations | | Migrations | `migrate dev`, `migrate deploy`, `migrate reset`, `migrate status`, `migrate diff`, `migrate resolve` | Schema migrations | -| Utility | `studio`, `mcp`, `version`, `debug` | Development and AI tooling | +| Utility | `complete`, `studio`, `mcp`, `version`, `debug` | Shell, development, and AI tooling | ## Quick Reference @@ -66,6 +67,7 @@ prisma init --db # Initialize with an example model prisma init --with-model + ``` ### Client Generation @@ -178,8 +180,22 @@ prisma validate # Format schema prisma format + +# Generate shell completion code +prisma complete zsh ``` +## AI Safety Checkpoint + +Prisma blocks destructive commands when it detects an AI agent until the agent has obtained explicit user consent. This covers `migrate reset`, `db push --force-reset`, and `db push --accept-data-loss`. + +- Explain the exact data-loss impact and ask for consent immediately before running the command. +- Do not infer consent from earlier or unrelated messages. +- If automation needs the consent variable, set `PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION` to the user's exact consent message. Do not invent the text. +- The Prisma MCP server deliberately has no `migrate-reset` tool. + +Read `references/agent-safety.md` before any destructive Prisma command. + ## Current Prisma CLI Setup ### New Configuration File @@ -237,6 +253,8 @@ references/migrate-resolve.md - Migration resolution references/migrate-diff.md - Schema diffing references/studio.md - Database GUI references/mcp.md - Prisma MCP server +references/complete.md - Shell completion generation +references/agent-safety.md - AI consent checkpoint for destructive commands references/validate.md - Schema validation references/format.md - Schema formatting references/debug.md - Debug info diff --git a/plugins/prisma/.agents/skills/prisma-cli/references/agent-safety.md b/plugins/prisma/.agents/skills/prisma-cli/references/agent-safety.md new file mode 100644 index 00000000..651c6c34 --- /dev/null +++ b/plugins/prisma/.agents/skills/prisma-cli/references/agent-safety.md @@ -0,0 +1,27 @@ +# AI safety checkpoint for destructive commands + +Prisma detects common AI-agent environments and blocks these commands until the user gives explicit consent: + +- `prisma migrate reset` +- `prisma db push --force-reset` +- `prisma db push --accept-data-loss` + +## Required workflow + +1. Inspect the target database/config and explain exactly what can be deleted or reset. +2. Ask the user for explicit consent immediately before the action. +3. Run the command only after that consent. + +For an agent-run subprocess, Prisma accepts the exact consent text through: + +```bash +PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION='' prisma migrate reset --force +``` + +The value must match the user's message exactly and must not contain added quotes or newlines. Never fabricate consent, reuse an old unrelated approval, or bypass the checkpoint by hiding agent-detection environment variables. + +The MCP server has no `migrate-reset` tool. Use the shell command only after consent. + +## Reference + +- [Prisma ORM 7.9.0 release](https://github.com/prisma/prisma/releases/tag/7.9.0) diff --git a/plugins/prisma/.agents/skills/prisma-cli/references/complete.md b/plugins/prisma/.agents/skills/prisma-cli/references/complete.md new file mode 100644 index 00000000..73324407 --- /dev/null +++ b/plugins/prisma/.agents/skills/prisma-cli/references/complete.md @@ -0,0 +1,22 @@ +# prisma complete + +Prints a shell completion script. + +```bash +prisma complete zsh +prisma complete bash +prisma complete fish +prisma complete powershell +``` + +For a direct global CLI installation, load the output using the shell's normal startup mechanism. For example, in zsh: + +```bash +source <(prisma complete zsh) +``` + +Prisma also integrates with supported package-manager completion flows. `npx` and `bunx` do not themselves provide completion; invoke the installed binary or the package manager's supported execution form such as `npm exec` or `bun x`. + +## Reference + +- [Prisma ORM 7.9.0 release](https://github.com/prisma/prisma/releases/tag/7.9.0) diff --git a/plugins/prisma/.agents/skills/prisma-cli/references/db-push.md b/plugins/prisma/.agents/skills/prisma-cli/references/db-push.md index 51acdbb2..74da0cf4 100644 --- a/plugins/prisma/.agents/skills/prisma-cli/references/db-push.md +++ b/plugins/prisma/.agents/skills/prisma-cli/references/db-push.md @@ -25,6 +25,8 @@ prisma db push [options] | `--config` | Custom path to your Prisma config file | | `--url` | Override the datasource URL from the Prisma config file | +When Prisma detects an AI agent, `--force-reset` and `--accept-data-loss` require explicit user consent. Follow `agent-safety.md`; never infer or fabricate the consent text. + ### Follow-up Command - Run `prisma generate` explicitly when you need refreshed client output diff --git a/plugins/prisma/.agents/skills/prisma-cli/references/init.md b/plugins/prisma/.agents/skills/prisma-cli/references/init.md index a255c7b5..c4390ed8 100644 --- a/plugins/prisma/.agents/skills/prisma-cli/references/init.md +++ b/plugins/prisma/.agents/skills/prisma-cli/references/init.md @@ -34,6 +34,9 @@ bunx --bun prisma init | `--output` | Define Prisma Client generator output path to use | - | | `--preview-feature` | Define a preview feature to use | - | | `--with-model` | Add example model to created schema file | - | +| `--no-skills` | Skip the best-effort installation of Prisma agent skills | - | + +`prisma init` attempts to install `prisma/skills` for detected agents. This is best-effort and does not make project initialization fail. Use `--no-skills` in minimal or controlled environments. ## Examples diff --git a/plugins/prisma/.agents/skills/prisma-cli/references/mcp.md b/plugins/prisma/.agents/skills/prisma-cli/references/mcp.md index 26cb963f..284130fa 100644 --- a/plugins/prisma/.agents/skills/prisma-cli/references/mcp.md +++ b/plugins/prisma/.agents/skills/prisma-cli/references/mcp.md @@ -30,7 +30,8 @@ prisma mcp - Run this from the project that contains your Prisma schema and `prisma.config.ts` - The command is separate from Prisma Studio and does not open a browser UI -- The MCP server wraps Prisma CLI commands. For exact behavior of commands like `migrate dev` or `migrate reset`, follow the underlying CLI command docs rather than relying only on the MCP tool descriptions. +- The MCP server exposes `migrate-status`, `migrate-dev`, and Prisma Studio tooling. It does not expose the destructive `migrate-reset` tool; do not claim it is available or try to bypass that safety boundary. +- For destructive shell commands, follow `agent-safety.md` and obtain explicit user consent. ## References diff --git a/plugins/prisma/.agents/skills/prisma-cli/references/migrate-dev.md b/plugins/prisma/.agents/skills/prisma-cli/references/migrate-dev.md index 6b6352b4..48643b64 100644 --- a/plugins/prisma/.agents/skills/prisma-cli/references/migrate-dev.md +++ b/plugins/prisma/.agents/skills/prisma-cli/references/migrate-dev.md @@ -31,7 +31,7 @@ prisma migrate dev [options] - Run `prisma generate` explicitly when you need refreshed client output - Run `prisma db seed` explicitly when you need seed data -Note: Prisma CLI help for `7.6.0` still says `migrate dev` "trigger[s] generators", but local verification in a temp Prisma 7.6.0 project did not emit generated client files. Treat `prisma generate` as an explicit follow-up step when you need generated artifacts on disk. +Run `prisma generate` as an explicit follow-up when you need refreshed generated artifacts. Do not rely on historical CLI help that described generators as part of `migrate dev`. ## Examples diff --git a/plugins/prisma/.agents/skills/prisma-cli/references/migrate-reset.md b/plugins/prisma/.agents/skills/prisma-cli/references/migrate-reset.md index 38d6fbdc..900e2885 100644 --- a/plugins/prisma/.agents/skills/prisma-cli/references/migrate-reset.md +++ b/plugins/prisma/.agents/skills/prisma-cli/references/migrate-reset.md @@ -17,6 +17,8 @@ prisma migrate reset [options] **Warning: All data will be lost.** +When Prisma detects an AI agent, this command is blocked until the user gives explicit consent. Follow `agent-safety.md`; `--force` skips the ordinary prompt but does not constitute user consent for an agent. + ## Options | Option | Description | diff --git a/plugins/prisma/.agents/skills/prisma-client-api/SKILL.md b/plugins/prisma/.agents/skills/prisma-client-api/SKILL.md index 8325eeac..57aa8a52 100644 --- a/plugins/prisma/.agents/skills/prisma-client-api/SKILL.md +++ b/plugins/prisma/.agents/skills/prisma-client-api/SKILL.md @@ -4,7 +4,7 @@ description: Prisma Client API reference covering model queries, filters, operat license: MIT metadata: author: prisma - version: "7.6.0" + version: "7.9.1" --- # Prisma Client API Reference diff --git a/plugins/prisma/.agents/skills/prisma-client-api/references/constructor.md b/plugins/prisma/.agents/skills/prisma-client-api/references/constructor.md index 7c7825f3..a9fb5e8f 100644 --- a/plugins/prisma/.agents/skills/prisma-client-api/references/constructor.md +++ b/plugins/prisma/.agents/skills/prisma-client-api/references/constructor.md @@ -127,6 +127,19 @@ const prisma = new PrismaClient({ }) ``` +### queryPlanCacheMaxSize + +Use `queryPlanCacheMaxSize` to limit the in-memory query-plan cache: + +```typescript +const prisma = new PrismaClient({ + adapter, + queryPlanCacheMaxSize: 2_000, +}) +``` + +The value must be a non-negative integer. Set it to `0` to disable query-plan caching; omit it to use Prisma's default. Treat this as a process-local memory/performance control, not a database prepared-statement setting. + ## Singleton Pattern Prevent multiple client instances in development: diff --git a/plugins/prisma/.agents/skills/prisma-client-api/references/raw-queries.md b/plugins/prisma/.agents/skills/prisma-client-api/references/raw-queries.md index 5d3a220b..e444ce2f 100644 --- a/plugins/prisma/.agents/skills/prisma-client-api/references/raw-queries.md +++ b/plugins/prisma/.agents/skills/prisma-client-api/references/raw-queries.md @@ -192,3 +192,7 @@ const users = await prisma.$queryRaw` ` // createdAt is already a Date object ``` + +Invalid JavaScript `Date` values passed to raw queries fail validation instead of being silently serialized as `null`. Validate date input at the application boundary; do not rely on `new Date(badValue)` reaching the database. + +When a driver adapter returns an unmapped database-specific error, Prisma surfaces `P2039` with the adapter's preserved original code/message. If those details are missing, fix the adapter mapping rather than parsing rendered error text. diff --git a/plugins/prisma/.agents/skills/prisma-database-setup/SKILL.md b/plugins/prisma/.agents/skills/prisma-database-setup/SKILL.md index 1de91a64..51643e74 100644 --- a/plugins/prisma/.agents/skills/prisma-database-setup/SKILL.md +++ b/plugins/prisma/.agents/skills/prisma-database-setup/SKILL.md @@ -170,7 +170,7 @@ generator client { } ``` -For MongoDB, stay on the latest Prisma 6.x line and keep the connection URL in `schema.prisma`. Do not move a MongoDB project to the Prisma 7 SQL adapter setup. +For MongoDB, stay on the latest Prisma 6.x line and keep the connection URL in `schema.prisma`. Do not move a MongoDB project to the Prisma 7 SQL adapter setup. If a MongoDB project asks about upgrading Prisma versions, route to the `prisma-mongodb-upgrade` skill (stay-on-v6 vs Prisma Next is the real decision; Prisma 7 is not an option). ## Rule Files diff --git a/plugins/prisma/.agents/skills/prisma-driver-adapter-implementation/SKILL.md b/plugins/prisma/.agents/skills/prisma-driver-adapter-implementation/SKILL.md index 12d3dae5..4bf3479c 100644 --- a/plugins/prisma/.agents/skills/prisma-driver-adapter-implementation/SKILL.md +++ b/plugins/prisma/.agents/skills/prisma-driver-adapter-implementation/SKILL.md @@ -1,638 +1,270 @@ --- name: prisma-driver-adapter-implementation -description: Required reference for Prisma v7 driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter/Transaction interfaces. Contains critical contract details not inferable from code examples — including the transaction lifecycle protocol, error mapping requirements, and verification checklist. Existing implementations do not replace this skill. +description: Required reference for Prisma ORM 7 SQL driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter, Transaction, savepoint, result mapping, or DriverAdapterError behavior. Covers current transaction lifecycle, optional savepoint hooks, original database-error preservation, and verification. license: MIT metadata: - author: Tyler Benfield - version: "7.6.0" + author: prisma + version: "7.9.1" --- -# Prisma 7 Driver Adapter Implementation Guide +# Prisma SQL Driver Adapter Implementation -This skill provides everything needed to implement a Prisma ORM v7 driver adapter for any database. +Use this guide with the exact `@prisma/driver-adapter-utils` version installed by the target Prisma release. Driver adapters are a protocol boundary: type-compatible code can still corrupt values, leak connections, or break transactions. -## Architecture Overview +## When to Apply -``` -┌─────────────────────────────────────────────────────────────────┐ -│ PrismaClient │ -│ (requires adapter factory) │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ SqlMigrationAwareDriverAdapterFactory │ -│ ┌─────────────────────┐ ┌─────────────────────────────┐ │ -│ │ connect() │ │ connectToShadowDb() │ │ -│ │ → SqlDriverAdapter │ │ → SqlDriverAdapter │ │ -│ └─────────────────────┘ └─────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ SqlDriverAdapter │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ -│ │ queryRaw() │ │ executeRaw() │ │ startTransaction() │ │ -│ │ → ResultSet │ │ → number │ │ → Transaction │ │ -│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ -│ │executeScript │ │ dispose() │ │ getConnectionInfo() │ │ -│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ Transaction │ -│ Extends SqlQueryable + commit() + rollback() + options │ -│ (lifecycle hooks only — Prisma sends SQL via executeRaw) │ -└─────────────────────────────────────────────────────────────────┘ -``` - -## Required Interfaces +- Implementing `SqlDriverAdapterFactory`, `SqlMigrationAwareDriverAdapterFactory`, `SqlDriverAdapter`, or `Transaction` +- Adding nested-transaction/savepoint support +- Mapping driver values, column metadata, bind arguments, or database errors +- Debugging `P2039`, transaction leaks, shadow-database failures, or adapter-specific query behavior -Import from `@prisma/driver-adapter-utils`: +## Contract snapshot ```typescript -import type { - ColumnType, - IsolationLevel, - SqlDriverAdapter, - SqlMigrationAwareDriverAdapterFactory, - SqlQuery, - SqlQueryable, - SqlResultSet, - Transaction, - TransactionOptions, - ArgType, - ConnectionInfo, - MappedError, -} from "@prisma/driver-adapter-utils"; -import { - ColumnTypeEnum, - DriverAdapterError, -} from "@prisma/driver-adapter-utils"; -``` - -## Interface Definitions - -### SqlQuery (input to queryRaw/executeRaw) - -```typescript -type SqlQuery = { - sql: string; // Parameterized SQL with placeholders - args: Array; // Bound parameter values - argTypes: Array; // Type hints for each argument -}; - -type ArgType = { - scalarType: ArgScalarType; // 'string' | 'int' | 'bigint' | 'float' | 'decimal' | 'boolean' | 'enum' | 'uuid' | 'json' | 'datetime' | 'bytes' | 'unknown' - dbType?: string; - arity: "scalar" | "list"; -}; -``` - -### SqlResultSet (output from queryRaw) - -```typescript -interface SqlResultSet { - columnNames: Array; // Column names in order - columnTypes: Array; // Column types matching columnNames - rows: Array>; // Row data as arrays - lastInsertId?: string; // For INSERT without RETURNING +interface SqlDriverAdapterFactory extends AdapterInfo { + connect(): Promise } -``` - -### ColumnTypeEnum values - -```typescript -const ColumnTypeEnum = { - Int32: 0, - Int64: 1, - Float: 2, - Double: 3, - Numeric: 4, - Boolean: 5, - Character: 6, - Text: 7, - Date: 8, - Time: 9, - DateTime: 10, - Json: 11, - Enum: 12, - Bytes: 13, - Set: 14, - Uuid: 15, - Int32Array: 64, - Int64Array: 65, - FloatArray: 66, - DoubleArray: 67, - NumericArray: 68, - BooleanArray: 69, - CharacterArray: 70, - TextArray: 71, - DateArray: 72, - TimeArray: 73, - DateTimeArray: 74, - JsonArray: 75, - EnumArray: 76, - BytesArray: 77, - UuidArray: 78, - UnknownNumber: 128, -} as const; -``` - -### SqlDriverAdapter -```typescript -interface SqlDriverAdapter extends SqlQueryable { - executeScript(script: string): Promise; - startTransaction(isolationLevel?: IsolationLevel): Promise; - getConnectionInfo?(): ConnectionInfo; - dispose(): Promise; +interface SqlMigrationAwareDriverAdapterFactory extends SqlDriverAdapterFactory { + connectToShadowDb(): Promise } -``` -### Transaction - -```typescript -interface Transaction extends SqlQueryable { - readonly options: TransactionOptions; - commit(): Promise; - rollback(): Promise; +interface SqlDriverAdapter extends AdapterInfo { + queryRaw(query: SqlQuery): Promise + executeRaw(query: SqlQuery): Promise + executeScript(script: string): Promise + startTransaction(isolationLevel?: IsolationLevel): Promise + getConnectionInfo?(): ConnectionInfo + dispose(): Promise } -type TransactionOptions = { usePhantomQuery: boolean }; +interface Transaction extends AdapterInfo { + readonly options: { usePhantomQuery: boolean } + queryRaw(query: SqlQuery): Promise + executeRaw(query: SqlQuery): Promise + commit(): Promise + rollback(): Promise + createSavepoint?(name: string): Promise + rollbackToSavepoint?(name: string): Promise + releaseSavepoint?(name: string): Promise +} ``` -### SqlMigrationAwareDriverAdapterFactory +`IsolationLevel` currently includes `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, `SNAPSHOT`, and `SERIALIZABLE`; validate what the concrete database supports. -```typescript -interface SqlMigrationAwareDriverAdapterFactory { - readonly provider: "mysql" | "postgres" | "sqlite" | "sqlserver"; - readonly adapterName: string; - connect(): Promise; - connectToShadowDb(): Promise; -} -``` +## Priority rules -## Implementation Steps +| Priority | Rule | Impact | +|----------|------|--------| +| CRITICAL | One dedicated connection per transaction | Prevents interleaving and leaks | +| CRITICAL | `commit`/`rollback` are lifecycle cleanup hooks | Prevents duplicate COMMIT/ROLLBACK | +| CRITICAL | Savepoints live on `Transaction`, not adapter-global depth | Makes nested scopes connection-local | +| CRITICAL | Preserve original database error code/message | Enables useful `P2039` fallback | +| HIGH | Map arguments and result metadata exactly | Prevents silent value corruption | +| HIGH | Shadow databases are isolated and always cleaned up | Makes Migrate safe | +| HIGH | Dispose only resources the adapter owns | Prevents shutting down caller-owned pools | -### Step 1: Create the Queryable base class +## Query implementation + +`SqlQuery` contains `sql`, `args`, and parallel `argTypes`. Map each argument using both value and `ArgType`; do not discard type/arity information. Execute in the driver's array/tuple row mode so column order is stable. ```typescript -class MyQueryable implements SqlQueryable { - readonly provider = "postgres" as const; // or 'sqlite' | 'mysql' | 'sqlserver' - readonly adapterName = "@my-org/adapter-mydb" as const; +class ExampleQueryable { + readonly provider = 'postgres' as const + readonly adapterName = '@acme/adapter-example' - constructor(protected readonly client: TClient) {} + constructor(protected readonly connection: DriverConnection) {} async queryRaw(query: SqlQuery): Promise { try { - const args = query.args.map((arg, i) => - mapArg(arg, query.argTypes[i] ?? { scalarType: "unknown", arity: "scalar" }) - ); - - // Execute query with your driver - const result = await this.client.query(query.sql, args); + const result = await this.connection.query({ + text: query.sql, + values: query.args.map((value, index) => + mapArg(value, query.argTypes[index]), + ), + rowMode: 'array', + }) - // Extract column metadata - const columnNames = /* get from result */; - const columnTypes = /* map to ColumnTypeEnum */; - - // Map rows to ResultValue arrays - const rows = result.map(row => mapRow(row, columnTypes)); - - return { columnNames, columnTypes, rows }; - } catch (e) { - this.onError(e); + return { + columnNames: result.fields.map((field) => field.name), + columnTypes: result.fields.map(mapColumnType), + rows: result.rows, + } + } catch (error) { + throwAdapterError(error) } } async executeRaw(query: SqlQuery): Promise { try { - const args = query.args.map((arg, i) => - mapArg(arg, query.argTypes[i] ?? { scalarType: "unknown", arity: "scalar" }) - ); - const result = await this.client.query(query.sql, args); - return result.affectedRows ?? 0; - } catch (e) { - this.onError(e); + const result = await this.connection.execute( + query.sql, + query.args.map((value, index) => mapArg(value, query.argTypes[index])), + ) + return result.rowsAffected ?? 0 + } catch (error) { + throwAdapterError(error) } } - - protected onError(error: unknown): never { - throw new DriverAdapterError(convertDriverError(error)); - } } ``` -### Step 2: Create the Transaction class +### Result mapping -**Critical**: `commit()` and `rollback()` are **lifecycle hooks only**. They must NOT issue SQL. Prisma sends `COMMIT`/`ROLLBACK` via `executeRaw` on the transaction object. +Return `columnNames`, `columnTypes`, and `rows` with identical lengths/order. Map driver metadata to `ColumnTypeEnum` deliberately: -```typescript -class MyTransaction extends MyQueryable implements Transaction { - readonly options: TransactionOptions; - readonly #release: () => void; - - constructor( - client: TClient, - options: TransactionOptions, - release: () => void, - ) { - super(client); - this.options = options; - this.#release = release; - } +- signed integer widths to `Int32`/`Int64`; preserve 64-bit values without JS number truncation +- decimal/numeric to `Numeric` using the representation expected by Prisma +- binary to `Uint8Array`/`Bytes` +- date-only, time-only, and timestamp to `Date`, `Time`, and `DateTime` +- UUID, JSON, enum, arrays, and provider-specific unknown values to their explicit types +- unsupported native types to `DriverAdapterError({ kind: 'UnsupportedNativeDataType', type })` - commit(): Promise { - // DO NOT issue COMMIT SQL here — Prisma does it via executeRaw - this.#release(); // Release connection/resources - return Promise.resolve(); - } +Test `null`, empty arrays, array element types, big integers, decimals, byte arrays, JSON, dates, and user-defined/unknown native types. - rollback(): Promise { - // DO NOT issue ROLLBACK SQL here — Prisma does it via executeRaw - this.#release(); - return Promise.resolve(); - } -} -``` +### Script execution -### Step 3: Create the Adapter class +`executeScript` must execute a migration script as the provider expects. Prefer the driver's native multi-statement/script facility or a real SQL parser. Naively splitting on `;` breaks functions, triggers, quoted strings, and dialect-specific blocks. -```typescript -class MyAdapter extends MyQueryable implements SqlDriverAdapter { - #transactionDepth = 0; +## Transaction protocol - constructor(client: TClient) { - super(client); - } +`startTransaction` must acquire one dedicated connection, start the database transaction, apply the requested isolation level, and return a `Transaction` bound to that same connection. If setup fails, release it immediately. - async executeScript(script: string): Promise { - // For SQLite: split on ';' and run each statement - // For Postgres: use multi-statement execution - try { - // Implementation depends on driver capabilities - } catch (e) { - this.onError(e); +```typescript +async startTransaction(level?: IsolationLevel): Promise { + const connection = await this.pool.acquire() + try { + const tx = new ExampleTransaction(connection, () => connection.release()) + await tx.executeRaw({ sql: 'BEGIN', args: [], argTypes: [] }) + if (level) { + await tx.executeRaw({ + sql: `SET TRANSACTION ISOLATION LEVEL ${validateLevel(level)}`, + args: [], + argTypes: [], + }) } + return tx + } catch (error) { + connection.release(error) + throwAdapterError(error) } +} +``` - async startTransaction( - isolationLevel?: IsolationLevel, - ): Promise { - // Validate isolation level for your database - const validLevels = new Set([ - "READ UNCOMMITTED", - "READ COMMITTED", - "REPEATABLE READ", - "SERIALIZABLE", - ]); - - if (isolationLevel !== undefined && !validLevels.has(isolationLevel)) { - throw new DriverAdapterError({ - kind: "InvalidIsolationLevel", - level: isolationLevel, - }); - } - - const options: TransactionOptions = { usePhantomQuery: false }; +### Commit and rollback - this.#transactionDepth += 1; - const depth = this.#transactionDepth; +Prisma coordinates the SQL `COMMIT`/`ROLLBACK` through `executeRaw`. The transaction object's `commit()` and `rollback()` methods are lifecycle hooks: detach listeners and release the dedicated connection exactly once. They must not issue a second SQL commit/rollback. - try { - if (depth === 1) { - // Issue BEGIN (with isolation level if specified) - const beginSql = isolationLevel - ? `BEGIN ISOLATION LEVEL ${isolationLevel}` - : "BEGIN"; - await this.client.query(beginSql); - } else { - // Nested: use savepoints - await this.client.query(`SAVEPOINT sp_${depth}`); - } - } catch (e) { - this.#transactionDepth -= 1; - this.onError(e); - } +```typescript +class ExampleTransaction extends ExampleQueryable implements Transaction { + readonly options = { usePhantomQuery: false } + #closed = false - const release = () => { - this.#transactionDepth -= 1; - }; - return new MyTransaction(this.client, options, release); + constructor(connection: DriverConnection, private readonly release: () => void) { + super(connection) } - getConnectionInfo(): ConnectionInfo { - return { supportsRelationJoins: true }; - } + async commit() { this.finish() } + async rollback() { this.finish() } - async dispose(): Promise { - await this.client.close(); + private finish() { + if (this.#closed) return + this.#closed = true + this.release() } -} -``` - -### Step 4: Create the Factory class - -```typescript -export type MyAdapterConfig = { - url: string; -}; - -export type MyAdapterOptions = { - shadowDatabaseUrl?: string; -}; -export class MyAdapterFactory implements SqlMigrationAwareDriverAdapterFactory { - readonly provider = "postgres" as const; - readonly adapterName = "@my-org/adapter-mydb" as const; + async createSavepoint(name: string) { + await this.control(`SAVEPOINT ${safeSavepoint(name)}`) + } - constructor( - private readonly config: MyAdapterConfig, - private readonly options?: MyAdapterOptions, - ) {} + async rollbackToSavepoint(name: string) { + await this.control(`ROLLBACK TO SAVEPOINT ${safeSavepoint(name)}`) + } - connect(): Promise { - return Promise.resolve(new MyAdapter(openConnection(this.config.url))); + async releaseSavepoint(name: string) { + await this.control(`RELEASE SAVEPOINT ${safeSavepoint(name)}`) } - connectToShadowDb(): Promise { - const url = this.options?.shadowDatabaseUrl ?? this.config.url; - return Promise.resolve(new MyAdapter(openConnection(url))); + private async control(sql: string) { + await this.executeRaw({ sql, args: [], argTypes: [] }) } } ``` -## Conversion Helpers - -### Argument Mapping (input) - -Convert Prisma argument values to driver-native types: - -```typescript -function mapArg(arg: unknown, argType: ArgType): unknown { - if (arg === null || arg === undefined) return null; +Implement the optional savepoint methods only where the provider supports them. Validate/quote savepoint identifiers. For providers whose savepoints are intentionally no-ops, document and test that limitation. - // String → number for int columns - if (typeof arg === "string" && argType.scalarType === "int") - return Number.parseInt(arg, 10); +Never keep transaction depth on the shared adapter. Parallel transactions make adapter-global depth incorrect; nested state belongs to the returned transaction connection and Prisma's savepoint calls. - // String → number for float columns - if (typeof arg === "string" && argType.scalarType === "float") - return Number.parseFloat(arg); +## Error mapping - // String → BigInt for bigint columns - if (typeof arg === "string" && argType.scalarType === "bigint") - return BigInt(arg); +Wrap recognized driver failures in `DriverAdapterError`. Map known conditions to `MappedError` kinds such as constraint violations, authentication/reachability, missing table/column/database, timeouts, closed transactions, invalid input, value range, and write conflicts. - // Base64 string → Buffer for bytes columns - if (typeof arg === "string" && argType.scalarType === "bytes") - return Buffer.from(arg, "base64"); - - // Boolean → 0/1 for SQLite - if (typeof arg === "boolean" && /* SQLite */) - return arg ? 1 : 0; - - return arg; -} -``` - -### Row Mapping (output) - -Convert driver result values to Prisma-expected types: +For database errors, preserve `originalCode` and `originalMessage` even when falling back to the provider-specific raw variant: ```typescript -function mapRow(row: unknown[], columnTypes: ColumnType[]): ResultValue[] { - const result: ResultValue[] = []; - - for (let i = 0; i < row.length; i++) { - const value = row[i] ?? null; - const colType = columnTypes[i]; - - if (value === null) { - result.push(null); - continue; - } - - // bigint → string for Int64 (JSON-safe) - if (typeof value === "bigint") { - result.push(value.toString()); - continue; - } - - // Date → ISO 8601 string for DateTime - if (value instanceof Date) { - result.push(value.toISOString()); - continue; - } - - // JSON objects → stringified - if (colType === ColumnTypeEnum.Json && typeof value === "object") { - result.push(JSON.stringify(value)); - continue; - } - - result.push(value as ResultValue); +import { + DriverAdapterError, + type Error as DriverAdapterErrorObject, + type MappedError, +} from '@prisma/driver-adapter-utils' + +function convertDriverError(error: DatabaseError): DriverAdapterErrorObject { + return { + originalCode: String(error.code), + originalMessage: error.message, + ...mapKnownOrRaw(error), } - - return result; -} -``` - -### Column Type Inference - -When the driver doesn't provide type metadata, infer from JS values: - -```typescript -function inferColumnType(value: NonNullable): ColumnType { - if (typeof value === "boolean") return ColumnTypeEnum.Boolean; - if (typeof value === "bigint") return ColumnTypeEnum.Int64; - if (value instanceof Uint8Array) return ColumnTypeEnum.Bytes; - if (value instanceof Date) return ColumnTypeEnum.DateTime; - if (Array.isArray(value)) return ColumnTypeEnum.Text; // fallback - if (typeof value === "object") return ColumnTypeEnum.Json; - if (typeof value === "number") return ColumnTypeEnum.UnknownNumber; - return ColumnTypeEnum.Text; } -``` - -## Error Handling - -Map driver errors to `MappedError` for Prisma to handle correctly: - -```typescript -function convertDriverError(error: unknown): MappedError { - if (error instanceof Error) { - // Database-specific error mapping - const dbError = error as Error & { code?: string; errno?: number }; - - // PostgreSQL example - if (dbError.code === "23505") { - return { kind: "UniqueConstraintViolation" }; - } - if (dbError.code === "23502") { - return { kind: "NullConstraintViolation" }; - } - if (dbError.code === "23503") { - return { kind: "ForeignKeyConstraintViolation" }; - } - if (dbError.code === "42P01") { - return { kind: "TableDoesNotExist" }; - } - // SQLite example - if (error.name === "SQLiteError") { - return { - kind: "sqlite", - extendedCode: dbError.errno ?? 1, - message: error.message, - }; - } - - // PostgreSQL raw error - if (dbError.code) { - return { - kind: "postgres", - code: dbError.code, - severity: "ERROR", - message: error.message, - detail: undefined, - column: undefined, - hint: undefined, - }; - } +function mapKnownOrRaw(error: DatabaseError): MappedError { + if (error.code === '23505') { + return { kind: 'UniqueConstraintViolation', constraint: parsedConstraint(error) } + } + return { + kind: 'postgres', + code: String(error.code ?? 'N/A'), + severity: error.severity ?? 'N/A', + message: error.message, + detail: error.detail, + column: error.column, + hint: error.hint, } - - return { kind: "GenericJs", id: 0 }; } -``` - -## Database-Specific Notes - -### SQLite - -- Set `safeIntegers: true` when opening the database to get `bigint` for large integers -- Only `SERIALIZABLE` isolation level is valid -- `executeScript`: split on `;` and run each statement individually -- Boolean values: store as 0/1, return as boolean - -### PostgreSQL - -- All standard isolation levels are valid -- For connection pooling (PgBouncer), use `prepare: false` -- Transactions require a dedicated connection (`reserve()` pattern) -- `executeScript`: use multi-statement execution (`.simple()` in some drivers) -- `int8` columns may return as string (already stringified by driver) -- `numeric` columns return as string to preserve precision - -### MySQL/MariaDB - -- Supports `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, `SERIALIZABLE` -- Use `?` placeholders for parameters -- Handle `BIGINT` as string for large values - -## Testing Strategy -### Unit Tests (no PrismaClient) - -Test the adapter directly with the raw database driver: - -```typescript -describe("queryRaw", () => { - test("returns column names and types", async () => { - const adapter = new MyAdapter(createTestConnection()); - const result = await adapter.queryRaw({ - sql: "SELECT id, name FROM users", - args: [], - argTypes: [], - }); - expect(result.columnNames).toEqual(["id", "name"]); - expect(result.columnTypes[0]).toBe(ColumnTypeEnum.Int32); - }); -}); - -describe("startTransaction", () => { - test("commit persists changes", async () => { - const adapter = new MyAdapter(createTestConnection()); - const tx = await adapter.startTransaction(); - await tx.executeRaw({ - sql: "INSERT INTO users (name) VALUES (?)", - args: ["Alice"], - argTypes: [], - }); - // Prisma sends COMMIT via executeRaw - await tx.executeRaw({ sql: "COMMIT", args: [], argTypes: [] }); - await tx.commit(); // lifecycle hook only - // Verify data persisted - }); -}); -``` - -### E2E Tests (with PrismaClient) - -Test the full integration: - -```typescript -describe("E2E", () => { - let prisma: PrismaClient; - - beforeEach(async () => { - const factory = new MyAdapterFactory({ url: TEST_DB_URL }); - prisma = new PrismaClient({ adapter: factory }); - }); - - test("CRUD operations", async () => { - const user = await prisma.user.create({ data: { name: "Alice" } }); - expect(user.id).toBeGreaterThan(0); - - const found = await prisma.user.findUnique({ where: { id: user.id } }); - expect(found?.name).toBe("Alice"); - }); - - test("transactions roll back on error", async () => { - await expect( - prisma.$transaction(async (tx) => { - await tx.user.create({ data: { name: "Bob" } }); - throw new Error("Rollback!"); - }), - ).rejects.toThrow(); - - expect(await prisma.user.count()).toBe(0); - }); -}); -``` - -## Usage Example - -```typescript -import { PrismaClient } from "./generated/prisma/client"; -import { MyAdapterFactory } from "@my-org/adapter-mydb"; - -const factory = new MyAdapterFactory({ - url: process.env.DATABASE_URL!, -}); - -const prisma = new PrismaClient({ adapter: factory }); - -// Use prisma normally -const users = await prisma.user.findMany(); +function throwAdapterError(error: unknown): never { + if (!isDatabaseError(error)) throw error + throw new DriverAdapterError(convertDriverError(error)) +} ``` -## Checklist - -Before considering the adapter complete: - -- [ ] `SqlMigrationAwareDriverAdapterFactory` implemented with `connect()` and `connectToShadowDb()` -- [ ] `SqlDriverAdapter` implements `queryRaw`, `executeRaw`, `executeScript`, `startTransaction`, `dispose` -- [ ] `Transaction` implements `queryRaw`, `executeRaw`, `commit`, `rollback` with `options: { usePhantomQuery: false }` -- [ ] `commit()` and `rollback()` are lifecycle hooks only (no SQL issued) -- [ ] `startTransaction` issues `BEGIN` (depth 1) or `SAVEPOINT sp_N` (nested) -- [ ] Argument mapping handles: string→int, string→bigint, string→float, base64→bytes -- [ ] Row mapping handles: bigint→string, Date→ISO string, JSON→string -- [ ] Column types correctly mapped to `ColumnTypeEnum` -- [ ] Errors wrapped in `DriverAdapterError` with proper `MappedError` kind -- [ ] Isolation level validation for the target database -- [ ] Unit tests pass for queryRaw, executeRaw, executeScript, transactions -- [ ] E2E tests pass with real PrismaClient +Prisma uses preserved original details when an unmapped driver error becomes `P2039`. Do not replace every unknown exception with a fabricated `GenericJs` id; rethrow genuinely unexpected non-driver errors so programming bugs remain visible. + +## Factory, ownership, and shadow database + +- `connect()` returns a fresh usable adapter connection/pool wrapper. +- Track whether the factory created the pool. `dispose()` closes owned pools and only detaches listeners from caller-owned pools unless an explicit option transfers ownership. +- Implement `SqlMigrationAwareDriverAdapterFactory` only when `connectToShadowDb()` can create an isolated shadow database, connect to it, and drop it during disposal/failure cleanup. +- Never point the shadow adapter at the primary database. Quote generated identifiers and use cryptographically unique names. +- `getConnectionInfo()` should accurately report `schemaName`, `maxBindValues` when applicable, and `supportsRelationJoins`. + +## Verification checklist + +- [ ] Typecheck against the exact target `@prisma/driver-adapter-utils` version +- [ ] `queryRaw` preserves column order, types, nulls, and precision +- [ ] `executeRaw` reports affected rows correctly +- [ ] `executeScript` handles provider-specific multi-statement syntax +- [ ] Concurrent interactive transactions use distinct dedicated connections +- [ ] Success commits and releases once; failure rolls back and releases once +- [ ] Nested transaction tests exercise create/rollback/release savepoint hooks +- [ ] Unsupported isolation levels fail as `InvalidIsolationLevel` +- [ ] Known constraints map to structured errors +- [ ] Unmapped database errors retain original code/message and surface useful `P2039` +- [ ] Dispose ownership is tested for internal and external pools +- [ ] Shadow database creation, use, failure cleanup, and disposal are isolated +- [ ] Run Prisma Client integration/E2E tests, not only adapter unit tests + +## Source references + +- [Driver adapter interfaces](https://github.com/prisma/prisma/blob/v7/packages/driver-adapter-utils/src/types.ts) +- [PostgreSQL adapter transaction implementation](https://github.com/prisma/prisma/blob/v7/packages/adapter-pg/src/pg.ts) +- [PostgreSQL adapter error mapping](https://github.com/prisma/prisma/blob/v7/packages/adapter-pg/src/errors.ts) diff --git a/plugins/prisma/.agents/skills/prisma-postgres/SKILL.md b/plugins/prisma/.agents/skills/prisma-postgres/SKILL.md index 86024dd2..568543fb 100644 --- a/plugins/prisma/.agents/skills/prisma-postgres/SKILL.md +++ b/plugins/prisma/.agents/skills/prisma-postgres/SKILL.md @@ -4,7 +4,7 @@ description: Prisma Postgres setup and operations guidance across Console, creat license: MIT metadata: author: prisma - version: "7.6.0" + version: "7.9.1" --- # Prisma Postgres @@ -67,6 +67,22 @@ For app integrations, you can also use the programmatic API (`create()` / `regio Temporary databases auto-delete after ~24 hours unless claimed. +### 2b. Persistent databases with the Platform CLI + +For databases that belong to a Project (not throwaway `create-db` databases), use `@prisma/cli`: + +```bash +npx -y @prisma/cli@latest database create --help +npx -y @prisma/cli@latest database list --json +npx -y @prisma/cli@latest database connection create db_123 +npx -y @prisma/cli@latest database usage db_123 +npx -y @prisma/cli@latest database backup list db_123 +``` + +`database create` and `database connection create` print a one-time connection URL; store it immediately. Destructive commands (`remove`, `restore`) require exact `--confirm `. + +For automation, prefer `--json --no-interactive`, resolve ids before mutations, and verify the installed command's help because this CLI is beta. + ### 3. Link an existing local project Use `prisma postgres link` when the database already exists and you want to wire a local project to it: @@ -111,6 +127,8 @@ npm install @prisma/management-api-sdk Use `createManagementApiClient` for existing tokens, or `createManagementApiSdk` for OAuth + token refresh. +The SDK exposes typed workspace service-token list, create, and revoke routes. A newly created token value is returned exactly once. Let the installed SDK types or OpenAPI document settle exact beta endpoint shapes. + ## Rule Files Detailed guidance lives in: diff --git a/plugins/prisma/.agents/skills/prisma-postgres/references/console-and-connections.md b/plugins/prisma/.agents/skills/prisma-postgres/references/console-and-connections.md index c6e30f9a..4025d12b 100644 --- a/plugins/prisma/.agents/skills/prisma-postgres/references/console-and-connections.md +++ b/plugins/prisma/.agents/skills/prisma-postgres/references/console-and-connections.md @@ -55,6 +55,8 @@ Typical direct TCP format: DATABASE_URL="postgres://identifier:key@db.prisma.io:5432/postgres?sslmode=require" ``` +Management API connection responses expose both `endpoints.direct` (`db.prisma.io:5432`) and `endpoints.pooled` (`pooled.db.prisma.io:5432`); prefer those fields over the deprecated flat `connectionString`. Connection secrets are shown once at creation (one-time view); store them immediately. + ## Adapter choices - Standard Node.js apps: prefer `@prisma/adapter-pg` with the direct TCP URL above. diff --git a/plugins/prisma/.agents/skills/prisma-postgres/references/management-api-sdk.md b/plugins/prisma/.agents/skills/prisma-postgres/references/management-api-sdk.md index d4ad6bb0..026aa5f8 100644 --- a/plugins/prisma/.agents/skills/prisma-postgres/references/management-api-sdk.md +++ b/plugins/prisma/.agents/skills/prisma-postgres/references/management-api-sdk.md @@ -2,6 +2,8 @@ Use `@prisma/management-api-sdk` for typed API integration with optional OAuth and token refresh. +The Platform API evolves independently from Prisma ORM. Inspect the installed package's generated `api.d.ts` for exact paths and request/response shapes. + ## Priority HIGH @@ -25,6 +27,18 @@ const client = createManagementApiClient({ token: process.env.PRISMA_SERVICE_TOK const { data: workspaces } = await client.GET('/v1/workspaces') ``` +Check the generated client result before using `data`; typed clients surface HTTP failures separately. Never log a full response from connection/key creation because it may contain one-time credentials. + +## Workspace service tokens + +The typed client exposes routes to list, create, and revoke workspace service tokens: + +- `GET /v1/workspaces/{workspaceId}/service-tokens` +- `POST /v1/workspaces/{workspaceId}/service-tokens` +- `DELETE /v1/workspaces/{workspaceId}/service-tokens/{serviceTokenId}` + +Creation accepts a display `name`. The response's `data.value` is the complete token and is returned exactly once; transfer it directly to the intended secret store without logging the response. Later list calls return metadata and `valueHint`, not the token value. Treat revocation as destructive and resolve both ids explicitly. + ## Full SDK (OAuth + refresh) ```typescript diff --git a/plugins/prisma/.agents/skills/prisma-postgres/references/management-api.md b/plugins/prisma/.agents/skills/prisma-postgres/references/management-api.md index ff37c39e..4e4e76f9 100644 --- a/plugins/prisma/.agents/skills/prisma-postgres/references/management-api.md +++ b/plugins/prisma/.agents/skills/prisma-postgres/references/management-api.md @@ -42,17 +42,35 @@ Authorization: Bearer $TOKEN 3. Exchange code at `https://auth.prisma.io/token`. 4. Use returned access token in Management API requests. -## Common endpoints +## Resource model -- `GET /workspaces` -- `GET /projects` -- `POST /projects` -- Database management endpoints under project/database paths +Workspace -> Project -> Branch -> Database. Branches are a first-class resource: databases attach to a Branch, and branch-scoped env/databases are how preview isolation works. + +## Current resource inventory + +The 1.55 OpenAPI surface includes: + +- workspaces, subscriptions, workspace integrations, workspace service tokens, and current-user metadata +- projects, transfers, project databases, and project/branch environment variables +- branches under a project plus branch get/update/delete operations +- databases, usage, backups, restore, connections, and connection rotation +- apps, deployments, promotion/rollback, runtime logs, domains, and build logs +- buckets and bucket keys +- source repositories, SCM installations/install intents, and repositories +- integrations and regions + +App/deployment, branch mutation, SCM, and bucket routes include experimental surfaces. Read the installed SDK types or live OpenAPI before building durable automation around them. + +Connection create/rotate responses reveal credentials once. Later reads redact or omit the secret, so store the URL immediately. Use the structured direct/pooled endpoint returned by the concrete operation; do not assume a historical flat response shape. + +Workspace service-token creation also returns the complete token value exactly once. List calls expose only metadata and a `valueHint`; delete revokes the token. Keep workspace and token ids opaque, and never log a create response. + +Database create supports explicit project, region, branch, and source context. A source may be empty, a backup, or another database. Backup records are incremental; rely on current fields and documented units rather than old full-backup examples. ## Notes -- Management API responses may include direct connection credentials for databases. -- Build PostgreSQL `DATABASE_URL` from direct connection values when needed. +- Management API mutation responses may include direct connection credentials; treat the entire response as secret until redacted. +- Prefer an API-provided connection string over manually assembling one from fields. ## References diff --git a/plugins/prisma/.agents/skills/prisma-upgrade-v7/SKILL.md b/plugins/prisma/.agents/skills/prisma-upgrade-v7/SKILL.md index 1ac08b97..23d4956a 100644 --- a/plugins/prisma/.agents/skills/prisma-upgrade-v7/SKILL.md +++ b/plugins/prisma/.agents/skills/prisma-upgrade-v7/SKILL.md @@ -41,9 +41,15 @@ Reference this skill when: - `removed-features` - removed middleware, metrics, and legacy CLI behavior - `accelerate-users` - migration notes for Accelerate users +## Using MongoDB? This guide does not apply + +Prisma 7 has no MongoDB connector. Do not apply any step in this guide to a project with +`provider = "mongodb"` — see the `prisma-mongodb-upgrade` skill for the actual decision +(stay on v6 deliberately vs migrate to Prisma Next). + ## Important Notes -- **MongoDB projects should stay on Prisma 6.x** - do not migrate MongoDB apps to Prisma 7's SQL client path +- **MongoDB projects should stay on Prisma 6.x or migrate to Prisma Next** - do not migrate MongoDB apps to Prisma 7's SQL client path (see `prisma-mongodb-upgrade`) - **Node.js 20.19.0+** required - **TypeScript 5.4.0+** required - **Latest stable Prisma ORM version**: `7.6.0` diff --git a/plugins/prisma/.claude/skills/prisma-cli b/plugins/prisma/.claude/skills/prisma-cli new file mode 120000 index 00000000..bc38a65a --- /dev/null +++ b/plugins/prisma/.claude/skills/prisma-cli @@ -0,0 +1 @@ +../../.agents/skills/prisma-cli \ No newline at end of file diff --git a/plugins/prisma/.claude/skills/prisma-client-api b/plugins/prisma/.claude/skills/prisma-client-api new file mode 120000 index 00000000..25a84415 --- /dev/null +++ b/plugins/prisma/.claude/skills/prisma-client-api @@ -0,0 +1 @@ +../../.agents/skills/prisma-client-api \ No newline at end of file diff --git a/plugins/prisma/.claude/skills/prisma-database-setup b/plugins/prisma/.claude/skills/prisma-database-setup new file mode 120000 index 00000000..f188a790 --- /dev/null +++ b/plugins/prisma/.claude/skills/prisma-database-setup @@ -0,0 +1 @@ +../../.agents/skills/prisma-database-setup \ No newline at end of file diff --git a/plugins/prisma/.claude/skills/prisma-driver-adapter-implementation b/plugins/prisma/.claude/skills/prisma-driver-adapter-implementation new file mode 120000 index 00000000..c4bd907d --- /dev/null +++ b/plugins/prisma/.claude/skills/prisma-driver-adapter-implementation @@ -0,0 +1 @@ +../../.agents/skills/prisma-driver-adapter-implementation \ No newline at end of file diff --git a/plugins/prisma/.claude/skills/prisma-postgres b/plugins/prisma/.claude/skills/prisma-postgres new file mode 120000 index 00000000..e195028e --- /dev/null +++ b/plugins/prisma/.claude/skills/prisma-postgres @@ -0,0 +1 @@ +../../.agents/skills/prisma-postgres \ No newline at end of file diff --git a/plugins/prisma/.claude/skills/prisma-upgrade-v7 b/plugins/prisma/.claude/skills/prisma-upgrade-v7 new file mode 120000 index 00000000..95a4d863 --- /dev/null +++ b/plugins/prisma/.claude/skills/prisma-upgrade-v7 @@ -0,0 +1 @@ +../../.agents/skills/prisma-upgrade-v7 \ No newline at end of file diff --git a/plugins/prisma/agent/skills/prisma-cli/SKILL.md b/plugins/prisma/agent/skills/prisma-cli/SKILL.md index 38e72c8f..da55af3f 100644 --- a/plugins/prisma/agent/skills/prisma-cli/SKILL.md +++ b/plugins/prisma/agent/skills/prisma-cli/SKILL.md @@ -1,15 +1,15 @@ --- -description: "Prisma ORM CLI commands reference covering init, generate, migrate, db, dev, studio, validate, format, debug, and mcp. Use for ORM/database CLI workflows, not Prisma Compute app deployment. For Prisma Compute, `@prisma/cli app deploy`, `compute:deploy`, `create-prisma --deploy`, apps, deployments, logs, or domains, use the `prisma-compute` skill instead. Triggers on \"prisma init\", \"prisma generate\", \"prisma migrate\", \"prisma db\", \"prisma studio\", \"prisma mcp\"." +description: "Prisma ORM CLI commands reference covering init, generate, migrate, db, dev, complete, studio, validate, format, debug, and mcp. Use for ORM/database CLI workflows, not the Prisma Platform CLI. Triggers on \"prisma init\", \"prisma generate\", \"prisma migrate\", \"prisma db\", \"prisma complete\", \"prisma studio\", \"prisma mcp\"." license: "MIT" -metadata: {"author":"prisma","version":"7.6.0"} +metadata: {"author":"prisma","version":"7.9.1"} --- # Prisma CLI Reference Reference for Prisma ORM CLI commands. This skill provides guidance on command usage, options, and best practices for current Prisma ORM releases. -## Boundary: Compute +## Boundary: Platform and Compute -Do not use this skill for Prisma Compute app deployment. Use `prisma-compute` for `@prisma/cli app deploy`, `compute:deploy`, `create-prisma --deploy`, Compute apps, deployments, logs, domains, and framework deploy readiness. +Do not confuse the stable ORM command (`prisma`) with the public-beta Platform package (`@prisma/cli`, binary `prisma-cli`). Use `prisma-compute` for Compute apps and workspace auth, and `prisma-postgres` for Platform projects and databases. ## When to Apply @@ -20,6 +20,7 @@ Reference this skill when: - Managing database state (`prisma db push/pull`) - Using local development database (`prisma dev`) - Debugging Prisma issues (`prisma debug`) +- Generating shell completions (`prisma complete`) ## Rule Categories by Priority @@ -30,19 +31,19 @@ Reference this skill when: | 3 | Development | HIGH | `dev` | | 4 | Database | HIGH | `db-` | | 5 | Migrations | CRITICAL | `migrate-` | -| 6 | Utility | MEDIUM | `studio`, `validate`, `format`, `debug`, `mcp` | +| 6 | Utility | MEDIUM | `complete`, `studio`, `validate`, `format`, `debug`, `mcp` | ## Command Categories | Category | Commands | Purpose | |----------|----------|---------| -| Setup | `init` | Bootstrap new Prisma project | +| Setup | `init` | Initialize a Prisma project | | Generation | `generate` | Generate Prisma Client | | Validation | `validate`, `format` | Schema validation and formatting | | Development | `dev` | Local Prisma Postgres for development | | Database | `db pull`, `db push`, `db seed`, `db execute` | Direct database operations | | Migrations | `migrate dev`, `migrate deploy`, `migrate reset`, `migrate status`, `migrate diff`, `migrate resolve` | Schema migrations | -| Utility | `studio`, `mcp`, `version`, `debug` | Development and AI tooling | +| Utility | `complete`, `studio`, `mcp`, `version`, `debug` | Shell, development, and AI tooling | ## Quick Reference @@ -62,6 +63,7 @@ prisma init --db # Initialize with an example model prisma init --with-model + ``` ### Client Generation @@ -174,8 +176,22 @@ prisma validate # Format schema prisma format + +# Generate shell completion code +prisma complete zsh ``` +## AI Safety Checkpoint + +Prisma blocks destructive commands when it detects an AI agent until the agent has obtained explicit user consent. This covers `migrate reset`, `db push --force-reset`, and `db push --accept-data-loss`. + +- Explain the exact data-loss impact and ask for consent immediately before running the command. +- Do not infer consent from earlier or unrelated messages. +- If automation needs the consent variable, set `PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION` to the user's exact consent message. Do not invent the text. +- The Prisma MCP server deliberately has no `migrate-reset` tool. + +Read `references/agent-safety.md` before any destructive Prisma command. + ## Current Prisma CLI Setup ### New Configuration File @@ -233,6 +249,8 @@ references/migrate-resolve.md - Migration resolution references/migrate-diff.md - Schema diffing references/studio.md - Database GUI references/mcp.md - Prisma MCP server +references/complete.md - Shell completion generation +references/agent-safety.md - AI consent checkpoint for destructive commands references/validate.md - Schema validation references/format.md - Schema formatting references/debug.md - Debug info diff --git a/plugins/prisma/agent/skills/prisma-cli/references/agent-safety.md b/plugins/prisma/agent/skills/prisma-cli/references/agent-safety.md new file mode 100644 index 00000000..651c6c34 --- /dev/null +++ b/plugins/prisma/agent/skills/prisma-cli/references/agent-safety.md @@ -0,0 +1,27 @@ +# AI safety checkpoint for destructive commands + +Prisma detects common AI-agent environments and blocks these commands until the user gives explicit consent: + +- `prisma migrate reset` +- `prisma db push --force-reset` +- `prisma db push --accept-data-loss` + +## Required workflow + +1. Inspect the target database/config and explain exactly what can be deleted or reset. +2. Ask the user for explicit consent immediately before the action. +3. Run the command only after that consent. + +For an agent-run subprocess, Prisma accepts the exact consent text through: + +```bash +PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION='' prisma migrate reset --force +``` + +The value must match the user's message exactly and must not contain added quotes or newlines. Never fabricate consent, reuse an old unrelated approval, or bypass the checkpoint by hiding agent-detection environment variables. + +The MCP server has no `migrate-reset` tool. Use the shell command only after consent. + +## Reference + +- [Prisma ORM 7.9.0 release](https://github.com/prisma/prisma/releases/tag/7.9.0) diff --git a/plugins/prisma/agent/skills/prisma-cli/references/complete.md b/plugins/prisma/agent/skills/prisma-cli/references/complete.md new file mode 100644 index 00000000..73324407 --- /dev/null +++ b/plugins/prisma/agent/skills/prisma-cli/references/complete.md @@ -0,0 +1,22 @@ +# prisma complete + +Prints a shell completion script. + +```bash +prisma complete zsh +prisma complete bash +prisma complete fish +prisma complete powershell +``` + +For a direct global CLI installation, load the output using the shell's normal startup mechanism. For example, in zsh: + +```bash +source <(prisma complete zsh) +``` + +Prisma also integrates with supported package-manager completion flows. `npx` and `bunx` do not themselves provide completion; invoke the installed binary or the package manager's supported execution form such as `npm exec` or `bun x`. + +## Reference + +- [Prisma ORM 7.9.0 release](https://github.com/prisma/prisma/releases/tag/7.9.0) diff --git a/plugins/prisma/agent/skills/prisma-cli/references/db-push.md b/plugins/prisma/agent/skills/prisma-cli/references/db-push.md index 51acdbb2..74da0cf4 100644 --- a/plugins/prisma/agent/skills/prisma-cli/references/db-push.md +++ b/plugins/prisma/agent/skills/prisma-cli/references/db-push.md @@ -25,6 +25,8 @@ prisma db push [options] | `--config` | Custom path to your Prisma config file | | `--url` | Override the datasource URL from the Prisma config file | +When Prisma detects an AI agent, `--force-reset` and `--accept-data-loss` require explicit user consent. Follow `agent-safety.md`; never infer or fabricate the consent text. + ### Follow-up Command - Run `prisma generate` explicitly when you need refreshed client output diff --git a/plugins/prisma/agent/skills/prisma-cli/references/init.md b/plugins/prisma/agent/skills/prisma-cli/references/init.md index a255c7b5..c4390ed8 100644 --- a/plugins/prisma/agent/skills/prisma-cli/references/init.md +++ b/plugins/prisma/agent/skills/prisma-cli/references/init.md @@ -34,6 +34,9 @@ bunx --bun prisma init | `--output` | Define Prisma Client generator output path to use | - | | `--preview-feature` | Define a preview feature to use | - | | `--with-model` | Add example model to created schema file | - | +| `--no-skills` | Skip the best-effort installation of Prisma agent skills | - | + +`prisma init` attempts to install `prisma/skills` for detected agents. This is best-effort and does not make project initialization fail. Use `--no-skills` in minimal or controlled environments. ## Examples diff --git a/plugins/prisma/agent/skills/prisma-cli/references/mcp.md b/plugins/prisma/agent/skills/prisma-cli/references/mcp.md index 26cb963f..284130fa 100644 --- a/plugins/prisma/agent/skills/prisma-cli/references/mcp.md +++ b/plugins/prisma/agent/skills/prisma-cli/references/mcp.md @@ -30,7 +30,8 @@ prisma mcp - Run this from the project that contains your Prisma schema and `prisma.config.ts` - The command is separate from Prisma Studio and does not open a browser UI -- The MCP server wraps Prisma CLI commands. For exact behavior of commands like `migrate dev` or `migrate reset`, follow the underlying CLI command docs rather than relying only on the MCP tool descriptions. +- The MCP server exposes `migrate-status`, `migrate-dev`, and Prisma Studio tooling. It does not expose the destructive `migrate-reset` tool; do not claim it is available or try to bypass that safety boundary. +- For destructive shell commands, follow `agent-safety.md` and obtain explicit user consent. ## References diff --git a/plugins/prisma/agent/skills/prisma-cli/references/migrate-dev.md b/plugins/prisma/agent/skills/prisma-cli/references/migrate-dev.md index 6b6352b4..48643b64 100644 --- a/plugins/prisma/agent/skills/prisma-cli/references/migrate-dev.md +++ b/plugins/prisma/agent/skills/prisma-cli/references/migrate-dev.md @@ -31,7 +31,7 @@ prisma migrate dev [options] - Run `prisma generate` explicitly when you need refreshed client output - Run `prisma db seed` explicitly when you need seed data -Note: Prisma CLI help for `7.6.0` still says `migrate dev` "trigger[s] generators", but local verification in a temp Prisma 7.6.0 project did not emit generated client files. Treat `prisma generate` as an explicit follow-up step when you need generated artifacts on disk. +Run `prisma generate` as an explicit follow-up when you need refreshed generated artifacts. Do not rely on historical CLI help that described generators as part of `migrate dev`. ## Examples diff --git a/plugins/prisma/agent/skills/prisma-cli/references/migrate-reset.md b/plugins/prisma/agent/skills/prisma-cli/references/migrate-reset.md index 38d6fbdc..900e2885 100644 --- a/plugins/prisma/agent/skills/prisma-cli/references/migrate-reset.md +++ b/plugins/prisma/agent/skills/prisma-cli/references/migrate-reset.md @@ -17,6 +17,8 @@ prisma migrate reset [options] **Warning: All data will be lost.** +When Prisma detects an AI agent, this command is blocked until the user gives explicit consent. Follow `agent-safety.md`; `--force` skips the ordinary prompt but does not constitute user consent for an agent. + ## Options | Option | Description | diff --git a/plugins/prisma/agent/skills/prisma-client-api/SKILL.md b/plugins/prisma/agent/skills/prisma-client-api/SKILL.md index efce8029..b1cc41f4 100644 --- a/plugins/prisma/agent/skills/prisma-client-api/SKILL.md +++ b/plugins/prisma/agent/skills/prisma-client-api/SKILL.md @@ -1,7 +1,7 @@ --- description: "Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on \"prisma query\", \"findMany\", \"create\", \"update\", \"delete\", \"$transaction\"." license: "MIT" -metadata: {"author":"prisma","version":"7.6.0"} +metadata: {"author":"prisma","version":"7.9.1"} --- # Prisma Client API Reference diff --git a/plugins/prisma/agent/skills/prisma-client-api/references/constructor.md b/plugins/prisma/agent/skills/prisma-client-api/references/constructor.md index 7c7825f3..a9fb5e8f 100644 --- a/plugins/prisma/agent/skills/prisma-client-api/references/constructor.md +++ b/plugins/prisma/agent/skills/prisma-client-api/references/constructor.md @@ -127,6 +127,19 @@ const prisma = new PrismaClient({ }) ``` +### queryPlanCacheMaxSize + +Use `queryPlanCacheMaxSize` to limit the in-memory query-plan cache: + +```typescript +const prisma = new PrismaClient({ + adapter, + queryPlanCacheMaxSize: 2_000, +}) +``` + +The value must be a non-negative integer. Set it to `0` to disable query-plan caching; omit it to use Prisma's default. Treat this as a process-local memory/performance control, not a database prepared-statement setting. + ## Singleton Pattern Prevent multiple client instances in development: diff --git a/plugins/prisma/agent/skills/prisma-client-api/references/raw-queries.md b/plugins/prisma/agent/skills/prisma-client-api/references/raw-queries.md index 5d3a220b..e444ce2f 100644 --- a/plugins/prisma/agent/skills/prisma-client-api/references/raw-queries.md +++ b/plugins/prisma/agent/skills/prisma-client-api/references/raw-queries.md @@ -192,3 +192,7 @@ const users = await prisma.$queryRaw` ` // createdAt is already a Date object ``` + +Invalid JavaScript `Date` values passed to raw queries fail validation instead of being silently serialized as `null`. Validate date input at the application boundary; do not rely on `new Date(badValue)` reaching the database. + +When a driver adapter returns an unmapped database-specific error, Prisma surfaces `P2039` with the adapter's preserved original code/message. If those details are missing, fix the adapter mapping rather than parsing rendered error text. diff --git a/plugins/prisma/agent/skills/prisma-database-setup/SKILL.md b/plugins/prisma/agent/skills/prisma-database-setup/SKILL.md index accd1c1c..56e92f46 100644 --- a/plugins/prisma/agent/skills/prisma-database-setup/SKILL.md +++ b/plugins/prisma/agent/skills/prisma-database-setup/SKILL.md @@ -166,7 +166,7 @@ generator client { } ``` -For MongoDB, stay on the latest Prisma 6.x line and keep the connection URL in `schema.prisma`. Do not move a MongoDB project to the Prisma 7 SQL adapter setup. +For MongoDB, stay on the latest Prisma 6.x line and keep the connection URL in `schema.prisma`. Do not move a MongoDB project to the Prisma 7 SQL adapter setup. If a MongoDB project asks about upgrading Prisma versions, route to the `prisma-mongodb-upgrade` skill (stay-on-v6 vs Prisma Next is the real decision; Prisma 7 is not an option). ## Rule Files diff --git a/plugins/prisma/agent/skills/prisma-driver-adapter-implementation/SKILL.md b/plugins/prisma/agent/skills/prisma-driver-adapter-implementation/SKILL.md index 780ae665..136b6cfd 100644 --- a/plugins/prisma/agent/skills/prisma-driver-adapter-implementation/SKILL.md +++ b/plugins/prisma/agent/skills/prisma-driver-adapter-implementation/SKILL.md @@ -1,634 +1,266 @@ --- -description: "Required reference for Prisma v7 driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter/Transaction interfaces. Contains critical contract details not inferable from code examples — including the transaction lifecycle protocol, error mapping requirements, and verification checklist. Existing implementations do not replace this skill." +description: "Required reference for Prisma ORM 7 SQL driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter, Transaction, savepoint, result mapping, or DriverAdapterError behavior. Covers current transaction lifecycle, optional savepoint hooks, original database-error preservation, and verification." license: "MIT" -metadata: {"author":"Tyler Benfield","version":"7.6.0"} +metadata: {"author":"prisma","version":"7.9.1"} --- -# Prisma 7 Driver Adapter Implementation Guide +# Prisma SQL Driver Adapter Implementation -This skill provides everything needed to implement a Prisma ORM v7 driver adapter for any database. +Use this guide with the exact `@prisma/driver-adapter-utils` version installed by the target Prisma release. Driver adapters are a protocol boundary: type-compatible code can still corrupt values, leak connections, or break transactions. -## Architecture Overview +## When to Apply -``` -┌─────────────────────────────────────────────────────────────────┐ -│ PrismaClient │ -│ (requires adapter factory) │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ SqlMigrationAwareDriverAdapterFactory │ -│ ┌─────────────────────┐ ┌─────────────────────────────┐ │ -│ │ connect() │ │ connectToShadowDb() │ │ -│ │ → SqlDriverAdapter │ │ → SqlDriverAdapter │ │ -│ └─────────────────────┘ └─────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ SqlDriverAdapter │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ -│ │ queryRaw() │ │ executeRaw() │ │ startTransaction() │ │ -│ │ → ResultSet │ │ → number │ │ → Transaction │ │ -│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ -│ │executeScript │ │ dispose() │ │ getConnectionInfo() │ │ -│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ Transaction │ -│ Extends SqlQueryable + commit() + rollback() + options │ -│ (lifecycle hooks only — Prisma sends SQL via executeRaw) │ -└─────────────────────────────────────────────────────────────────┘ -``` - -## Required Interfaces +- Implementing `SqlDriverAdapterFactory`, `SqlMigrationAwareDriverAdapterFactory`, `SqlDriverAdapter`, or `Transaction` +- Adding nested-transaction/savepoint support +- Mapping driver values, column metadata, bind arguments, or database errors +- Debugging `P2039`, transaction leaks, shadow-database failures, or adapter-specific query behavior -Import from `@prisma/driver-adapter-utils`: +## Contract snapshot ```typescript -import type { - ColumnType, - IsolationLevel, - SqlDriverAdapter, - SqlMigrationAwareDriverAdapterFactory, - SqlQuery, - SqlQueryable, - SqlResultSet, - Transaction, - TransactionOptions, - ArgType, - ConnectionInfo, - MappedError, -} from "@prisma/driver-adapter-utils"; -import { - ColumnTypeEnum, - DriverAdapterError, -} from "@prisma/driver-adapter-utils"; -``` - -## Interface Definitions - -### SqlQuery (input to queryRaw/executeRaw) - -```typescript -type SqlQuery = { - sql: string; // Parameterized SQL with placeholders - args: Array; // Bound parameter values - argTypes: Array; // Type hints for each argument -}; - -type ArgType = { - scalarType: ArgScalarType; // 'string' | 'int' | 'bigint' | 'float' | 'decimal' | 'boolean' | 'enum' | 'uuid' | 'json' | 'datetime' | 'bytes' | 'unknown' - dbType?: string; - arity: "scalar" | "list"; -}; -``` - -### SqlResultSet (output from queryRaw) - -```typescript -interface SqlResultSet { - columnNames: Array; // Column names in order - columnTypes: Array; // Column types matching columnNames - rows: Array>; // Row data as arrays - lastInsertId?: string; // For INSERT without RETURNING +interface SqlDriverAdapterFactory extends AdapterInfo { + connect(): Promise } -``` - -### ColumnTypeEnum values - -```typescript -const ColumnTypeEnum = { - Int32: 0, - Int64: 1, - Float: 2, - Double: 3, - Numeric: 4, - Boolean: 5, - Character: 6, - Text: 7, - Date: 8, - Time: 9, - DateTime: 10, - Json: 11, - Enum: 12, - Bytes: 13, - Set: 14, - Uuid: 15, - Int32Array: 64, - Int64Array: 65, - FloatArray: 66, - DoubleArray: 67, - NumericArray: 68, - BooleanArray: 69, - CharacterArray: 70, - TextArray: 71, - DateArray: 72, - TimeArray: 73, - DateTimeArray: 74, - JsonArray: 75, - EnumArray: 76, - BytesArray: 77, - UuidArray: 78, - UnknownNumber: 128, -} as const; -``` - -### SqlDriverAdapter -```typescript -interface SqlDriverAdapter extends SqlQueryable { - executeScript(script: string): Promise; - startTransaction(isolationLevel?: IsolationLevel): Promise; - getConnectionInfo?(): ConnectionInfo; - dispose(): Promise; +interface SqlMigrationAwareDriverAdapterFactory extends SqlDriverAdapterFactory { + connectToShadowDb(): Promise } -``` -### Transaction - -```typescript -interface Transaction extends SqlQueryable { - readonly options: TransactionOptions; - commit(): Promise; - rollback(): Promise; +interface SqlDriverAdapter extends AdapterInfo { + queryRaw(query: SqlQuery): Promise + executeRaw(query: SqlQuery): Promise + executeScript(script: string): Promise + startTransaction(isolationLevel?: IsolationLevel): Promise + getConnectionInfo?(): ConnectionInfo + dispose(): Promise } -type TransactionOptions = { usePhantomQuery: boolean }; +interface Transaction extends AdapterInfo { + readonly options: { usePhantomQuery: boolean } + queryRaw(query: SqlQuery): Promise + executeRaw(query: SqlQuery): Promise + commit(): Promise + rollback(): Promise + createSavepoint?(name: string): Promise + rollbackToSavepoint?(name: string): Promise + releaseSavepoint?(name: string): Promise +} ``` -### SqlMigrationAwareDriverAdapterFactory +`IsolationLevel` currently includes `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, `SNAPSHOT`, and `SERIALIZABLE`; validate what the concrete database supports. -```typescript -interface SqlMigrationAwareDriverAdapterFactory { - readonly provider: "mysql" | "postgres" | "sqlite" | "sqlserver"; - readonly adapterName: string; - connect(): Promise; - connectToShadowDb(): Promise; -} -``` +## Priority rules -## Implementation Steps +| Priority | Rule | Impact | +|----------|------|--------| +| CRITICAL | One dedicated connection per transaction | Prevents interleaving and leaks | +| CRITICAL | `commit`/`rollback` are lifecycle cleanup hooks | Prevents duplicate COMMIT/ROLLBACK | +| CRITICAL | Savepoints live on `Transaction`, not adapter-global depth | Makes nested scopes connection-local | +| CRITICAL | Preserve original database error code/message | Enables useful `P2039` fallback | +| HIGH | Map arguments and result metadata exactly | Prevents silent value corruption | +| HIGH | Shadow databases are isolated and always cleaned up | Makes Migrate safe | +| HIGH | Dispose only resources the adapter owns | Prevents shutting down caller-owned pools | -### Step 1: Create the Queryable base class +## Query implementation + +`SqlQuery` contains `sql`, `args`, and parallel `argTypes`. Map each argument using both value and `ArgType`; do not discard type/arity information. Execute in the driver's array/tuple row mode so column order is stable. ```typescript -class MyQueryable implements SqlQueryable { - readonly provider = "postgres" as const; // or 'sqlite' | 'mysql' | 'sqlserver' - readonly adapterName = "@my-org/adapter-mydb" as const; +class ExampleQueryable { + readonly provider = 'postgres' as const + readonly adapterName = '@acme/adapter-example' - constructor(protected readonly client: TClient) {} + constructor(protected readonly connection: DriverConnection) {} async queryRaw(query: SqlQuery): Promise { try { - const args = query.args.map((arg, i) => - mapArg(arg, query.argTypes[i] ?? { scalarType: "unknown", arity: "scalar" }) - ); - - // Execute query with your driver - const result = await this.client.query(query.sql, args); + const result = await this.connection.query({ + text: query.sql, + values: query.args.map((value, index) => + mapArg(value, query.argTypes[index]), + ), + rowMode: 'array', + }) - // Extract column metadata - const columnNames = /* get from result */; - const columnTypes = /* map to ColumnTypeEnum */; - - // Map rows to ResultValue arrays - const rows = result.map(row => mapRow(row, columnTypes)); - - return { columnNames, columnTypes, rows }; - } catch (e) { - this.onError(e); + return { + columnNames: result.fields.map((field) => field.name), + columnTypes: result.fields.map(mapColumnType), + rows: result.rows, + } + } catch (error) { + throwAdapterError(error) } } async executeRaw(query: SqlQuery): Promise { try { - const args = query.args.map((arg, i) => - mapArg(arg, query.argTypes[i] ?? { scalarType: "unknown", arity: "scalar" }) - ); - const result = await this.client.query(query.sql, args); - return result.affectedRows ?? 0; - } catch (e) { - this.onError(e); + const result = await this.connection.execute( + query.sql, + query.args.map((value, index) => mapArg(value, query.argTypes[index])), + ) + return result.rowsAffected ?? 0 + } catch (error) { + throwAdapterError(error) } } - - protected onError(error: unknown): never { - throw new DriverAdapterError(convertDriverError(error)); - } } ``` -### Step 2: Create the Transaction class +### Result mapping -**Critical**: `commit()` and `rollback()` are **lifecycle hooks only**. They must NOT issue SQL. Prisma sends `COMMIT`/`ROLLBACK` via `executeRaw` on the transaction object. +Return `columnNames`, `columnTypes`, and `rows` with identical lengths/order. Map driver metadata to `ColumnTypeEnum` deliberately: -```typescript -class MyTransaction extends MyQueryable implements Transaction { - readonly options: TransactionOptions; - readonly #release: () => void; - - constructor( - client: TClient, - options: TransactionOptions, - release: () => void, - ) { - super(client); - this.options = options; - this.#release = release; - } +- signed integer widths to `Int32`/`Int64`; preserve 64-bit values without JS number truncation +- decimal/numeric to `Numeric` using the representation expected by Prisma +- binary to `Uint8Array`/`Bytes` +- date-only, time-only, and timestamp to `Date`, `Time`, and `DateTime` +- UUID, JSON, enum, arrays, and provider-specific unknown values to their explicit types +- unsupported native types to `DriverAdapterError({ kind: 'UnsupportedNativeDataType', type })` - commit(): Promise { - // DO NOT issue COMMIT SQL here — Prisma does it via executeRaw - this.#release(); // Release connection/resources - return Promise.resolve(); - } +Test `null`, empty arrays, array element types, big integers, decimals, byte arrays, JSON, dates, and user-defined/unknown native types. - rollback(): Promise { - // DO NOT issue ROLLBACK SQL here — Prisma does it via executeRaw - this.#release(); - return Promise.resolve(); - } -} -``` +### Script execution -### Step 3: Create the Adapter class +`executeScript` must execute a migration script as the provider expects. Prefer the driver's native multi-statement/script facility or a real SQL parser. Naively splitting on `;` breaks functions, triggers, quoted strings, and dialect-specific blocks. -```typescript -class MyAdapter extends MyQueryable implements SqlDriverAdapter { - #transactionDepth = 0; +## Transaction protocol - constructor(client: TClient) { - super(client); - } +`startTransaction` must acquire one dedicated connection, start the database transaction, apply the requested isolation level, and return a `Transaction` bound to that same connection. If setup fails, release it immediately. - async executeScript(script: string): Promise { - // For SQLite: split on ';' and run each statement - // For Postgres: use multi-statement execution - try { - // Implementation depends on driver capabilities - } catch (e) { - this.onError(e); +```typescript +async startTransaction(level?: IsolationLevel): Promise { + const connection = await this.pool.acquire() + try { + const tx = new ExampleTransaction(connection, () => connection.release()) + await tx.executeRaw({ sql: 'BEGIN', args: [], argTypes: [] }) + if (level) { + await tx.executeRaw({ + sql: `SET TRANSACTION ISOLATION LEVEL ${validateLevel(level)}`, + args: [], + argTypes: [], + }) } + return tx + } catch (error) { + connection.release(error) + throwAdapterError(error) } +} +``` - async startTransaction( - isolationLevel?: IsolationLevel, - ): Promise { - // Validate isolation level for your database - const validLevels = new Set([ - "READ UNCOMMITTED", - "READ COMMITTED", - "REPEATABLE READ", - "SERIALIZABLE", - ]); - - if (isolationLevel !== undefined && !validLevels.has(isolationLevel)) { - throw new DriverAdapterError({ - kind: "InvalidIsolationLevel", - level: isolationLevel, - }); - } - - const options: TransactionOptions = { usePhantomQuery: false }; +### Commit and rollback - this.#transactionDepth += 1; - const depth = this.#transactionDepth; +Prisma coordinates the SQL `COMMIT`/`ROLLBACK` through `executeRaw`. The transaction object's `commit()` and `rollback()` methods are lifecycle hooks: detach listeners and release the dedicated connection exactly once. They must not issue a second SQL commit/rollback. - try { - if (depth === 1) { - // Issue BEGIN (with isolation level if specified) - const beginSql = isolationLevel - ? `BEGIN ISOLATION LEVEL ${isolationLevel}` - : "BEGIN"; - await this.client.query(beginSql); - } else { - // Nested: use savepoints - await this.client.query(`SAVEPOINT sp_${depth}`); - } - } catch (e) { - this.#transactionDepth -= 1; - this.onError(e); - } +```typescript +class ExampleTransaction extends ExampleQueryable implements Transaction { + readonly options = { usePhantomQuery: false } + #closed = false - const release = () => { - this.#transactionDepth -= 1; - }; - return new MyTransaction(this.client, options, release); + constructor(connection: DriverConnection, private readonly release: () => void) { + super(connection) } - getConnectionInfo(): ConnectionInfo { - return { supportsRelationJoins: true }; - } + async commit() { this.finish() } + async rollback() { this.finish() } - async dispose(): Promise { - await this.client.close(); + private finish() { + if (this.#closed) return + this.#closed = true + this.release() } -} -``` - -### Step 4: Create the Factory class - -```typescript -export type MyAdapterConfig = { - url: string; -}; - -export type MyAdapterOptions = { - shadowDatabaseUrl?: string; -}; -export class MyAdapterFactory implements SqlMigrationAwareDriverAdapterFactory { - readonly provider = "postgres" as const; - readonly adapterName = "@my-org/adapter-mydb" as const; + async createSavepoint(name: string) { + await this.control(`SAVEPOINT ${safeSavepoint(name)}`) + } - constructor( - private readonly config: MyAdapterConfig, - private readonly options?: MyAdapterOptions, - ) {} + async rollbackToSavepoint(name: string) { + await this.control(`ROLLBACK TO SAVEPOINT ${safeSavepoint(name)}`) + } - connect(): Promise { - return Promise.resolve(new MyAdapter(openConnection(this.config.url))); + async releaseSavepoint(name: string) { + await this.control(`RELEASE SAVEPOINT ${safeSavepoint(name)}`) } - connectToShadowDb(): Promise { - const url = this.options?.shadowDatabaseUrl ?? this.config.url; - return Promise.resolve(new MyAdapter(openConnection(url))); + private async control(sql: string) { + await this.executeRaw({ sql, args: [], argTypes: [] }) } } ``` -## Conversion Helpers - -### Argument Mapping (input) - -Convert Prisma argument values to driver-native types: - -```typescript -function mapArg(arg: unknown, argType: ArgType): unknown { - if (arg === null || arg === undefined) return null; +Implement the optional savepoint methods only where the provider supports them. Validate/quote savepoint identifiers. For providers whose savepoints are intentionally no-ops, document and test that limitation. - // String → number for int columns - if (typeof arg === "string" && argType.scalarType === "int") - return Number.parseInt(arg, 10); +Never keep transaction depth on the shared adapter. Parallel transactions make adapter-global depth incorrect; nested state belongs to the returned transaction connection and Prisma's savepoint calls. - // String → number for float columns - if (typeof arg === "string" && argType.scalarType === "float") - return Number.parseFloat(arg); +## Error mapping - // String → BigInt for bigint columns - if (typeof arg === "string" && argType.scalarType === "bigint") - return BigInt(arg); +Wrap recognized driver failures in `DriverAdapterError`. Map known conditions to `MappedError` kinds such as constraint violations, authentication/reachability, missing table/column/database, timeouts, closed transactions, invalid input, value range, and write conflicts. - // Base64 string → Buffer for bytes columns - if (typeof arg === "string" && argType.scalarType === "bytes") - return Buffer.from(arg, "base64"); - - // Boolean → 0/1 for SQLite - if (typeof arg === "boolean" && /* SQLite */) - return arg ? 1 : 0; - - return arg; -} -``` - -### Row Mapping (output) - -Convert driver result values to Prisma-expected types: +For database errors, preserve `originalCode` and `originalMessage` even when falling back to the provider-specific raw variant: ```typescript -function mapRow(row: unknown[], columnTypes: ColumnType[]): ResultValue[] { - const result: ResultValue[] = []; - - for (let i = 0; i < row.length; i++) { - const value = row[i] ?? null; - const colType = columnTypes[i]; - - if (value === null) { - result.push(null); - continue; - } - - // bigint → string for Int64 (JSON-safe) - if (typeof value === "bigint") { - result.push(value.toString()); - continue; - } - - // Date → ISO 8601 string for DateTime - if (value instanceof Date) { - result.push(value.toISOString()); - continue; - } - - // JSON objects → stringified - if (colType === ColumnTypeEnum.Json && typeof value === "object") { - result.push(JSON.stringify(value)); - continue; - } - - result.push(value as ResultValue); +import { + DriverAdapterError, + type Error as DriverAdapterErrorObject, + type MappedError, +} from '@prisma/driver-adapter-utils' + +function convertDriverError(error: DatabaseError): DriverAdapterErrorObject { + return { + originalCode: String(error.code), + originalMessage: error.message, + ...mapKnownOrRaw(error), } - - return result; -} -``` - -### Column Type Inference - -When the driver doesn't provide type metadata, infer from JS values: - -```typescript -function inferColumnType(value: NonNullable): ColumnType { - if (typeof value === "boolean") return ColumnTypeEnum.Boolean; - if (typeof value === "bigint") return ColumnTypeEnum.Int64; - if (value instanceof Uint8Array) return ColumnTypeEnum.Bytes; - if (value instanceof Date) return ColumnTypeEnum.DateTime; - if (Array.isArray(value)) return ColumnTypeEnum.Text; // fallback - if (typeof value === "object") return ColumnTypeEnum.Json; - if (typeof value === "number") return ColumnTypeEnum.UnknownNumber; - return ColumnTypeEnum.Text; } -``` - -## Error Handling - -Map driver errors to `MappedError` for Prisma to handle correctly: - -```typescript -function convertDriverError(error: unknown): MappedError { - if (error instanceof Error) { - // Database-specific error mapping - const dbError = error as Error & { code?: string; errno?: number }; - - // PostgreSQL example - if (dbError.code === "23505") { - return { kind: "UniqueConstraintViolation" }; - } - if (dbError.code === "23502") { - return { kind: "NullConstraintViolation" }; - } - if (dbError.code === "23503") { - return { kind: "ForeignKeyConstraintViolation" }; - } - if (dbError.code === "42P01") { - return { kind: "TableDoesNotExist" }; - } - // SQLite example - if (error.name === "SQLiteError") { - return { - kind: "sqlite", - extendedCode: dbError.errno ?? 1, - message: error.message, - }; - } - - // PostgreSQL raw error - if (dbError.code) { - return { - kind: "postgres", - code: dbError.code, - severity: "ERROR", - message: error.message, - detail: undefined, - column: undefined, - hint: undefined, - }; - } +function mapKnownOrRaw(error: DatabaseError): MappedError { + if (error.code === '23505') { + return { kind: 'UniqueConstraintViolation', constraint: parsedConstraint(error) } + } + return { + kind: 'postgres', + code: String(error.code ?? 'N/A'), + severity: error.severity ?? 'N/A', + message: error.message, + detail: error.detail, + column: error.column, + hint: error.hint, } - - return { kind: "GenericJs", id: 0 }; } -``` - -## Database-Specific Notes - -### SQLite - -- Set `safeIntegers: true` when opening the database to get `bigint` for large integers -- Only `SERIALIZABLE` isolation level is valid -- `executeScript`: split on `;` and run each statement individually -- Boolean values: store as 0/1, return as boolean - -### PostgreSQL - -- All standard isolation levels are valid -- For connection pooling (PgBouncer), use `prepare: false` -- Transactions require a dedicated connection (`reserve()` pattern) -- `executeScript`: use multi-statement execution (`.simple()` in some drivers) -- `int8` columns may return as string (already stringified by driver) -- `numeric` columns return as string to preserve precision - -### MySQL/MariaDB - -- Supports `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, `SERIALIZABLE` -- Use `?` placeholders for parameters -- Handle `BIGINT` as string for large values - -## Testing Strategy -### Unit Tests (no PrismaClient) - -Test the adapter directly with the raw database driver: - -```typescript -describe("queryRaw", () => { - test("returns column names and types", async () => { - const adapter = new MyAdapter(createTestConnection()); - const result = await adapter.queryRaw({ - sql: "SELECT id, name FROM users", - args: [], - argTypes: [], - }); - expect(result.columnNames).toEqual(["id", "name"]); - expect(result.columnTypes[0]).toBe(ColumnTypeEnum.Int32); - }); -}); - -describe("startTransaction", () => { - test("commit persists changes", async () => { - const adapter = new MyAdapter(createTestConnection()); - const tx = await adapter.startTransaction(); - await tx.executeRaw({ - sql: "INSERT INTO users (name) VALUES (?)", - args: ["Alice"], - argTypes: [], - }); - // Prisma sends COMMIT via executeRaw - await tx.executeRaw({ sql: "COMMIT", args: [], argTypes: [] }); - await tx.commit(); // lifecycle hook only - // Verify data persisted - }); -}); -``` - -### E2E Tests (with PrismaClient) - -Test the full integration: - -```typescript -describe("E2E", () => { - let prisma: PrismaClient; - - beforeEach(async () => { - const factory = new MyAdapterFactory({ url: TEST_DB_URL }); - prisma = new PrismaClient({ adapter: factory }); - }); - - test("CRUD operations", async () => { - const user = await prisma.user.create({ data: { name: "Alice" } }); - expect(user.id).toBeGreaterThan(0); - - const found = await prisma.user.findUnique({ where: { id: user.id } }); - expect(found?.name).toBe("Alice"); - }); - - test("transactions roll back on error", async () => { - await expect( - prisma.$transaction(async (tx) => { - await tx.user.create({ data: { name: "Bob" } }); - throw new Error("Rollback!"); - }), - ).rejects.toThrow(); - - expect(await prisma.user.count()).toBe(0); - }); -}); -``` - -## Usage Example - -```typescript -import { PrismaClient } from "./generated/prisma/client"; -import { MyAdapterFactory } from "@my-org/adapter-mydb"; - -const factory = new MyAdapterFactory({ - url: process.env.DATABASE_URL!, -}); - -const prisma = new PrismaClient({ adapter: factory }); - -// Use prisma normally -const users = await prisma.user.findMany(); +function throwAdapterError(error: unknown): never { + if (!isDatabaseError(error)) throw error + throw new DriverAdapterError(convertDriverError(error)) +} ``` -## Checklist - -Before considering the adapter complete: - -- [ ] `SqlMigrationAwareDriverAdapterFactory` implemented with `connect()` and `connectToShadowDb()` -- [ ] `SqlDriverAdapter` implements `queryRaw`, `executeRaw`, `executeScript`, `startTransaction`, `dispose` -- [ ] `Transaction` implements `queryRaw`, `executeRaw`, `commit`, `rollback` with `options: { usePhantomQuery: false }` -- [ ] `commit()` and `rollback()` are lifecycle hooks only (no SQL issued) -- [ ] `startTransaction` issues `BEGIN` (depth 1) or `SAVEPOINT sp_N` (nested) -- [ ] Argument mapping handles: string→int, string→bigint, string→float, base64→bytes -- [ ] Row mapping handles: bigint→string, Date→ISO string, JSON→string -- [ ] Column types correctly mapped to `ColumnTypeEnum` -- [ ] Errors wrapped in `DriverAdapterError` with proper `MappedError` kind -- [ ] Isolation level validation for the target database -- [ ] Unit tests pass for queryRaw, executeRaw, executeScript, transactions -- [ ] E2E tests pass with real PrismaClient +Prisma uses preserved original details when an unmapped driver error becomes `P2039`. Do not replace every unknown exception with a fabricated `GenericJs` id; rethrow genuinely unexpected non-driver errors so programming bugs remain visible. + +## Factory, ownership, and shadow database + +- `connect()` returns a fresh usable adapter connection/pool wrapper. +- Track whether the factory created the pool. `dispose()` closes owned pools and only detaches listeners from caller-owned pools unless an explicit option transfers ownership. +- Implement `SqlMigrationAwareDriverAdapterFactory` only when `connectToShadowDb()` can create an isolated shadow database, connect to it, and drop it during disposal/failure cleanup. +- Never point the shadow adapter at the primary database. Quote generated identifiers and use cryptographically unique names. +- `getConnectionInfo()` should accurately report `schemaName`, `maxBindValues` when applicable, and `supportsRelationJoins`. + +## Verification checklist + +- [ ] Typecheck against the exact target `@prisma/driver-adapter-utils` version +- [ ] `queryRaw` preserves column order, types, nulls, and precision +- [ ] `executeRaw` reports affected rows correctly +- [ ] `executeScript` handles provider-specific multi-statement syntax +- [ ] Concurrent interactive transactions use distinct dedicated connections +- [ ] Success commits and releases once; failure rolls back and releases once +- [ ] Nested transaction tests exercise create/rollback/release savepoint hooks +- [ ] Unsupported isolation levels fail as `InvalidIsolationLevel` +- [ ] Known constraints map to structured errors +- [ ] Unmapped database errors retain original code/message and surface useful `P2039` +- [ ] Dispose ownership is tested for internal and external pools +- [ ] Shadow database creation, use, failure cleanup, and disposal are isolated +- [ ] Run Prisma Client integration/E2E tests, not only adapter unit tests + +## Source references + +- [Driver adapter interfaces](https://github.com/prisma/prisma/blob/v7/packages/driver-adapter-utils/src/types.ts) +- [PostgreSQL adapter transaction implementation](https://github.com/prisma/prisma/blob/v7/packages/adapter-pg/src/pg.ts) +- [PostgreSQL adapter error mapping](https://github.com/prisma/prisma/blob/v7/packages/adapter-pg/src/errors.ts) diff --git a/plugins/prisma/agent/skills/prisma-postgres/SKILL.md b/plugins/prisma/agent/skills/prisma-postgres/SKILL.md index d49d0e83..bb8f9140 100644 --- a/plugins/prisma/agent/skills/prisma-postgres/SKILL.md +++ b/plugins/prisma/agent/skills/prisma-postgres/SKILL.md @@ -1,7 +1,7 @@ --- description: "Prisma Postgres setup and operations guidance across Console, create-db CLI, Management API, and Management API SDK. Use when creating Prisma Postgres databases, working in Prisma Console, provisioning with create-db/create-pg/create-postgres, or integrating programmatic provisioning with service tokens or OAuth." license: "MIT" -metadata: {"author":"prisma","version":"7.6.0"} +metadata: {"author":"prisma","version":"7.9.1"} --- # Prisma Postgres @@ -63,6 +63,22 @@ For app integrations, you can also use the programmatic API (`create()` / `regio Temporary databases auto-delete after ~24 hours unless claimed. +### 2b. Persistent databases with the Platform CLI + +For databases that belong to a Project (not throwaway `create-db` databases), use `@prisma/cli`: + +```bash +npx -y @prisma/cli@latest database create --help +npx -y @prisma/cli@latest database list --json +npx -y @prisma/cli@latest database connection create db_123 +npx -y @prisma/cli@latest database usage db_123 +npx -y @prisma/cli@latest database backup list db_123 +``` + +`database create` and `database connection create` print a one-time connection URL; store it immediately. Destructive commands (`remove`, `restore`) require exact `--confirm `. + +For automation, prefer `--json --no-interactive`, resolve ids before mutations, and verify the installed command's help because this CLI is beta. + ### 3. Link an existing local project Use `prisma postgres link` when the database already exists and you want to wire a local project to it: @@ -107,6 +123,8 @@ npm install @prisma/management-api-sdk Use `createManagementApiClient` for existing tokens, or `createManagementApiSdk` for OAuth + token refresh. +The SDK exposes typed workspace service-token list, create, and revoke routes. A newly created token value is returned exactly once. Let the installed SDK types or OpenAPI document settle exact beta endpoint shapes. + ## Rule Files Detailed guidance lives in: diff --git a/plugins/prisma/agent/skills/prisma-postgres/references/console-and-connections.md b/plugins/prisma/agent/skills/prisma-postgres/references/console-and-connections.md index c6e30f9a..4025d12b 100644 --- a/plugins/prisma/agent/skills/prisma-postgres/references/console-and-connections.md +++ b/plugins/prisma/agent/skills/prisma-postgres/references/console-and-connections.md @@ -55,6 +55,8 @@ Typical direct TCP format: DATABASE_URL="postgres://identifier:key@db.prisma.io:5432/postgres?sslmode=require" ``` +Management API connection responses expose both `endpoints.direct` (`db.prisma.io:5432`) and `endpoints.pooled` (`pooled.db.prisma.io:5432`); prefer those fields over the deprecated flat `connectionString`. Connection secrets are shown once at creation (one-time view); store them immediately. + ## Adapter choices - Standard Node.js apps: prefer `@prisma/adapter-pg` with the direct TCP URL above. diff --git a/plugins/prisma/agent/skills/prisma-postgres/references/management-api-sdk.md b/plugins/prisma/agent/skills/prisma-postgres/references/management-api-sdk.md index d4ad6bb0..026aa5f8 100644 --- a/plugins/prisma/agent/skills/prisma-postgres/references/management-api-sdk.md +++ b/plugins/prisma/agent/skills/prisma-postgres/references/management-api-sdk.md @@ -2,6 +2,8 @@ Use `@prisma/management-api-sdk` for typed API integration with optional OAuth and token refresh. +The Platform API evolves independently from Prisma ORM. Inspect the installed package's generated `api.d.ts` for exact paths and request/response shapes. + ## Priority HIGH @@ -25,6 +27,18 @@ const client = createManagementApiClient({ token: process.env.PRISMA_SERVICE_TOK const { data: workspaces } = await client.GET('/v1/workspaces') ``` +Check the generated client result before using `data`; typed clients surface HTTP failures separately. Never log a full response from connection/key creation because it may contain one-time credentials. + +## Workspace service tokens + +The typed client exposes routes to list, create, and revoke workspace service tokens: + +- `GET /v1/workspaces/{workspaceId}/service-tokens` +- `POST /v1/workspaces/{workspaceId}/service-tokens` +- `DELETE /v1/workspaces/{workspaceId}/service-tokens/{serviceTokenId}` + +Creation accepts a display `name`. The response's `data.value` is the complete token and is returned exactly once; transfer it directly to the intended secret store without logging the response. Later list calls return metadata and `valueHint`, not the token value. Treat revocation as destructive and resolve both ids explicitly. + ## Full SDK (OAuth + refresh) ```typescript diff --git a/plugins/prisma/agent/skills/prisma-postgres/references/management-api.md b/plugins/prisma/agent/skills/prisma-postgres/references/management-api.md index ff37c39e..4e4e76f9 100644 --- a/plugins/prisma/agent/skills/prisma-postgres/references/management-api.md +++ b/plugins/prisma/agent/skills/prisma-postgres/references/management-api.md @@ -42,17 +42,35 @@ Authorization: Bearer $TOKEN 3. Exchange code at `https://auth.prisma.io/token`. 4. Use returned access token in Management API requests. -## Common endpoints +## Resource model -- `GET /workspaces` -- `GET /projects` -- `POST /projects` -- Database management endpoints under project/database paths +Workspace -> Project -> Branch -> Database. Branches are a first-class resource: databases attach to a Branch, and branch-scoped env/databases are how preview isolation works. + +## Current resource inventory + +The 1.55 OpenAPI surface includes: + +- workspaces, subscriptions, workspace integrations, workspace service tokens, and current-user metadata +- projects, transfers, project databases, and project/branch environment variables +- branches under a project plus branch get/update/delete operations +- databases, usage, backups, restore, connections, and connection rotation +- apps, deployments, promotion/rollback, runtime logs, domains, and build logs +- buckets and bucket keys +- source repositories, SCM installations/install intents, and repositories +- integrations and regions + +App/deployment, branch mutation, SCM, and bucket routes include experimental surfaces. Read the installed SDK types or live OpenAPI before building durable automation around them. + +Connection create/rotate responses reveal credentials once. Later reads redact or omit the secret, so store the URL immediately. Use the structured direct/pooled endpoint returned by the concrete operation; do not assume a historical flat response shape. + +Workspace service-token creation also returns the complete token value exactly once. List calls expose only metadata and a `valueHint`; delete revokes the token. Keep workspace and token ids opaque, and never log a create response. + +Database create supports explicit project, region, branch, and source context. A source may be empty, a backup, or another database. Backup records are incremental; rely on current fields and documented units rather than old full-backup examples. ## Notes -- Management API responses may include direct connection credentials for databases. -- Build PostgreSQL `DATABASE_URL` from direct connection values when needed. +- Management API mutation responses may include direct connection credentials; treat the entire response as secret until redacted. +- Prefer an API-provided connection string over manually assembling one from fields. ## References diff --git a/plugins/prisma/agent/skills/prisma-upgrade-v7/SKILL.md b/plugins/prisma/agent/skills/prisma-upgrade-v7/SKILL.md index 047773ae..3bbc4096 100644 --- a/plugins/prisma/agent/skills/prisma-upgrade-v7/SKILL.md +++ b/plugins/prisma/agent/skills/prisma-upgrade-v7/SKILL.md @@ -37,9 +37,15 @@ Reference this skill when: - `removed-features` - removed middleware, metrics, and legacy CLI behavior - `accelerate-users` - migration notes for Accelerate users +## Using MongoDB? This guide does not apply + +Prisma 7 has no MongoDB connector. Do not apply any step in this guide to a project with +`provider = "mongodb"` — see the `prisma-mongodb-upgrade` skill for the actual decision +(stay on v6 deliberately vs migrate to Prisma Next). + ## Important Notes -- **MongoDB projects should stay on Prisma 6.x** - do not migrate MongoDB apps to Prisma 7's SQL client path +- **MongoDB projects should stay on Prisma 6.x or migrate to Prisma Next** - do not migrate MongoDB apps to Prisma 7's SQL client path (see `prisma-mongodb-upgrade`) - **Node.js 20.19.0+** required - **TypeScript 5.4.0+** required - **Latest stable Prisma ORM version**: `7.6.0` diff --git a/plugins/prisma/skills-lock.json b/plugins/prisma/skills-lock.json index 0396f7c1..add2ed45 100644 --- a/plugins/prisma/skills-lock.json +++ b/plugins/prisma/skills-lock.json @@ -5,37 +5,37 @@ "source": "prisma/skills", "sourceType": "github", "skillPath": "prisma-cli/SKILL.md", - "computedHash": "1cfdc0011df8699a2ece587f229a1e892df2a9a3eec40b5407492cb4c6570e95" + "computedHash": "129e77cba93984b88d66cf0f66d5788f5487431f17f189a1ab2d839976874e3b" }, "prisma-client-api": { "source": "prisma/skills", "sourceType": "github", "skillPath": "prisma-client-api/SKILL.md", - "computedHash": "0434410e48fb796bbdc3ad0050654bfc9cf72c89b30dbad098ee5c9f655b7f85" + "computedHash": "cbbabff961c8e16b2d59f8c1ffb4e7e20be52154e778ab95121e0211d6d09969" }, "prisma-database-setup": { "source": "prisma/skills", "sourceType": "github", "skillPath": "prisma-database-setup/SKILL.md", - "computedHash": "46d2dc2f9c968722376bbb6a1a1389c82bd7cceb5973ac1486c54890514b8959" + "computedHash": "6a991a2530a6da7131e3fc71c353e8278702d0146b8b6e1d2fdfc2d3d0a21bd2" }, "prisma-driver-adapter-implementation": { "source": "prisma/skills", "sourceType": "github", "skillPath": "prisma-driver-adapter-implementation/SKILL.md", - "computedHash": "2e00b0d05f0da23796f6d44684e19219b74cf993f8a2df6e5b044812f375bbc6" + "computedHash": "cc487f1bf36f616ddb3a9478b3527d288dd706d77a9e5dbee9af52dca1eee8e4" }, "prisma-postgres": { "source": "prisma/skills", "sourceType": "github", "skillPath": "prisma-postgres/SKILL.md", - "computedHash": "d090d1206fb6a2dd9f7b9f658d851ccd653132512c53c6e0a164110d9ee80005" + "computedHash": "0422727e18019add9208b86ce862964025acc011ff31fba1d59a01b836bd6272" }, "prisma-upgrade-v7": { "source": "prisma/skills", "sourceType": "github", "skillPath": "prisma-upgrade-v7/SKILL.md", - "computedHash": "2e4d54a4c7a0fc2476e81686718a765f5eb2cacfb2fb0822f3109c76bf4a9bb0" + "computedHash": "0e07dc2831f86cf27627df914db43d862b0fc75d72c1f9070434aab7940bad7c" } } } 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 new file mode 100644 index 00000000..600eb5bc --- /dev/null +++ b/plugins/react-native/.agents/skills/vercel-react-native-skills/metadata.json @@ -0,0 +1,16 @@ +{ + "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/.claude/skills/vercel-react-native-skills b/plugins/react-native/.claude/skills/vercel-react-native-skills new file mode 120000 index 00000000..8c988434 --- /dev/null +++ b/plugins/react-native/.claude/skills/vercel-react-native-skills @@ -0,0 +1 @@ +../../.agents/skills/vercel-react-native-skills \ No newline at end of file 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 new file mode 100644 index 00000000..600eb5bc --- /dev/null +++ b/plugins/react-native/agent/skills/vercel-react-native-skills/metadata.json @@ -0,0 +1,16 @@ +{ + "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 8545c572..14674bd4 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": "2e9088a7333666d8c2833b8ff58bd51b955501c42b4c7244f72b4cbf22dafcc4" + "computedHash": "41d24eafa7c3d82e270439808f7cfbc4d51aeb2d14f2809a2267c16275784d06" } } } diff --git a/plugins/react/.agents/skills/vercel-composition-patterns/metadata.json b/plugins/react/.agents/skills/vercel-composition-patterns/metadata.json new file mode 100644 index 00000000..3470b744 --- /dev/null +++ b/plugins/react/.agents/skills/vercel-composition-patterns/metadata.json @@ -0,0 +1,11 @@ +{ + "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 new file mode 100644 index 00000000..3bec38b1 --- /dev/null +++ b/plugins/react/.agents/skills/vercel-react-best-practices/metadata.json @@ -0,0 +1,15 @@ +{ + "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/metadata.json b/plugins/react/.agents/skills/vercel-react-view-transitions/metadata.json new file mode 100644 index 00000000..aabe3e14 --- /dev/null +++ b/plugins/react/.agents/skills/vercel-react-view-transitions/metadata.json @@ -0,0 +1,12 @@ +{ + "version": "1.0.0", + "organization": "Vercel Engineering", + "date": "March 2026", + "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, JavaScript animations via Web Animations API, and Next.js integration including the transitionTypes prop on next/link. Includes ready-to-use CSS animation recipes and real-world patterns from production Next.js apps.", + "references": [ + "https://react.dev/reference/react/ViewTransition", + "https://react.dev/reference/react/addTransitionType", + "https://nextjs.org/docs/app/api-reference/config/next-config-js/viewTransition", + "https://github.com/vercel/next-app-router-playground/tree/main/app/view-transitions" + ] +} diff --git a/plugins/react/.claude/skills/vercel-composition-patterns b/plugins/react/.claude/skills/vercel-composition-patterns new file mode 120000 index 00000000..55a19e8f --- /dev/null +++ b/plugins/react/.claude/skills/vercel-composition-patterns @@ -0,0 +1 @@ +../../.agents/skills/vercel-composition-patterns \ No newline at end of file diff --git a/plugins/react/.claude/skills/vercel-react-best-practices b/plugins/react/.claude/skills/vercel-react-best-practices new file mode 120000 index 00000000..e567923b --- /dev/null +++ b/plugins/react/.claude/skills/vercel-react-best-practices @@ -0,0 +1 @@ +../../.agents/skills/vercel-react-best-practices \ No newline at end of file diff --git a/plugins/react/.claude/skills/vercel-react-view-transitions b/plugins/react/.claude/skills/vercel-react-view-transitions new file mode 120000 index 00000000..78a32226 --- /dev/null +++ b/plugins/react/.claude/skills/vercel-react-view-transitions @@ -0,0 +1 @@ +../../.agents/skills/vercel-react-view-transitions \ No newline at end of file diff --git a/plugins/react/agent/skills/vercel-composition-patterns/metadata.json b/plugins/react/agent/skills/vercel-composition-patterns/metadata.json new file mode 100644 index 00000000..3470b744 --- /dev/null +++ b/plugins/react/agent/skills/vercel-composition-patterns/metadata.json @@ -0,0 +1,11 @@ +{ + "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/agent/skills/vercel-react-best-practices/metadata.json b/plugins/react/agent/skills/vercel-react-best-practices/metadata.json new file mode 100644 index 00000000..3bec38b1 --- /dev/null +++ b/plugins/react/agent/skills/vercel-react-best-practices/metadata.json @@ -0,0 +1,15 @@ +{ + "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/agent/skills/vercel-react-view-transitions/metadata.json b/plugins/react/agent/skills/vercel-react-view-transitions/metadata.json new file mode 100644 index 00000000..aabe3e14 --- /dev/null +++ b/plugins/react/agent/skills/vercel-react-view-transitions/metadata.json @@ -0,0 +1,12 @@ +{ + "version": "1.0.0", + "organization": "Vercel Engineering", + "date": "March 2026", + "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, JavaScript animations via Web Animations API, and Next.js integration including the transitionTypes prop on next/link. Includes ready-to-use CSS animation recipes and real-world patterns from production Next.js apps.", + "references": [ + "https://react.dev/reference/react/ViewTransition", + "https://react.dev/reference/react/addTransitionType", + "https://nextjs.org/docs/app/api-reference/config/next-config-js/viewTransition", + "https://github.com/vercel/next-app-router-playground/tree/main/app/view-transitions" + ] +} diff --git a/plugins/react/skills-lock.json b/plugins/react/skills-lock.json index 90bc6dcb..82fa09e5 100644 --- a/plugins/react/skills-lock.json +++ b/plugins/react/skills-lock.json @@ -5,19 +5,19 @@ "source": "vercel-labs/agent-skills", "sourceType": "github", "skillPath": "skills/composition-patterns/SKILL.md", - "computedHash": "f98931159fa9c7fed043bcd18a891a46dcf89ababa38df13a4c5b7b30dc0ce07" + "computedHash": "575757e3e25761c8c562d6e395d29f0b76c98b1273c0bd72d88e6ab1bc9c7d42" }, "vercel-react-best-practices": { "source": "vercel-labs/agent-skills", "sourceType": "github", "skillPath": "skills/react-best-practices/SKILL.md", - "computedHash": "3219a1944e404ffc14d1d9d6aef6dd2e3855b81387ee0a044ccbfe14d34c2357" + "computedHash": "ca7b0c0c6e5f2750043f7f0cd72d16ac4e2abc48f9b5500d047a4b77a2506212" }, "vercel-react-view-transitions": { "source": "vercel-labs/agent-skills", "sourceType": "github", "skillPath": "skills/react-view-transitions/SKILL.md", - "computedHash": "4fea9144f604256d0a21faaea904b7e205e7676e316721ba5fdd68c2600c7d42" + "computedHash": "c8952fc9127fa0564d0cb71dccb4215a5608ac933b5831104f09173a97a46f85" } } } diff --git a/plugins/shadcn-ui/.agents/skills/shadcn/SKILL.md b/plugins/shadcn-ui/.agents/skills/shadcn/SKILL.md index 8c01af92..1b4c414c 100644 --- a/plugins/shadcn-ui/.agents/skills/shadcn/SKILL.md +++ b/plugins/shadcn-ui/.agents/skills/shadcn/SKILL.md @@ -64,7 +64,9 @@ These rules are **always enforced**. Each links to a file with Incorrect/Correct - **Use existing components before custom markup.** Check if a component exists before writing a styled `div`. - **Callouts use `Alert`.** Don't build custom styled divs. - **Empty states use `Empty`.** Don't build custom empty state markup. -- **Toast via `sonner`.** Use `toast()` from `sonner`. +- **Toast follows the project base.** Use `toast` from the `toast` component for + Base UI projects. Use `toast()` from `sonner` for Radix and React Aria + projects. - **Use `Separator`** instead of `
` or `
`. - **Use `Skeleton`** for loading placeholders. No custom `animate-pulse` divs. - **Use `Badge`** instead of custom styled spans. @@ -135,7 +137,7 @@ These are the most common patterns that differentiate correct shadcn/ui code. Fo | Data display | `Table`, `Card`, `Badge`, `Avatar` | | Navigation | `Sidebar`, `NavigationMenu`, `Breadcrumb`, `Tabs`, `Pagination` | | Overlays | `Dialog` (modal), `Sheet` (side panel), `Drawer` (bottom sheet), `AlertDialog` (confirmation) | -| Feedback | `sonner` (toast), `Alert`, `Progress`, `Skeleton`, `Spinner` | +| Feedback | `toast` (Base UI), `sonner` (Radix/Aria), `Alert`, `Progress`, `Skeleton`, `Spinner` | | Command palette | `Command` inside `Dialog` | | Charts | `Chart` (wraps Recharts) | | Layout | `Card`, `Separator`, `Resizable`, `ScrollArea`, `Accordion`, `Collapsible` | diff --git a/plugins/shadcn-ui/.agents/skills/shadcn/rules/composition.md b/plugins/shadcn-ui/.agents/skills/shadcn/rules/composition.md index 0654245a..a8aa4274 100644 --- a/plugins/shadcn-ui/.agents/skills/shadcn/rules/composition.md +++ b/plugins/shadcn-ui/.agents/skills/shadcn/rules/composition.md @@ -5,7 +5,7 @@ - Items always inside their Group component - Callouts use Alert - Empty states use Empty component -- Toast notifications use sonner +- Toast notifications follow the project base - Choosing between overlay components - Dialog, Sheet, and Drawer always need a Title - Card structure @@ -88,7 +88,19 @@ Chat components nest in a fixed order (`MessageScrollerProvider` → `MessageScr --- -## Toast notifications use sonner +## Toast notifications follow the project base + +For Base UI projects, use the `toast` component: + +```tsx +import { toast } from "@/components/ui/toast" + +toast.add({ + title: "Changes saved.", +}) +``` + +For Radix and React Aria projects, use Sonner: ```tsx import { toast } from "sonner" diff --git a/plugins/shadcn-ui/.claude/skills/migrate-radix-to-base b/plugins/shadcn-ui/.claude/skills/migrate-radix-to-base new file mode 120000 index 00000000..1ee49446 --- /dev/null +++ b/plugins/shadcn-ui/.claude/skills/migrate-radix-to-base @@ -0,0 +1 @@ +../../.agents/skills/migrate-radix-to-base \ No newline at end of file diff --git a/plugins/shadcn-ui/.claude/skills/shadcn b/plugins/shadcn-ui/.claude/skills/shadcn new file mode 120000 index 00000000..8d5af6f6 --- /dev/null +++ b/plugins/shadcn-ui/.claude/skills/shadcn @@ -0,0 +1 @@ +../../.agents/skills/shadcn \ No newline at end of file diff --git a/plugins/shadcn-ui/agent/skills/migrate-radix-to-base/SKILL.md b/plugins/shadcn-ui/agent/skills/migrate-radix-to-base/SKILL.md new file mode 100644 index 00000000..005e00b3 --- /dev/null +++ b/plugins/shadcn-ui/agent/skills/migrate-radix-to-base/SKILL.md @@ -0,0 +1,171 @@ +--- +description: "Migrates React projects and components from Radix UI to Base UI. Use when asked to migrate from radix, move to base-ui, convert radix primitives, or switch a shadcn project's base library. Handles single components (\"migrate accordion\") and whole projects." +--- +# Radix UI -> Base UI migration + +You migrate shadcn wrappers, hand-rolled radix compositions, and their +consumers to `@base-ui/react`, keeping the project buildable at every step. +Be precise; never guess a mapping. When a prop or part is not in these +reference files, check `node_modules/@base-ui/react/**/*.d.ts` before +transforming, and record gaps in the report. + +## Preflight (always) + +1. `npx shadcn@latest info --json` (or the project's runner): gives the + current base, STYLE (e.g. `radix-lyra`), tailwind version, aliases, + installed components, and package manager. Trust it over inference. +2. Detect the package manager (packageManager field / lockfile: + pnpm-lock.yaml, bun.lock, yarn.lock, package-lock.json) and use IT for + every install. Never leave a stale lockfile. +3. Require a clean git tree; work on a branch; one commit per component. +4. Baseline check BEFORE touching dependencies: run the project's + typecheck/build so pre-existing failures are never attributed to you. +5. Install `@base-ui/react` alongside radix. Radix packages are removed only + after the LAST component is migrated (both coexist fine). + +## Strategy: golden pair first, transformation engine second + +- **Golden pair via the CLI (preferred).** If the project is shadcn with a + known style (`radix-