@tanstack/cli 0.60.1 → 0.62.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 (66) hide show
  1. package/dist/cli.js +266 -11
  2. package/dist/command-line.js +103 -8
  3. package/dist/discovery.js +144 -0
  4. package/dist/options.js +35 -2
  5. package/dist/types/command-line.d.ts +7 -0
  6. package/dist/types/{mcp/types.d.ts → discovery.d.ts} +23 -75
  7. package/dist/types/types.d.ts +1 -2
  8. package/dist/types/ui-prompts.d.ts +5 -0
  9. package/dist/ui-prompts.js +26 -0
  10. package/package.json +6 -5
  11. package/skills/CHANGELOG.md +18 -0
  12. package/skills/add-addons-existing-app/SKILL.md +113 -0
  13. package/skills/choose-ecosystem-integrations/SKILL.md +140 -0
  14. package/skills/choose-ecosystem-integrations/references/authentication-providers.md +19 -0
  15. package/skills/choose-ecosystem-integrations/references/data-layer-providers.md +20 -0
  16. package/skills/choose-ecosystem-integrations/references/deployment-targets.md +19 -0
  17. package/skills/create-app-scaffold/SKILL.md +132 -0
  18. package/skills/create-app-scaffold/references/create-flag-compatibility-matrix.md +34 -0
  19. package/skills/create-app-scaffold/references/deployment-providers.md +19 -0
  20. package/skills/create-app-scaffold/references/framework-adapters.md +17 -0
  21. package/skills/create-app-scaffold/references/toolchains.md +17 -0
  22. package/skills/maintain-custom-addons-dev-watch/SKILL.md +118 -0
  23. package/skills/query-docs-library-metadata/SKILL.md +85 -0
  24. package/skills/query-docs-library-metadata/references/discovery-command-output-schemas.md +70 -0
  25. package/CHANGELOG.md +0 -787
  26. package/dist/mcp/api.js +0 -31
  27. package/dist/mcp/tools.js +0 -250
  28. package/dist/mcp/types.js +0 -37
  29. package/dist/mcp.js +0 -181
  30. package/dist/types/mcp/api.d.ts +0 -4
  31. package/dist/types/mcp/tools.d.ts +0 -2
  32. package/dist/types/mcp.d.ts +0 -5
  33. package/playwright-report/index.html +0 -85
  34. package/playwright.config.ts +0 -21
  35. package/src/bin.ts +0 -15
  36. package/src/cli.ts +0 -767
  37. package/src/command-line.ts +0 -473
  38. package/src/dev-watch.ts +0 -564
  39. package/src/file-syncer.ts +0 -263
  40. package/src/index.ts +0 -21
  41. package/src/mcp/api.ts +0 -42
  42. package/src/mcp/tools.ts +0 -323
  43. package/src/mcp/types.ts +0 -46
  44. package/src/mcp.ts +0 -263
  45. package/src/options.ts +0 -234
  46. package/src/types.ts +0 -28
  47. package/src/ui-environment.ts +0 -74
  48. package/src/ui-prompts.ts +0 -355
  49. package/src/utils.ts +0 -30
  50. package/test-results/.last-run.json +0 -4
  51. package/tests/command-line.test.ts +0 -622
  52. package/tests/index.test.ts +0 -9
  53. package/tests/mcp.test.ts +0 -225
  54. package/tests/options.test.ts +0 -216
  55. package/tests/setupVitest.ts +0 -6
  56. package/tests/ui-environment.test.ts +0 -97
  57. package/tests/ui-prompts.test.ts +0 -205
  58. package/tests-e2e/addons-smoke.spec.ts +0 -31
  59. package/tests-e2e/create-smoke.spec.ts +0 -39
  60. package/tests-e2e/helpers.ts +0 -526
  61. package/tests-e2e/matrix-opportunistic.spec.ts +0 -142
  62. package/tests-e2e/router-only-smoke.spec.ts +0 -68
  63. package/tests-e2e/solid-smoke.spec.ts +0 -25
  64. package/tests-e2e/templates-smoke.spec.ts +0 -52
  65. package/tsconfig.json +0 -17
  66. package/vitest.config.js +0 -8
package/src/mcp.ts DELETED
@@ -1,263 +0,0 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
- import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'
3
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
4
- import express from 'express'
5
- import { z } from 'zod'
6
-
7
- import {
8
- createApp,
9
- createDefaultEnvironment,
10
- finalizeAddOns,
11
- getFrameworkByName,
12
- getFrameworks,
13
- populateAddOnOptionsDefaults,
14
- } from '@tanstack/create'
15
-
16
- import { registerDocTools } from './mcp/tools.js'
17
-
18
- function createServer({
19
- appName,
20
- forcedAddOns = [],
21
- }: {
22
- appName?: string
23
- forcedAddOns?: Array<string>
24
- name?: string
25
- }) {
26
- const server = new McpServer({
27
- name: `${appName} Application Builder`,
28
- version: '1.0.0',
29
- })
30
-
31
- const frameworks = getFrameworks()
32
- const frameworkNames = frameworks.map((framework) => framework.name)
33
-
34
- server.tool(
35
- 'listTanStackAddOns',
36
- 'List the available add-ons for creating TanStack applications',
37
- {
38
- framework: z
39
- .string()
40
- .describe(
41
- `The framework to use. Available frameworks: ${frameworkNames.join(', ')}`,
42
- ),
43
- },
44
- ({ framework: frameworkName }) => {
45
- const framework = getFrameworkByName(frameworkName)!
46
- return {
47
- content: [
48
- {
49
- type: 'text',
50
- text: JSON.stringify(
51
- framework
52
- .getAddOns()
53
- .filter((addOn) => addOn.modes.includes('file-router'))
54
- .map((addOn) => ({
55
- id: addOn.id,
56
- name: addOn.name,
57
- description: addOn.description,
58
- type: addOn.type,
59
- category: addOn.category,
60
- link: addOn.link,
61
- warning: addOn.warning,
62
- exclusive: addOn.exclusive,
63
- options: addOn.options,
64
- dependsOn: addOn.dependsOn,
65
- })),
66
- ),
67
- },
68
- ],
69
- }
70
- },
71
- )
72
-
73
- server.tool(
74
- 'getAddOnDetails',
75
- 'Get detailed information about a specific add-on including implementation patterns, routes, dependencies, and documentation',
76
- {
77
- framework: z
78
- .string()
79
- .describe(
80
- `The framework to use. Available frameworks: ${frameworkNames.join(', ')}`,
81
- ),
82
- addOnId: z
83
- .string()
84
- .describe('The ID of the add-on to get details for'),
85
- },
86
- async ({ framework: frameworkName, addOnId }) => {
87
- const framework = getFrameworkByName(frameworkName)!
88
- const allAddOns = framework.getAddOns()
89
- const addOn =
90
- allAddOns.find((a) => a.id === addOnId) ??
91
- allAddOns.find(
92
- (a) => a.id.toLowerCase() === addOnId.toLowerCase(),
93
- )
94
-
95
- if (!addOn) {
96
- return {
97
- content: [
98
- {
99
- type: 'text',
100
- text: JSON.stringify({ error: `Add-on '${addOnId}' not found` }),
101
- },
102
- ],
103
- }
104
- }
105
-
106
- // Get file list for context
107
- const files = await addOn.getFiles()
108
-
109
- return {
110
- content: [
111
- {
112
- type: 'text',
113
- text: JSON.stringify({
114
- id: addOn.id,
115
- name: addOn.name,
116
- description: addOn.description,
117
- type: addOn.type,
118
- category: addOn.category,
119
- phase: addOn.phase,
120
- modes: addOn.modes,
121
- link: addOn.link,
122
- warning: addOn.warning,
123
- exclusive: addOn.exclusive,
124
- dependsOn: addOn.dependsOn,
125
- options: addOn.options,
126
- routes: addOn.routes,
127
- packageAdditions: addOn.packageAdditions,
128
- shadcnComponents: addOn.shadcnComponents,
129
- integrations: addOn.integrations,
130
- readme: addOn.readme,
131
- files,
132
- author: addOn.author,
133
- version: addOn.version,
134
- license: addOn.license,
135
- }),
136
- },
137
- ],
138
- }
139
- },
140
- )
141
-
142
- server.tool(
143
- 'createTanStackApplication',
144
- 'Create a new TanStack application',
145
- {
146
- framework: z
147
- .string()
148
- .describe(
149
- `The framework to use. Available frameworks: ${frameworkNames.join(', ')}`,
150
- ),
151
- projectName: z
152
- .string()
153
- .describe(
154
- 'The package.json module name of the application (will also be the directory name)',
155
- ),
156
- cwd: z.string().describe('The directory to create the application in'),
157
- addOns: z.array(z.string()).describe('Array of add-on IDs to install. Use listTanStackAddOns tool to see available add-ons and their configuration options. Example: ["prisma", "shadcn", "tanstack-query"]'),
158
- addOnOptions: z.record(z.record(z.any())).optional().describe('Configuration options for add-ons. Format: {"addOnId": {"optionName": "value"}}. Use listTanStackAddOns to see available options for each add-on.'),
159
- targetDir: z
160
- .string()
161
- .describe(
162
- 'The directory to create the application in. Use the absolute path of the directory you want the application to be created in',
163
- ),
164
- },
165
- async ({
166
- framework: frameworkName,
167
- projectName,
168
- addOns,
169
- addOnOptions,
170
- cwd,
171
- targetDir,
172
- }) => {
173
- const framework = getFrameworkByName(frameworkName)!
174
- try {
175
- process.chdir(cwd)
176
- try {
177
- const chosenAddOns = await finalizeAddOns(
178
- framework,
179
- 'file-router',
180
- Array.from(
181
- new Set([
182
- ...(addOns as unknown as Array<string>),
183
- ...forcedAddOns,
184
- ]),
185
- ),
186
- )
187
- await createApp(createDefaultEnvironment(), {
188
- projectName: projectName.replace(/^\//, './'),
189
- targetDir,
190
- framework,
191
- typescript: true,
192
- tailwind: true,
193
- packageManager: 'pnpm',
194
- mode: 'file-router',
195
- chosenAddOns,
196
- addOnOptions: addOnOptions || populateAddOnOptionsDefaults(chosenAddOns),
197
- git: true,
198
- })
199
- } catch (error) {
200
- console.error(error)
201
- return {
202
- content: [
203
- { type: 'text', text: `Error creating application: ${error}` },
204
- ],
205
- }
206
- }
207
- return {
208
- content: [{ type: 'text', text: 'Application created successfully' }],
209
- }
210
- } catch (error) {
211
- return {
212
- content: [
213
- { type: 'text', text: `Error creating application: ${error}` },
214
- ],
215
- }
216
- }
217
- },
218
- )
219
-
220
- // Register doc/ecosystem tools from TanStack API
221
- registerDocTools(server)
222
-
223
- return server
224
- }
225
-
226
- export async function runMCPServer(
227
- sse: boolean,
228
- {
229
- forcedAddOns,
230
- appName,
231
- name,
232
- }: {
233
- forcedAddOns?: Array<string>
234
- appName?: string
235
- name?: string
236
- },
237
- ) {
238
- let transport: SSEServerTransport | null = null
239
-
240
- const server = createServer({ appName, forcedAddOns, name })
241
- if (sse) {
242
- const app = express()
243
-
244
- app.get('/sse', (req, res) => {
245
- transport = new SSEServerTransport('/messages', res)
246
- server.connect(transport)
247
- })
248
-
249
- app.post('/messages', (req, res) => {
250
- if (transport) {
251
- transport.handlePostMessage(req, res)
252
- }
253
- })
254
-
255
- const port = process.env.PORT || 8080
256
- app.listen(port, () => {
257
- console.log(`Server is running on port http://localhost:${port}/sse`)
258
- })
259
- } else {
260
- const transport = new StdioServerTransport()
261
- await server.connect(transport)
262
- }
263
- }
package/src/options.ts DELETED
@@ -1,234 +0,0 @@
1
- import { intro } from '@clack/prompts'
2
-
3
- import {
4
- finalizeAddOns,
5
- getFrameworkById,
6
- getPackageManager,
7
- populateAddOnOptionsDefaults,
8
- readConfigFile,
9
- } from '@tanstack/create'
10
-
11
- import {
12
- getProjectName,
13
- promptForAddOnOptions,
14
- promptForEnvVars,
15
- selectAddOns,
16
- selectDeployment,
17
- selectExamples,
18
- selectGit,
19
- selectPackageManager,
20
- selectToolchain,
21
- } from './ui-prompts.js'
22
-
23
- import {
24
- getCurrentDirectoryName,
25
- sanitizePackageName,
26
- validateProjectName,
27
- } from './utils.js'
28
- import type { Options } from '@tanstack/create'
29
-
30
- import type { CliOptions } from './types.js'
31
-
32
- export async function promptForCreateOptions(
33
- cliOptions: CliOptions,
34
- {
35
- forcedAddOns = [],
36
- showDeploymentOptions = false,
37
- }: {
38
- forcedAddOns?: Array<string>
39
- showDeploymentOptions?: boolean
40
- },
41
- ): Promise<Required<Options> | undefined> {
42
- const options = {} as Required<Options>
43
-
44
- options.framework = getFrameworkById(cliOptions.framework || 'react')!
45
-
46
- // Validate project name
47
- if (cliOptions.projectName) {
48
- // Handle "." as project name - use sanitized current directory name
49
- if (cliOptions.projectName === '.') {
50
- options.projectName = sanitizePackageName(getCurrentDirectoryName())
51
- } else {
52
- options.projectName = cliOptions.projectName
53
- }
54
- const { valid, error } = validateProjectName(options.projectName)
55
- if (!valid) {
56
- console.error(error)
57
- process.exit(1)
58
- }
59
- } else {
60
- options.projectName = await getProjectName()
61
- }
62
-
63
- // Mode is always file-router (TanStack Start)
64
- options.mode = 'file-router'
65
- const template = cliOptions.template?.toLowerCase().trim()
66
- const isLegacyTemplate =
67
- template &&
68
- ['file-router', 'typescript', 'tsx', 'javascript', 'js', 'jsx'].includes(
69
- template,
70
- )
71
- const routerOnly =
72
- !!cliOptions.routerOnly ||
73
- (isLegacyTemplate ? template !== 'file-router' : false)
74
-
75
- // TypeScript is always enabled with file-router
76
- options.typescript = true
77
-
78
- // Package manager selection
79
- if (cliOptions.packageManager) {
80
- options.packageManager = cliOptions.packageManager
81
- } else {
82
- const detectedPackageManager = await getPackageManager()
83
- options.packageManager =
84
- detectedPackageManager || (await selectPackageManager())
85
- }
86
-
87
- // Toolchain selection
88
- const toolchain = await selectToolchain(
89
- options.framework,
90
- cliOptions.toolchain,
91
- )
92
-
93
- // Deployment selection
94
- const deployment = showDeploymentOptions
95
- ? routerOnly
96
- ? undefined
97
- : await selectDeployment(options.framework, cliOptions.deployment)
98
- : undefined
99
-
100
- // Add-ons selection
101
- const addOns: Set<string> = new Set()
102
-
103
- // Examples/demo pages are enabled by default
104
- const includeExamples =
105
- cliOptions.examples ?? (routerOnly ? false : await selectExamples())
106
- ;(options as Required<Options> & { includeExamples?: boolean }).includeExamples =
107
- includeExamples
108
-
109
- if (toolchain) {
110
- addOns.add(toolchain)
111
- }
112
- if (deployment) {
113
- addOns.add(deployment)
114
- }
115
-
116
- if (!routerOnly) {
117
- for (const addOn of forcedAddOns) {
118
- addOns.add(addOn)
119
- }
120
- }
121
-
122
- if (!routerOnly && Array.isArray(cliOptions.addOns)) {
123
- for (const addOn of cliOptions.addOns) {
124
- if (addOn.toLowerCase() === 'start') {
125
- continue
126
- }
127
- addOns.add(addOn)
128
- }
129
- } else if (!routerOnly) {
130
- for (const addOn of await selectAddOns(
131
- options.framework,
132
- options.mode,
133
- 'add-on',
134
- 'What add-ons would you like for your project?',
135
- forcedAddOns,
136
- )) {
137
- addOns.add(addOn)
138
- }
139
-
140
- if (includeExamples) {
141
- for (const addOn of await selectAddOns(
142
- options.framework,
143
- options.mode,
144
- 'example',
145
- 'Would you like an example?',
146
- forcedAddOns,
147
- false,
148
- )) {
149
- addOns.add(addOn)
150
- }
151
- }
152
- }
153
-
154
- const chosenAddOns = Array.from(
155
- await finalizeAddOns(options.framework, options.mode, Array.from(addOns)),
156
- )
157
- options.chosenAddOns = includeExamples
158
- ? chosenAddOns
159
- : chosenAddOns.filter((addOn) => addOn.type !== 'example')
160
-
161
- // Tailwind is always enabled
162
- options.tailwind = true
163
-
164
- // Prompt for add-on options in interactive mode
165
- if (Array.isArray(cliOptions.addOns)) {
166
- // Non-interactive mode: use defaults
167
- options.addOnOptions = populateAddOnOptionsDefaults(options.chosenAddOns)
168
- } else {
169
- // Interactive mode: prompt for options
170
- const userOptions = await promptForAddOnOptions(
171
- options.chosenAddOns.map((a) => a.id),
172
- options.framework,
173
- )
174
- const defaultOptions = populateAddOnOptionsDefaults(options.chosenAddOns)
175
- // Merge user options with defaults
176
- options.addOnOptions = { ...defaultOptions, ...userOptions }
177
- }
178
-
179
- // Prompt for env vars exposed by selected add-ons in interactive mode
180
- const envVarValues = Array.isArray(cliOptions.addOns)
181
- ? {}
182
- : await promptForEnvVars(options.chosenAddOns)
183
- ;(options as Required<Options> & { envVarValues?: Record<string, string> }).envVarValues =
184
- envVarValues
185
-
186
- options.git = cliOptions.git ?? (await selectGit())
187
- if (cliOptions.install === false) {
188
- options.install = false
189
- }
190
-
191
- return options
192
- }
193
-
194
- export async function promptForAddOns(): Promise<Array<string>> {
195
- const config = await readConfigFile(process.cwd())
196
-
197
- if (!config) {
198
- console.error('No config file found')
199
- process.exit(1)
200
- }
201
-
202
- const framework = getFrameworkById(config.framework)
203
-
204
- if (!framework) {
205
- console.error(`Unknown framework: ${config.framework}`)
206
- process.exit(1)
207
- }
208
-
209
- intro(`Adding new add-ons to '${config.projectName}'`)
210
-
211
- const addOns: Set<string> = new Set()
212
-
213
- for (const addOn of await selectAddOns(
214
- framework,
215
- config.mode!,
216
- 'add-on',
217
- 'What add-ons would you like for your project?',
218
- config.chosenAddOns,
219
- )) {
220
- addOns.add(addOn)
221
- }
222
-
223
- for (const addOn of await selectAddOns(
224
- framework,
225
- config.mode!,
226
- 'example',
227
- 'Would you like any examples?',
228
- config.chosenAddOns,
229
- )) {
230
- addOns.add(addOn)
231
- }
232
-
233
- return Array.from(addOns)
234
- }
package/src/types.ts DELETED
@@ -1,28 +0,0 @@
1
- import type { PackageManager } from '@tanstack/create'
2
-
3
- export interface CliOptions {
4
- framework?: string
5
- packageManager?: PackageManager
6
- toolchain?: string | false
7
- deployment?: string
8
- projectName?: string
9
- git?: boolean
10
- addOns?: Array<string> | boolean
11
- listAddOns?: boolean
12
- addonDetails?: string
13
- mcp?: boolean
14
- mcpSse?: boolean
15
- starter?: string
16
- templateId?: string
17
- targetDir?: string
18
- interactive?: boolean
19
- devWatch?: string
20
- runDev?: boolean
21
- install?: boolean
22
- addOnConfig?: string
23
- force?: boolean
24
- routerOnly?: boolean
25
- template?: string
26
- tailwind?: boolean
27
- examples?: boolean
28
- }
@@ -1,74 +0,0 @@
1
- import {
2
- cancel,
3
- confirm,
4
- intro,
5
- isCancel,
6
- log,
7
- outro,
8
- spinner,
9
- } from '@clack/prompts'
10
- import chalk from 'chalk'
11
-
12
- import { createDefaultEnvironment } from '@tanstack/create'
13
-
14
- import type { Environment } from '@tanstack/create'
15
-
16
- export function createUIEnvironment(
17
- appName: string,
18
- silent: boolean,
19
- ): Environment {
20
- const defaultEnvironment = createDefaultEnvironment()
21
-
22
- let newEnvironment = {
23
- ...defaultEnvironment,
24
- appName,
25
- }
26
-
27
- if (!silent) {
28
- newEnvironment = {
29
- ...newEnvironment,
30
- intro: (message: string) => {
31
- intro(message)
32
- },
33
- outro: (message: string) => {
34
- outro(message)
35
- },
36
- info: (title?: string, message?: string) => {
37
- log.info(
38
- `${title ? chalk.red(title) : ''}${message ? '\n' + chalk.green(message) : ''}`,
39
- )
40
- },
41
- error: (title?: string, message?: string) => {
42
- log.error(
43
- `${title ? `${title}: ` : ''}${message ? '\n' + message : ''}`,
44
- )
45
- },
46
- warn: (title?: string, message?: string) => {
47
- log.warn(`${title ? `${title}: ` : ''}${message ? '\n' + message : ''}`)
48
- },
49
- confirm: async (message: string) => {
50
- const shouldContinue = await confirm({
51
- message,
52
- })
53
- if (isCancel(shouldContinue)) {
54
- cancel('Operation cancelled.')
55
- process.exit(0)
56
- }
57
- return shouldContinue
58
- },
59
- spinner: () => {
60
- const s = spinner()
61
- return {
62
- start: (message: string) => {
63
- s.start(message)
64
- },
65
- stop: (message: string) => {
66
- s.stop(message)
67
- },
68
- }
69
- },
70
- }
71
- }
72
-
73
- return newEnvironment
74
- }