diff --git a/.changeset/remove-init-prompts.md b/.changeset/remove-init-prompts.md new file mode 100644 index 00000000..e2afdc3b --- /dev/null +++ b/.changeset/remove-init-prompts.md @@ -0,0 +1,5 @@ +--- +"clerk": minor +--- + +Remove `clerk init --prompt` and the bundled per-framework agent prompt templates. Agents should run `clerk init -y` to perform the full setup non-interactively, or run `skills add clerk/skills` directly via their preferred package runner. The internal `pmInstallCommand` helper has moved from `commands/init/prompts/` to `lib/package-manager.ts`. diff --git a/packages/cli-core/src/cli-program.ts b/packages/cli-core/src/cli-program.ts index 6266fb01..30e16d50 100644 --- a/packages/cli-core/src/cli-program.ts +++ b/packages/cli-core/src/cli-program.ts @@ -131,7 +131,6 @@ Give AI agents better Clerk context: install the Clerk skills ) .option("--name ", "Project name for --starter (skips prompt)") .option("--app ", "Application ID to link (skips interactive picker)") - .option("--prompt", "Output a prompt for an AI agent to integrate Clerk") .option("--starter", "Create a new project from a starter template") .option("-y, --yes", "Skip confirmation prompts") .option("--no-skills", "Skip the optional agent skills install prompt") @@ -150,7 +149,6 @@ Give AI agents better Clerk context: install the Clerk skills command: "clerk init --starter --framework next --pm bun", description: "Bootstrap with Bun", }, - { command: "clerk init --prompt", description: "Output a setup prompt for an AI agent" }, { command: "clerk init -y", description: "Skip all confirmation prompts" }, { command: "clerk init --no-skills", description: "Skip the agent skills install prompt" }, ]) diff --git a/packages/cli-core/src/commands/init/README.md b/packages/cli-core/src/commands/init/README.md index 8cdea2d5..3e0c71c6 100644 --- a/packages/cli-core/src/commands/init/README.md +++ b/packages/cli-core/src/commands/init/README.md @@ -11,7 +11,6 @@ clerk init --framework next clerk init --starter clerk init --starter --framework next --pm bun clerk init --starter --framework next --pm bun --name my-app -clerk init --prompt clerk init -y clerk init --yes clerk init --no-skills @@ -26,7 +25,6 @@ clerk init --no-skills | `--name ` | Project name for `--starter` (skips prompt). Must be lowercase, no spaces, no path separators | | `--app ` | Application ID to link (skips the interactive app picker during authenticated linking) | | `--starter` | Bootstrap a new project from a starter template (runs the framework generator, installs deps, and scaffolds Clerk) | -| `--prompt` | Output a prompt for an AI agent to integrate Clerk, then exit | | `-y, --yes` | Skip confirmation prompts (also skips authentication after bootstrap, letting you connect your account later) | | `--no-skills` | Skip the optional agent skills install prompt at the end of init | @@ -39,31 +37,28 @@ When running in agent mode (`--mode agent` or non-TTY), the command runs the ful - For **new projects** (`--starter` or blank directory): `--framework` is required (no way to auto-detect in an empty dir). Package manager is auto-selected by availability (bun → pnpm → yarn → npm) unless `--pm` is provided - Project name defaults to the framework's default (e.g. `my-clerk-next-app`) unless `--name` is provided -Use `--prompt` to output a setup prompt for an AI agent without running init. - ## Flow -1. **`--prompt`**: outputs a framework-specific prompt, then exits -2. Gathers project context (framework, router variant, TypeScript, `src/` directory, package manager) -3. Determines auth mode from credential presence (no user prompt): +1. Gathers project context (framework, router variant, TypeScript, `src/` directory, package manager) +2. Determines auth mode from credential presence (no user prompt): - **Authenticated** (OAuth token or `CLERK_PLATFORM_API_KEY` set): uses the authenticated flow — runs `clerk link` if not already linked and pulls real API keys into `.env` at the end - **Bootstrap + keyless-capable framework + not authenticated**: automatically uses keyless mode — the app runs on auto-generated dev keys and the user can connect a Clerk account later with `clerk auth login` - **Bootstrap + non-keyless framework + not authenticated** (with `--yes` or agent mode): skips authentication and prints manual setup instructions (run `clerk auth login` / `clerk link` / `clerk env pull` when ready) - **Existing project + not authenticated**: runs the authenticated flow, which triggers an interactive login so real keys can be pulled -4. **Authenticated mode only**: authenticates via `clerk auth login` (skipped if already authenticated) and links the project via `clerk link` (skipped if already linked) -5. Displays detected framework and variant -6. Detects existing auth libraries (NextAuth, Auth0, Supabase, Firebase, Passport, Better Auth, Kinde) and shows migration guidance -7. Installs the appropriate Clerk SDK (skips if already present) -8. Generates a scaffold plan for the detected framework -9. Warns if the git working tree has uncommitted changes -10. Previews planned file changes and asks for confirmation -11. Writes scaffold files to disk -12. Runs project formatters (Prettier/Biome) on generated files -13. Scans for issues: hardcoded keys, leftover auth-library imports, stale API calls -14. Prints a summary of created, modified, and skipped files with recommendations -15. **Authenticated mode**: pulls development instance API keys via `clerk env pull` -16. **Unauthenticated mode**: prints instructions for development without API keys and how to connect a Clerk account later -17. Optionally installs Clerk agent skills (core + features, plus a framework-specific skill) via the project's package runner (see [Agent skills install](#agent-skills-install)) +3. **Authenticated mode only**: authenticates via `clerk auth login` (skipped if already authenticated) and links the project via `clerk link` (skipped if already linked) +4. Displays detected framework and variant +5. Detects existing auth libraries (NextAuth, Auth0, Supabase, Firebase, Passport, Better Auth, Kinde) and shows migration guidance +6. Installs the appropriate Clerk SDK (skips if already present) +7. Generates a scaffold plan for the detected framework +8. Warns if the git working tree has uncommitted changes +9. Previews planned file changes and asks for confirmation +10. Writes scaffold files to disk +11. Runs project formatters (Prettier/Biome) on generated files +12. Scans for issues: hardcoded keys, leftover auth-library imports, stale API calls +13. Prints a summary of created, modified, and skipped files with recommendations +14. **Authenticated mode**: pulls development instance API keys via `clerk env pull` +15. **Unauthenticated mode**: prints instructions for development without API keys and how to connect a Clerk account later +16. Optionally installs Clerk agent skills (core + features, plus a framework-specific skill) via the project's package runner (see [Agent skills install](#agent-skills-install)) ## Framework Detection @@ -185,7 +180,6 @@ After scaffolding (and after env keys are pulled or keyless instructions are pri - **Human mode**: prompts `Install agent skills? (...)` defaulting to yes. Pass `--no-skills` to suppress the prompt entirely, or `-y/--yes` to accept it without confirmation. When more than one runner is available, a second prompt picks which one to use (the project's package manager wins by default). - **Agent mode**: skills are installed non-interactively with `-y -g` flags (no prompt shown). Pass `--no-skills` to skip entirely. -- **`--prompt`**: exits before the skills step runs. Agent users should run `skills add clerk/skills` via their preferred runner manually; the bundled `clerk` skill is only installable via `clerk init` itself, since its source lives inside the CLI binary. Two install commands run, sharing one runner: diff --git a/packages/cli-core/src/commands/init/heuristics.ts b/packages/cli-core/src/commands/init/heuristics.ts index d6fdb4fc..fab992ba 100644 --- a/packages/cli-core/src/commands/init/heuristics.ts +++ b/packages/cli-core/src/commands/init/heuristics.ts @@ -6,7 +6,7 @@ import { log } from "../../lib/log.js"; import { getValidToken, hasStoredCredentials } from "../../lib/credential-store.js"; import { fetchUserInfo } from "../../lib/token-exchange.js"; import { printFindings } from "./scan.js"; -import { pmInstallCommand } from "./prompts/index.js"; +import { pmInstallCommand } from "../../lib/package-manager.js"; import { withSpinner } from "../../lib/spinner.js"; import type { FileAction, ProjectContext, ScaffoldPlan } from "./frameworks/types.js"; import type { ScanFinding } from "./scan.js"; diff --git a/packages/cli-core/src/commands/init/index.test.ts b/packages/cli-core/src/commands/init/index.test.ts index 2f05a203..bc32b697 100644 --- a/packages/cli-core/src/commands/init/index.test.ts +++ b/packages/cli-core/src/commands/init/index.test.ts @@ -169,16 +169,6 @@ describe("init", () => { expect(bootstrapMod.promptAndBootstrap).not.toHaveBeenCalled(); }); - test("--prompt flag prints guidance and exits", async () => { - const { captured } = setup({ isAgent: false }); - - await captured.run(() => init({ prompt: true })); - - expect(captured.out).toContain("clerk init -y"); - expect(loginMod.login).not.toHaveBeenCalled(); - expect(bootstrapMod.promptAndBootstrap).not.toHaveBeenCalled(); - }); - test("blank dir in human mode triggers bootstrap flow", async () => { setup(); setupBootstrapSuccess(); diff --git a/packages/cli-core/src/commands/init/index.ts b/packages/cli-core/src/commands/init/index.ts index 79e35ace..e69908d1 100644 --- a/packages/cli-core/src/commands/init/index.ts +++ b/packages/cli-core/src/commands/init/index.ts @@ -46,8 +46,6 @@ type InitOptions = { pm?: PackageManager; name?: string; yes?: boolean; - /** Output a prompt for an AI agent to integrate Clerk, then exit. */ - prompt?: boolean; /** Install the optional agent skills (set to false via `--no-skills` to skip). */ skills?: boolean; /** Create a new project from a starter template. */ @@ -63,13 +61,6 @@ export async function init(options: InitOptions = {}) { ? (lookupFramework(options.framework) ?? undefined) : undefined; - if (options.prompt) { - log.data( - "Run `clerk init -y` to automatically detect the framework, install the Clerk SDK, and scaffold authentication files without interactive prompts.", - ); - return; - } - // In agent mode, implicitly enable --yes to skip all confirmation prompts. const overrides: BootstrapOverrides = { skipConfirm: options.yes || isAgent(), diff --git a/packages/cli-core/src/commands/init/prompts/astro.md b/packages/cli-core/src/commands/init/prompts/astro.md deleted file mode 100644 index 2702e735..00000000 --- a/packages/cli-core/src/commands/init/prompts/astro.md +++ /dev/null @@ -1,113 +0,0 @@ -# Add Clerk to Astro - -Install `{{SDK}}`. Add `clerk()` integration to `astro.config.mjs`. Create middleware with `clerkMiddleware()`. Use ``, ``, ``, `` from `@clerk/astro/components`. - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## astro.config.mjs - -```typescript -import { defineConfig } from "astro/config"; -import clerk from "@clerk/astro"; - -export default defineConfig({ - integrations: [clerk()], - output: "server", -}); -``` - -## src/middleware.ts - -```typescript -import { clerkMiddleware } from "@clerk/astro/server"; - -export const onRequest = clerkMiddleware(); -``` - -## src/pages/sign-in.astro - -```astro ---- -import { SignIn } from '@clerk/astro/components'; ---- - -``` - -## src/pages/sign-up.astro - -```astro ---- -import { SignUp } from '@clerk/astro/components'; ---- - -``` - -## Example usage in a layout - -```astro ---- -import { Show, SignInButton, SignUpButton, UserButton } from '@clerk/astro/components'; ---- -
- - - - - - - -
-``` - -## Environment - -Env vars (`{{ENV_VAR}}` and `CLERK_SECRET_KEY`) are in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Add `clerk()` to `integrations` in `astro.config.mjs` -- Set `output: 'server'` (SSR required) with an SSR adapter -- Use `clerkMiddleware()` from `@clerk/astro/server` in `src/middleware.ts` -- Import components from `@clerk/astro/components` -- Use `` for conditional rendering -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Use `authMiddleware()` (replaced by `clerkMiddleware()`) -- Use deprecated ``, `` (replaced by ``) -- Import from `@clerk/react` or `@clerk/nextjs` — use `@clerk/astro` -- Skip `output: 'server'` (Clerk requires SSR) - -## Deprecated (DO NOT use) - -```typescript -import { authMiddleware } from '@clerk/astro' // WRONG — use clerkMiddleware - // WRONG — use - // WRONG — use -output: 'static' // WRONG — Clerk requires SSR -``` - -## Verify Before Responding - -1. Is `clerk()` in `integrations` in `astro.config.mjs`? -2. Is `output: 'server'` set? -3. Is `clerkMiddleware()` exported as `onRequest` in `src/middleware.ts`? -4. Are components imported from `@clerk/astro/components`? -5. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user. After signup succeeds and a profile icon appears, congratulate them. Then recommend exploring: Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/expo.md b/packages/cli-core/src/commands/init/prompts/expo.md deleted file mode 100644 index 77d43fb5..00000000 --- a/packages/cli-core/src/commands/init/prompts/expo.md +++ /dev/null @@ -1,119 +0,0 @@ -# Add Clerk to Expo - -Install `{{SDK}}`. Wrap the app in `` with a secure token cache. Use ``, ``, ``, `` from `@clerk/expo`. - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## Token Cache - -Create a secure token cache using `expo-secure-store`: - -```bash -{{INSTALL_CMD_EXTRA}} -``` - -```typescript -import * as SecureStore from "expo-secure-store"; - -export const tokenCache = { - async getToken(key: string) { - return SecureStore.getItemAsync(key); - }, - async saveToken(key: string, value: string) { - return SecureStore.setItemAsync(key, value); - }, - async clearToken(key: string) { - return SecureStore.deleteItemAsync(key); - }, -}; -``` - -## App entry (app/\_layout.{{JSX}} or App.{{JSX}}) - -```typescript -import { ClerkProvider, ClerkLoaded } from "@clerk/expo"; -import { tokenCache } from "./token-cache"; - -export default function RootLayout() { - const publishableKey = process.env.{{ENV_VAR}}; - - return ( - - - {/* your app content */} - - - ); -} -``` - -## Example usage - -```typescript -import { Show, SignInButton, SignUpButton, UserButton } from "@clerk/expo"; - -export default function Home() { - return ( - <> - - - - - - - - - ); -} -``` - -## Environment - -Env var (`{{ENV_VAR}}`) is in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Use `@clerk/expo` (not `@clerk/react` or `@clerk/nextjs`) -- Pass `tokenCache` to `` for secure token storage -- Pass `publishableKey` explicitly from `process.env.{{ENV_VAR}}` -- Wrap content with `` inside `` -- Use `` for conditional rendering -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Use `@clerk/react` or `@clerk/nextjs` — use `@clerk/expo` -- Use deprecated ``, `` (replaced by ``) -- Skip the `tokenCache` (tokens won't persist across app restarts) -- Use `localStorage` or `AsyncStorage` directly for tokens - -## Deprecated (DO NOT use) - -```typescript -import { ClerkProvider } from "@clerk/react" // WRONG — use @clerk/expo - // WRONG — use - // WRONG — use -``` - -## Verify Before Responding - -1. Is `@clerk/expo` used (not `@clerk/react`)? -2. Is `tokenCache` passed to ``? -3. Is `` wrapping the app content? -4. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user. After signup succeeds, congratulate them. Then recommend exploring: Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/express.md b/packages/cli-core/src/commands/init/prompts/express.md deleted file mode 100644 index 7ef0893c..00000000 --- a/packages/cli-core/src/commands/init/prompts/express.md +++ /dev/null @@ -1,78 +0,0 @@ -# Add Clerk to Express - -Install `{{SDK}}`. Add `clerkMiddleware()` to the Express app. Use `requireAuth()` to protect routes. Use `getAuth()` to access auth state in handlers. - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## Server setup - -```typescript -import express from "express"; -import { clerkMiddleware, requireAuth, getAuth } from "@clerk/express"; - -const app = express(); - -// Apply Clerk middleware to all routes -app.use(clerkMiddleware()); - -// Public route — no auth required -app.get("/", (req, res) => { - res.json({ message: "Public route" }); -}); - -// Protected route — requires authentication -app.get("/protected", requireAuth(), (req, res) => { - const { userId } = getAuth(req); - res.json({ userId }); -}); - -app.listen(3000); -``` - -## Environment - -Env vars (`{{ENV_VAR}}` and `CLERK_SECRET_KEY`) are in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Use `clerkMiddleware()` from `@clerk/express` as Express middleware -- Use `requireAuth()` to protect routes that need authentication -- Use `getAuth(req)` to access auth state (`userId`, `sessionId`, etc.) -- Apply `clerkMiddleware()` before any route that needs auth -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Use `@clerk/nextjs` or `@clerk/react` — use `@clerk/express` -- Use deprecated `ClerkExpressRequireAuth` or `ClerkExpressWithAuth` (replaced by `requireAuth` and `getAuth`) -- Skip `clerkMiddleware()` — it's required for `requireAuth()` and `getAuth()` to work - -## Deprecated (DO NOT use) - -```typescript -import { ClerkExpressRequireAuth } from "@clerk/express"; // WRONG — use requireAuth -import { ClerkExpressWithAuth } from "@clerk/express"; // WRONG — use clerkMiddleware + getAuth -``` - -## Verify Before Responding - -1. Is `clerkMiddleware()` applied as middleware? -2. Are protected routes using `requireAuth()`? -3. Is `getAuth(req)` used to access auth state (not `req.auth` directly)? -4. Are imports from `@clerk/express`? - -If any fails, revise. - -## After Setup - -Have the user test the protected route by sending a request without authentication (should get 401) and with a valid session token (should succeed). Then recommend exploring: Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/fastify.md b/packages/cli-core/src/commands/init/prompts/fastify.md deleted file mode 100644 index a9c712ac..00000000 --- a/packages/cli-core/src/commands/init/prompts/fastify.md +++ /dev/null @@ -1,78 +0,0 @@ -# Add Clerk to Fastify - -Install `{{SDK}}`. Register `clerkPlugin` with the Fastify instance. Use `getAuth()` to access auth state in handlers. - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## Server setup - -```typescript -import Fastify from "fastify"; -import { clerkPlugin, getAuth } from "@clerk/fastify"; - -const fastify = Fastify(); - -// Register Clerk plugin -fastify.register(clerkPlugin); - -// Public route — no auth required -fastify.get("/", async (request, reply) => { - return { message: "Public route" }; -}); - -// Protected route — check auth in handler -fastify.get("/protected", async (request, reply) => { - const { userId } = getAuth(request); - if (!userId) { - return reply.code(401).send({ error: "Unauthorized" }); - } - return { userId }; -}); - -fastify.listen({ port: 3000 }); -``` - -## Environment - -Env vars (`{{ENV_VAR}}` and `CLERK_SECRET_KEY`) are in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Register `clerkPlugin` from `@clerk/fastify` with `fastify.register()` -- Use `getAuth(request)` to access auth state (`userId`, `sessionId`, etc.) -- Register the plugin before defining routes that need auth -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Use `@clerk/nextjs` or `@clerk/express` — use `@clerk/fastify` -- Use deprecated `clerkPreHandler` (replaced by `clerkPlugin` + `getAuth`) -- Skip `clerkPlugin` registration — it's required for `getAuth()` to work - -## Deprecated (DO NOT use) - -```typescript -import { clerkPreHandler } from "@clerk/fastify"; // WRONG — use clerkPlugin + getAuth -``` - -## Verify Before Responding - -1. Is `clerkPlugin` registered with `fastify.register()`? -2. Is `getAuth(request)` used to access auth state? -3. Are imports from `@clerk/fastify`? - -If any fails, revise. - -## After Setup - -Have the user test the protected route by sending a request without authentication (should get 401) and with a valid session token (should succeed). Then recommend exploring: Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/generic-fallback.md b/packages/cli-core/src/commands/init/prompts/generic-fallback.md deleted file mode 100644 index f82ea61d..00000000 --- a/packages/cli-core/src/commands/init/prompts/generic-fallback.md +++ /dev/null @@ -1,49 +0,0 @@ -# Add Clerk to {{FRAMEWORK_NAME}} - -Install `{{SDK}}`. Set up the Clerk provider/middleware for {{FRAMEWORK_NAME}}. Use `` for conditional auth rendering. - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## Steps - -1. Set up the Clerk provider/middleware for {{FRAMEWORK_NAME}}. -2. Create sign-in and sign-up routes/components. -3. Use `` and `` for conditional rendering. - -## Environment - -Env vars (`{{ENV_VAR}}` and `CLERK_SECRET_KEY`) are in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Use `{{SDK}}` — the correct SDK for {{FRAMEWORK_NAME}} -- Use `` for conditional rendering -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Use deprecated ``, `` (replaced by ``) -- Use `authMiddleware()` (replaced by `clerkMiddleware()`) -- Import from the wrong Clerk SDK package - -## Verify Before Responding - -1. Is `{{SDK}}` installed? -2. Is the Clerk provider wrapping the application? -3. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user. After signup succeeds and a profile icon appears, congratulate them. Then recommend exploring: Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/generic.md b/packages/cli-core/src/commands/init/prompts/generic.md deleted file mode 100644 index 84fb01a6..00000000 --- a/packages/cli-core/src/commands/init/prompts/generic.md +++ /dev/null @@ -1,44 +0,0 @@ -# Add Clerk Authentication - -Integrate Clerk auth into this project. Use the Clerk SDK appropriate for the project's framework. - -Latest docs: https://clerk.com/docs - -## Steps - -1. Authenticate with Clerk: Run `clerk auth login` to log in via the browser. -2. Link a Clerk application: Run `clerk link` to associate this directory with a Clerk application. -3. Install the Clerk SDK appropriate for the project's framework (see https://clerk.com/docs). -4. Pull environment variables with `clerk env pull`. -5. Set up the Clerk provider at the root of the application. -6. Add sign-in and sign-up routes/components. -7. Protect routes that require authentication. - -## Rules - -ALWAYS: - -- Use the framework-specific Clerk SDK (e.g. `@clerk/nextjs`, `@clerk/react`, `@clerk/vue`) -- Use `` for conditional rendering based on auth state -- Use existing package manager -- Follow the framework-specific quickstart at https://clerk.com/docs - -NEVER: - -- Use deprecated ``, `` (replaced by ``) -- Use `authMiddleware()` (replaced by `clerkMiddleware()`) -- Use `frontendApi` (removed, use `publishableKey` env var) - -## Verify Before Responding - -1. Is the correct framework-specific Clerk SDK installed? -2. Is the Clerk provider wrapping the application root? -3. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user. After signup succeeds and a profile icon appears, congratulate them. Then recommend exploring: Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/index.ts b/packages/cli-core/src/commands/init/prompts/index.ts deleted file mode 100644 index 1dc36bbc..00000000 --- a/packages/cli-core/src/commands/init/prompts/index.ts +++ /dev/null @@ -1,160 +0,0 @@ -import type { ProjectContext } from "../frameworks/types.js"; -import { jsxExt, scriptExt, srcPrefix } from "../frameworks/helpers.js"; - -// Static text imports — embedded at build time, safe for compiled binaries. -import genericMd from "./generic.md" with { type: "text" }; -import genericFallbackMd from "./generic-fallback.md" with { type: "text" }; -import nextjsAppRouterMd from "./nextjs-app-router.md" with { type: "text" }; -import nextjsPagesRouterMd from "./nextjs-pages-router.md" with { type: "text" }; -import reactMd from "./react.md" with { type: "text" }; -import reactRouterMd from "./react-router.md" with { type: "text" }; -import nuxtMd from "./nuxt.md" with { type: "text" }; -import tanstackStartMd from "./tanstack-start.md" with { type: "text" }; -import astroMd from "./astro.md" with { type: "text" }; -import vueMd from "./vue.md" with { type: "text" }; -import expoMd from "./expo.md" with { type: "text" }; -import expressMd from "./express.md" with { type: "text" }; -import fastifyMd from "./fastify.md" with { type: "text" }; - -const TEMPLATES = { - generic: genericMd, - "generic-fallback": genericFallbackMd, - "nextjs-app-router": nextjsAppRouterMd, - "nextjs-pages-router": nextjsPagesRouterMd, - react: reactMd, - "react-router": reactRouterMd, - nuxt: nuxtMd, - "tanstack-start": tanstackStartMd, - astro: astroMd, - vue: vueMd, - expo: expoMd, - express: expressMd, - fastify: fastifyMd, -} satisfies Record; - -type TemplateName = keyof typeof TEMPLATES; -type FrameworkTemplateName = Exclude; -type FrameworkPromptInfo = { template: FrameworkTemplateName; docsUrl: string }; - -function loadTemplate(name: TemplateName): string { - const template = TEMPLATES[name]; - // The project formatter escapes underscores in markdown headings (e.g. `_app` → `\_app`). - // These templates are output as plain text, so undo that escaping. - return template.replaceAll("\\_", "_"); -} - -function interpolate(template: string, vars: Record): string { - return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`); -} - -const PM_COMMANDS = { - bun: "bun add", - yarn: "yarn add", - pnpm: "pnpm add", - npm: "npm install", -} satisfies Record; - -export function pmInstallCommand(pm: ProjectContext["packageManager"]): string { - return PM_COMMANDS[pm]; -} - -// Maps framework dep to its template filename and docs URL. -// Next.js defaults to app-router; pages-router variant is handled in resolveTemplate. -const FRAMEWORK_PROMPTS: Record = { - next: { - template: "nextjs-app-router", - docsUrl: "https://clerk.com/docs/nextjs/getting-started/quickstart", - }, - react: { template: "react", docsUrl: "https://clerk.com/docs/react/getting-started/quickstart" }, - "react-router": { - template: "react-router", - docsUrl: "https://clerk.com/docs/react-router/getting-started/quickstart", - }, - nuxt: { template: "nuxt", docsUrl: "https://clerk.com/docs/nuxt/getting-started/quickstart" }, - "@tanstack/react-start": { - template: "tanstack-start", - docsUrl: "https://clerk.com/docs/tanstack-start/getting-started/quickstart", - }, - astro: { template: "astro", docsUrl: "https://clerk.com/docs/astro/getting-started/quickstart" }, - vue: { template: "vue", docsUrl: "https://clerk.com/docs/vue/getting-started/quickstart" }, - vite: { - template: "generic-fallback", - docsUrl: "https://clerk.com/docs/js-frontend/getting-started/quickstart", - }, - expo: { template: "expo", docsUrl: "https://clerk.com/docs/expo/getting-started/quickstart" }, - express: { - template: "express", - docsUrl: "https://clerk.com/docs/express/getting-started/quickstart", - }, - fastify: { - template: "fastify", - docsUrl: "https://clerk.com/docs/fastify/getting-started/quickstart", - }, -}; - -const DEFAULT_DOCS_URL = "https://clerk.com/docs"; - -type RequiredPromptVar = - | "SDK" - | "ENV_VAR" - | "INSTALL_CMD" - | "BASE" - | "BASE_DISPLAY" - | "EXT" - | "JSX" - | "MIDDLEWARE_BASENAME" - | "LAYOUT_PATH" - | "ENV_FILE" - | "PM" - | "DOCS_URL" - | "FRAMEWORK_NAME"; - -type OptionalPromptVar = "INSTALL_CMD_EXTRA"; -type PromptVars = Record & Partial>; - -// NOTE: The agent prompts show simple `clerkMiddleware()` (matching official docs). -// The scaffold code in `frameworks/helpers.ts` uses `createRouteMatcher` + `auth.protect()` -// which is more opinionated. This divergence is intentional — agents should follow the -// docs pattern; scaffolded code provides a production-ready starting point. - -function buildVars(ctx: ProjectContext): PromptVars { - const base = srcPrefix(ctx); - const ext = scriptExt(ctx); - const jsx = jsxExt(ctx); - const installCmd = `${pmInstallCommand(ctx.packageManager)} ${ctx.framework.sdk}`; - - const vars: PromptVars = { - SDK: ctx.framework.sdk, - ENV_VAR: ctx.framework.envVar, - INSTALL_CMD: installCmd, - BASE: base, - BASE_DISPLAY: base || "project root", - EXT: ext, - JSX: jsx, - MIDDLEWARE_BASENAME: ctx.middlewareBasename ?? "proxy", - LAYOUT_PATH: ctx.layoutPath ?? `${base}app/layout.${jsx}`, - ENV_FILE: ctx.envFile, - PM: ctx.packageManager, - DOCS_URL: FRAMEWORK_PROMPTS[ctx.framework.dep]?.docsUrl ?? DEFAULT_DOCS_URL, - FRAMEWORK_NAME: ctx.framework.name, - }; - - if (ctx.framework.dep === "expo") { - vars.INSTALL_CMD_EXTRA = `${pmInstallCommand(ctx.packageManager)} expo-secure-store`; - } - - return vars; -} - -function resolveTemplate(ctx: ProjectContext): TemplateName { - if (ctx.framework.dep === "next" && ctx.variant === "pages-router") { - return "nextjs-pages-router"; - } - return FRAMEWORK_PROMPTS[ctx.framework.dep]?.template ?? "generic-fallback"; -} - -export const GENERIC_AGENT_PROMPT = loadTemplate("generic"); - -export function buildAgentPrompt(ctx: ProjectContext): string { - return interpolate(loadTemplate(resolveTemplate(ctx)), buildVars(ctx)); -} diff --git a/packages/cli-core/src/commands/init/prompts/md.d.ts b/packages/cli-core/src/commands/init/prompts/md.d.ts deleted file mode 100644 index c94d67b1..00000000 --- a/packages/cli-core/src/commands/init/prompts/md.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module "*.md" { - const content: string; - export default content; -} diff --git a/packages/cli-core/src/commands/init/prompts/nextjs-app-router.md b/packages/cli-core/src/commands/init/prompts/nextjs-app-router.md deleted file mode 100644 index 29c90651..00000000 --- a/packages/cli-core/src/commands/init/prompts/nextjs-app-router.md +++ /dev/null @@ -1,129 +0,0 @@ -# Add Clerk to Next.js App Router - -Install `{{SDK}}`. Create `{{MIDDLEWARE_BASENAME}}.{{EXT}}` with `clerkMiddleware()` from `@clerk/nextjs/server` (in `{{BASE_DISPLAY}}`). Add `` inside `` in `{{LAYOUT_PATH}}`. Use ``, ``, ``, `` from `@clerk/nextjs`. - -Latest docs: {{DOCS_URL}} - -## Keyless Mode - -No signup required. Without env vars (`{{ENV_VAR}}`, `CLERK_SECRET_KEY`), Clerk auto-generates temporary keys. A "Configure your application" prompt appears to claim later. Do NOT tell users to sign up, create accounts, get API keys, or add env vars before running. - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## {{BASE}}{{MIDDLEWARE_BASENAME}}.{{EXT}} - -```typescript -import { clerkMiddleware } from "@clerk/nextjs/server"; - -export default clerkMiddleware(); - -export const config = { - matcher: [ - "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", - "/(api|trpc)(.*)", - ], -}; -``` - -## {{LAYOUT_PATH}} - -```typescript -import { ClerkProvider, SignInButton, SignUpButton, Show, UserButton } from "@clerk/nextjs"; - -export default function RootLayout({ children }: { children: React.ReactNode }) { - return ( - - - -
- - - - - - - -
- {children} -
- - - ); -} -``` - -## {{BASE}}app/sign-in/[[...sign-in]]/page.{{JSX}} - -```typescript -import { SignIn } from "@clerk/nextjs"; -export default function SignInPage() { return ; } -``` - -## {{BASE}}app/sign-up/[[...sign-up]]/page.{{JSX}} - -```typescript -import { SignUp } from "@clerk/nextjs"; -export default function SignUpPage() { return ; } -``` - -## Environment - -Add to `.env.local`: - -``` -NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in -NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up -``` - -Env vars (`{{ENV_VAR}}` and `CLERK_SECRET_KEY`) are in `.env.local` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Use `clerkMiddleware()` from `@clerk/nextjs/server` in `{{MIDDLEWARE_BASENAME}}.{{EXT}}` -- Add `` inside `` in `{{LAYOUT_PATH}}` -- Import from `@clerk/nextjs` or `@clerk/nextjs/server` -- Use App Router (`app/page.tsx`, `app/layout.tsx`) -- Use `async/await` with `auth()` from `@clerk/nextjs/server` -- Use existing package manager (`{{PM}}`) -- Rely on keyless mode — skip account creation and API keys - -NEVER: - -- Reference `_app.tsx` or pages router -- Use `authMiddleware()` (replaced by `clerkMiddleware()`) -- Use old env var patterns -- Import deprecated APIs (`withAuth`, old `currentUser`) -- Use deprecated ``, `` (replaced by ``) -- Tell users to sign up or get API keys first - -## Deprecated (DO NOT use) - -```typescript -import { authMiddleware } from '@clerk/nextjs' // WRONG — use clerkMiddleware -function MyApp({ Component, pageProps }) {} // WRONG — pages router pattern -pages/signin.js // WRONG — use app/ directory - // WRONG — use - // WRONG — use -``` - -## Verify Before Responding - -1. Is `clerkMiddleware()` used in `{{MIDDLEWARE_BASENAME}}.{{EXT}}`? -2. Is `ClerkProvider` inside `` in `{{LAYOUT_PATH}}`? -3. Are imports only from `@clerk/nextjs` or `@clerk/nextjs/server`? -4. Is it using App Router, not `_app.tsx` or `pages/`? -5. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user in the nav. After signup succeeds and a profile icon appears, congratulate them. If a "Configure your application" callout appears, tell them to click it. Then recommend exploring: Organizations (https://clerk.com/docs/guides/organizations/overview), Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/nextjs-pages-router.md b/packages/cli-core/src/commands/init/prompts/nextjs-pages-router.md deleted file mode 100644 index be2447a3..00000000 --- a/packages/cli-core/src/commands/init/prompts/nextjs-pages-router.md +++ /dev/null @@ -1,112 +0,0 @@ -# Add Clerk to Next.js Pages Router - -Install `{{SDK}}`. Create `{{MIDDLEWARE_BASENAME}}.{{EXT}}` with `clerkMiddleware()` from `@clerk/nextjs/server`. Wrap `` with `` in `_app.{{JSX}}`. Use ``, ``, ``, `` from `@clerk/nextjs`. - -Latest docs: {{DOCS_URL}} - -## Keyless Mode - -No signup required. Without env vars (`{{ENV_VAR}}`, `CLERK_SECRET_KEY`), Clerk auto-generates temporary keys. A "Configure your application" prompt appears to claim later. Do NOT tell users to sign up, create accounts, get API keys, or add env vars before running. - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## {{BASE}}{{MIDDLEWARE_BASENAME}}.{{EXT}} - -```typescript -import { clerkMiddleware } from "@clerk/nextjs/server"; - -export default clerkMiddleware(); - -export const config = { - matcher: [ - "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", - "/(api|trpc)(.*)", - ], -}; -``` - -## {{BASE}}pages/\_app.{{JSX}} - -```typescript -import { ClerkProvider } from "@clerk/nextjs"; -import type { AppProps } from "next/app"; - -export default function MyApp({ Component, pageProps }: AppProps) { - return ( - - - - ); -} -``` - -## {{BASE}}pages/sign-in/[[...sign-in]].{{JSX}} - -```typescript -import { SignIn } from "@clerk/nextjs"; -export default function SignInPage() { return ; } -``` - -## {{BASE}}pages/sign-up/[[...sign-up]].{{JSX}} - -```typescript -import { SignUp } from "@clerk/nextjs"; -export default function SignUpPage() { return ; } -``` - -## Environment - -Add to `.env.local`: - -``` -NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in -NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up -``` - -Env vars (`{{ENV_VAR}}` and `CLERK_SECRET_KEY`) are in `.env.local` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Use `clerkMiddleware()` from `@clerk/nextjs/server` in `{{MIDDLEWARE_BASENAME}}.{{EXT}}` -- Wrap `` with `` in `_app.{{JSX}}` -- Import from `@clerk/nextjs` or `@clerk/nextjs/server` -- Use `async/await` with `auth()` from `@clerk/nextjs/server` in API routes -- Use existing package manager (`{{PM}}`) -- Rely on keyless mode — skip account creation and API keys - -NEVER: - -- Use `authMiddleware()` (replaced by `clerkMiddleware()`) -- Use old env var patterns -- Import deprecated APIs (`withAuth`, old `currentUser`) -- Use deprecated ``, `` (replaced by ``) -- Tell users to sign up or get API keys first - -## Deprecated (DO NOT use) - -```typescript -import { authMiddleware } from '@clerk/nextjs' // WRONG — use clerkMiddleware - // WRONG — use - // WRONG — use -``` - -## Verify Before Responding - -1. Is `clerkMiddleware()` used in `{{MIDDLEWARE_BASENAME}}.{{EXT}}`? -2. Is `` wrapping `` in `_app.{{JSX}}`? -3. Are imports only from `@clerk/nextjs` or `@clerk/nextjs/server`? -4. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user in the nav. After signup succeeds and a profile icon appears, congratulate them. If a "Configure your application" callout appears, tell them to click it. Then recommend exploring: Organizations (https://clerk.com/docs/guides/organizations/overview), Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/nuxt.md b/packages/cli-core/src/commands/init/prompts/nuxt.md deleted file mode 100644 index 7fa00a3a..00000000 --- a/packages/cli-core/src/commands/init/prompts/nuxt.md +++ /dev/null @@ -1,94 +0,0 @@ -# Add Clerk to Nuxt - -Install `{{SDK}}`. Add `@clerk/nuxt` to the `modules` array in `nuxt.config.ts`. Middleware is auto-configured. Use ``, ``, ``, `` (auto-imported). - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## nuxt.config.ts - -```typescript -export default defineNuxtConfig({ - modules: ["@clerk/nuxt"], -}); -``` - -## pages/sign-in/[...slug].vue - -```vue - -``` - -## pages/sign-up/[...slug].vue - -```vue - -``` - -## Example usage in a component - -```vue - -``` - -## Environment - -Env vars (`{{ENV_VAR}}` and `NUXT_CLERK_SECRET_KEY`) are in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Add `'@clerk/nuxt'` to the `modules` array in `nuxt.config.ts` -- Let Nuxt auto-import Clerk components (no manual imports needed) -- Use `` for conditional rendering -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Manually configure middleware (Nuxt module handles it) -- Manually import Clerk components in `.vue` files (auto-imported) -- Use deprecated ``, `` (replaced by ``) -- Import from `@clerk/vue` — use `@clerk/nuxt` - -## Deprecated (DO NOT use) - -```typescript -import { clerkMiddleware } from '@clerk/nuxt' // WRONG — module auto-configures middleware - // WRONG — use - // WRONG — use -import { SignIn } from "@clerk/vue" // WRONG — auto-imported by Nuxt module -``` - -## Verify Before Responding - -1. Is `'@clerk/nuxt'` in the `modules` array in `nuxt.config.ts`? -2. Are Clerk components used without manual imports? -3. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user. After signup succeeds and a profile icon appears, congratulate them. Then recommend exploring: Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/react-router.md b/packages/cli-core/src/commands/init/prompts/react-router.md deleted file mode 100644 index 70a4dbc0..00000000 --- a/packages/cli-core/src/commands/init/prompts/react-router.md +++ /dev/null @@ -1,115 +0,0 @@ -# Add Clerk to React Router - -Install `{{SDK}}`. Enable middleware in `react-router.config.ts`. Set up `clerkMiddleware()` and `ClerkProvider` in `app/root.tsx`. Use ``, ``, ``, `` from `@clerk/react-router`. - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## react-router.config.ts - -```typescript -import type { Config } from "@react-router/dev/config"; - -export default { - future: { - v8_middleware: true, - }, -} satisfies Config; -``` - -## app/root.tsx - -```typescript -import { clerkMiddleware, rootAuthLoader } from "@clerk/react-router/server"; -import { ClerkProvider } from "@clerk/react-router"; -import type { Route } from "./+types/root"; - -export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()]; - -export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args); - -export default function Root({ loaderData }: Route.ComponentProps) { - return ( - - {/* your app content */} - - ); -} -``` - -## app/routes/sign-in.{{JSX}} - -```typescript -import { SignIn } from "@clerk/react-router"; -export default function SignInPage() { return ; } -``` - -## app/routes/sign-up.{{JSX}} - -```typescript -import { SignUp } from "@clerk/react-router"; -export default function SignUpPage() { return ; } -``` - -## app/routes.ts - -```typescript -import { type RouteConfig, route } from "@react-router/dev/routes"; - -export default [ - route("sign-in/*", "routes/sign-in.tsx"), - route("sign-up/*", "routes/sign-up.tsx"), -] satisfies RouteConfig; -``` - -## Environment - -Env vars (`{{ENV_VAR}}` and `CLERK_SECRET_KEY`) are in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Enable `future: { v8_middleware: true }` in `react-router.config.ts` -- Use `clerkMiddleware()` from `@clerk/react-router/server` in `app/root.tsx` -- Use `rootAuthLoader` for the root loader -- Wrap content with `` -- Use `` for conditional rendering -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Use `authMiddleware()` (replaced by `clerkMiddleware()`) -- Skip the `rootAuthLoader` in the root route -- Use deprecated ``, `` (replaced by ``) -- Import from `@clerk/nextjs` or `@clerk/react` — use `@clerk/react-router` - -## Deprecated (DO NOT use) - -```typescript -import { authMiddleware } from '@clerk/react-router' // WRONG — use clerkMiddleware - // WRONG — use - // WRONG — use -import { ClerkProvider } from "@clerk/react" // WRONG — use @clerk/react-router -``` - -## Verify Before Responding - -1. Is `v8_middleware: true` set in `react-router.config.ts`? -2. Is `clerkMiddleware()` exported in `app/root.tsx`? -3. Is `rootAuthLoader` used as the root loader? -4. Is `` wrapping the app? -5. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user. After signup succeeds and a profile icon appears, congratulate them. Then recommend exploring: Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/react.md b/packages/cli-core/src/commands/init/prompts/react.md deleted file mode 100644 index 67a86c93..00000000 --- a/packages/cli-core/src/commands/init/prompts/react.md +++ /dev/null @@ -1,94 +0,0 @@ -# Add Clerk to React - -Install `{{SDK}}`. Wrap the app in `` in `{{BASE}}main.{{JSX}}`. Use ``, ``, ``, `` from `@clerk/react`. - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## {{BASE}}main.{{JSX}} - -```typescript -import { StrictMode } from "react"; -import { createRoot } from "react-dom/client"; -import App from "./App.{{JSX}}"; -import { ClerkProvider } from "@clerk/react"; - -createRoot(document.getElementById("root")!).render( - - - - - -); -``` - -## App.{{JSX}} - -```typescript -import { Show, SignInButton, SignUpButton, UserButton } from "@clerk/react"; - -export default function App() { - return ( -
- - - - - - - -
- ); -} -``` - -## Environment - -Env var (`{{ENV_VAR}}`) is in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Use `@clerk/react` (not any other Clerk package) -- Reference env var as `{{ENV_VAR}}` in `{{ENV_FILE}}` -- Wrap the entire app in `` within `{{BASE}}main.{{JSX}}` -- Use ``, ``, ``, `` -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Use `frontendApi` in place of `publishableKey` -- Use older env var names like `REACT_APP_CLERK_FRONTEND_API` or `VITE_REACT_APP_CLERK_PUBLISHABLE_KEY` -- Manually pass `publishableKey` as a prop to `` -- Place `` deeper in the component tree instead of `{{BASE}}main.{{JSX}}` -- Use deprecated ``, `` (replaced by ``) - -## Deprecated (DO NOT use) - -```typescript -import { SignedIn, SignedOut } from "@clerk/react" // WRONG — use - // WRONG — reads from env automatically -frontendApi="..." // WRONG — removed, use publishableKey env var -REACT_APP_CLERK_FRONTEND_API // WRONG — use {{ENV_VAR}} -``` - -## Verify Before Responding - -1. Is `` in `{{BASE}}main.{{JSX}}` without a manual `publishableKey` prop? -2. Is env var named `{{ENV_VAR}}`? -3. Is it using `` instead of ``/``? -4. No usage of `frontendApi`? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user. After signup succeeds and a profile icon appears, congratulate them. Then recommend exploring: Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/tanstack-start.md b/packages/cli-core/src/commands/init/prompts/tanstack-start.md deleted file mode 100644 index a6478fc4..00000000 --- a/packages/cli-core/src/commands/init/prompts/tanstack-start.md +++ /dev/null @@ -1,108 +0,0 @@ -# Add Clerk to TanStack Start - -Install `{{SDK}}`. Add `clerkMiddleware()` to `src/start.ts`. Wrap content with `` in `src/routes/__root.tsx`. Use ``, ``, ``, `` from `@clerk/tanstack-react-start`. - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## src/start.ts - -```typescript -import { createStart } from "@tanstack/react-start/server"; -import { clerkMiddleware } from "@clerk/tanstack-react-start/server"; - -export default createStart({ - requestMiddleware: [clerkMiddleware()], -}); -``` - -## src/routes/\_\_root.tsx - -```typescript -import { ClerkProvider } from "@clerk/tanstack-react-start"; -import { createRootRoute, Outlet } from "@tanstack/react-router"; - -export const Route = createRootRoute({ - component: RootLayout, -}); - -function RootLayout() { - return ( - - - - ); -} -``` - -## src/routes/sign-in.$.{{JSX}} - -```typescript -import { createFileRoute } from "@tanstack/react-router"; -import { SignIn } from "@clerk/tanstack-react-start"; - -export const Route = createFileRoute("/sign-in/$")({ - component: () => , -}); -``` - -## src/routes/sign-up.$.{{JSX}} - -```typescript -import { createFileRoute } from "@tanstack/react-router"; -import { SignUp } from "@clerk/tanstack-react-start"; - -export const Route = createFileRoute("/sign-up/$")({ - component: () => , -}); -``` - -## Environment - -Env vars (`{{ENV_VAR}}` and `CLERK_SECRET_KEY`) are in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Use `clerkMiddleware()` from `@clerk/tanstack-react-start/server` in `src/start.ts` -- Wrap content with `` in `src/routes/__root.tsx` -- Use `createFileRoute` for sign-in/sign-up routes with `$` splat -- Use `` for conditional rendering -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Use `authMiddleware()` (replaced by `clerkMiddleware()`) -- Use deprecated ``, `` (replaced by ``) -- Import from `@clerk/react` or `@clerk/nextjs` — use `@clerk/tanstack-react-start` -- Skip `requestMiddleware` in `createStart` config - -## Deprecated (DO NOT use) - -```typescript -import { authMiddleware } from '@clerk/tanstack-react-start' // WRONG — use clerkMiddleware - // WRONG — use - // WRONG — use -import { ClerkProvider } from "@clerk/react" // WRONG — use @clerk/tanstack-react-start -``` - -## Verify Before Responding - -1. Is `clerkMiddleware()` in `requestMiddleware` in `src/start.ts`? -2. Is `` wrapping `` in `src/routes/__root.tsx`? -3. Are sign-in/sign-up routes using `createFileRoute` with `$` splat? -4. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user. After signup succeeds and a profile icon appears, congratulate them. Then recommend exploring: Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/prompts/vue.md b/packages/cli-core/src/commands/init/prompts/vue.md deleted file mode 100644 index 80bb8a92..00000000 --- a/packages/cli-core/src/commands/init/prompts/vue.md +++ /dev/null @@ -1,85 +0,0 @@ -# Add Clerk to Vue - -Install `{{SDK}}`. Add `clerkPlugin` to the Vue app in `{{BASE}}main.ts`. Use ``, ``, ``, `` from `@clerk/vue`. - -Latest docs: {{DOCS_URL}} - -## Install - -```bash -{{INSTALL_CMD}} -``` - -## {{BASE}}main.ts - -```typescript -import { createApp } from 'vue'; -import App from './App.vue'; -import { clerkPlugin } from '@clerk/vue'; - -const app = createApp(App); -app.use(clerkPlugin, { - publishableKey: import.meta.env.{{ENV_VAR}}, -}); -app.mount('#app'); -``` - -## Example usage in App.vue - -```vue - - - -``` - -## Environment - -Env var (`{{ENV_VAR}}`) is in `{{ENV_FILE}}` via `clerk env pull`. - -## Rules - -ALWAYS: - -- Use `clerkPlugin` from `@clerk/vue` in `{{BASE}}main.ts` -- Pass `publishableKey: import.meta.env.{{ENV_VAR}}` to `clerkPlugin` -- Use `` for conditional rendering -- Use existing package manager (`{{PM}}`) - -NEVER: - -- Use deprecated ``, `` (replaced by ``) -- Import from `@clerk/react` or `@clerk/nextjs` — use `@clerk/vue` -- Use old env var names - -## Deprecated (DO NOT use) - -```typescript - // WRONG — use - // WRONG — use -``` - -## Verify Before Responding - -1. Is `clerkPlugin` used with `app.use()` in `{{BASE}}main.ts`? -2. Is `publishableKey` reading from `import.meta.env.{{ENV_VAR}}`? -3. Is it using `` instead of ``/``? - -If any fails, revise. - -## After Setup - -Have the user sign up as their first test user. After signup succeeds and a profile icon appears, congratulate them. Then recommend exploring: Components (https://clerk.com/docs/reference/components/overview), Dashboard (https://dashboard.clerk.com/). - -If you (the agent) need to create test users programmatically to exercise auth-gated code, use a `+clerk_test` email suffix. Clerk accepts `424242` as the OTP for these addresses, so you don't need inbox access. Development instances only. diff --git a/packages/cli-core/src/commands/init/scaffold.ts b/packages/cli-core/src/commands/init/scaffold.ts index ef50b862..2eafa6ed 100644 --- a/packages/cli-core/src/commands/init/scaffold.ts +++ b/packages/cli-core/src/commands/init/scaffold.ts @@ -25,7 +25,7 @@ const SCAFFOLDERS = [ /** * Run the matching scaffolder's enrichContext to populate framework-specific * fields (variant, layoutPath, middlewareBasename) on the context. - * Must be called before scaffold() or buildAgentPrompt(). + * Must be called before scaffold(). */ export async function enrichProjectContext(ctx: ProjectContext): Promise { const scaffolder = SCAFFOLDERS.find((s) => s.dep === ctx.framework.dep); diff --git a/packages/cli-core/src/globals.d.ts b/packages/cli-core/src/globals.d.ts index ed01cbdb..0d3ac77d 100644 --- a/packages/cli-core/src/globals.d.ts +++ b/packages/cli-core/src/globals.d.ts @@ -1,3 +1,8 @@ +declare module "*.md" { + const content: string; + export default content; +} + declare const CLI_VERSION: string | undefined; declare const CLI_ENV_PROFILES: diff --git a/packages/cli-core/src/lib/installer.ts b/packages/cli-core/src/lib/installer.ts index ae59eb30..3e04b960 100644 --- a/packages/cli-core/src/lib/installer.ts +++ b/packages/cli-core/src/lib/installer.ts @@ -12,6 +12,7 @@ import { access, realpath, stat } from "node:fs/promises"; import { homedir } from "node:os"; import { delimiter, join, sep } from "node:path"; import { UPDATE_PACKAGE_NAME } from "./constants.ts"; +import { pmInstallCommand, type PackageManager } from "./package-manager.ts"; // ── Types ──────────────────────────────────────────────────────────────────── @@ -354,16 +355,10 @@ function normalizeWindowsPath(p: string): string { /** Human-readable install/update command for the given installer. */ export function globalInstallCommand(installer: Installer, packageSpec: string): string { - switch (installer) { - case "bun": - return `bun add -g ${packageSpec}`; - case "pnpm": - return `pnpm add -g ${packageSpec}`; - case "yarn": - return `yarn global add ${packageSpec}`; - case "homebrew": - return `brew upgrade ${UPDATE_PACKAGE_NAME}`; - default: - return `npm install -g ${packageSpec}`; - } + if (installer === "homebrew") return `brew upgrade ${UPDATE_PACKAGE_NAME}`; + if (installer === "yarn") return `yarn global add ${packageSpec}`; + + // For bun, pnpm, and npm the global form is the local ` add` command + // with a `-g` flag appended — reuse `pmInstallCommand` as the base. + return `${pmInstallCommand(installer as PackageManager)} -g ${packageSpec}`; } diff --git a/packages/cli-core/src/lib/package-manager.ts b/packages/cli-core/src/lib/package-manager.ts index 06c67266..34fb6c18 100644 --- a/packages/cli-core/src/lib/package-manager.ts +++ b/packages/cli-core/src/lib/package-manager.ts @@ -8,6 +8,18 @@ export const PACKAGE_MANAGERS = ["bun", "pnpm", "yarn", "npm"] as const; export type PackageManager = (typeof PACKAGE_MANAGERS)[number]; +const PM_INSTALL_COMMANDS = { + bun: "bun add", + yarn: "yarn add", + pnpm: "pnpm add", + npm: "npm install", +} satisfies Record; + +/** Returns the ` add`-style command for installing dependencies. */ +export function pmInstallCommand(pm: PackageManager): string { + return PM_INSTALL_COMMANDS[pm]; +} + /** Detects the package manager in use by checking for lockfiles in `cwd`. */ export async function detectPackageManager(cwd: string): Promise { const checks: Array<{ files: string[]; pm: PackageManager }> = [ diff --git a/packages/cli-core/src/test/integration/agent-mode.test.ts b/packages/cli-core/src/test/integration/agent-mode.test.ts index bf7b4b60..327a5877 100644 --- a/packages/cli-core/src/test/integration/agent-mode.test.ts +++ b/packages/cli-core/src/test/integration/agent-mode.test.ts @@ -8,12 +8,6 @@ import { useIntegrationTestHarness, http, clerk, readConfig, MOCK_APP } from "./ useIntegrationTestHarness(); -test("init --prompt outputs structured agent prompt without API calls", async () => { - const { stdout } = await clerk("init", "--prompt"); - expect(stdout).toContain("clerk init -y"); - expect(http.requests.length).toBe(0); -}); - test("link with --app writes the profile in agent mode", async () => { http.mock({ [`/applications/${MOCK_APP.application_id}`]: MOCK_APP, diff --git a/packages/cli-core/src/test/integration/init-options.test.ts b/packages/cli-core/src/test/integration/init-options.test.ts index a67f063b..5e9bc1d3 100644 --- a/packages/cli-core/src/test/integration/init-options.test.ts +++ b/packages/cli-core/src/test/integration/init-options.test.ts @@ -10,11 +10,10 @@ import { useIntegrationTestHarness, clerk } from "./lib/harness.ts"; useIntegrationTestHarness(); test("init accepts --app without rejecting it as an unknown option", async () => { - // --prompt exits before any auth/link/scaffold side effects. If --app is - // not registered with Commander, parseAsync throws `commander.unknownOption` - // before --prompt can short-circuit, and `clerk` (strict) throws. - const { stdout, stderr, exitCode } = await clerk("init", "--app", "app_123", "--prompt"); - expect(exitCode).toBe(0); - expect(stderr).not.toContain("unknown option"); - expect(stdout).toContain("clerk init -y"); + // If --app is not registered with Commander, parseAsync throws + // `commander.unknownOption` which surfaces as "unknown option" in stderr. + // Init exits non-zero in this test cwd (no framework detected) — that's + // expected; we only care that the option pipeline accepted --app. + const result = await clerk.raw("init", "--app", "app_123"); + expect(result.stderr).not.toContain("unknown option"); }); diff --git a/packages/cli-core/src/test/integration/input-json.test.ts b/packages/cli-core/src/test/integration/input-json.test.ts index 93e6a3eb..0c23560b 100644 --- a/packages/cli-core/src/test/integration/input-json.test.ts +++ b/packages/cli-core/src/test/integration/input-json.test.ts @@ -27,23 +27,21 @@ beforeEach(async () => { }); test("init --input-json passes options through Commander pipeline", async () => { - // {"prompt":true} expands to --prompt, short-circuiting init to log the agent handoff - const { stdout } = await clerk("init", "--input-json", '{"prompt":true}'); - expect(stdout).toContain("clerk init -y"); + // {"yes":true} expands to --yes; init still fails (no framework in test cwd) but + // the "unknown option" path would surface a different stderr if Commander rejected + // the expanded flag, so this proves expansion + acceptance. + const result = await clerk.raw("init", "--input-json", '{"yes":true}'); + expect(result.stderr).not.toContain("unknown option"); }); test("explicit CLI flags override --input-json values", async () => { - // --input-json sets mode=human, but --mode agent after it overrides. --prompt on the - // subcommand short-circuits so we don't trigger bootstrap. - const { stdout } = await clerk( - "init", - "--prompt", - "--input-json", - '{"mode":"human"}', - "--mode", - "agent", - ); - expect(stdout).toContain("clerk init -y"); + // --input-json sets mode=human, but --mode agent later overrides. The post-parse + // mode is observable via the structured-JSON error format that agent mode emits. + const result = await clerk.raw("init", "--input-json", '{"mode":"human"}', "--mode", "agent"); + expect(result.exitCode).not.toBe(0); + // Agent mode emits structured JSON to stderr; human mode emits plain text. + const parsed = JSON.parse(result.stderr); + expect(parsed.error).toBeDefined(); }); test("doctor --input-json with json:true outputs JSON", async () => { @@ -103,8 +101,11 @@ test("rejects unknown options via Commander", async () => { }); test("empty JSON object is a no-op", async () => { - const { stdout } = await clerk("init", "--prompt", "--input-json", "{}"); - expect(stdout).toContain("clerk init -y"); + // Empty JSON adds no flags. doctor is non-side-effecting and returns JSON + // when --json is in argv elsewhere — here we just need a clean exit. + const result = await clerk.raw("doctor", "--input-json", "{}", "--json"); + const parsed = JSON.parse(result.stdout); + expect(Array.isArray(parsed)).toBe(true); }); test("@file.json reads options from a temp file", async () => { @@ -219,21 +220,22 @@ test("--input-json after nested subcommand targets that subcommand", async () => }); test("noSkills negated boolean via --input-json", async () => { - // noSkills:true → --no-skills. Commander must accept the negated flag without error. - const { stdout } = await clerk( - "init", - "--prompt", - "--input-json", - '{"prompt":true,"noSkills":true}', - ); - expect(stdout).toContain("clerk init -y"); + // noSkills:true → --no-skills. Init will exit non-zero (no framework in test cwd) + // but Commander would surface "unknown option" before the action runs if the + // negated flag wasn't accepted. + const result = await clerk.raw("init", "--input-json", '{"yes":true,"noSkills":true}'); + expect(result.stderr).not.toContain("unknown option"); }); test("--input-json is registered as a global option", async () => { // --input-json before the subcommand expands to flags at the root-program level. // --mode is a root-level flag, so this works; subcommand-specific flags would not. - const { stdout } = await clerk("--input-json", '{"mode":"agent"}', "init", "--prompt"); - expect(stdout).toContain("clerk init -y"); + // doctor exits non-zero in this harness; the structured-JSON error format on + // stderr is the agent-mode signature, which proves --mode agent was applied. + const result = await clerk.raw("--input-json", '{"mode":"agent"}', "doctor", "--json"); + expect(result.exitCode).not.toBe(0); + const parsed = JSON.parse(result.stderr); + expect(parsed.error).toBeDefined(); }); test("structured JSON error in agent mode for invalid JSON", async () => {