@tanstack/create 0.63.9 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/add-to-app.js +5 -0
  3. package/dist/create-app.js +74 -16
  4. package/dist/custom-add-ons/shared.js +2 -0
  5. package/dist/file-helpers.js +14 -0
  6. package/dist/frameworks/react/add-ons/ai/info.json +1 -1
  7. package/dist/frameworks/react/add-ons/better-auth/info.json +1 -1
  8. package/dist/frameworks/react/add-ons/clerk/README.md +42 -1
  9. package/dist/frameworks/react/add-ons/clerk/assets/src/routes/demo/clerk.tsx +94 -11
  10. package/dist/frameworks/react/add-ons/clerk/info.json +1 -1
  11. package/dist/frameworks/react/add-ons/compiler/info.json +1 -1
  12. package/dist/frameworks/react/add-ons/convex/info.json +1 -1
  13. package/dist/frameworks/react/add-ons/drizzle/info.json +1 -1
  14. package/dist/frameworks/react/add-ons/mcp/info.json +1 -1
  15. package/dist/frameworks/react/add-ons/neon/info.json +1 -1
  16. package/dist/frameworks/react/add-ons/paraglide/info.json +1 -1
  17. package/dist/frameworks/react/add-ons/prisma/info.json +1 -1
  18. package/dist/frameworks/react/add-ons/shadcn/info.json +1 -1
  19. package/dist/frameworks/react/add-ons/strapi/info.json +1 -1
  20. package/dist/frameworks/react/add-ons/t3env/info.json +1 -1
  21. package/dist/frameworks/react/add-ons/workos/info.json +1 -1
  22. package/dist/frameworks/react/hosts/cloudflare/README.md +11 -0
  23. package/dist/frameworks/react/hosts/cloudflare/info.json +1 -1
  24. package/dist/frameworks/react/hosts/netlify/README.md +11 -0
  25. package/dist/frameworks/react/hosts/netlify/info.json +1 -1
  26. package/dist/frameworks/react/hosts/nitro/README.md +12 -0
  27. package/dist/frameworks/react/hosts/nitro/info.json +1 -1
  28. package/dist/frameworks/react/hosts/railway/README.md +10 -0
  29. package/dist/frameworks/react/hosts/railway/info.json +1 -1
  30. package/dist/frameworks/solid/add-ons/better-auth/info.json +1 -1
  31. package/dist/frameworks/solid/add-ons/convex/info.json +1 -1
  32. package/dist/frameworks/solid/add-ons/solid-ui/info.json +1 -1
  33. package/dist/frameworks/solid/add-ons/strapi/info.json +1 -1
  34. package/dist/frameworks/solid/add-ons/t3env/info.json +1 -1
  35. package/dist/frameworks/solid/hosts/cloudflare/README.md +11 -0
  36. package/dist/frameworks/solid/hosts/cloudflare/info.json +1 -1
  37. package/dist/frameworks/solid/hosts/netlify/README.md +11 -0
  38. package/dist/frameworks/solid/hosts/netlify/info.json +1 -1
  39. package/dist/frameworks/solid/hosts/nitro/README.md +12 -0
  40. package/dist/frameworks/solid/hosts/nitro/info.json +1 -1
  41. package/dist/frameworks/solid/hosts/railway/README.md +10 -0
  42. package/dist/frameworks/solid/hosts/railway/info.json +1 -1
  43. package/dist/index.js +1 -1
  44. package/dist/integrations/intent.js +25 -0
  45. package/dist/types/add-to-app.d.ts +1 -0
  46. package/dist/types/file-helpers.d.ts +1 -0
  47. package/dist/types/index.d.ts +1 -1
  48. package/dist/types/integrations/intent.d.ts +2 -0
  49. package/dist/types/types.d.ts +1 -0
  50. package/package.json +1 -1
  51. package/src/add-to-app.ts +7 -0
  52. package/src/create-app.ts +89 -22
  53. package/src/custom-add-ons/shared.ts +2 -0
  54. package/src/file-helpers.ts +20 -0
  55. package/src/frameworks/react/add-ons/ai/info.json +1 -1
  56. package/src/frameworks/react/add-ons/better-auth/info.json +1 -1
  57. package/src/frameworks/react/add-ons/clerk/README.md +42 -1
  58. package/src/frameworks/react/add-ons/clerk/assets/src/routes/demo/clerk.tsx +94 -11
  59. package/src/frameworks/react/add-ons/clerk/info.json +1 -1
  60. package/src/frameworks/react/add-ons/compiler/info.json +1 -1
  61. package/src/frameworks/react/add-ons/convex/info.json +1 -1
  62. package/src/frameworks/react/add-ons/drizzle/info.json +1 -1
  63. package/src/frameworks/react/add-ons/mcp/info.json +1 -1
  64. package/src/frameworks/react/add-ons/neon/info.json +1 -1
  65. package/src/frameworks/react/add-ons/paraglide/info.json +1 -1
  66. package/src/frameworks/react/add-ons/prisma/info.json +1 -1
  67. package/src/frameworks/react/add-ons/shadcn/info.json +1 -1
  68. package/src/frameworks/react/add-ons/strapi/info.json +1 -1
  69. package/src/frameworks/react/add-ons/t3env/info.json +1 -1
  70. package/src/frameworks/react/add-ons/workos/info.json +1 -1
  71. package/src/frameworks/react/hosts/cloudflare/README.md +11 -0
  72. package/src/frameworks/react/hosts/cloudflare/info.json +1 -1
  73. package/src/frameworks/react/hosts/netlify/README.md +11 -0
  74. package/src/frameworks/react/hosts/netlify/info.json +1 -1
  75. package/src/frameworks/react/hosts/nitro/README.md +12 -0
  76. package/src/frameworks/react/hosts/nitro/info.json +1 -1
  77. package/src/frameworks/react/hosts/railway/README.md +10 -0
  78. package/src/frameworks/react/hosts/railway/info.json +1 -1
  79. package/src/frameworks/solid/add-ons/better-auth/info.json +1 -1
  80. package/src/frameworks/solid/add-ons/convex/info.json +1 -1
  81. package/src/frameworks/solid/add-ons/solid-ui/info.json +1 -1
  82. package/src/frameworks/solid/add-ons/strapi/info.json +1 -1
  83. package/src/frameworks/solid/add-ons/t3env/info.json +1 -1
  84. package/src/frameworks/solid/hosts/cloudflare/README.md +11 -0
  85. package/src/frameworks/solid/hosts/cloudflare/info.json +1 -1
  86. package/src/frameworks/solid/hosts/netlify/README.md +11 -0
  87. package/src/frameworks/solid/hosts/netlify/info.json +1 -1
  88. package/src/frameworks/solid/hosts/nitro/README.md +12 -0
  89. package/src/frameworks/solid/hosts/nitro/info.json +1 -1
  90. package/src/frameworks/solid/hosts/railway/README.md +10 -0
  91. package/src/frameworks/solid/hosts/railway/info.json +1 -1
  92. package/src/index.ts +1 -0
  93. package/src/integrations/intent.ts +47 -0
  94. package/src/types.ts +1 -0
  95. package/tests/create-app.test.ts +95 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Strapi",
3
- "description": "Use the Strapi CMS to manage your content.",
3
+ "description": "Headless CMS with admin UI (self-hosted, content models in TypeScript).",
4
4
  "link": "https://strapi.io/",
5
5
  "phase": "add-on",
6
6
  "type": "add-on",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "T3Env",
3
- "description": "Add type safety to your environment variables",
3
+ "description": "Validate process.env at build time (catch missing/wrong env vars before runtime).",
4
4
  "phase": "add-on",
5
5
  "link": "https://github.com/t3-oss/t3-env",
6
6
  "type": "add-on",
@@ -0,0 +1,11 @@
1
+ ## Deploy to Cloudflare Workers
2
+
3
+ This project uses the Cloudflare Vite plugin (configured in `vite.config.ts`) and `wrangler.jsonc`:
4
+
5
+ 1. Install Wrangler: `npm install -g wrangler`
6
+ 2. Authenticate: `wrangler login`
7
+ 3. Deploy: `npx wrangler deploy`
8
+
9
+ For production env vars, run `wrangler secret put MY_VAR` for each secret listed in `.env.example`. Public (non-secret) vars go in `wrangler.jsonc` under `vars`.
10
+
11
+ KV, D1, R2, and Durable Object bindings are configured in `wrangler.jsonc` — see https://developers.cloudflare.com/workers/wrangler/configuration/.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Cloudflare",
3
- "description": "Cloudflare deployment setup",
3
+ "description": "Deploy to Cloudflare Workers (edge runtime, KV/D1/R2 bindings).",
4
4
  "link": "https://developers.cloudflare.com/workers/vite-plugin/",
5
5
  "phase": "add-on",
6
6
  "modes": ["file-router", "code-router"],
@@ -0,0 +1,11 @@
1
+ ## Deploy to Netlify
2
+
3
+ This project ships with `netlify.toml` configured for a Netlify site:
4
+
5
+ 1. Push this repo to GitHub
6
+ 2. Visit https://app.netlify.com/start and import the repo
7
+ 3. Netlify auto-detects the build (`vite build` → `dist/client`)
8
+ 4. Open **Site settings → Environment variables** and add anything from `.env.example` that needs a real value in production
9
+ 5. Trigger the first deploy
10
+
11
+ Server functions and API routes run on Netlify Functions. For lower-latency request handling, see Netlify Edge Functions: https://docs.netlify.com/edge-functions/overview.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Netlify",
3
- "description": "Netlify deployment setup",
3
+ "description": "Deploy to Netlify (Functions and Edge Functions, GitHub-driven).",
4
4
  "link": "https://docs.netlify.com",
5
5
  "phase": "add-on",
6
6
  "modes": ["file-router", "code-router"],
@@ -0,0 +1,12 @@
1
+ ## Deploy with Nitro
2
+
3
+ This project uses Nitro as a generic server adapter, so it can run on any Node-compatible host.
4
+
5
+ ```bash
6
+ npm run build
7
+ node dist/server/index.mjs
8
+ ```
9
+
10
+ The build output is a self-contained Node server. To deploy, push the `dist/` directory to your host (Render, Fly.io, your own VPS, etc.) and run the server command above.
11
+
12
+ For host-specific presets (Vercel, Netlify, Cloudflare, AWS Lambda, etc.) and tuning, see https://v3.nitro.build/deploy.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Nitro (agnostic)",
3
- "description": "Nitro deployment setup",
3
+ "description": "Generic Nitro adapter (deploy to any Node-compatible host).",
4
4
  "link": "https://v3.nitro.build/",
5
5
  "phase": "add-on",
6
6
  "modes": ["file-router", "code-router"],
@@ -0,0 +1,10 @@
1
+ ## Deploy to Railway
2
+
3
+ This project ships with `nixpacks.toml` so Railway detects the build automatically:
4
+
5
+ 1. Push this repo to GitHub
6
+ 2. Visit https://railway.com/new and create a project from your repo
7
+ 3. In the **Variables** tab, add the entries from `.env.example` with their production values
8
+ 4. Railway runs `vite build` and serves from `dist/client`
9
+
10
+ Need a database? Click **+ New** in your project to provision Postgres, MySQL, or Redis directly into the same environment — the connection string is auto-injected as `DATABASE_URL`.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Railway",
3
- "description": "Railway deployment setup",
3
+ "description": "Deploy to Railway (full-stack PaaS for Node + databases).",
4
4
  "link": "https://railway.com/",
5
5
  "phase": "add-on",
6
6
  "modes": ["file-router", "code-router"],
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ export { CONFIG_FILE } from './constants.js';
14
14
  export { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getPackageManager, } from './package-manager.js';
15
15
  export { registerFramework, getFrameworkById, getFrameworkByName, getFrameworks, scanProjectDirectory, scanAddOnDirectories, __testRegisterFramework, __testClearFrameworks, } from './frameworks.js';
16
16
  export { writeConfigFileToEnvironment, readConfigFileFromEnvironment, readConfigFile, } from './config-file.js';
17
- export { cleanUpFiles, cleanUpFileArray, readFileHelper, getBinaryFile, recursivelyGatherFiles, relativePath, toCleanPath, } from './file-helpers.js';
17
+ export { cleanUpFiles, cleanUpFileArray, isDemoFilePath, readFileHelper, getBinaryFile, recursivelyGatherFiles, relativePath, toCleanPath, } from './file-helpers.js';
18
18
  export { formatCommand, handleSpecialURL } from './utils.js';
19
19
  export { initStarter, compileStarter } from './custom-add-ons/starter.js';
20
20
  export { initAddOn, compileAddOn, devAddOn } from './custom-add-ons/add-on.js';
@@ -0,0 +1,25 @@
1
+ import { resolve } from 'node:path';
2
+ import { packageManagerExecute } from '../package-manager.js';
3
+ export async function setupIntent(environment, targetDir, options) {
4
+ if (!options.intent) {
5
+ return;
6
+ }
7
+ const s = environment.spinner();
8
+ s.start('Setting up TanStack Intent skill mappings...');
9
+ environment.startStep({
10
+ id: 'setup-intent',
11
+ type: 'command',
12
+ message: 'Setting up TanStack Intent skill mappings...',
13
+ });
14
+ try {
15
+ await packageManagerExecute(environment, resolve(targetDir), options.packageManager, '@tanstack/intent', ['install', '--map']);
16
+ environment.finishStep('setup-intent', 'TanStack Intent configured');
17
+ s.stop('TanStack Intent configured');
18
+ }
19
+ catch (error) {
20
+ const message = error instanceof Error ? error.message : 'Unknown error';
21
+ environment.finishStep('setup-intent', `TanStack Intent setup skipped: ${message}`);
22
+ s.stop('TanStack Intent setup skipped');
23
+ environment.warn('TanStack Intent setup failed', `Continuing without it. You can run it later with: npx @tanstack/intent install\n\n${message}`);
24
+ }
25
+ }
@@ -14,4 +14,5 @@ export declare function runNewCommands(environment: Environment, originalOptions
14
14
  }): Promise<void>;
15
15
  export declare function addToApp(environment: Environment, addOns: Array<string>, cwd: string, options?: {
16
16
  forced?: boolean;
17
+ intent?: boolean;
17
18
  }): Promise<void>;
@@ -10,6 +10,7 @@ export declare function getBinaryFile(content: string): string | null;
10
10
  export declare function toCleanPath(absolutePath: string, baseDir: string): string;
11
11
  export declare function relativePath(from: string, to: string, stripExtension?: boolean): string;
12
12
  export declare function isDirectory(path: string): boolean;
13
+ export declare function isDemoFilePath(path?: string): boolean;
13
14
  export declare function findFilesRecursively(path: string, files: Record<string, string>): void;
14
15
  export declare function recursivelyGatherFiles(path: string, includeProjectFiles?: boolean): Promise<Record<string, string>>;
15
16
  export declare function recursivelyGatherFilesFromEnvironment(environment: Environment, path: string, includeProjectFiles?: boolean): Promise<Record<string, string>>;
@@ -9,7 +9,7 @@ export { CONFIG_FILE } from './constants.js';
9
9
  export { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getPackageManager, } from './package-manager.js';
10
10
  export { registerFramework, getFrameworkById, getFrameworkByName, getFrameworks, scanProjectDirectory, scanAddOnDirectories, __testRegisterFramework, __testClearFrameworks, } from './frameworks.js';
11
11
  export { writeConfigFileToEnvironment, readConfigFileFromEnvironment, readConfigFile, } from './config-file.js';
12
- export { cleanUpFiles, cleanUpFileArray, readFileHelper, getBinaryFile, recursivelyGatherFiles, relativePath, toCleanPath, } from './file-helpers.js';
12
+ export { cleanUpFiles, cleanUpFileArray, isDemoFilePath, readFileHelper, getBinaryFile, recursivelyGatherFiles, relativePath, toCleanPath, } from './file-helpers.js';
13
13
  export { formatCommand, handleSpecialURL } from './utils.js';
14
14
  export { initStarter, compileStarter } from './custom-add-ons/starter.js';
15
15
  export { initAddOn, compileAddOn, devAddOn } from './custom-add-ons/add-on.js';
@@ -0,0 +1,2 @@
1
+ import type { Environment, Options } from '../types.js';
2
+ export declare function setupIntent(environment: Environment, targetDir: string, options: Options): Promise<void>;
@@ -1461,6 +1461,7 @@ export interface Options {
1461
1461
  packageManager: PackageManager;
1462
1462
  git: boolean;
1463
1463
  install?: boolean;
1464
+ intent: boolean;
1464
1465
  chosenAddOns: Array<AddOn>;
1465
1466
  addOnOptions: Record<string, Record<string, any>>;
1466
1467
  starter?: Starter | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/create",
3
- "version": "0.63.9",
3
+ "version": "0.65.0",
4
4
  "description": "TanStack Application Builder Engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/add-to-app.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  import { mergePackageJSON } from './package-json.js'
19
19
  import { runSpecialSteps } from './special-steps/index.js'
20
20
  import { loadStarter } from './custom-add-ons/starter.js'
21
+ import { setupIntent } from './integrations/intent.js'
21
22
 
22
23
  import type { Environment, Options } from './types.js'
23
24
  import type { PersistedOptions } from './config-file.js'
@@ -54,6 +55,7 @@ async function createOptions(
54
55
  ]),
55
56
  targetDir,
56
57
  starter,
58
+ intent: json.intent ?? false,
57
59
  } as Options
58
60
  }
59
61
 
@@ -230,6 +232,7 @@ export async function addToApp(
230
232
  cwd: string,
231
233
  options?: {
232
234
  forced?: boolean
235
+ intent?: boolean
233
236
  },
234
237
  ) {
235
238
  const persistedOptions = await getCurrentConfiguration(environment, cwd)
@@ -318,6 +321,10 @@ export async function addToApp(
318
321
 
319
322
  await runNewCommands(environment, persistedOptions, cwd, output)
320
323
 
324
+ const intent = options?.intent ?? persistedOptions.intent ?? true
325
+ newOptions.intent = intent
326
+ await setupIntent(environment, cwd, newOptions)
327
+
321
328
  environment.startStep({
322
329
  id: 'write-config-file',
323
330
  type: 'file',
package/src/create-app.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { basename, resolve } from 'node:path'
2
2
 
3
- import { isBase64 } from './file-helpers.js'
3
+ import { isBase64, isDemoFilePath } from './file-helpers.js'
4
4
  import { formatCommand } from './utils.js'
5
5
  import { writeConfigFileToEnvironment } from './config-file.js'
6
6
  import {
@@ -13,30 +13,11 @@ import { resolvePackageJSONLatest } from './npm-resolver.js'
13
13
  import { createTemplateFile } from './template-file.js'
14
14
  import { installShadcnComponents } from './integrations/shadcn.js'
15
15
  import { setupGit } from './integrations/git.js'
16
+ import { setupIntent } from './integrations/intent.js'
16
17
  import { runSpecialSteps } from './special-steps/index.js'
17
18
 
18
19
  import type { Environment, FileBundleHandler, Options } from './types.js'
19
20
 
20
- function isDemoFilePath(path?: string) {
21
- if (!path) return false
22
- const normalized = path.replace(/\\/g, '/')
23
-
24
- if (
25
- normalized.includes('/routes/demo/') ||
26
- normalized.includes('/routes/example/')
27
- ) {
28
- return true
29
- }
30
-
31
- const filename = normalized.split('/').pop() || ''
32
- return (
33
- filename.startsWith('demo.') ||
34
- filename.startsWith('demo-') ||
35
- filename.startsWith('example.') ||
36
- filename.startsWith('example-')
37
- )
38
- }
39
-
40
21
  function stripExamplesFromOptions(options: Options): Options {
41
22
  if (options.includeExamples !== false) {
42
23
  return options
@@ -294,6 +275,8 @@ async function runCommandsAndInstallDependencies(
294
275
  }
295
276
 
296
277
  await installShadcnComponents(environment, options.targetDir, options)
278
+
279
+ await setupIntent(environment, options.targetDir, options)
297
280
  }
298
281
 
299
282
  async function seedEnvValues(environment: Environment, options: Options) {
@@ -322,6 +305,87 @@ async function seedEnvValues(environment: Environment, options: Options) {
322
305
  await environment.writeFile(envLocalPath, envContents)
323
306
  }
324
307
 
308
+ async function writeEnvExample(environment: Environment, options: Options) {
309
+ const envExamplePath = resolve(options.targetDir, '.env.example')
310
+ const existing = environment.exists(envExamplePath)
311
+ ? await environment.readFile(envExamplePath)
312
+ : ''
313
+
314
+ const declared = new Set<string>()
315
+ for (const match of existing.matchAll(/^([A-Z_][A-Z0-9_]*)=/gm)) {
316
+ declared.add(match[1])
317
+ }
318
+
319
+ const sections: Array<string> = []
320
+ for (const addOn of options.chosenAddOns) {
321
+ const lines: Array<string> = []
322
+ for (const envVar of addOn.envVars || []) {
323
+ if (declared.has(envVar.name)) continue
324
+ declared.add(envVar.name)
325
+ if (envVar.description) {
326
+ const required = envVar.required ? ' (required)' : ''
327
+ lines.push(`# ${envVar.description}${required}`)
328
+ }
329
+ lines.push(`${envVar.name}=`)
330
+ }
331
+ if (lines.length > 0) {
332
+ sections.push(`# ${addOn.name}\n${lines.join('\n')}`)
333
+ }
334
+ }
335
+
336
+ if (sections.length === 0) return
337
+
338
+ const additions = sections.join('\n\n')
339
+ const newContent = existing
340
+ ? `${existing.trimEnd()}\n\n${additions}\n`
341
+ : `${additions}\n`
342
+
343
+ await environment.writeFile(envExamplePath, newContent)
344
+ }
345
+
346
+ const SHIPPING_CATEGORIES = new Set(['auth', 'database', 'orm', 'deploy'])
347
+
348
+ function buildNextSteps(options: Options): string {
349
+ const collectedEnv = new Set(Object.keys(options.envVarValues || {}))
350
+ const listedEnvVars = new Set<string>()
351
+ const envVarLines: Array<string> = []
352
+ const docLines: Array<string> = []
353
+
354
+ for (const addOn of options.chosenAddOns) {
355
+ if (addOn.link && addOn.category && SHIPPING_CATEGORIES.has(addOn.category)) {
356
+ docLines.push(` • ${addOn.name} (${addOn.category}) — ${addOn.link}`)
357
+ }
358
+
359
+ for (const envVar of addOn.envVars || []) {
360
+ if (listedEnvVars.has(envVar.name)) continue
361
+ listedEnvVars.add(envVar.name)
362
+ const required = envVar.required ? ' (required)' : ''
363
+ const status = collectedEnv.has(envVar.name)
364
+ ? ' — already set from your input'
365
+ : ' — needs a value'
366
+ const desc = envVar.description ? ` — ${envVar.description}` : ''
367
+ envVarLines.push(` • ${envVar.name}${required}${desc}${status}`)
368
+ }
369
+ }
370
+
371
+ const sections: Array<string> = []
372
+ if (envVarLines.length > 0) {
373
+ sections.push(
374
+ `Environment variables (review/fill in .env.local before deploying):\n${envVarLines.join('\n')}`,
375
+ )
376
+ }
377
+ if (docLines.length > 0) {
378
+ sections.push(`Docs for the integrations you picked:\n${docLines.join('\n')}`)
379
+ }
380
+ if (options.intent) {
381
+ sections.push(
382
+ `Working with an AI agent? Your agent config (AGENTS.md / CLAUDE.md) was wired up by TanStack Intent\nwith explicit skill mappings for the libraries you installed. Try asking your agent:\n • "migrate this Next.js page to TanStack Start"\n • "add a protected /dashboard route"\n • "show me how to use TanStack Router search params"`,
383
+ )
384
+ }
385
+
386
+ return sections.length > 0 ? `\nNext steps:\n\n${sections.join('\n\n')}\n` : ''
387
+ }
388
+
325
389
  function report(environment: Environment, options: Options) {
326
390
  const warnings: Array<string> = []
327
391
  for (const addOn of options.chosenAddOns) {
@@ -355,6 +419,8 @@ ${environment.getErrors().join('\n')}`
355
419
  : `% cd ${options.projectName}
356
420
  `
357
421
 
422
+ const nextSteps = buildNextSteps(options)
423
+
358
424
  // Use the force luke! :)
359
425
  environment.outro(
360
426
  `${locationMessage}
@@ -363,7 +429,7 @@ Use the following commands to start your app:
363
429
  ${cdInstruction}% ${formatCommand(
364
430
  getPackageManagerScriptCommand(options.packageManager, ['dev']),
365
431
  )}
366
-
432
+ ${nextSteps}
367
433
  Please read the README.md file for information on testing, styling, adding routes, etc.${errorStatement}`,
368
434
  )
369
435
  }
@@ -374,6 +440,7 @@ export async function createApp(environment: Environment, options: Options) {
374
440
  environment.startRun()
375
441
  await writeFiles(environment, effectiveOptions)
376
442
  await seedEnvValues(environment, effectiveOptions)
443
+ await writeEnvExample(environment, effectiveOptions)
377
444
  await runCommandsAndInstallDependencies(environment, effectiveOptions)
378
445
  environment.finishRun()
379
446
 
@@ -82,6 +82,7 @@ export async function createAppOptionsFromPersisted(
82
82
  starter: json.starter ? await loadStarter(json.starter) : undefined,
83
83
  chosenAddOns,
84
84
  addOnOptions: populateAddOnOptionsDefaults(chosenAddOns),
85
+ intent: json.intent ?? false,
85
86
  }
86
87
  }
87
88
 
@@ -103,6 +104,7 @@ export function createSerializedOptionsFromPersisted(
103
104
  framework: json.framework,
104
105
  starter: json.starter,
105
106
  addOnOptions: {},
107
+ intent: json.intent ?? false,
106
108
  }
107
109
  }
108
110
 
@@ -112,6 +112,26 @@ export function isDirectory(path: string): boolean {
112
112
  return statSync(path).isDirectory()
113
113
  }
114
114
 
115
+ export function isDemoFilePath(path?: string): boolean {
116
+ if (!path) return false
117
+ const normalized = path.replace(/\\/g, '/')
118
+
119
+ if (
120
+ normalized.includes('/routes/demo/') ||
121
+ normalized.includes('/routes/example/')
122
+ ) {
123
+ return true
124
+ }
125
+
126
+ const filename = normalized.split('/').pop() || ''
127
+ return (
128
+ filename.startsWith('demo.') ||
129
+ filename.startsWith('demo-') ||
130
+ filename.startsWith('example.') ||
131
+ filename.startsWith('example-')
132
+ )
133
+ }
134
+
115
135
  export function findFilesRecursively(
116
136
  path: string,
117
137
  files: Record<string, string>,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "AI",
3
- "description": "TanStack AI integration and examples.",
3
+ "description": "Streaming chat UI with model-agnostic backend (OpenAI, Anthropic, etc).",
4
4
  "phase": "add-on",
5
5
  "modes": ["file-router"],
6
6
  "type": "add-on",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Better Auth",
3
- "description": "Add Better Auth authentication to your application.",
3
+ "description": "Self-hosted user accounts and sessions (open source, full control).",
4
4
  "phase": "add-on",
5
5
  "type": "add-on",
6
6
  "category": "auth",
@@ -1,3 +1,44 @@
1
1
  ## Setting up Clerk
2
2
 
3
- - Set the `VITE_CLERK_PUBLISHABLE_KEY` in your `.env.local`.
3
+ 1. Sign up at [clerk.com](https://clerk.com) and create an application
4
+ 2. Copy the **Publishable Key** from the Clerk dashboard
5
+ 3. Set it in your `.env.local`:
6
+ ```bash
7
+ VITE_CLERK_PUBLISHABLE_KEY=pk_test_...
8
+ ```
9
+ 4. Visit the demo route at `/demo/clerk` once `npm run dev` is running
10
+
11
+ ### What's wired up
12
+
13
+ - **`<ClerkProvider>`** at the app root (`src/integrations/clerk/provider.tsx`) handles auth context for the whole tree
14
+ - **`<SignInButton>` / `<UserButton>`** in the header swap based on auth state
15
+ - **`/demo/clerk`** shows Clerk's prebuilt sign-in UI and a signed-in greeting
16
+
17
+ ### Protecting a route
18
+
19
+ Wrap any component in `<SignedIn>` / `<SignedOut>`:
20
+
21
+ ```tsx
22
+ import { SignedIn, SignedOut, RedirectToSignIn } from '@clerk/clerk-react'
23
+
24
+ function ProtectedPage() {
25
+ return (
26
+ <>
27
+ <SignedIn>
28
+ <YourPageContent />
29
+ </SignedIn>
30
+ <SignedOut>
31
+ <RedirectToSignIn />
32
+ </SignedOut>
33
+ </>
34
+ )
35
+ }
36
+ ```
37
+
38
+ For server-side checks (route loaders, server functions), see the Clerk docs on [`auth()`](https://clerk.com/docs/references/backend/auth).
39
+
40
+ ### Production checklist
41
+
42
+ - Replace the test keys with **production keys** from a dedicated production Clerk instance
43
+ - Configure your production domain under **Domains** in the Clerk dashboard
44
+ - Set up social providers (Google, GitHub, etc.) under **User & Authentication → Social Connections**
@@ -1,20 +1,103 @@
1
1
  import { createFileRoute } from '@tanstack/react-router'
2
- import { useUser } from '@clerk/clerk-react'
2
+ import {
3
+ SignIn,
4
+ SignedIn,
5
+ SignedOut,
6
+ useUser,
7
+ } from '@clerk/clerk-react'
3
8
 
4
9
  export const Route = createFileRoute('/demo/clerk')({
5
- component: App,
10
+ component: ClerkDemo,
6
11
  })
7
12
 
8
- function App() {
9
- const { isSignedIn, user, isLoaded } = useUser()
13
+ function ClerkDemo() {
14
+ return (
15
+ <div className="flex justify-center py-10 px-4">
16
+ <div className="w-full max-w-md p-6 space-y-6">
17
+ <SignedOut>
18
+ <div className="space-y-1.5">
19
+ <h1 className="text-lg font-semibold leading-none tracking-tight">
20
+ Sign in to continue
21
+ </h1>
22
+ <p className="text-sm text-neutral-500 dark:text-neutral-400">
23
+ Clerk renders the sign-in UI, manages sessions, and handles social providers for you.
24
+ </p>
25
+ </div>
26
+ <div className="flex justify-center pt-2">
27
+ <SignIn routing="hash" />
28
+ </div>
29
+ <p className="text-xs text-center text-neutral-400 dark:text-neutral-500">
30
+ Built with{' '}
31
+ <a
32
+ href="https://clerk.com"
33
+ target="_blank"
34
+ rel="noopener noreferrer"
35
+ className="font-medium hover:text-neutral-600 dark:hover:text-neutral-300"
36
+ >
37
+ CLERK
38
+ </a>
39
+ .
40
+ </p>
41
+ </SignedOut>
10
42
 
11
- if (!isLoaded) {
12
- return <div className="p-4">Loading...</div>
13
- }
43
+ <SignedIn>
44
+ <SignedInGreeting />
45
+ </SignedIn>
46
+ </div>
47
+ </div>
48
+ )
49
+ }
50
+
51
+ function SignedInGreeting() {
52
+ const { user } = useUser()
53
+ if (!user) return null
54
+
55
+ const email = user.primaryEmailAddress?.emailAddress
56
+ const initial = (user.firstName || email || 'U').charAt(0).toUpperCase()
57
+
58
+ return (
59
+ <div className="space-y-6">
60
+ <div className="space-y-1.5">
61
+ <h1 className="text-lg font-semibold leading-none tracking-tight">
62
+ Welcome back
63
+ </h1>
64
+ <p className="text-sm text-neutral-500 dark:text-neutral-400">
65
+ You're signed in as {email}
66
+ </p>
67
+ </div>
14
68
 
15
- if (!isSignedIn) {
16
- return <div className="p-4">Sign in to view this page</div>
17
- }
69
+ <div className="flex items-center gap-3">
70
+ {user.imageUrl ? (
71
+ <img src={user.imageUrl} alt="" className="h-10 w-10 rounded-full" />
72
+ ) : (
73
+ <div className="h-10 w-10 bg-neutral-200 dark:bg-neutral-800 flex items-center justify-center rounded-full">
74
+ <span className="text-sm font-medium text-neutral-600 dark:text-neutral-400">
75
+ {initial}
76
+ </span>
77
+ </div>
78
+ )}
79
+ <div className="flex-1 min-w-0">
80
+ <p className="text-sm font-medium truncate">
81
+ {user.firstName} {user.lastName}
82
+ </p>
83
+ <p className="text-xs text-neutral-500 dark:text-neutral-400 truncate">
84
+ {email}
85
+ </p>
86
+ </div>
87
+ </div>
18
88
 
19
- return <div className="p-4">Hello {user.firstName}!</div>
89
+ <p className="text-xs text-center text-neutral-400 dark:text-neutral-500">
90
+ Manage your account from the avatar in the header. Built with{' '}
91
+ <a
92
+ href="https://clerk.com"
93
+ target="_blank"
94
+ rel="noopener noreferrer"
95
+ className="font-medium hover:text-neutral-600 dark:hover:text-neutral-300"
96
+ >
97
+ CLERK
98
+ </a>
99
+ .
100
+ </p>
101
+ </div>
102
+ )
20
103
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Clerk",
3
- "description": "Add Clerk authentication to your application.",
3
+ "description": "Hosted user accounts with prebuilt sign-in UI and social providers (managed).",
4
4
  "phase": "add-on",
5
5
  "modes": ["file-router"],
6
6
  "type": "add-on",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "Compiler",
3
3
  "phase": "setup",
4
- "description": "Add React Compiler",
4
+ "description": "Auto-memoize components and hooks (fewer re-renders, no manual useMemo/useCallback).",
5
5
  "link": "https://react.dev/learn/react-compiler",
6
6
  "modes": ["code-router", "file-router"],
7
7
  "type": "add-on",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Convex",
3
- "description": "Add the Convex database to your application.",
3
+ "description": "Reactive document database with real-time queries and serverless functions.",
4
4
  "link": "https://convex.dev",
5
5
  "phase": "add-on",
6
6
  "type": "add-on",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Drizzle",
3
- "description": "Add Drizzle ORM to your application.",
3
+ "description": "Type-safe SQL query builder for Postgres, SQLite, or MySQL.",
4
4
  "phase": "add-on",
5
5
  "type": "add-on",
6
6
  "category": "orm",