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"] +}