diff --git a/apps/web/content/docs/registry.mdx b/apps/web/content/docs/registry.mdx
index 76169d8..b66d94f 100644
--- a/apps/web/content/docs/registry.mdx
+++ b/apps/web/content/docs/registry.mdx
@@ -53,10 +53,10 @@ Single-choice, constraint-bearing. An optional layer between the framework and t
Single-choice, constraint-bearing, installs into `packages/ai/`.
-| Module | Status | Notes |
-| --------------- | --------------- | ------------------------------------ |
-| `vercel-ai-sdk` | 🟨 Planned | `ai` package + provider sub-recipes. |
-| `tanstack-ai` | 🟨 Planned | TanStack AI. |
+| Module | Status | Notes |
+| --------------- | ----------------- | ----------------------------------------------------------------------------------- |
+| `vercel-ai-sdk` | 🟢 Available | `ai` + `@ai-sdk/openai` with a streaming `/api/chat` route. Peers on `framework`. |
+| `tanstack-ai` | 🟢 Available | `@tanstack/ai` + `@tanstack/ai-openai` with a streaming `/api/chat` route over SSE. |
## Authentication
diff --git a/packages/registry/src/module.ts b/packages/registry/src/module.ts
index c369ac2..7f92a54 100644
--- a/packages/registry/src/module.ts
+++ b/packages/registry/src/module.ts
@@ -87,6 +87,13 @@ export const CATEGORIES = [
cardinality: "one",
home: { kind: "package", dir: "email" },
},
+ {
+ id: "ai",
+ label: "AI",
+ description: "AI SDK + provider wiring.",
+ cardinality: "one",
+ home: { kind: "package", dir: "ai" },
+ },
{
id: "tooling",
label: "Tooling",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 518bcab..72e86de 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -310,6 +310,24 @@ importers:
specifier: npm:@voidzero-dev/vite-plus-test@^0.1.22
version: '@voidzero-dev/vite-plus-test@0.1.22(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(jsdom@29.1.1(@noble/hashes@1.8.0))(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0))'
+ registry/modules/ai-tanstack-ai:
+ dependencies:
+ '@stanza/codemods':
+ specifier: workspace:*
+ version: link:../../../packages/codemods
+ '@stanza/registry':
+ specifier: workspace:*
+ version: link:../../../packages/registry
+
+ registry/modules/ai-vercel-ai-sdk:
+ dependencies:
+ '@stanza/codemods':
+ specifier: workspace:*
+ version: link:../../../packages/codemods
+ '@stanza/registry':
+ specifier: workspace:*
+ version: link:../../../packages/registry
+
registry/modules/auth-better-auth:
dependencies:
'@stanza/codemods':
@@ -374,6 +392,15 @@ importers:
specifier: workspace:*
version: link:../../../packages/registry
+ registry/modules/monorepo-turbo:
+ dependencies:
+ '@stanza/codemods':
+ specifier: workspace:*
+ version: link:../../../packages/codemods
+ '@stanza/registry':
+ specifier: workspace:*
+ version: link:../../../packages/registry
+
registry/modules/orm-drizzle:
dependencies:
'@stanza/codemods':
@@ -404,6 +431,51 @@ importers:
specifier: workspace:*
version: link:../../../packages/registry
+ registry/modules/testing-playwright:
+ dependencies:
+ '@stanza/codemods':
+ specifier: workspace:*
+ version: link:../../../packages/codemods
+ '@stanza/registry':
+ specifier: workspace:*
+ version: link:../../../packages/registry
+
+ registry/modules/testing-vitest:
+ dependencies:
+ '@stanza/codemods':
+ specifier: workspace:*
+ version: link:../../../packages/codemods
+ '@stanza/registry':
+ specifier: workspace:*
+ version: link:../../../packages/registry
+
+ registry/modules/tooling-biome:
+ dependencies:
+ '@stanza/codemods':
+ specifier: workspace:*
+ version: link:../../../packages/codemods
+ '@stanza/registry':
+ specifier: workspace:*
+ version: link:../../../packages/registry
+
+ registry/modules/tooling-eslint-prettier:
+ dependencies:
+ '@stanza/codemods':
+ specifier: workspace:*
+ version: link:../../../packages/codemods
+ '@stanza/registry':
+ specifier: workspace:*
+ version: link:../../../packages/registry
+
+ registry/modules/tooling-oxlint-oxfmt:
+ dependencies:
+ '@stanza/codemods':
+ specifier: workspace:*
+ version: link:../../../packages/codemods
+ '@stanza/registry':
+ specifier: workspace:*
+ version: link:../../../packages/registry
+
registry/modules/ui-shadcn-base:
dependencies:
'@stanza/codemods':
diff --git a/registry/modules/ai-tanstack-ai/logo.svg b/registry/modules/ai-tanstack-ai/logo.svg
new file mode 100644
index 0000000..9c596b7
--- /dev/null
+++ b/registry/modules/ai-tanstack-ai/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/registry/modules/ai-tanstack-ai/module.ts b/registry/modules/ai-tanstack-ai/module.ts
new file mode 100644
index 0000000..9f98956
--- /dev/null
+++ b/registry/modules/ai-tanstack-ai/module.ts
@@ -0,0 +1,55 @@
+import { defineModule } from "@stanza/registry";
+
+export default defineModule({
+ id: "tanstack-ai",
+ category: "ai",
+ label: "TanStack AI",
+ description: "Composable AI primitives across providers.",
+ version: "0.1.0",
+ peers: {
+ framework: ["next", "tanstack-start"],
+ },
+ homepage: "https://tanstack.com/ai",
+ dependencies: {
+ "@tanstack/ai": "^0.22.0",
+ "@tanstack/ai-openai": "^0.10.2",
+ },
+ env: [
+ {
+ name: "OPENAI_API_KEY",
+ example: "sk-...",
+ required: true,
+ description: "OpenAI API key for the chat completion adapter.",
+ },
+ ],
+ adapters: [
+ {
+ key: "next",
+ match: { framework: "next" },
+ templates: [
+ { src: "shared/index.ts", dest: "src/index.ts", scope: "package" },
+ { src: "shared/adapter.ts", dest: "src/adapter.ts", scope: "package" },
+ {
+ src: "next/route.ts",
+ dest: "app/api/chat/route.ts",
+ scope: "app",
+ template: true,
+ },
+ ],
+ },
+ {
+ key: "tanstack-start",
+ match: { framework: "tanstack-start" },
+ templates: [
+ { src: "shared/index.ts", dest: "src/index.ts", scope: "package" },
+ { src: "shared/adapter.ts", dest: "src/adapter.ts", scope: "package" },
+ {
+ src: "tanstack/route.ts",
+ dest: "src/routes/api/chat.ts",
+ scope: "app",
+ template: true,
+ },
+ ],
+ },
+ ],
+});
diff --git a/registry/modules/ai-tanstack-ai/package.json b/registry/modules/ai-tanstack-ai/package.json
new file mode 100644
index 0000000..9498fc2
--- /dev/null
+++ b/registry/modules/ai-tanstack-ai/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@stanza-modules/ai-tanstack-ai",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "type": "module",
+ "main": "./module.ts",
+ "dependencies": {
+ "@stanza/codemods": "workspace:*",
+ "@stanza/registry": "workspace:*"
+ }
+}
diff --git a/registry/modules/ai-tanstack-ai/readme.md b/registry/modules/ai-tanstack-ai/readme.md
new file mode 100644
index 0000000..c4f5b7c
--- /dev/null
+++ b/registry/modules/ai-tanstack-ai/readme.md
@@ -0,0 +1 @@
+Set `OPENAI_API_KEY` in `.env` and POST to `/api/chat` with an `{ messages }` body to stream a response over Server-Sent Events. The configured adapter is exported from `{{package.name}}` — edit `src/adapter.ts` to swap providers (`@tanstack/ai-anthropic`, `@tanstack/ai-google`, …) or models.
diff --git a/registry/modules/ai-tanstack-ai/templates/next/route.ts b/registry/modules/ai-tanstack-ai/templates/next/route.ts
new file mode 100644
index 0000000..267c77c
--- /dev/null
+++ b/registry/modules/ai-tanstack-ai/templates/next/route.ts
@@ -0,0 +1,11 @@
+import { chat, toServerSentEventsResponse } from "@tanstack/ai";
+
+import { adapter } from "{{package.name}}";
+
+export async function POST(request: Request) {
+ const { messages } = await request.json();
+
+ const stream = chat({ adapter, messages });
+
+ return toServerSentEventsResponse(stream);
+}
diff --git a/registry/modules/ai-tanstack-ai/templates/shared/adapter.ts b/registry/modules/ai-tanstack-ai/templates/shared/adapter.ts
new file mode 100644
index 0000000..0be22fe
--- /dev/null
+++ b/registry/modules/ai-tanstack-ai/templates/shared/adapter.ts
@@ -0,0 +1,3 @@
+import { openaiText } from "@tanstack/ai-openai";
+
+export const adapter = openaiText("gpt-4o-mini");
diff --git a/registry/modules/ai-tanstack-ai/templates/shared/index.ts b/registry/modules/ai-tanstack-ai/templates/shared/index.ts
new file mode 100644
index 0000000..ec43d9a
--- /dev/null
+++ b/registry/modules/ai-tanstack-ai/templates/shared/index.ts
@@ -0,0 +1 @@
+export { adapter } from "./adapter.ts";
diff --git a/registry/modules/ai-tanstack-ai/templates/tanstack/route.ts b/registry/modules/ai-tanstack-ai/templates/tanstack/route.ts
new file mode 100644
index 0000000..f09dde0
--- /dev/null
+++ b/registry/modules/ai-tanstack-ai/templates/tanstack/route.ts
@@ -0,0 +1,18 @@
+import { createFileRoute } from "@tanstack/react-router";
+import { chat, toServerSentEventsResponse } from "@tanstack/ai";
+
+import { adapter } from "{{package.name}}";
+
+export const Route = createFileRoute("/api/chat")({
+ server: {
+ handlers: {
+ POST: async ({ request }) => {
+ const { messages } = await request.json();
+
+ const stream = chat({ adapter, messages });
+
+ return toServerSentEventsResponse(stream);
+ },
+ },
+ },
+});
diff --git a/registry/modules/ai-tanstack-ai/tsconfig.json b/registry/modules/ai-tanstack-ai/tsconfig.json
new file mode 100644
index 0000000..5a91c52
--- /dev/null
+++ b/registry/modules/ai-tanstack-ai/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "../../../tsconfig.json",
+ "include": ["module.ts"],
+ "exclude": ["templates"]
+}
diff --git a/registry/modules/ai-vercel-ai-sdk/logo-dark.svg b/registry/modules/ai-vercel-ai-sdk/logo-dark.svg
new file mode 100644
index 0000000..7596891
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/logo-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/registry/modules/ai-vercel-ai-sdk/logo-light.svg b/registry/modules/ai-vercel-ai-sdk/logo-light.svg
new file mode 100644
index 0000000..611c021
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/logo-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/registry/modules/ai-vercel-ai-sdk/module.ts b/registry/modules/ai-vercel-ai-sdk/module.ts
new file mode 100644
index 0000000..223c284
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/module.ts
@@ -0,0 +1,55 @@
+import { defineModule } from "@stanza/registry";
+
+export default defineModule({
+ id: "vercel-ai-sdk",
+ category: "ai",
+ label: "Vercel AI SDK",
+ description: "Streaming AI SDK with tools, structured output, and React hooks.",
+ version: "0.1.0",
+ peers: {
+ framework: ["next", "tanstack-start"],
+ },
+ homepage: "https://ai-sdk.dev",
+ dependencies: {
+ ai: "^6.0.191",
+ "@ai-sdk/openai": "^3.0.65",
+ },
+ env: [
+ {
+ name: "OPENAI_API_KEY",
+ example: "sk-...",
+ required: true,
+ description: "OpenAI API key for the chat completion provider.",
+ },
+ ],
+ adapters: [
+ {
+ key: "next",
+ match: { framework: "next" },
+ templates: [
+ { src: "shared/index.ts", dest: "src/index.ts", scope: "package" },
+ { src: "shared/model.ts", dest: "src/model.ts", scope: "package" },
+ {
+ src: "next/route.ts",
+ dest: "app/api/chat/route.ts",
+ scope: "app",
+ template: true,
+ },
+ ],
+ },
+ {
+ key: "tanstack-start",
+ match: { framework: "tanstack-start" },
+ templates: [
+ { src: "shared/index.ts", dest: "src/index.ts", scope: "package" },
+ { src: "shared/model.ts", dest: "src/model.ts", scope: "package" },
+ {
+ src: "tanstack/route.ts",
+ dest: "src/routes/api/chat.ts",
+ scope: "app",
+ template: true,
+ },
+ ],
+ },
+ ],
+});
diff --git a/registry/modules/ai-vercel-ai-sdk/package.json b/registry/modules/ai-vercel-ai-sdk/package.json
new file mode 100644
index 0000000..c7a90d1
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@stanza-modules/ai-vercel-ai-sdk",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "type": "module",
+ "main": "./module.ts",
+ "dependencies": {
+ "@stanza/codemods": "workspace:*",
+ "@stanza/registry": "workspace:*"
+ }
+}
diff --git a/registry/modules/ai-vercel-ai-sdk/readme.md b/registry/modules/ai-vercel-ai-sdk/readme.md
new file mode 100644
index 0000000..72ed941
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/readme.md
@@ -0,0 +1 @@
+Set `OPENAI_API_KEY` in `.env` and POST to `/api/chat` with an `{ messages }` body to stream a response. The configured model is exported from `{{package.name}}` — edit `src/model.ts` to swap providers or models.
diff --git a/registry/modules/ai-vercel-ai-sdk/templates/next/route.ts b/registry/modules/ai-vercel-ai-sdk/templates/next/route.ts
new file mode 100644
index 0000000..df4318d
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/templates/next/route.ts
@@ -0,0 +1,14 @@
+import { convertToModelMessages, streamText, type UIMessage } from "ai";
+
+import { model } from "{{package.name}}";
+
+export async function POST(req: Request) {
+ const { messages }: { messages: UIMessage[] } = await req.json();
+
+ const result = streamText({
+ model,
+ messages: await convertToModelMessages(messages),
+ });
+
+ return result.toUIMessageStreamResponse();
+}
diff --git a/registry/modules/ai-vercel-ai-sdk/templates/shared/index.ts b/registry/modules/ai-vercel-ai-sdk/templates/shared/index.ts
new file mode 100644
index 0000000..34f0cfc
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/templates/shared/index.ts
@@ -0,0 +1 @@
+export { model } from "./model.ts";
diff --git a/registry/modules/ai-vercel-ai-sdk/templates/shared/model.ts b/registry/modules/ai-vercel-ai-sdk/templates/shared/model.ts
new file mode 100644
index 0000000..2f3f10c
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/templates/shared/model.ts
@@ -0,0 +1,3 @@
+import { openai } from "@ai-sdk/openai";
+
+export const model = openai("gpt-4o-mini");
diff --git a/registry/modules/ai-vercel-ai-sdk/templates/tanstack/route.ts b/registry/modules/ai-vercel-ai-sdk/templates/tanstack/route.ts
new file mode 100644
index 0000000..f7b0391
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/templates/tanstack/route.ts
@@ -0,0 +1,21 @@
+import { createFileRoute } from "@tanstack/react-router";
+import { convertToModelMessages, streamText, type UIMessage } from "ai";
+
+import { model } from "{{package.name}}";
+
+export const Route = createFileRoute("/api/chat")({
+ server: {
+ handlers: {
+ POST: async ({ request }) => {
+ const { messages }: { messages: UIMessage[] } = await request.json();
+
+ const result = streamText({
+ model,
+ messages: await convertToModelMessages(messages),
+ });
+
+ return result.toUIMessageStreamResponse();
+ },
+ },
+ },
+});
diff --git a/registry/modules/ai-vercel-ai-sdk/tsconfig.json b/registry/modules/ai-vercel-ai-sdk/tsconfig.json
new file mode 100644
index 0000000..5a91c52
--- /dev/null
+++ b/registry/modules/ai-vercel-ai-sdk/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "../../../tsconfig.json",
+ "include": ["module.ts"],
+ "exclude": ["templates"]
+}
diff --git a/registry/modules/monorepo-turbo/package.json b/registry/modules/monorepo-turbo/package.json
new file mode 100644
index 0000000..aabe8b6
--- /dev/null
+++ b/registry/modules/monorepo-turbo/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@stanza-modules/monorepo-turbo",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "type": "module",
+ "main": "./module.ts",
+ "dependencies": {
+ "@stanza/codemods": "workspace:*",
+ "@stanza/registry": "workspace:*"
+ }
+}
diff --git a/registry/modules/monorepo-turbo/tsconfig.json b/registry/modules/monorepo-turbo/tsconfig.json
new file mode 100644
index 0000000..5a91c52
--- /dev/null
+++ b/registry/modules/monorepo-turbo/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "../../../tsconfig.json",
+ "include": ["module.ts"],
+ "exclude": ["templates"]
+}
diff --git a/registry/modules/testing-playwright/package.json b/registry/modules/testing-playwright/package.json
new file mode 100644
index 0000000..c373fe9
--- /dev/null
+++ b/registry/modules/testing-playwright/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@stanza-modules/testing-playwright",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "type": "module",
+ "main": "./module.ts",
+ "dependencies": {
+ "@stanza/codemods": "workspace:*",
+ "@stanza/registry": "workspace:*"
+ }
+}
diff --git a/registry/modules/testing-playwright/tsconfig.json b/registry/modules/testing-playwright/tsconfig.json
new file mode 100644
index 0000000..5a91c52
--- /dev/null
+++ b/registry/modules/testing-playwright/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "../../../tsconfig.json",
+ "include": ["module.ts"],
+ "exclude": ["templates"]
+}
diff --git a/registry/modules/testing-vitest/package.json b/registry/modules/testing-vitest/package.json
new file mode 100644
index 0000000..9b2f6c8
--- /dev/null
+++ b/registry/modules/testing-vitest/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@stanza-modules/testing-vitest",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "type": "module",
+ "main": "./module.ts",
+ "dependencies": {
+ "@stanza/codemods": "workspace:*",
+ "@stanza/registry": "workspace:*"
+ }
+}
diff --git a/registry/modules/testing-vitest/tsconfig.json b/registry/modules/testing-vitest/tsconfig.json
new file mode 100644
index 0000000..5a91c52
--- /dev/null
+++ b/registry/modules/testing-vitest/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "../../../tsconfig.json",
+ "include": ["module.ts"],
+ "exclude": ["templates"]
+}
diff --git a/registry/modules/tooling-biome/package.json b/registry/modules/tooling-biome/package.json
new file mode 100644
index 0000000..679b9b2
--- /dev/null
+++ b/registry/modules/tooling-biome/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@stanza-modules/tooling-biome",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "type": "module",
+ "main": "./module.ts",
+ "dependencies": {
+ "@stanza/codemods": "workspace:*",
+ "@stanza/registry": "workspace:*"
+ }
+}
diff --git a/registry/modules/tooling-biome/tsconfig.json b/registry/modules/tooling-biome/tsconfig.json
new file mode 100644
index 0000000..5a91c52
--- /dev/null
+++ b/registry/modules/tooling-biome/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "../../../tsconfig.json",
+ "include": ["module.ts"],
+ "exclude": ["templates"]
+}
diff --git a/registry/modules/tooling-eslint-prettier/package.json b/registry/modules/tooling-eslint-prettier/package.json
new file mode 100644
index 0000000..95192f3
--- /dev/null
+++ b/registry/modules/tooling-eslint-prettier/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@stanza-modules/tooling-eslint-prettier",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "type": "module",
+ "main": "./module.ts",
+ "dependencies": {
+ "@stanza/codemods": "workspace:*",
+ "@stanza/registry": "workspace:*"
+ }
+}
diff --git a/registry/modules/tooling-eslint-prettier/tsconfig.json b/registry/modules/tooling-eslint-prettier/tsconfig.json
new file mode 100644
index 0000000..5a91c52
--- /dev/null
+++ b/registry/modules/tooling-eslint-prettier/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "../../../tsconfig.json",
+ "include": ["module.ts"],
+ "exclude": ["templates"]
+}
diff --git a/registry/modules/tooling-oxlint-oxfmt/package.json b/registry/modules/tooling-oxlint-oxfmt/package.json
new file mode 100644
index 0000000..8914f9e
--- /dev/null
+++ b/registry/modules/tooling-oxlint-oxfmt/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@stanza-modules/tooling-oxlint-oxfmt",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "type": "module",
+ "main": "./module.ts",
+ "dependencies": {
+ "@stanza/codemods": "workspace:*",
+ "@stanza/registry": "workspace:*"
+ }
+}
diff --git a/registry/modules/tooling-oxlint-oxfmt/tsconfig.json b/registry/modules/tooling-oxlint-oxfmt/tsconfig.json
new file mode 100644
index 0000000..5a91c52
--- /dev/null
+++ b/registry/modules/tooling-oxlint-oxfmt/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "../../../tsconfig.json",
+ "include": ["module.ts"],
+ "exclude": ["templates"]
+}