diff --git a/plugins/ai-sdk/.agents/skills/ai-sdk/SKILL.md b/plugins/ai-sdk/.agents/skills/ai-sdk/SKILL.md index 038ecd17..728d06bb 100644 --- a/plugins/ai-sdk/.agents/skills/ai-sdk/SKILL.md +++ b/plugins/ai-sdk/.agents/skills/ai-sdk/SKILL.md @@ -20,9 +20,9 @@ Whatever you remember about the AI SDK is likely outdated. The SDK changes frequ The `ai` package ships its full documentation and source code inside `node_modules`. These always match the installed version, so trust them over anything you remember. -1. Ensure `ai` is installed. If `node_modules/ai/` does not exist, install **only** the `ai` package using the project's package manager (e.g. `pnpm add ai`). Install provider packages (e.g. `@ai-sdk/openai`) and framework packages (e.g. `@ai-sdk/react`) later, when the task requires them. -2. Read and grep the bundled docs at `node_modules/ai/docs/` and the source at `node_modules/ai/src/`. -3. Provider and framework packages bundle their own docs at `node_modules/@ai-sdk//docs/`. +1. Ensure `ai` is installed. Check `node_modules/ai/` or locate the workspace package in a monorepo that depends on `ai` (e.g. `apps//node_modules/ai/`). If `ai` is not installed anywhere in the project, install **only** the `ai` package into the target package using the project's package manager (e.g. `pnpm add ai --filter ` or `npm install ai`). Install provider packages (e.g. `@ai-sdk/openai`) and framework packages (e.g. `@ai-sdk/react`) later, when the task requires them. +2. Read and grep the bundled docs at `node_modules/ai/docs/` (or `/node_modules/ai/docs/`) and the source at `node_modules/ai/src/`. +3. Provider and framework packages bundle their own docs at `node_modules/@ai-sdk//docs/` (or `/node_modules/@ai-sdk//docs/`). 4. If something isn't in the bundled docs, search https://ai-sdk.dev/docs. You can append `.md` to any docs page URL to get its markdown, and search via `https://ai-sdk.dev/api/search-docs?q=your_query`. 5. If you cannot find support for an answer in the docs or source, say so explicitly — do not guess. diff --git a/plugins/ai-sdk/agent/skills/ai-sdk/SKILL.md b/plugins/ai-sdk/agent/skills/ai-sdk/SKILL.md index edb003be..40ca751a 100644 --- a/plugins/ai-sdk/agent/skills/ai-sdk/SKILL.md +++ b/plugins/ai-sdk/agent/skills/ai-sdk/SKILL.md @@ -1,5 +1,14 @@ --- -description: "Answer questions about the AI SDK and help build AI-powered features. Use when developers: (1) Ask about AI SDK functions like generateText, streamText, ToolLoopAgent, embed, or tools, (2) Want to build AI agents, chatbots, RAG systems, or text generation features, (3) Have questions about AI providers (OpenAI, Anthropic, Google, etc.), streaming, tool calling, structured output, or embeddings, (4) Use React hooks like useChat or useCompletion. Triggers on: \"AI SDK\", \"Vercel AI SDK\", \"generateText\", \"streamText\", \"add AI to my app\", \"build an agent\", \"tool calling\", \"structured output\", \"useChat\"." +name: ai-sdk +description: 'Answer questions about the AI SDK and help build AI-powered + features. Use when developers: (1) Ask about AI SDK functions like + generateText, streamText, ToolLoopAgent, embed, or tools, (2) Want to build AI + agents, chatbots, RAG systems, or text generation features, (3) Have questions + about AI providers (OpenAI, Anthropic, Google, etc.), streaming, tool calling, + structured output, or embeddings, (4) Use React hooks like useChat or + useCompletion. Triggers on: "AI SDK", "Vercel AI SDK", "generateText", + "streamText", "add AI to my app", "build an agent", "tool calling", + "structured output", "useChat".' --- ## What the AI SDK Is @@ -18,9 +27,9 @@ Whatever you remember about the AI SDK is likely outdated. The SDK changes frequ The `ai` package ships its full documentation and source code inside `node_modules`. These always match the installed version, so trust them over anything you remember. -1. Ensure `ai` is installed. If `node_modules/ai/` does not exist, install **only** the `ai` package using the project's package manager (e.g. `pnpm add ai`). Install provider packages (e.g. `@ai-sdk/openai`) and framework packages (e.g. `@ai-sdk/react`) later, when the task requires them. -2. Read and grep the bundled docs at `node_modules/ai/docs/` and the source at `node_modules/ai/src/`. -3. Provider and framework packages bundle their own docs at `node_modules/@ai-sdk//docs/`. +1. Ensure `ai` is installed. Check `node_modules/ai/` or locate the workspace package in a monorepo that depends on `ai` (e.g. `apps//node_modules/ai/`). If `ai` is not installed anywhere in the project, install **only** the `ai` package into the target package using the project's package manager (e.g. `pnpm add ai --filter ` or `npm install ai`). Install provider packages (e.g. `@ai-sdk/openai`) and framework packages (e.g. `@ai-sdk/react`) later, when the task requires them. +2. Read and grep the bundled docs at `node_modules/ai/docs/` (or `/node_modules/ai/docs/`) and the source at `node_modules/ai/src/`. +3. Provider and framework packages bundle their own docs at `node_modules/@ai-sdk//docs/` (or `/node_modules/@ai-sdk//docs/`). 4. If something isn't in the bundled docs, search https://ai-sdk.dev/docs. You can append `.md` to any docs page URL to get its markdown, and search via `https://ai-sdk.dev/api/search-docs?q=your_query`. 5. If you cannot find support for an answer in the docs or source, say so explicitly — do not guess. diff --git a/plugins/ai-sdk/skills-lock.json b/plugins/ai-sdk/skills-lock.json index 70f032b3..b745916c 100644 --- a/plugins/ai-sdk/skills-lock.json +++ b/plugins/ai-sdk/skills-lock.json @@ -5,7 +5,7 @@ "source": "vercel/ai", "sourceType": "github", "skillPath": "skills/use-ai-sdk/SKILL.md", - "computedHash": "0883204d11b055e968dee697c5858e68064c27dfd4deb6310212895daa65c5ad" + "computedHash": "aee5bf7ecb066b54eae76799610fa8a9be9ff1ca9a000ea92d17c14ec862938b" } } } diff --git a/plugins/antfu/.agents/skills/antfu/SKILL.md b/plugins/antfu/.agents/skills/antfu/SKILL.md index 0e78e96a..148183c6 100644 --- a/plugins/antfu/.agents/skills/antfu/SKILL.md +++ b/plugins/antfu/.agents/skills/antfu/SKILL.md @@ -3,9 +3,11 @@ name: antfu description: Anthony Fu's opinionated tooling and conventions for JavaScript/TypeScript projects. Use when setting up new projects, configuring ESLint/Prettier alternatives, monorepos, library publishing, or when the user mentions Anthony Fu's preferences. metadata: author: Anthony Fu - version: "2026.06.22" + version: "2026.09.30" --- +> Reference template: [antfu/starter-ts](https://github.com/antfu/starter-ts). When scaffolding a new TypeScript project, mirror its `package.json` scripts, `eslint.config.js`, `knip.json`, `tsdown.config.ts` and workflows rather than inventing a new layout. + ## Coding Practices ### Code Organization @@ -100,31 +102,82 @@ Prefer this over `npm view version` when you only need the latest version, ### ESLint Setup ```js -// eslint.config.mjs +// eslint.config.js import antfu from '@antfu/eslint-config' -export default antfu() +export default antfu({ + type: 'lib', // or 'app' (default) + pnpm: true, // enforce pnpm catalogs in package.json + antislop: true, // flag redundant/duplicated AI-style code, ban explicit `any` +}) ``` - -When completing tasks, run `pnpm run lint --fix` to format the code and fix coding style. +Always enable `antislop: true`. It requires `eslint-plugin-slop` and `eslint-plugin-sonarjs` as dev dependencies. For detailed configuration options: [antfu-eslint-config](references/antfu-eslint-config.md) +### Knip + +Use [Knip](https://knip.dev) to catch unused files, exports and dependencies — the linter only sees one file at a time. + +```json +// knip.json +{ + "$schema": "https://unpkg.com/knip@6/schema.json", + "project": ["src/**/*.ts"], + "ignoreDependencies": [] +} +``` + +Fix what Knip reports by deleting; only add to `ignoreDependencies` for tools invoked outside `package.json` scripts (e.g. `taze`). + +### Scripts and the `ci` Gate + +Every project exposes a `ci` script that runs all checks in one command: + +```json +{ + "scripts": { + "build": "tsdown", + "lint": "eslint --cache", + "typecheck": "tsc", + "knip": "knip", + "test": "pnpm run build && vitest", + "ci": "pnpm run lint && pnpm run typecheck && pnpm run knip && pnpm run test --run" + } +} +``` + +**Before every commit, run `nr lint --fix` to format, then `nr ci` and make sure it passes.** Do not commit with a failing `ci`; fix the root cause instead of silencing rules or adding ignores. + ### Git Hooks +`simple-git-hooks` + `nano-staged`. The pre-commit hook runs the full `ci` gate; `prepare` also installs agent skills via `skills-npm`: + ```json { + "scripts": { + "prepare": "git config core.hooksPath .githooks && simple-git-hooks && skills-npm" + }, "simple-git-hooks": { - "pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && npx lint-staged" + "pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && pnpm run ci && pnpx nano-staged" }, - "lint-staged": { "*": "eslint --fix" }, - "scripts": { - "prepare": "npx simple-git-hooks" + "nano-staged": { + "*": "eslint --fix --no-warn-ignored" } } ``` +Add `.githooks` to `.gitignore` and `simple-git-hooks: true` under `allowBuilds` in `pnpm-workspace.yaml`. + +### skills-npm + +[`skills-npm`](https://github.com/antfu/skills-npm) symlinks agent skills shipped inside installed npm packages (and fetches those declared in a `skills` field) into the agent's skills directory on every install. Install as a dev dependency and wire it into `prepare` as above; add `skills/npm-*` to `.gitignore`. Commit the generated `skills-npm-lock.json`. + +### Publishing + +Prefer npm Trusted Publishing (OIDC) over `NPM_TOKEN` secrets: releases run on CI from a `v*` tag via `release.yml` with `id-token: write`, and `nr release` (`bumpp`) only bumps, tags and pushes. Details: [setting-up](references/setting-up.md#publishing-with-npm-trusted-publishing-oidc). + ### pnpm Catalogs Use named catalogs in `pnpm-workspace.yaml` for version management: @@ -144,8 +197,8 @@ Avoid the default catalog. Catalog names can be adjusted per project needs. | Topic | Description | Reference | |-------|-------------|-----------| -| ESLint Config | Framework support, formatters, rule overrides, VS Code settings | [antfu-eslint-config](references/antfu-eslint-config.md) | -| Project Setup | .gitignore, GitHub Actions, VS Code extensions | [setting-up](references/setting-up.md) | +| ESLint Config | Framework support, antislop, formatters, rule overrides, VS Code settings | [antfu-eslint-config](references/antfu-eslint-config.md) | +| Project Setup | .gitignore, GitHub Actions, OIDC trusted publishing, VS Code extensions | [setting-up](references/setting-up.md) | | App Development | Vue/Nuxt/UnoCSS conventions, auto-import control, Storybook component testing | [app-development](references/app-development.md) | -| Library Development | tsdown bundling, pure ESM publishing | [library-development](references/library-development.md) | +| Library Development | tsdown bundling, pure ESM publishing, publint, API snapshots | [library-development](references/library-development.md) | | Monorepo | pnpm workspaces, centralized alias, Turborepo | [monorepo](references/monorepo.md) | diff --git a/plugins/antfu/.agents/skills/antfu/references/antfu-eslint-config.md b/plugins/antfu/.agents/skills/antfu/references/antfu-eslint-config.md index e92fcba5..9bb734d3 100644 --- a/plugins/antfu/.agents/skills/antfu/references/antfu-eslint-config.md +++ b/plugins/antfu/.agents/skills/antfu/references/antfu-eslint-config.md @@ -18,6 +18,12 @@ export default antfu({ // Project type: 'lib' for libraries, 'app' (default) for applications type: 'lib', + // Enforce pnpm catalogs / workspace settings in package.json + pnpm: true, + + // Always on: catch redundant, duplicated, AI-style code + antislop: true, + // Global ignores (extends defaults, doesn't override) ignores: ['**/fixtures', '**/dist'], @@ -37,6 +43,32 @@ export default antfu({ }) ``` +## Antislop + +```js +export default antfu({ + antislop: true, +}) +// Requires: pnpm add -D eslint-plugin-slop eslint-plugin-sonarjs +``` + +Enables [`eslint-plugin-slop`](https://github.com/antfu/eslint-plugin-slop) plus a curated subset of `eslint-plugin-sonarjs` targeting redundant and duplicated code, and bans explicit `any` when TypeScript is enabled. Enable it on every project; when it flags code, simplify the code rather than disabling the rule. + +To toggle a plugin or scope inspection to recent changes: + +```js +export default antfu({ + antislop: { + sonarjs: false, + slop: { + inspection: { mode: 'recent-changes', tracebackCommits: 5 }, + }, + }, +}) +``` + +Linters see one file at a time, so pair it with [Knip](https://knip.dev) for unused files/exports/dependencies (and `jscpd` for cross-file duplication when needed). + ## Framework Support ### Vue @@ -230,24 +262,28 @@ export default antfu({ }) ``` -## Lint-Staged Setup +## Pre-commit Setup ```json { + "scripts": { + "prepare": "git config core.hooksPath .githooks && simple-git-hooks && skills-npm" + }, "simple-git-hooks": { - "pre-commit": "pnpm lint-staged" + "pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && pnpm run ci && pnpx nano-staged" }, - "lint-staged": { - "*": "eslint --fix" + "nano-staged": { + "*": "eslint --fix --no-warn-ignored" } } ``` ```bash -pnpm add -D lint-staged simple-git-hooks -npx simple-git-hooks +pnpm add -D nano-staged simple-git-hooks skills-npm ``` +The hook runs the full `ci` script (lint, typecheck, knip, tests) before auto-fixing staged files, so a commit never lands with a red check. + ## VS Code Settings Add to `.vscode/settings.json`: diff --git a/plugins/antfu/.agents/skills/antfu/references/library-development.md b/plugins/antfu/.agents/skills/antfu/references/library-development.md index 55f00744..298477bc 100644 --- a/plugins/antfu/.agents/skills/antfu/references/library-development.md +++ b/plugins/antfu/.agents/skills/antfu/references/library-development.md @@ -11,6 +11,8 @@ description: Building and publishing TypeScript libraries with tsdown. Use when | Output | Pure ESM only (no CJS) | | DTS | Generated via tsdown | | Exports | Auto-generated via tsdown | +| Package validation | publint (via tsdown) | +| Publishing | npm Trusted Publishing (OIDC) from CI | ## tsdown Configuration @@ -25,6 +27,7 @@ export default defineConfig({ format: ['esm'], dts: true, exports: true, + publint: true, }) ``` @@ -33,6 +36,7 @@ export default defineConfig({ | `format` | `['esm']` | Pure ESM, no CommonJS | | `dts` | `true` | Generate `.d.ts` files | | `exports` | `true` | Auto-update `exports` field in `package.json` | +| `publint` | `true` | Validate `package.json`/`exports` on every build (requires `publint` dev dependency) | ### Multiple Entry Points @@ -129,21 +133,29 @@ Required fields for pure ESM library: ```json { "type": "module", - "main": "./dist/index.mjs", - "module": "./dist/index.mjs", + "sideEffects": false, "types": "./dist/index.d.mts", "files": ["dist"], "scripts": { "build": "tsdown", - "prepack": "pnpm build", - "test": "vitest", - "release": "bumpp -r" + "dev": "tsdown --watch", + "lint": "eslint --cache", + "typecheck": "tsc", + "knip": "knip", + "test": "pnpm run build && vitest", + "ci": "pnpm run lint && pnpm run typecheck && pnpm run knip && pnpm run test --run", + "prepack": "nr build", + "release": "bumpp" } } ``` -The `exports` field is managed by tsdown when `exports: true`. +The `exports` field is managed by tsdown when `exports: true`. Use `bumpp -r` in monorepos. ### prepack Script -For each public package, add `"prepack": "pnpm build"` to `scripts`. This ensures the package is automatically built before publishing (e.g., when running `npm publish` or `pnpm publish`). This prevents accidentally publishing stale or missing build artifacts. +For each public package, add `"prepack": "nr build"` to `scripts`. This ensures the package is automatically built before publishing (e.g., when running `npm publish` or `pnpm publish`). This prevents accidentally publishing stale or missing build artifacts. + +### Releasing + +Run `nr ci` first — it must pass. Then `nr release`: `bumpp` bumps the version, commits, tags and pushes. The `v*` tag triggers `release.yml`, which builds and publishes via npm Trusted Publishing (OIDC). Do not publish from a local machine after the initial package creation; see [setting-up](setting-up.md#publishing-with-npm-trusted-publishing-oidc). diff --git a/plugins/antfu/.agents/skills/antfu/references/setting-up.md b/plugins/antfu/.agents/skills/antfu/references/setting-up.md index da3e6faa..f4541227 100644 --- a/plugins/antfu/.agents/skills/antfu/references/setting-up.md +++ b/plugins/antfu/.agents/skills/antfu/references/setting-up.md @@ -1,10 +1,12 @@ --- name: setting-up -description: Project setup files including .gitignore, GitHub Actions workflows, and VS Code extensions. Use when initializing new projects or adding CI/editor config. +description: Project setup files including .gitignore, GitHub Actions workflows, npm OIDC trusted publishing, and VS Code extensions. Use when initializing new projects or adding CI/editor/release config. --- # Project Setup +Start from [antfu/starter-ts](https://github.com/antfu/starter-ts); the files below mirror it. + ## .gitignore Create when `.gitignore` is not present: @@ -15,6 +17,7 @@ Create when `.gitignore` is not present: .cache .DS_Store .eslintcache +.githooks .idea .env .nuxt @@ -27,6 +30,7 @@ dist lib-cov logs node_modules +skills/npm-* temp ``` @@ -50,12 +54,12 @@ jobs: contents: read ``` -### Unit Test Workflow +### CI Workflow -**`.github/workflows/unit-test.yml`** - Run tests on push/PR: +**`.github/workflows/ci.yml`** - Run the checks on push/PR: ```yaml -name: Unit Test +name: CI on: push: @@ -92,6 +96,17 @@ jobs: id-token: write ``` +## Publishing with npm Trusted Publishing (OIDC) + +Prefer [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers) over long-lived `NPM_TOKEN` secrets. The workflow above authenticates with a short-lived GitHub OIDC token (`id-token: write`), so there is nothing to leak or rotate, and every release is built on CI from a tagged commit. + +One-time setup per package: + +1. Run `pnpm publish` locally once so the package exists on npm. +2. Open `https://www.npmjs.com/package//access` and link the GitHub repository + `release.yml` workflow as a trusted publisher. + +After that, `nr release` (`bumpp`) bumps the version, commits, tags and pushes; the tag triggers `release.yml`, which publishes. Never add an `NPM_TOKEN` secret to new projects, and don't run `npm publish` from a local machine for subsequent releases. + ## VS Code Extensions Configure in `.vscode/extensions.json`: diff --git a/plugins/antfu/agent/skills/antfu/SKILL.md b/plugins/antfu/agent/skills/antfu/SKILL.md index e4c8ed3a..107be794 100644 --- a/plugins/antfu/agent/skills/antfu/SKILL.md +++ b/plugins/antfu/agent/skills/antfu/SKILL.md @@ -6,8 +6,10 @@ description: Anthony Fu's opinionated tooling and conventions for mentions Anthony Fu's preferences. metadata: author: Anthony Fu - version: 2026.06.22 + version: 2026.09.30 --- +> Reference template: [antfu/starter-ts](https://github.com/antfu/starter-ts). When scaffolding a new TypeScript project, mirror its `package.json` scripts, `eslint.config.js`, `knip.json`, `tsdown.config.ts` and workflows rather than inventing a new layout. + ## Coding Practices ### Code Organization @@ -102,31 +104,82 @@ Prefer this over `npm view version` when you only need the latest version, ### ESLint Setup ```js -// eslint.config.mjs +// eslint.config.js import antfu from '@antfu/eslint-config' -export default antfu() +export default antfu({ + type: 'lib', // or 'app' (default) + pnpm: true, // enforce pnpm catalogs in package.json + antislop: true, // flag redundant/duplicated AI-style code, ban explicit `any` +}) ``` - -When completing tasks, run `pnpm run lint --fix` to format the code and fix coding style. +Always enable `antislop: true`. It requires `eslint-plugin-slop` and `eslint-plugin-sonarjs` as dev dependencies. For detailed configuration options: [antfu-eslint-config](references/antfu-eslint-config.md) +### Knip + +Use [Knip](https://knip.dev) to catch unused files, exports and dependencies — the linter only sees one file at a time. + +```json +// knip.json +{ + "$schema": "https://unpkg.com/knip@6/schema.json", + "project": ["src/**/*.ts"], + "ignoreDependencies": [] +} +``` + +Fix what Knip reports by deleting; only add to `ignoreDependencies` for tools invoked outside `package.json` scripts (e.g. `taze`). + +### Scripts and the `ci` Gate + +Every project exposes a `ci` script that runs all checks in one command: + +```json +{ + "scripts": { + "build": "tsdown", + "lint": "eslint --cache", + "typecheck": "tsc", + "knip": "knip", + "test": "pnpm run build && vitest", + "ci": "pnpm run lint && pnpm run typecheck && pnpm run knip && pnpm run test --run" + } +} +``` + +**Before every commit, run `nr lint --fix` to format, then `nr ci` and make sure it passes.** Do not commit with a failing `ci`; fix the root cause instead of silencing rules or adding ignores. + ### Git Hooks +`simple-git-hooks` + `nano-staged`. The pre-commit hook runs the full `ci` gate; `prepare` also installs agent skills via `skills-npm`: + ```json { + "scripts": { + "prepare": "git config core.hooksPath .githooks && simple-git-hooks && skills-npm" + }, "simple-git-hooks": { - "pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && npx lint-staged" + "pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && pnpm run ci && pnpx nano-staged" }, - "lint-staged": { "*": "eslint --fix" }, - "scripts": { - "prepare": "npx simple-git-hooks" + "nano-staged": { + "*": "eslint --fix --no-warn-ignored" } } ``` +Add `.githooks` to `.gitignore` and `simple-git-hooks: true` under `allowBuilds` in `pnpm-workspace.yaml`. + +### skills-npm + +[`skills-npm`](https://github.com/antfu/skills-npm) symlinks agent skills shipped inside installed npm packages (and fetches those declared in a `skills` field) into the agent's skills directory on every install. Install as a dev dependency and wire it into `prepare` as above; add `skills/npm-*` to `.gitignore`. Commit the generated `skills-npm-lock.json`. + +### Publishing + +Prefer npm Trusted Publishing (OIDC) over `NPM_TOKEN` secrets: releases run on CI from a `v*` tag via `release.yml` with `id-token: write`, and `nr release` (`bumpp`) only bumps, tags and pushes. Details: [setting-up](references/setting-up.md#publishing-with-npm-trusted-publishing-oidc). + ### pnpm Catalogs Use named catalogs in `pnpm-workspace.yaml` for version management: @@ -146,8 +199,8 @@ Avoid the default catalog. Catalog names can be adjusted per project needs. | Topic | Description | Reference | |-------|-------------|-----------| -| ESLint Config | Framework support, formatters, rule overrides, VS Code settings | [antfu-eslint-config](references/antfu-eslint-config.md) | -| Project Setup | .gitignore, GitHub Actions, VS Code extensions | [setting-up](references/setting-up.md) | +| ESLint Config | Framework support, antislop, formatters, rule overrides, VS Code settings | [antfu-eslint-config](references/antfu-eslint-config.md) | +| Project Setup | .gitignore, GitHub Actions, OIDC trusted publishing, VS Code extensions | [setting-up](references/setting-up.md) | | App Development | Vue/Nuxt/UnoCSS conventions, auto-import control, Storybook component testing | [app-development](references/app-development.md) | -| Library Development | tsdown bundling, pure ESM publishing | [library-development](references/library-development.md) | +| Library Development | tsdown bundling, pure ESM publishing, publint, API snapshots | [library-development](references/library-development.md) | | Monorepo | pnpm workspaces, centralized alias, Turborepo | [monorepo](references/monorepo.md) | diff --git a/plugins/antfu/agent/skills/antfu/references/antfu-eslint-config.md b/plugins/antfu/agent/skills/antfu/references/antfu-eslint-config.md index e92fcba5..9bb734d3 100644 --- a/plugins/antfu/agent/skills/antfu/references/antfu-eslint-config.md +++ b/plugins/antfu/agent/skills/antfu/references/antfu-eslint-config.md @@ -18,6 +18,12 @@ export default antfu({ // Project type: 'lib' for libraries, 'app' (default) for applications type: 'lib', + // Enforce pnpm catalogs / workspace settings in package.json + pnpm: true, + + // Always on: catch redundant, duplicated, AI-style code + antislop: true, + // Global ignores (extends defaults, doesn't override) ignores: ['**/fixtures', '**/dist'], @@ -37,6 +43,32 @@ export default antfu({ }) ``` +## Antislop + +```js +export default antfu({ + antislop: true, +}) +// Requires: pnpm add -D eslint-plugin-slop eslint-plugin-sonarjs +``` + +Enables [`eslint-plugin-slop`](https://github.com/antfu/eslint-plugin-slop) plus a curated subset of `eslint-plugin-sonarjs` targeting redundant and duplicated code, and bans explicit `any` when TypeScript is enabled. Enable it on every project; when it flags code, simplify the code rather than disabling the rule. + +To toggle a plugin or scope inspection to recent changes: + +```js +export default antfu({ + antislop: { + sonarjs: false, + slop: { + inspection: { mode: 'recent-changes', tracebackCommits: 5 }, + }, + }, +}) +``` + +Linters see one file at a time, so pair it with [Knip](https://knip.dev) for unused files/exports/dependencies (and `jscpd` for cross-file duplication when needed). + ## Framework Support ### Vue @@ -230,24 +262,28 @@ export default antfu({ }) ``` -## Lint-Staged Setup +## Pre-commit Setup ```json { + "scripts": { + "prepare": "git config core.hooksPath .githooks && simple-git-hooks && skills-npm" + }, "simple-git-hooks": { - "pre-commit": "pnpm lint-staged" + "pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && pnpm run ci && pnpx nano-staged" }, - "lint-staged": { - "*": "eslint --fix" + "nano-staged": { + "*": "eslint --fix --no-warn-ignored" } } ``` ```bash -pnpm add -D lint-staged simple-git-hooks -npx simple-git-hooks +pnpm add -D nano-staged simple-git-hooks skills-npm ``` +The hook runs the full `ci` script (lint, typecheck, knip, tests) before auto-fixing staged files, so a commit never lands with a red check. + ## VS Code Settings Add to `.vscode/settings.json`: diff --git a/plugins/antfu/agent/skills/antfu/references/library-development.md b/plugins/antfu/agent/skills/antfu/references/library-development.md index 55f00744..298477bc 100644 --- a/plugins/antfu/agent/skills/antfu/references/library-development.md +++ b/plugins/antfu/agent/skills/antfu/references/library-development.md @@ -11,6 +11,8 @@ description: Building and publishing TypeScript libraries with tsdown. Use when | Output | Pure ESM only (no CJS) | | DTS | Generated via tsdown | | Exports | Auto-generated via tsdown | +| Package validation | publint (via tsdown) | +| Publishing | npm Trusted Publishing (OIDC) from CI | ## tsdown Configuration @@ -25,6 +27,7 @@ export default defineConfig({ format: ['esm'], dts: true, exports: true, + publint: true, }) ``` @@ -33,6 +36,7 @@ export default defineConfig({ | `format` | `['esm']` | Pure ESM, no CommonJS | | `dts` | `true` | Generate `.d.ts` files | | `exports` | `true` | Auto-update `exports` field in `package.json` | +| `publint` | `true` | Validate `package.json`/`exports` on every build (requires `publint` dev dependency) | ### Multiple Entry Points @@ -129,21 +133,29 @@ Required fields for pure ESM library: ```json { "type": "module", - "main": "./dist/index.mjs", - "module": "./dist/index.mjs", + "sideEffects": false, "types": "./dist/index.d.mts", "files": ["dist"], "scripts": { "build": "tsdown", - "prepack": "pnpm build", - "test": "vitest", - "release": "bumpp -r" + "dev": "tsdown --watch", + "lint": "eslint --cache", + "typecheck": "tsc", + "knip": "knip", + "test": "pnpm run build && vitest", + "ci": "pnpm run lint && pnpm run typecheck && pnpm run knip && pnpm run test --run", + "prepack": "nr build", + "release": "bumpp" } } ``` -The `exports` field is managed by tsdown when `exports: true`. +The `exports` field is managed by tsdown when `exports: true`. Use `bumpp -r` in monorepos. ### prepack Script -For each public package, add `"prepack": "pnpm build"` to `scripts`. This ensures the package is automatically built before publishing (e.g., when running `npm publish` or `pnpm publish`). This prevents accidentally publishing stale or missing build artifacts. +For each public package, add `"prepack": "nr build"` to `scripts`. This ensures the package is automatically built before publishing (e.g., when running `npm publish` or `pnpm publish`). This prevents accidentally publishing stale or missing build artifacts. + +### Releasing + +Run `nr ci` first — it must pass. Then `nr release`: `bumpp` bumps the version, commits, tags and pushes. The `v*` tag triggers `release.yml`, which builds and publishes via npm Trusted Publishing (OIDC). Do not publish from a local machine after the initial package creation; see [setting-up](setting-up.md#publishing-with-npm-trusted-publishing-oidc). diff --git a/plugins/antfu/agent/skills/antfu/references/setting-up.md b/plugins/antfu/agent/skills/antfu/references/setting-up.md index da3e6faa..f4541227 100644 --- a/plugins/antfu/agent/skills/antfu/references/setting-up.md +++ b/plugins/antfu/agent/skills/antfu/references/setting-up.md @@ -1,10 +1,12 @@ --- name: setting-up -description: Project setup files including .gitignore, GitHub Actions workflows, and VS Code extensions. Use when initializing new projects or adding CI/editor config. +description: Project setup files including .gitignore, GitHub Actions workflows, npm OIDC trusted publishing, and VS Code extensions. Use when initializing new projects or adding CI/editor/release config. --- # Project Setup +Start from [antfu/starter-ts](https://github.com/antfu/starter-ts); the files below mirror it. + ## .gitignore Create when `.gitignore` is not present: @@ -15,6 +17,7 @@ Create when `.gitignore` is not present: .cache .DS_Store .eslintcache +.githooks .idea .env .nuxt @@ -27,6 +30,7 @@ dist lib-cov logs node_modules +skills/npm-* temp ``` @@ -50,12 +54,12 @@ jobs: contents: read ``` -### Unit Test Workflow +### CI Workflow -**`.github/workflows/unit-test.yml`** - Run tests on push/PR: +**`.github/workflows/ci.yml`** - Run the checks on push/PR: ```yaml -name: Unit Test +name: CI on: push: @@ -92,6 +96,17 @@ jobs: id-token: write ``` +## Publishing with npm Trusted Publishing (OIDC) + +Prefer [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers) over long-lived `NPM_TOKEN` secrets. The workflow above authenticates with a short-lived GitHub OIDC token (`id-token: write`), so there is nothing to leak or rotate, and every release is built on CI from a tagged commit. + +One-time setup per package: + +1. Run `pnpm publish` locally once so the package exists on npm. +2. Open `https://www.npmjs.com/package//access` and link the GitHub repository + `release.yml` workflow as a trusted publisher. + +After that, `nr release` (`bumpp`) bumps the version, commits, tags and pushes; the tag triggers `release.yml`, which publishes. Never add an `NPM_TOKEN` secret to new projects, and don't run `npm publish` from a local machine for subsequent releases. + ## VS Code Extensions Configure in `.vscode/extensions.json`: diff --git a/plugins/antfu/skills-lock.json b/plugins/antfu/skills-lock.json index 2aaebe12..807e63eb 100644 --- a/plugins/antfu/skills-lock.json +++ b/plugins/antfu/skills-lock.json @@ -5,7 +5,7 @@ "source": "antfu/skills", "sourceType": "github", "skillPath": "skills/antfu/SKILL.md", - "computedHash": "0aada58e7385bcc85abfd1d184ed7cf3757e1eb9e23d018e44ee6022093c5726" + "computedHash": "46935e4dc837849a1df6fa968c3ec32c647bd0faecf34f7a3577503f85d7bc52" } } } diff --git a/plugins/chat-sdk/.agents/skills/chat-sdk/SKILL.md b/plugins/chat-sdk/.agents/skills/chat-sdk/SKILL.md index 4e81c195..da786b3f 100644 --- a/plugins/chat-sdk/.agents/skills/chat-sdk/SKILL.md +++ b/plugins/chat-sdk/.agents/skills/chat-sdk/SKILL.md @@ -58,14 +58,15 @@ Listed in `node_modules/chat/resources/templates.json`: See the 'Official Adapters', 'Vendor-Official Adapters', and 'Community Adapters' sections in the [Chat SDK llms.txt file](https://chat-sdk.dev/llms.txt) for the current list of official, vendor-official, and community adapters. -### Adapter catalog subpath +### Adapter catalog package -Chat SDK exposes a zero-dependency static catalog at `chat/adapters`. +`@chat-adapter/catalog` is a zero-dependency static catalog of official, vendor-official, and community adapters. It replaces the deprecated `chat/adapters` subpath, which still re-exports the original API. -Agents can import `ADAPTERS`, `ADAPTER_NAMES`, `getAdapter`, `isAdapterSlug`, `listEnvVars`, `getSecretEnvVars`, and metadata types like `CatalogAdapter` and `AdapterSlug` from this subpath without importing any adapter implementation package. +Agents can import `listAdapters`, `getCatalogEntry`, `getFeatureSupport`, `ADAPTERS`, `ADAPTER_NAMES`, `getAdapter`, `isAdapterSlug`, `listEnvVars`, `getSecretEnvVars`, and metadata types like `CatalogAdapter`, `CatalogEntry`, and `AdapterSlug` without importing any adapter implementation package. Use it for: -- Listing official and vendor-official adapter slugs, names, npm packages, groups, and platform vs state types. +- Listing adapter slugs, names, npm packages, maintainers, groups, and platform vs state types. +- Checking which capabilities an adapter supports, such as streaming, modals, or distributed locking. - Building setup or onboarding flows that need package names, peer dependencies, and install guidance before any adapter is installed. -- Discovering required, optional, and credential-mode environment variables for an adapter, including which variables are secrets. -- Keeping vendor-official adapter docs and metadata aligned with the catalog when adding or updating a listed adapter. \ No newline at end of file +- Discovering required, optional, and credential-mode environment variables for official and vendor-official adapters, including which variables are secrets. +- Keeping adapter docs and metadata aligned with the catalog when adding or updating a listed adapter. \ No newline at end of file diff --git a/plugins/chat-sdk/agent/skills/chat-sdk/SKILL.md b/plugins/chat-sdk/agent/skills/chat-sdk/SKILL.md index 1f50ea90..d98c495e 100644 --- a/plugins/chat-sdk/agent/skills/chat-sdk/SKILL.md +++ b/plugins/chat-sdk/agent/skills/chat-sdk/SKILL.md @@ -63,14 +63,15 @@ Listed in `node_modules/chat/resources/templates.json`: See the 'Official Adapters', 'Vendor-Official Adapters', and 'Community Adapters' sections in the [Chat SDK llms.txt file](https://chat-sdk.dev/llms.txt) for the current list of official, vendor-official, and community adapters. -### Adapter catalog subpath +### Adapter catalog package -Chat SDK exposes a zero-dependency static catalog at `chat/adapters`. +`@chat-adapter/catalog` is a zero-dependency static catalog of official, vendor-official, and community adapters. It replaces the deprecated `chat/adapters` subpath, which still re-exports the original API. -Agents can import `ADAPTERS`, `ADAPTER_NAMES`, `getAdapter`, `isAdapterSlug`, `listEnvVars`, `getSecretEnvVars`, and metadata types like `CatalogAdapter` and `AdapterSlug` from this subpath without importing any adapter implementation package. +Agents can import `listAdapters`, `getCatalogEntry`, `getFeatureSupport`, `ADAPTERS`, `ADAPTER_NAMES`, `getAdapter`, `isAdapterSlug`, `listEnvVars`, `getSecretEnvVars`, and metadata types like `CatalogAdapter`, `CatalogEntry`, and `AdapterSlug` without importing any adapter implementation package. Use it for: -- Listing official and vendor-official adapter slugs, names, npm packages, groups, and platform vs state types. +- Listing adapter slugs, names, npm packages, maintainers, groups, and platform vs state types. +- Checking which capabilities an adapter supports, such as streaming, modals, or distributed locking. - Building setup or onboarding flows that need package names, peer dependencies, and install guidance before any adapter is installed. -- Discovering required, optional, and credential-mode environment variables for an adapter, including which variables are secrets. -- Keeping vendor-official adapter docs and metadata aligned with the catalog when adding or updating a listed adapter. \ No newline at end of file +- Discovering required, optional, and credential-mode environment variables for official and vendor-official adapters, including which variables are secrets. +- Keeping adapter docs and metadata aligned with the catalog when adding or updating a listed adapter. \ No newline at end of file diff --git a/plugins/chat-sdk/skills-lock.json b/plugins/chat-sdk/skills-lock.json index 27638740..7e529544 100644 --- a/plugins/chat-sdk/skills-lock.json +++ b/plugins/chat-sdk/skills-lock.json @@ -5,7 +5,7 @@ "source": "vercel/chat", "sourceType": "github", "skillPath": "skills/chat/SKILL.md", - "computedHash": "dd2e9e18aa536e4f2625a6830247e4f593ba0c65ba7527365c18375c2cc618bd" + "computedHash": "15ee91013fc51b443d2de8f0e81831b4f45d73ef59f0028472eeef62fd2dd22d" } } } diff --git a/plugins/edgeparse/.agents/skills/edgeparse/SKILL.md b/plugins/edgeparse/.agents/skills/edgeparse/SKILL.md index 0c594b6c..54329ef2 100644 --- a/plugins/edgeparse/.agents/skills/edgeparse/SKILL.md +++ b/plugins/edgeparse/.agents/skills/edgeparse/SKILL.md @@ -1,6 +1,6 @@ --- name: edgeparse -description: Extract structured content from any PDF for AI agents, RAG pipelines, and Copilot Skills. Use this skill whenever the user wants to read, analyze, or reason about a PDF document; needs to feed document content to an LLM; mentions PDF extraction, parsing, or conversion; wants tables, headings, or bounding boxes from a PDF; is building a RAG pipeline; or asks an agent to process a document. Install with: pip install edgeparse +description: "Extract structured content from any PDF for AI agents, RAG pipelines, and Copilot Skills. Use this skill whenever the user wants to read, analyze, or reason about a PDF document; needs to feed document content to an LLM; mentions PDF extraction, parsing, or conversion; wants tables, headings, or bounding boxes from a PDF; is building a RAG pipeline; or asks an agent to process a document. Install with: pip install edgeparse" license: Apache-2.0 metadata: authors: "EdgeParse Contributors" diff --git a/plugins/edgeparse/skills-lock.json b/plugins/edgeparse/skills-lock.json index 12e742d9..4aa88490 100644 --- a/plugins/edgeparse/skills-lock.json +++ b/plugins/edgeparse/skills-lock.json @@ -3,7 +3,9 @@ "skills": { "edgeparse": { "source": "raphaelmansuy/edgeparse", - "sourceType": "github" + "sourceType": "github", + "skillPath": "skills/edgeparse/SKILL.md", + "computedHash": "e710ee51a1362bd4f707f4041291cc3ddc87b4c31fa3a977af28a3c52f4be24c" } } } diff --git a/plugins/emulate/.agents/skills/apple/SKILL.md b/plugins/emulate/.agents/skills/apple/SKILL.md index f9f0a268..8d119c7f 100644 --- a/plugins/emulate/.agents/skills/apple/SKILL.md +++ b/plugins/emulate/.agents/skills/apple/SKILL.md @@ -10,6 +10,8 @@ Sign in with Apple emulation with authorization code flow, PKCE support, RS256 I ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Apple only npx emulate --service apple @@ -239,3 +241,7 @@ curl -X POST $APPLE_URL/auth/token \ ### Private Relay Email When a user has `is_private_email: true` in the seed config, the `id_token` will contain a generated `@privaterelay.appleid.com` email instead of the user's real email. This matches Apple's Hide My Email behavior. + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "apple" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/.agents/skills/aws/SKILL.md b/plugins/emulate/.agents/skills/aws/SKILL.md index 56511349..51cbbfac 100644 --- a/plugins/emulate/.agents/skills/aws/SKILL.md +++ b/plugins/emulate/.agents/skills/aws/SKILL.md @@ -10,6 +10,8 @@ S3, SQS, IAM, and STS emulation with AWS SDK-compatible S3 paths and query-style ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # AWS only npx emulate --service aws @@ -383,3 +385,7 @@ curl -X POST $BASE/iam/ \ -H "Authorization: Bearer $TOKEN" \ -d "Action=CreateAccessKey&UserName=ci-user" ``` + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "aws" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/.agents/skills/emulate/SKILL.md b/plugins/emulate/.agents/skills/emulate/SKILL.md index ea60d764..bbb75ffc 100644 --- a/plugins/emulate/.agents/skills/emulate/SKILL.md +++ b/plugins/emulate/.agents/skills/emulate/SKILL.md @@ -14,7 +14,7 @@ Local drop-in replacement services for CI and no-network sandboxes. Fully statef npx emulate ``` -All services start with sensible defaults: +All services listen on IPv4 loopback (`127.0.0.1`) by default: | Service | Default Port | |-----------|-------------| @@ -66,6 +66,7 @@ npx emulate list | Flag | Default | Description | |------|---------|-------------| | `-p, --port` | `4000` | Base port (auto-increments per service) | +| `--host` | `127.0.0.1` | Listening address; use `0.0.0.0` to allow network access | | `-s, --service` | all | Comma-separated services to enable | | `--seed` | auto-detect | Path to seed config (YAML or JSON) | | `--base-url` | none | Override advertised base URL (supports `{service}` template) | @@ -74,6 +75,8 @@ npx emulate list The port can also be set via `EMULATE_PORT` or `PORT` environment variables. +For access from a container or another machine, use `npx emulate start --host 0.0.0.0`. Set `--base-url` to a URL reachable by those clients when using OAuth redirects or other advertised URLs. + The generated-secrets destination must not exist. emulate removes inherited ACLs, verifies effective owner-only access, and publishes complete JSON before opening listeners or configuring portless. Handled startup failures remove the invocation-owned artifact. A hard termination can leave a complete artifact that must be removed manually after confirming no invocation is using it. Only service-generated values appear in the artifact. Linux requires `setfacl` and `getfacl` from the `acl` package. The flag fails closed when access controls cannot be verified and is not supported on Windows. The advertised base URL (used in OAuth redirects, webhook URLs, etc.) can be overridden via `--base-url`, the `EMULATE_BASE_URL` env var (supports `{service}` template), or per-service `baseUrl` in the seed config. When running under portless, the `PORTLESS_URL` env var is also detected automatically. @@ -107,9 +110,12 @@ For GitHub App tests, inspect secret-free minted installation-token metadata at |--------|---------|-------------| | `service` | *(required)* | `'vercel'`, `'github'`, `'google'`, `'slack'`, `'apple'`, `'microsoft'`, `'okta'`, `'aws'`, `'resend'`, `'stripe'`, `'mongoatlas'`, `'clerk'`, `'linear'`, or `'twilio'` | | `port` | `4000` | Port for the HTTP server | +| `hostname` | `127.0.0.1` | Listening address for built-in and custom emulators | | `seed` | none | Inline seed data (same shape as YAML config) | | `baseUrl` | none | Override advertised base URL. Per-service `baseUrl` in seed config takes highest priority, then this option, then `EMULATE_BASE_URL` env var (supports `{service}`), then `PORTLESS_URL` (supports `{service}`, automatically set by the `portless` CLI wrapper), then `http://localhost:`. | +Use `hostname: '0.0.0.0'` to allow connections from containers or other machines, and `baseUrl` to advertise a URL reachable by those clients. + ### Instance Methods | Method | Description | diff --git a/plugins/emulate/.agents/skills/github/SKILL.md b/plugins/emulate/.agents/skills/github/SKILL.md index c2039ed7..77e915b1 100644 --- a/plugins/emulate/.agents/skills/github/SKILL.md +++ b/plugins/emulate/.agents/skills/github/SKILL.md @@ -10,6 +10,8 @@ Fully stateful GitHub REST API emulation. Creates, updates, and deletes persist ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # GitHub only npx emulate --service github @@ -633,3 +635,7 @@ The inspection route is emulator-specific. It lists App, installation, account, 3. Emulator redirects back with `?code=...&state=...` 4. Exchange code for token via `POST /login/oauth/access_token` 5. Use token to call API endpoints + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "github" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/.agents/skills/google/SKILL.md b/plugins/emulate/.agents/skills/google/SKILL.md index 77c0bfbe..55817709 100644 --- a/plugins/emulate/.agents/skills/google/SKILL.md +++ b/plugins/emulate/.agents/skills/google/SKILL.md @@ -10,6 +10,8 @@ OAuth 2.0 and OpenID Connect emulation with authorization code flow, PKCE suppor ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Google only npx emulate --service google diff --git a/plugins/emulate/.agents/skills/linear/SKILL.md b/plugins/emulate/.agents/skills/linear/SKILL.md index 2511a752..c4dc8e74 100644 --- a/plugins/emulate/.agents/skills/linear/SKILL.md +++ b/plugins/emulate/.agents/skills/linear/SKILL.md @@ -10,6 +10,8 @@ Stateful Linear GraphQL API emulation with organizations, users, teams, workflow ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Linear only npx emulate --service linear @@ -125,3 +127,7 @@ Open `GET /` in the Linear emulator to inspect issues, teams, users, projects, a ## Current Limits Full Linear schema coverage, exact production rate limiting, notification inbox behavior, rich document APIs, customer APIs, initiative APIs, exact search relevance, and full production agent behavior are not implemented. + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "linear" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/.agents/skills/microsoft/SKILL.md b/plugins/emulate/.agents/skills/microsoft/SKILL.md index b18a5d1f..95b979aa 100644 --- a/plugins/emulate/.agents/skills/microsoft/SKILL.md +++ b/plugins/emulate/.agents/skills/microsoft/SKILL.md @@ -10,6 +10,8 @@ Microsoft Entra ID (Azure AD) v2.0 OAuth 2.0 and OpenID Connect emulation with a ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Microsoft only npx emulate --service microsoft @@ -361,3 +363,7 @@ const client = new microsoftIssuer.Client({ redirect_uris: ['http://localhost:3000/api/auth/callback/microsoft-entra-id'], }) ``` + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "microsoft" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/.agents/skills/next/SKILL.md b/plugins/emulate/.agents/skills/next/SKILL.md index 2f455043..de57f8a3 100644 --- a/plugins/emulate/.agents/skills/next/SKILL.md +++ b/plugins/emulate/.agents/skills/next/SKILL.md @@ -26,7 +26,7 @@ import { createEmulateHandler } from '@emulators/adapter-next' import * as github from '@emulators/github' import * as google from '@emulators/google' -export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({ +export const { GET, POST, PUT, PATCH, DELETE, OPTIONS } = createEmulateHandler({ services: { github: { emulator: github, @@ -108,7 +108,7 @@ const kvAdapter = { async save(data: string) { await kv.set('emulate-state', data) }, } -export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({ +export const { GET, POST, PUT, PATCH, DELETE, OPTIONS } = createEmulateHandler({ services: { github: { emulator: github } }, persistence: kvAdapter, }) @@ -181,3 +181,7 @@ interface PersistenceAdapter { ``` `initialize` must atomically create the initial value or return the value another instance created first. Implement it with compare-and-set semantics such as Redis `SET NX`. The built-in `filePersistence(path)` from `@emulators/core` provides this behavior for local development. + +## Custom emulators + +The adapter accepts definitions created with `defineEmulator` from `emulate` in `services[name].emulator`. Reuse the definition from CLI/tests; custom inspector is opt-in. Root-relative redirect locations stay under the service mount, while custom HTML bodies pass through unchanged. Await the returned handler's `close()` in test teardown. Install `emulate` as a runtime dependency for deployed definitions. See https://emulate.dev/docs/custom-emulators for seeds, snapshots, persistence, and lifecycle rules. diff --git a/plugins/emulate/.agents/skills/resend/SKILL.md b/plugins/emulate/.agents/skills/resend/SKILL.md index 41d84036..50cf1d2d 100644 --- a/plugins/emulate/.agents/skills/resend/SKILL.md +++ b/plugins/emulate/.agents/skills/resend/SKILL.md @@ -12,6 +12,8 @@ No real emails are sent. Every call to `POST /emails` stores the message locally ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Resend only npx emulate --service resend @@ -350,6 +352,6 @@ const { data: emails } = await res.json() console.log(emails[0].html) // contains "123456" ``` -## Custom HTTP APIs alongside built-ins +## Custom emulators alongside built-ins -Use `npx emulate init --custom inventory` to scaffold a custom stateful API and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "resend" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing custom APIs, see https://emulate.dev/docs/custom-apis. +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "resend" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/.agents/skills/slack/SKILL.md b/plugins/emulate/.agents/skills/slack/SKILL.md index 520b7112..75e714f8 100644 --- a/plugins/emulate/.agents/skills/slack/SKILL.md +++ b/plugins/emulate/.agents/skills/slack/SKILL.md @@ -12,6 +12,8 @@ Slack message text is limited to 40,000 Unicode characters across chat writes, i ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Slack only npx emulate --service slack @@ -200,6 +202,8 @@ slack: When no OAuth apps are configured, the emulator accepts any `client_id`. With apps configured, strict validation is enforced for `client_id`, `client_secret`, and `redirect_uri`. +`signing_secret` signs every outbound event subscription callback. Configure the receiving app with the same secret; signed callbacks include `X-Slack-Request-Timestamp` and `X-Slack-Signature`, with `X-Slack-Signature` set to `v0=:")>`. Verify against the unparsed request body. When the secret is absent or empty, callbacks are unsigned. + ## API Endpoints ### Auth @@ -666,7 +670,9 @@ Open `GET /` in the Slack emulator to inspect conversations, messages, files, vi ## Event Dispatching -When supported Slack writes mutate state, the emulator dispatches `event_callback` payloads to configured webhook URLs. These payloads match Slack's Events API format: +For supported Slack writes that emit events, the emulator dispatches `event_callback` payloads to configured webhook URLs. These payloads match Slack's Events API format: + +Each callback includes outer `team_id`, `event_id`, and integer Unix-seconds `event_time` alongside the inner `event`. For authenticated Web API writes, `team_id` comes from the presented Slack token's installation. Development tokens without a stored Slack record fall back to the affected channel, user, or file's team, then the seeded workspace team (or `T000000001`). Incoming webhook posts use their webhook record's team, or the target channel's team when no record matches. Each logical event gets a distinct `event_id` shared by deliveries to multiple subscribers. - `message` events on `chat.postMessage` - `message` with `subtype: message_changed` on `chat.update` @@ -686,6 +692,8 @@ When supported Slack writes mutate state, the emulator dispatches `event_callbac - `file_created`, `file_shared`, and `file_deleted` on file writes - `message` with `subtype: file_share` on shared file uploads +When `slack.signing_secret` is configured, every existing outbound event subscription callback includes `X-Slack-Request-Timestamp` and `X-Slack-Signature`. The signature is `v0=:")>`, using the exact serialized callback body. Configure the receiver with the same secret and verify the unparsed request body. With an absent or empty secret, callbacks are unsigned. + ## Current Limits Slack Connect, Enterprise Grid admin APIs, Audit Logs API, SCIM, Legal Holds, Socket Mode, slash command and interaction simulation, user groups, reminders, stars, calls, canvases, lists, functions, workflows, chat streaming, legacy `files.upload`, exact rate limiting, and paid-plan behavior are not implemented. @@ -729,3 +737,7 @@ curl -X POST http://localhost:4003/services/T000000001/B000000001/X000000001 \ -H "Content-Type: application/json" \ -d '{"text": "Build passed on main"}' ``` + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "slack" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/.agents/skills/stripe/SKILL.md b/plugins/emulate/.agents/skills/stripe/SKILL.md index eb9f779b..0c4603b1 100644 --- a/plugins/emulate/.agents/skills/stripe/SKILL.md +++ b/plugins/emulate/.agents/skills/stripe/SKILL.md @@ -12,6 +12,8 @@ No real payments are processed. Every Stripe SDK call hits the emulator and prod ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Stripe only npx emulate --service stripe diff --git a/plugins/emulate/.agents/skills/vercel/SKILL.md b/plugins/emulate/.agents/skills/vercel/SKILL.md index 0c29a31e..9e2925d2 100644 --- a/plugins/emulate/.agents/skills/vercel/SKILL.md +++ b/plugins/emulate/.agents/skills/vercel/SKILL.md @@ -10,6 +10,8 @@ Fully stateful Vercel REST API emulation with Vercel-style JSON responses and cu ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Vercel only npx emulate --service vercel @@ -483,3 +485,7 @@ curl "http://localhost:4000/v10/projects?teamId=team_abc123" \ curl "http://localhost:4000/v10/projects?slug=my-team" \ -H "Authorization: Bearer $TOKEN" ``` + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "vercel" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/agent/skills/apple/SKILL.md b/plugins/emulate/agent/skills/apple/SKILL.md index 40191caf..3aedb37e 100644 --- a/plugins/emulate/agent/skills/apple/SKILL.md +++ b/plugins/emulate/agent/skills/apple/SKILL.md @@ -14,6 +14,8 @@ Sign in with Apple emulation with authorization code flow, PKCE support, RS256 I ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Apple only npx emulate --service apple @@ -243,3 +245,7 @@ curl -X POST $APPLE_URL/auth/token \ ### Private Relay Email When a user has `is_private_email: true` in the seed config, the `id_token` will contain a generated `@privaterelay.appleid.com` email instead of the user's real email. This matches Apple's Hide My Email behavior. + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "apple" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/agent/skills/aws/SKILL.md b/plugins/emulate/agent/skills/aws/SKILL.md index 460b0832..c4ea7524 100644 --- a/plugins/emulate/agent/skills/aws/SKILL.md +++ b/plugins/emulate/agent/skills/aws/SKILL.md @@ -14,6 +14,8 @@ S3, SQS, IAM, and STS emulation with AWS SDK-compatible S3 paths and query-style ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # AWS only npx emulate --service aws @@ -387,3 +389,7 @@ curl -X POST $BASE/iam/ \ -H "Authorization: Bearer $TOKEN" \ -d "Action=CreateAccessKey&UserName=ci-user" ``` + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "aws" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/agent/skills/emulate/SKILL.md b/plugins/emulate/agent/skills/emulate/SKILL.md index c88d1d36..5ead9599 100644 --- a/plugins/emulate/agent/skills/emulate/SKILL.md +++ b/plugins/emulate/agent/skills/emulate/SKILL.md @@ -19,7 +19,7 @@ Local drop-in replacement services for CI and no-network sandboxes. Fully statef npx emulate ``` -All services start with sensible defaults: +All services listen on IPv4 loopback (`127.0.0.1`) by default: | Service | Default Port | |-----------|-------------| @@ -71,6 +71,7 @@ npx emulate list | Flag | Default | Description | |------|---------|-------------| | `-p, --port` | `4000` | Base port (auto-increments per service) | +| `--host` | `127.0.0.1` | Listening address; use `0.0.0.0` to allow network access | | `-s, --service` | all | Comma-separated services to enable | | `--seed` | auto-detect | Path to seed config (YAML or JSON) | | `--base-url` | none | Override advertised base URL (supports `{service}` template) | @@ -79,6 +80,8 @@ npx emulate list The port can also be set via `EMULATE_PORT` or `PORT` environment variables. +For access from a container or another machine, use `npx emulate start --host 0.0.0.0`. Set `--base-url` to a URL reachable by those clients when using OAuth redirects or other advertised URLs. + The generated-secrets destination must not exist. emulate removes inherited ACLs, verifies effective owner-only access, and publishes complete JSON before opening listeners or configuring portless. Handled startup failures remove the invocation-owned artifact. A hard termination can leave a complete artifact that must be removed manually after confirming no invocation is using it. Only service-generated values appear in the artifact. Linux requires `setfacl` and `getfacl` from the `acl` package. The flag fails closed when access controls cannot be verified and is not supported on Windows. The advertised base URL (used in OAuth redirects, webhook URLs, etc.) can be overridden via `--base-url`, the `EMULATE_BASE_URL` env var (supports `{service}` template), or per-service `baseUrl` in the seed config. When running under portless, the `PORTLESS_URL` env var is also detected automatically. @@ -112,9 +115,12 @@ For GitHub App tests, inspect secret-free minted installation-token metadata at |--------|---------|-------------| | `service` | *(required)* | `'vercel'`, `'github'`, `'google'`, `'slack'`, `'apple'`, `'microsoft'`, `'okta'`, `'aws'`, `'resend'`, `'stripe'`, `'mongoatlas'`, `'clerk'`, `'linear'`, or `'twilio'` | | `port` | `4000` | Port for the HTTP server | +| `hostname` | `127.0.0.1` | Listening address for built-in and custom emulators | | `seed` | none | Inline seed data (same shape as YAML config) | | `baseUrl` | none | Override advertised base URL. Per-service `baseUrl` in seed config takes highest priority, then this option, then `EMULATE_BASE_URL` env var (supports `{service}`), then `PORTLESS_URL` (supports `{service}`, automatically set by the `portless` CLI wrapper), then `http://localhost:`. | +Use `hostname: '0.0.0.0'` to allow connections from containers or other machines, and `baseUrl` to advertise a URL reachable by those clients. + ### Instance Methods | Method | Description | diff --git a/plugins/emulate/agent/skills/github/SKILL.md b/plugins/emulate/agent/skills/github/SKILL.md index fc6bc89e..3709b758 100644 --- a/plugins/emulate/agent/skills/github/SKILL.md +++ b/plugins/emulate/agent/skills/github/SKILL.md @@ -14,6 +14,8 @@ Fully stateful GitHub REST API emulation. Creates, updates, and deletes persist ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # GitHub only npx emulate --service github @@ -637,3 +639,7 @@ The inspection route is emulator-specific. It lists App, installation, account, 3. Emulator redirects back with `?code=...&state=...` 4. Exchange code for token via `POST /login/oauth/access_token` 5. Use token to call API endpoints + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "github" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/agent/skills/google/SKILL.md b/plugins/emulate/agent/skills/google/SKILL.md index 54a18d02..f0871ec2 100644 --- a/plugins/emulate/agent/skills/google/SKILL.md +++ b/plugins/emulate/agent/skills/google/SKILL.md @@ -16,6 +16,8 @@ OAuth 2.0 and OpenID Connect emulation with authorization code flow, PKCE suppor ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Google only npx emulate --service google diff --git a/plugins/emulate/agent/skills/linear/SKILL.md b/plugins/emulate/agent/skills/linear/SKILL.md index 2b2ff491..d0276ab6 100644 --- a/plugins/emulate/agent/skills/linear/SKILL.md +++ b/plugins/emulate/agent/skills/linear/SKILL.md @@ -14,6 +14,8 @@ Stateful Linear GraphQL API emulation with organizations, users, teams, workflow ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Linear only npx emulate --service linear @@ -129,3 +131,7 @@ Open `GET /` in the Linear emulator to inspect issues, teams, users, projects, a ## Current Limits Full Linear schema coverage, exact production rate limiting, notification inbox behavior, rich document APIs, customer APIs, initiative APIs, exact search relevance, and full production agent behavior are not implemented. + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "linear" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/agent/skills/microsoft/SKILL.md b/plugins/emulate/agent/skills/microsoft/SKILL.md index c89433e4..cee4a9eb 100644 --- a/plugins/emulate/agent/skills/microsoft/SKILL.md +++ b/plugins/emulate/agent/skills/microsoft/SKILL.md @@ -16,6 +16,8 @@ Microsoft Entra ID (Azure AD) v2.0 OAuth 2.0 and OpenID Connect emulation with a ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Microsoft only npx emulate --service microsoft @@ -367,3 +369,7 @@ const client = new microsoftIssuer.Client({ redirect_uris: ['http://localhost:3000/api/auth/callback/microsoft-entra-id'], }) ``` + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "microsoft" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/agent/skills/next/SKILL.md b/plugins/emulate/agent/skills/next/SKILL.md index fe756b35..1041fcc8 100644 --- a/plugins/emulate/agent/skills/next/SKILL.md +++ b/plugins/emulate/agent/skills/next/SKILL.md @@ -31,7 +31,7 @@ import { createEmulateHandler } from '@emulators/adapter-next' import * as github from '@emulators/github' import * as google from '@emulators/google' -export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({ +export const { GET, POST, PUT, PATCH, DELETE, OPTIONS } = createEmulateHandler({ services: { github: { emulator: github, @@ -113,7 +113,7 @@ const kvAdapter = { async save(data: string) { await kv.set('emulate-state', data) }, } -export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({ +export const { GET, POST, PUT, PATCH, DELETE, OPTIONS } = createEmulateHandler({ services: { github: { emulator: github } }, persistence: kvAdapter, }) @@ -186,3 +186,7 @@ interface PersistenceAdapter { ``` `initialize` must atomically create the initial value or return the value another instance created first. Implement it with compare-and-set semantics such as Redis `SET NX`. The built-in `filePersistence(path)` from `@emulators/core` provides this behavior for local development. + +## Custom emulators + +The adapter accepts definitions created with `defineEmulator` from `emulate` in `services[name].emulator`. Reuse the definition from CLI/tests; custom inspector is opt-in. Root-relative redirect locations stay under the service mount, while custom HTML bodies pass through unchanged. Await the returned handler's `close()` in test teardown. Install `emulate` as a runtime dependency for deployed definitions. See https://emulate.dev/docs/custom-emulators for seeds, snapshots, persistence, and lifecycle rules. diff --git a/plugins/emulate/agent/skills/resend/SKILL.md b/plugins/emulate/agent/skills/resend/SKILL.md index 34784cb5..1bf66ae0 100644 --- a/plugins/emulate/agent/skills/resend/SKILL.md +++ b/plugins/emulate/agent/skills/resend/SKILL.md @@ -16,6 +16,8 @@ No real emails are sent. Every call to `POST /emails` stores the message locally ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Resend only npx emulate --service resend @@ -354,6 +356,6 @@ const { data: emails } = await res.json() console.log(emails[0].html) // contains "123456" ``` -## Custom HTTP APIs alongside built-ins +## Custom emulators alongside built-ins -Use `npx emulate init --custom inventory` to scaffold a custom stateful API and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "resend" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing custom APIs, see https://emulate.dev/docs/custom-apis. +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "resend" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/agent/skills/slack/SKILL.md b/plugins/emulate/agent/skills/slack/SKILL.md index 9cb115a4..a95428a9 100644 --- a/plugins/emulate/agent/skills/slack/SKILL.md +++ b/plugins/emulate/agent/skills/slack/SKILL.md @@ -16,6 +16,8 @@ Slack message text is limited to 40,000 Unicode characters across chat writes, i ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Slack only npx emulate --service slack @@ -204,6 +206,8 @@ slack: When no OAuth apps are configured, the emulator accepts any `client_id`. With apps configured, strict validation is enforced for `client_id`, `client_secret`, and `redirect_uri`. +`signing_secret` signs every outbound event subscription callback. Configure the receiving app with the same secret; signed callbacks include `X-Slack-Request-Timestamp` and `X-Slack-Signature`, with `X-Slack-Signature` set to `v0=:")>`. Verify against the unparsed request body. When the secret is absent or empty, callbacks are unsigned. + ## API Endpoints ### Auth @@ -670,7 +674,9 @@ Open `GET /` in the Slack emulator to inspect conversations, messages, files, vi ## Event Dispatching -When supported Slack writes mutate state, the emulator dispatches `event_callback` payloads to configured webhook URLs. These payloads match Slack's Events API format: +For supported Slack writes that emit events, the emulator dispatches `event_callback` payloads to configured webhook URLs. These payloads match Slack's Events API format: + +Each callback includes outer `team_id`, `event_id`, and integer Unix-seconds `event_time` alongside the inner `event`. For authenticated Web API writes, `team_id` comes from the presented Slack token's installation. Development tokens without a stored Slack record fall back to the affected channel, user, or file's team, then the seeded workspace team (or `T000000001`). Incoming webhook posts use their webhook record's team, or the target channel's team when no record matches. Each logical event gets a distinct `event_id` shared by deliveries to multiple subscribers. - `message` events on `chat.postMessage` - `message` with `subtype: message_changed` on `chat.update` @@ -690,6 +696,8 @@ When supported Slack writes mutate state, the emulator dispatches `event_callbac - `file_created`, `file_shared`, and `file_deleted` on file writes - `message` with `subtype: file_share` on shared file uploads +When `slack.signing_secret` is configured, every existing outbound event subscription callback includes `X-Slack-Request-Timestamp` and `X-Slack-Signature`. The signature is `v0=:")>`, using the exact serialized callback body. Configure the receiver with the same secret and verify the unparsed request body. With an absent or empty secret, callbacks are unsigned. + ## Current Limits Slack Connect, Enterprise Grid admin APIs, Audit Logs API, SCIM, Legal Holds, Socket Mode, slash command and interaction simulation, user groups, reminders, stars, calls, canvases, lists, functions, workflows, chat streaming, legacy `files.upload`, exact rate limiting, and paid-plan behavior are not implemented. @@ -733,3 +741,7 @@ curl -X POST http://localhost:4003/services/T000000001/B000000001/X000000001 \ -H "Content-Type: application/json" \ -d '{"text": "Build passed on main"}' ``` + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "slack" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/agent/skills/stripe/SKILL.md b/plugins/emulate/agent/skills/stripe/SKILL.md index 009b19e8..c14c2690 100644 --- a/plugins/emulate/agent/skills/stripe/SKILL.md +++ b/plugins/emulate/agent/skills/stripe/SKILL.md @@ -16,6 +16,8 @@ No real payments are processed. Every Stripe SDK call hits the emulator and prod ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Stripe only npx emulate --service stripe diff --git a/plugins/emulate/agent/skills/vercel/SKILL.md b/plugins/emulate/agent/skills/vercel/SKILL.md index 7118721c..583226ad 100644 --- a/plugins/emulate/agent/skills/vercel/SKILL.md +++ b/plugins/emulate/agent/skills/vercel/SKILL.md @@ -15,6 +15,8 @@ Fully stateful Vercel REST API emulation with Vercel-style JSON responses and cu ## Start +Local listeners bind to `127.0.0.1` by default. For access from containers or other machines, pass `--host 0.0.0.0` to the CLI or `hostname: '0.0.0.0'` to `createEmulator`. Configure `--base-url` or `baseUrl` for advertised URLs reachable by those clients. + ```bash # Vercel only npx emulate --service vercel @@ -488,3 +490,7 @@ curl "http://localhost:4000/v10/projects?teamId=team_abc123" \ curl "http://localhost:4000/v10/projects?slug=my-team" \ -H "Authorization: Bearer $TOKEN" ``` + +## Custom emulators alongside built-ins + +Use `npx emulate init --custom inventory` to scaffold a third-party API emulator and test. Register it in `emulate.config.ts` with `defineConfig` from `emulate`, alongside built-in entries such as `{ emulator: "vercel" }`. Run `npx emulate start --watch` to reload imports and inspect custom state at the printed `/_emulate` URL. Successful reloads reset the run to seed. Existing flat seed configs still work; `--config` selects an explicit file. For authoring and testing third-party API emulators, see https://emulate.dev/docs/custom-emulators. diff --git a/plugins/emulate/skills-lock.json b/plugins/emulate/skills-lock.json index 7301b885..a9aec46e 100644 --- a/plugins/emulate/skills-lock.json +++ b/plugins/emulate/skills-lock.json @@ -5,73 +5,73 @@ "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/apple/SKILL.md", - "computedHash": "14b0aeaebc8a8a8056f9f4f0babd39ecb168ab5e1cc89281e7fb64fc19ac48ee" + "computedHash": "69ab6f2682bfd5090e6b268ea0fd6e274c9c48e2ca4100f32e6e3b0f6946a10e" }, "aws": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/aws/SKILL.md", - "computedHash": "735781aaf3e694d6872280bacaf9ef132da68322ef21ea5f3f63a073c6065a4b" + "computedHash": "43e65db5ace38a2ccf20b3fe09f43b79396f58daf88a3929f19bc074a6befe5f" }, "emulate": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/emulate/SKILL.md", - "computedHash": "ebc9ed29875d7e356c9d9299b218bfdfe3583afad84e4b1ccf07adc91f908b61" + "computedHash": "f9f00dad70d82ec377bc6e0faf765b52bcde1784bc89905997a9ecfe553ea358" }, "github": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/github/SKILL.md", - "computedHash": "b41f5efdebb56ee6965d9599b68bd40f946c95386af198e05ffa77d378be59c7" + "computedHash": "9601905e146e70ed5b37a6ebf201ce52f1d30f8ae3724abbfbd8e090c8655f64" }, "google": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/google/SKILL.md", - "computedHash": "a80de94e5149e55daa865f7d5da60bdfd63f46c82682b0eb91ebb3db151c7a8e" + "computedHash": "e37d29cb245520e6303a45cf2dd9fea332e9f0c90db11421a8f6d5190f3cf62f" }, "linear": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/linear/SKILL.md", - "computedHash": "0959a13f8b49f4a32bcf3910a57a0c271b4ee1a68de9d35e44e5850ddbb64cb6" + "computedHash": "48d3b6c7869a98661c5bbd3a7b7df09ca4bbbae6b86450d6e57853edee822316" }, "microsoft": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/microsoft/SKILL.md", - "computedHash": "87bb1d04f8d6aaf21700d033cc71fd1150c5a8e8a397a235278b4b226ce25c2d" + "computedHash": "028538190fd0e34e00df9ba026a9d1f165c5d2388f7c71a6a2a92c8ed285b461" }, "next": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/next/SKILL.md", - "computedHash": "3f24bb5fea72610b2412ad6bc835ae913063e8e438ade72fc5a065e92b416e87" + "computedHash": "c581384e91811b427855b502280ee3fb143ed3b0d29c409afb0290c12697eb9a" }, "resend": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/resend/SKILL.md", - "computedHash": "56161171fb5dc02b27b323c258976a541abd76141e3f85c7001a9f97bb10d20c" + "computedHash": "eb8e09197e6acd002cdeb627fa0a8291d7ec71e640a6516950cee13669966bc8" }, "slack": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/slack/SKILL.md", - "computedHash": "b92398a0d0c3f5957bc15ac2ead16442238f0d423e19561bdea67ea61a134802" + "computedHash": "e2474842df7aecfba1866867fb08edce17cfe8b1bae0efed1f48988bf551e104" }, "stripe": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/stripe/SKILL.md", - "computedHash": "80159ad0e34e8e62ca1f4cca305ba73c5ec8a4bf95e9582afe94966934092be5" + "computedHash": "03845f355c42838653296330cdcf6ea91dec9929b29c12a1d26861f47d5f6b9e" }, "vercel": { "source": "vercel-labs/emulate", "sourceType": "github", "skillPath": "skills/vercel/SKILL.md", - "computedHash": "7f8a849d69b75ecc2528746521d0f9c226c61081a99124409787c8a4ceca9d2d" + "computedHash": "fd421c94f20b42ac54b1cdd65d5b38439289260103bb26dc9342da932f4c8fa6" } } } diff --git a/plugins/firebase/.agents/skills/extension-to-functions-codebase/SKILL.md b/plugins/firebase/.agents/skills/extension-to-functions-codebase/SKILL.md index 0581d43b..6bb3ea9f 100644 --- a/plugins/firebase/.agents/skills/extension-to-functions-codebase/SKILL.md +++ b/plugins/firebase/.agents/skills/extension-to-functions-codebase/SKILL.md @@ -2,6 +2,7 @@ name: extension-to-functions-codebase description: Skill for converting an installed Firebase Extension (or extension source) into a standalone Cloud Functions for Firebase codebase or publishable npm package, including V1 to V2 trigger upgrades, lifecycle hooks, and declarative security metadata: + author: Google LLC category: Serverless --- diff --git a/plugins/firebase/.agents/skills/firebase-ai-logic-basics/SKILL.md b/plugins/firebase/.agents/skills/firebase-ai-logic-basics/SKILL.md index 54949b2e..685921fe 100644 --- a/plugins/firebase/.agents/skills/firebase-ai-logic-basics/SKILL.md +++ b/plugins/firebase/.agents/skills/firebase-ai-logic-basics/SKILL.md @@ -1,8 +1,10 @@ --- name: firebase-ai-logic-basics -description: Official skill for integrating Firebase AI Logic (Gemini API) into web applications. Covers setup, multimodal inference, structured output, and security. +description: >- + Integrates Firebase AI Logic (formerly Vertex AI for Firebase) into mobile and web apps (Android, iOS, Web, and Flutter) to call Gemini models from client code. Use when provisioning or initializing Firebase AI Logic, or adding Gemini features to an app: text generation, multimodal input, chat, streaming, structured output, hybrid on-device inference, App Check protection, or switching models with Remote Config. version: 1.0.1 metadata: + author: Google LLC category: AiAndMachineLearning --- @@ -11,57 +13,98 @@ metadata: ## Overview Firebase AI Logic is a product of Firebase that allows developers to add gen AI -to their mobile and web apps using client-side SDKs. You can call Gemini models -directly from your app without managing a dedicated backend. Firebase AI Logic, -which was previously known as "Vertex AI for Firebase", represents the evolution -of Google's AI integration platform for mobile and web developers. +to their mobile and web apps using client-side SDKs. The app can directly call +Gemini models via Firebase AI Logic without the developer needing to manage a +dedicated backend. Firebase AI Logic was previously known as "Vertex AI for +Firebase". It supports the two Gemini API providers: -- **Gemini Developer API**: It has a free tier ideal for prototyping, and - pay-as-you-go for production -- **Agent Platform Gemini API** (formerly branded Vertex AI): Ideal for scale - with enterprise-grade production readiness, requires Blaze plan +- **Gemini Developer API**: It has a free tier ideal for prototyping, and + pay-as-you-go for production +- **Agent Platform Gemini API** (formerly branded Vertex AI): Ideal for scale + with enterprise-grade production readiness, requires Blaze pricing plan Use the Gemini Developer API as a default, and only Agent Platform Gemini API (formerly branded Vertex AI) if the application requires it. -## Setup & Initialization +______________________________________________________________________ + +## Setup & initialization ### Prerequisites -- Before starting, ensure you have **Node.js 16+** and npm installed. Install - them if they aren’t already available. -- Identify the platform the user is interested in building on prior to - starting: Android, iOS, Flutter or Web. -- If their platform is unsupported, Direct the user to Firebase Docs to learn - how to set up AI Logic for their application (share this link with the user - https://firebase.google.com/docs/ai-logic/get-started) +- Identify the platform the user is interested in building on prior to starting: + Android, iOS, Web, or Flutter. +- If their platform is unsupported, Direct the user to Firebase Docs to learn + how to set up AI Logic for their application (share this link with the user + https://firebase.google.com/docs/ai-logic/get-started) +- The Firebase CLI commands below run through `npx` on every platform, so they + need **Node.js 20+** and npm. Use the `firebase-basics` skill to check for + them and to log in to the Firebase CLI. If Node.js is missing or older than + version 20, ask the user to install it as that skill describes. Don't install + system packages yourself, for example with `sudo apt-get`, because that + changes the user's machine without their consent. + +### 1. Provision Firebase AI Logic (ALL platforms) + +Use the **Firebase CLI** to provision Firebase AI Logic for the Firebase +Project: + +1. Make sure that you're in a firebase directory (with a `firebase.json`). -### Installation +1. Verify that you're running Firebase CLI commands against the intended + Firebase Project. When you run this command, the active project is marked + with "current": -The library is part of the standard Firebase Web SDK. + ```bash + npx -y firebase-tools@latest projects:list + ``` -`npm install firebase@latest` +1. Make sure there's at least one Firebase App for the target platform + registered with the current Firebase Project: -If you're in a firebase directory (with a firebase.json) the currently selected -project will be marked with "current" using this command: + ```bash + npx -y firebase-tools@latest apps:list + ``` -`npx -y firebase-tools@latest projects:list` +1. Provision Firebase AI Logic for the Firebase Project: -Ensure there's at least one app associated with the current project + ```bash + npx -y firebase-tools@latest init ailogic + ``` + + This command will enable the Gemini Developer API in the Firebase project. + +> [!WARNING] **CRITICAL: Provisioning Firebase AI Logic is Required** For all +> platforms (Android, iOS, Web, or Flutter), even if the app already uses +> Firebase, you MUST run `npx -y firebase-tools@latest init ailogic` to +> provision the service. The configuration files of `GoogleService-Info.plist`, +> `google-services.json`, and `firebase_options.dart` ONLY handle client +> configuration and do NOT enable the Firebase AI Logic service, leading to +> `PERMISSION_DENIED` errors. -`npx -y firebase-tools@latest apps:list` +If you can't run `init ailogic` yourself (for example because Node.js is missing +or the Firebase CLI isn't logged in) do NOT skip it. The app's requests to +Gemini models will fail until the Firebase AI Logic service is provisioned. Tell +the user to run this command in their project directory, and include it in your +final summary: -Initialize AI logic SDK with the init command +```bash +npx -y firebase-tools@latest init ailogic +``` -`npx -y firebase-tools@latest init ailogic` +### 2. Add the SDK and initialize the service in the app -This will automatically enable the Gemini Developer API in the Firebase console. +Adding the SDK dependency and initializing the service are platform-specific. +Before writing code, read the reference for the target platform listed under +[Initialization Code References](#initialization-code-references). More info in [Firebase AI Logic Getting Started](https://firebase.google.com/docs/ai-logic/get-started.md.txt) +______________________________________________________________________ + ## Core Capabilities > [!WARNING] **CRITICAL: Use current model names:** Always check the @@ -91,16 +134,42 @@ To improve the user experience by showing partial results as they arrive (like a typing effect), use `generateContentStream` instead of `generateContent` for faster display of results. +### Text-to-Speech (TTS) Generation + +Generate spoken audio directly on client devices without a custom speech +backend. Firebase AI Logic supports speech synthesis using dedicated Gemini TTS +models: + +- **Supported Models**: `gemini-3.1-flash-tts-preview` +- **Capabilities**: + - Single-speaker voice persona selection (`voiceName`) and multi-speaker + dialogues (up to 2 distinct speakers) via `SpeechConfig` / + `MultiSpeakerVoiceConfig` + - Direct audio responses by setting `responseModalities` to audio + - Streaming speech responses with `generateContentStream` for low-latency + playback + - Audio directives (`[Audio Profile: ...]`, `[Scene: ...]`, + `[Director's Note: ...]`) and emotional tags (`[whispers]`, `[laughs]`, + `[slowly]`) + - Client-side decoding and playback of 24 kHz 16-bit linear PCM (`audio/l16`) + or WAV container format (`audio/x-wav`) + +See the [Text-to-Speech (TTS) Generation Guide](references/tts_generation.md) +for full implementation details, streaming patterns, and copy-paste code +snippets for iOS (Swift), Android (Kotlin), and Web. + ### Generate Images with Nano Banana > [!WARNING] **Use current Image model names:** Always check the > [Firebase AI Logic Models documentation](https://firebase.google.com/docs/ai-logic/models.md.txt) > for the currently supported image generation (Nano Banana) model names. -- Requires an upgraded Blaze pay-as-you-go billing plan. +- Requires an upgraded Blaze pay-as-you-go billing plan. ### Search Grounding with the built in googleSearch tool +______________________________________________________________________ + ## Supported Platforms and Frameworks Supported Platforms and Frameworks include Kotlin and Java for Android, Swift @@ -114,23 +183,26 @@ Enforce a specific JSON schema for the response. ### On-Device AI (Hybrid) -Hybrid on-device inference for web apps, where the Firebase Javascript SDK -automatically checks for Gemini Nano's availability (after installation) and -switches between on-device or cloud-hosted prompt execution. This requires -specific steps to enable model usage in the Chrome browser, more info in the -[hybrid-on-device-inference documentation](https://firebase.google.com/docs/ai-logic/hybrid-on-device-inference.md.txt). +Hybrid inference runs prompts with an on-device model when one is available and +falls back to a cloud-hosted model otherwise (or vice versa). Availability and +setup steps differ by platform, more info in the +[hybrid and on-device documentation](https://firebase.google.com/docs/ai-logic/hybrid.md.txt), +which links to each platform's guide. + +______________________________________________________________________ ## Security & Production -### App Check +### Firebase App Check > [!WARNING] **Critical Safety Requirement:** In order to use AI Logic safely, > you MUST set up App Check on your app. This prevents unauthorized clients from > using your API quota and accessing your backend resources. See -[App Check with reCAPTCHA Enterprise](https://firebase.google.com/docs/app-check/web/recaptcha-enterprise-provider.md.txt) -for setup instructions. +[App Check for Firebase AI Logic](https://firebase.google.com/docs/ai-logic/app-check.md.txt) +for setup instructions. Each platform uses its own attestation provider, so +follow the instructions for the target platform. #### App Check Debug Tokens for Local Development & CI/CD @@ -138,57 +210,69 @@ Because App Check attestation providers (like Play Integrity or DeviceCheck) reject emulators, simulators, or CI environments, you must use **App Check Debug Tokens** during development and testing to bypass standard attestation. +> [!WARNING] **CRITICAL: Never Hardcode or Commit Debug Tokens** App Check debug +> tokens allow clients to bypass attestation and access backend resources +> without a genuine device. Treat them as private secrets. **Never commit debug +> tokens to version control or hardcode raw token strings in client code** (Web, +> Android, iOS, or Flutter). Always inject them through local environment +> variables, gitignored local configurations, or CI secrets. If a token is +> compromised, revoke it immediately in the Firebase Console. + ##### Local Development (Auto-Generated) -1. Configure your code's App Check provider to use the debug factory: - * **Web**: Set `self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;` before - initializing App Check. - * **Android**: Install `DebugAppCheckProviderFactory.getInstance()`. - * **iOS**: Set provider factory to `AppCheckDebugProviderFactory()`. -2. Run your app in the emulator/localhost. -3. Look at your runtime debugger console / Logcat logs for the generated UUID: - * *Example:* `AppCheck debug token: - "123a4567-b89c-12d3-e456-789012345678"` -4. Register this token in the Firebase Console under **Security > App Check > - Apps > Manage debug tokens**. +1. Configure your code's App Check provider to use the debug factory. The API is + platform-specific; see the App Check section of the platform reference or the + App Check documentation linked above. +1. Run your app in the emulator/localhost. +1. Look at your runtime debugger console / Logcat logs for the generated UUID: + - *Example:* `AppCheck debug token: "123a4567-b89c-12d3-e456-789012345678"` +1. Register this token in the Firebase Console under **Security > App Check > + Apps > Manage debug tokens**. + +> **💡 Tip (Prevent Debug Token Churn):** Simulator or emulator resets, fresh +> installs, and clearing browser data can make the SDK generate a new debug +> token that you must register again. To keep a stable debug token without +> hardcoding it, see the App Check section of the platform reference. ##### CI/CD Pipelines (Pre-Provisioned) -1. Generate and register a new debug token in the Firebase Console under - **Security > App Check > Apps > Manage debug tokens**. -2. Add this token string as an encrypted secret in your CI system (e.g. - `APP_CHECK_DEBUG_TOKEN`). -3. Configure your build to pass this secret as an environment variable to the - SDK during test execution (e.g. `self.FIREBASE_APPCHECK_DEBUG_TOKEN = - process.env.APP_CHECK_DEBUG_TOKEN`). +1. Generate and register a new debug token in the Firebase Console under + **Security > App Check > Apps > Manage debug tokens**. +1. Add this token string as an encrypted secret in your CI system (e.g. + `APP_CHECK_DEBUG_TOKEN`). +1. Configure your build to pass this secret as an environment variable to the + SDK during test execution. -### Remote Config +### Firebase Remote Config Consider that you do not need to hardcode model names (e.g., a specific model version string). Use Firebase Remote Config to update model versions dynamically without deploying new client code. See [Changing model names remotely](https://firebase.google.com/docs/ai-logic/change-model-name-remotely.md.txt) -> [!WARNING] **CRITICAL: Backend Provisioning Required** For all platforms -> (Flutter, Android, iOS, Web), you MUST run `npx firebase-tools init ailogic` -> to provision the service. `flutterfire configure` ONLY handles client -> configuration and does NOT enable the AI service, leading to -> `PERMISSION_DENIED` errors. +______________________________________________________________________ ## Initialization Code References -- **Web Modular API** - - Provider: Gemini Developer API - - Reference: [usage_patterns_web.md](references/usage_patterns_web.md) -- **Android (Kotlin)** - - Provider: Gemini Developer API - - Reference: [usage_patterns_android.md](references/usage_patterns_android.md) -- **iOS (Swift)** - - Provider: Gemini Developer API - - Reference: [ios_setup.md](references/ios_setup.md) -- **Flutter (Dart)** - - Provider: Gemini Developer API - - Reference: [flutter_setup.md](references/flutter_setup.md) +- **Android (Kotlin)** + + - Gemini API Provider: Gemini Developer API + - Reference: [usage_patterns_android.md](references/usage_patterns_android.md) + +- **iOS (Swift)** + + - Gemini API Provider: Gemini Developer API + - Reference: [ios_setup.md](references/ios_setup.md) + +- **Web Modular API** + + - Gemini API Provider: Gemini Developer API + - Reference: [usage_patterns_web.md](references/usage_patterns_web.md) + +- **Flutter (Dart)** + + - Gemini API Provider: Gemini Developer API + - Reference: [flutter_setup.md](references/flutter_setup.md) > [!WARNING] **CRITICAL: Use current model names:** Always check the > [Firebase AI Logic Models documentation](https://firebase.google.com/docs/ai-logic/models.md.txt) @@ -197,8 +281,9 @@ without deploying new client code. See ## References -[Web SDK code examples and usage patterns](references/usage_patterns_web.md) +[Android (Kotlin) SDK usage patterns](references/usage_patterns_android.md) [iOS SDK code examples and usage patterns](references/ios_setup.md) -[Flutter SDK code examples and usage patterns](references/flutter_setup.md) +[Web SDK setup, code examples, and usage patterns](references/usage_patterns_web.md) -[Android (Kotlin) SDK usage patterns](references/usage_patterns_android.md) +[Flutter SDK code examples and usage patterns](references/flutter_setup.md) +[Client-side Text-to-Speech (TTS) generation](references/tts_generation.md) diff --git a/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/flutter_setup.md b/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/flutter_setup.md index 64dd5391..bf31dc24 100644 --- a/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/flutter_setup.md +++ b/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/flutter_setup.md @@ -96,3 +96,40 @@ final chat = model.startChat(history: [ final response = await chat.sendMessage(Content.text('What is CBT?')); ``` + +## App Check Debug Provider + +For App Check debug tokens during local development and CI/CD, add the plugin +with `flutter pub add firebase_app_check`, then activate the debug providers +right after `Firebase.initializeApp()`: + +```dart +import 'package:firebase_app_check/firebase_app_check.dart'; +import 'package:flutter/foundation.dart'; + +// In main(), after `await Firebase.initializeApp();` +if (kDebugMode) { + await FirebaseAppCheck.instance.activate( + providerAndroid: const AndroidDebugProvider(), + providerApple: const AppleDebugProvider(), + providerWeb: WebDebugProvider(), + ); +} +``` + +- Use `providerAndroid`, `providerApple`, and `providerWeb`. The older + `androidProvider` and `appleProvider` parameters (`AndroidProvider.debug`, + `AppleProvider.debug`) still appear in many examples but are deprecated. +- Only use the debug providers in debug builds, as gated by `kDebugMode` above. + Release builds must activate the production providers (Play Integrity, App + Attest or DeviceCheck, and reCAPTCHA Enterprise) instead; see + [App Check for Firebase AI Logic](https://firebase.google.com/docs/ai-logic/app-check.md.txt). +- **CI/CD**: Pass the pre-provisioned token to all three debug providers, e.g. + `debugToken: const String.fromEnvironment('APP_CHECK_DEBUG_TOKEN')`, and build + with `--dart-define=APP_CHECK_DEBUG_TOKEN=`. +- **Flutter web**: `providerWeb` is required on web; without it, `activate()` + throws. `WebDebugProvider` turns on the web debug token itself, so + `web/index.html` doesn't need `self.FIREBASE_APPCHECK_DEBUG_TOKEN`. +- If the app uses `firebase_ai` 3.11.0 or lower, also set the `appCheck` + parameter of `FirebaseAI.googleAI(...)` to `FirebaseAppCheck.instance`. Newer + versions don't need this. diff --git a/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/ios_setup.md b/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/ios_setup.md index fda5bb6a..b0bdf62c 100644 --- a/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/ios_setup.md +++ b/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/ios_setup.md @@ -3,17 +3,46 @@ ## 1. Import and Initialize Ensure you have installed the `FirebaseAILogic` SDK via Swift Package Manager. +Use the `xcode-project-setup` skill to automate adding the SPM dependency to the +Xcode project. ```swift import FirebaseAILogic -// Initialize the Firebase AI service and the generative model. -let ai = FirebaseAI.firebaseAI() +// To enable replay protection for generative or preview models, pass +// useLimitedUseAppCheckTokens: true with your backend: +let ai = FirebaseAI.firebaseAI( + backend: .googleAI(), + useLimitedUseAppCheckTokens: true +) // [AGENT] Replace "" with the latest model from https://firebase.google.com/docs/ai-logic/models.md.txt let model = ai.generativeModel(modelName: "") ``` +### App Check Replay Protection + +Generative and preview models enforce replay protection with 5-minute +limited-use App Check tokens. If you call a protected model without enabling +limited-use tokens, the request fails with: + +```text +HTTP 403: "To access this model, you must enforce Firebase App Check" +``` + +To resolve this error, initialize `FirebaseAI` with +`useLimitedUseAppCheckTokens: true`: + +```swift +let ai = FirebaseAI.firebaseAI( + backend: .googleAI(), + useLimitedUseAppCheckTokens: true +) +``` + +This ensures the SDK fetches a fresh, short-lived limited-use token for each +request rather than reusing a standard cached App Check token. + ## 2. SwiftUI Integration (Best Practices) Use the `@Observable` pattern to manage AI state and provide a smooth UX with @@ -33,7 +62,10 @@ import FirebaseAILogic @Observable final class AIViewModel { // [AGENT] Replace with the latest model from https://firebase.google.com/docs/ai-logic/models.md.txt - private lazy var model = FirebaseAI.firebaseAI().generativeModel(modelName: "") + private lazy var model = FirebaseAI.firebaseAI( + backend: .googleAI(), + useLimitedUseAppCheckTokens: true + ).generativeModel(modelName: "") var responseText: String = "" var isFetching: Bool = false @@ -98,6 +130,68 @@ let model = FirebaseAI.firebaseAI().generativeModel( ) ``` +## 4. App Check Debug Provider + +For App Check debug tokens during local development and CI/CD, add the +`FirebaseAppCheck` product to the app target (with the `xcode-project-setup` +skill), then set the debug provider factory in debug builds *before* +`FirebaseApp.configure()`. Otherwise the debug provider isn't used. + +```swift +import FirebaseAppCheck +import FirebaseCore + +// In your app's init(), before configuring Firebase: +#if DEBUG +AppCheck.setAppCheckProviderFactory(AppCheckDebugProviderFactory()) +#endif +FirebaseApp.configure() +``` + +For CI/CD, add an `AppCheckDebugToken` environment variable to the Xcode test +scheme with a value like `$(APP_CHECK_DEBUG_TOKEN)`, then pass the token from +the CI test command: + +```bash +xcodebuild test -scheme -workspace .xcworkspace \ + APP_CHECK_DEBUG_TOKEN= +``` + +### Debug Token Persistence + +When running on simulators or during development, the Firebase iOS SDK generates +a new debug token UUID whenever `NSUserDefaults` is cleared (e.g., simulator +reset or fresh install). To avoid invalidating tokens registered in the Firebase +Console and prevent token churn, persist a stable token by setting the +`AppCheckDebugToken` environment variable, which the SDK reads automatically at +runtime: + +> [!WARNING] **CRITICAL: Never Hardcode or Commit Debug Tokens** Debug tokens +> grant access to backend resources without device attestation. Never commit +> debug tokens to version control or hardcode token strings in source code. + +- **In Xcode Scheme (Recommended):** Edit Scheme -> Run -> Arguments -> + Environment Variables -> Add `AppCheckDebugToken = `. Keep + user schemes (`xcuserdata/`) unshared and gitignored. +- **In Code (Safe Dynamic Loading Only):** If setting the environment variable + in code before configuring `AppCheckDebugProviderFactory`, load the token + dynamically from a gitignored local file or environment rather than hardcoding + the token literal: + +```swift +#if DEBUG +// ✅ SAFE: Load from gitignored local file or process environment +// Note: loadGitIgnoredDebugToken() is a placeholder for your custom helper (e.g., reading from a gitignored plist) +if let debugToken = loadGitIgnoredDebugToken() { + setenv("AppCheckDebugToken", debugToken, 0) +} +let providerFactory = AppCheckDebugProviderFactory() +AppCheck.setAppCheckProviderFactory(providerFactory) +#endif + +FirebaseApp.configure() // Configure Firebase AFTER setting the App Check provider factory +``` + # Advanced Features ### Chat Session (Multi-turn) diff --git a/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/tts_generation.md b/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/tts_generation.md new file mode 100644 index 00000000..5f6d04e9 --- /dev/null +++ b/plugins/firebase/.agents/skills/firebase-ai-logic-basics/references/tts_generation.md @@ -0,0 +1,676 @@ +# Client-Side Text-to-Speech (TTS) Generation with Gemini + +Firebase AI Logic enables client-side Text-to-Speech (TTS) generation directly +from your mobile and web applications without maintaining custom backend speech +services. Using specialized Gemini TTS models, apps can synthesize natural, +expressive audio with custom voice personas, multi-speaker dialogues, and +in-flight audio streaming. + +______________________________________________________________________ + +## Supported Models + +Always specify a dedicated Gemini TTS model when generating audio: + +| Model ID | Description | +| :----------------------------- | :--------------------------------------------------------------------------------- | +| `gemini-3.1-flash-tts-preview` | Low-latency speech synthesis (preview); supports single- and multi-speaker output. | + +> [!WARNING] Always verify currently supported model availability in the +> [Firebase AI Logic Models documentation](https://firebase.google.com/docs/ai-logic/models.md.txt). + +______________________________________________________________________ + +## Core Concepts & Configurations + +### 1. Response Modality + +To instruct Gemini to synthesize and return audio, configure +`responseModalities` in the generation configuration to include audio: + +- **Swift**: `generationConfig = GenerationConfig(responseModalities: [.audio])` +- **Kotlin**: + `generationConfig { responseModalities = listOf(ResponseModality.AUDIO) }` +- **Web (JS/TS)**: `generationConfig: { responseModalities: ["AUDIO"] }` + +### 2. Single-Speaker `SpeechConfig` + +Configure voice persona and optional language code (`languageCode`, e.g. +`"en-US"`, `"es-ES"`, `"ja-JP"`) using `SpeechConfig`. Voice names map to Google +prebuilt neural voices (for example: `Puck`, `Charon`, `Kore`, `Fenrir`, +`Aoede`): + +- **Swift**: + ```swift + let speechConfig = SpeechConfig( + voiceConfig: VoiceConfig( + prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: "Puck") + ), + languageCode: "en-US" + ) + ``` +- **Kotlin**: + ```kotlin + val speechConfig = speechConfig { + voiceConfig = voiceConfig { + prebuiltVoiceConfig = prebuiltVoiceConfig { + voiceName = "Puck" + } + } + languageCode = "en-US" + } + ``` +- **Web**: + ```javascript + speechConfig: { + voiceConfig: { + prebuiltVoiceConfig: { + voiceName: "Puck" + } + }, + languageCode: "en-US" + } + ``` + +### 3. Multi-Speaker `MultiSpeakerVoiceConfig` (2-Speaker Dialogues) + +For scripts with two distinct characters or roles (e.g., host and guest, +narrator and character), configure `MultiSpeakerVoiceConfig` with +`speakerVoiceConfigs` linking speaker names to specific voices: + +- **Swift**: + ```swift + let multiSpeakerConfig = SpeechConfig( + multiSpeakerVoiceConfig: MultiSpeakerVoiceConfig( + speakerVoiceConfigs: [ + SpeakerVoiceConfig( + speaker: "Host", + voiceConfig: VoiceConfig( + prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: "Puck") + ) + ), + SpeakerVoiceConfig( + speaker: "Guest", + voiceConfig: VoiceConfig( + prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: "Aoede") + ) + ) + ] + ) + ) + ``` +- **Kotlin**: + ```kotlin + val multiSpeakerConfig = speechConfig { + multiSpeakerVoiceConfig = multiSpeakerVoiceConfig { + speakerVoiceConfigs = listOf( + speakerVoiceConfig { + speaker = "Host" + voiceConfig = voiceConfig { + prebuiltVoiceConfig = prebuiltVoiceConfig { voiceName = "Puck" } + } + }, + speakerVoiceConfig { + speaker = "Guest" + voiceConfig = voiceConfig { + prebuiltVoiceConfig = prebuiltVoiceConfig { voiceName = "Aoede" } + } + } + ) + } + } + ``` +- **Web**: + ```javascript + speechConfig: { + multiSpeakerVoiceConfig: { + speakerVoiceConfigs: [ + { + speaker: "Host", + voiceConfig: { prebuiltVoiceConfig: { voiceName: "Puck" } } + }, + { + speaker: "Guest", + voiceConfig: { prebuiltVoiceConfig: { voiceName: "Aoede" } } + } + ] + } + } + ``` + +______________________________________________________________________ + +## Audio Directives and Emotional Tags + +Gemini TTS models accept natural-language directives and inline tags in prompts +to control tone, cadence, pacing, and emotional expression. + +### Directives + +Include directives at the start of your prompt to set the overall tone and +scene: + +- `[Audio Profile: Warm, conversational podcast co-host with energetic delivery]` +- `[Scene: Quiet bedtime story in a calm room]` +- `[Director's Note: Speak deliberately, pausing for reflection after major questions]` + +### Emotional Tags & Delivery Cues + +Embed delivery markers inline within dialogue or narration: + +- `[whispers]` — Soft, confidential whisper. +- `[laughs]` / `[chuckles]` — Lighthearted laughter during delivery. +- `[sighs]` — Expressive exhale. +- `[slowly]` — Reduced tempo for emphasis or dramatic effect. +- `[excited]` — Higher energy and faster pacing. +- `[pause]` — Deliberate silence. + +### Multi-Speaker Prompt Example + +```text +[Scene: A lively coffee-shop tech discussion] +[Director's Note: Host is upbeat and curious; Guest is thoughtful and explanatory] + +Host: Welcome back! [excited] Today we are diving into client-side audio. +Guest: [laughs] It's about time! No servers in between means virtually zero latency. +Host: [whispers] Tell us the secret... how does it stream? +Guest: [slowly] Pure linear PCM, chunk by chunk, directly to your device's audio engine. +``` + +______________________________________________________________________ + +## Audio Decoding and Playback + +### Format Specifications + +Gemini TTS audio format depends on whether the request is unary or streaming: + +- **Unary Requests (`generateContent`)**: Returns complete **WAV (`audio/wav`)** + audio with a standard 44-byte RIFF header included by default (24 kHz, mono, + 16-bit signed little-endian PCM). These bytes can be passed directly to + standard media players (`AVAudioPlayer`, `MediaPlayer`, `