@tanstack/cli 0.0.8 → 0.48.2

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 (87) hide show
  1. package/dist/bin.js +7 -0
  2. package/dist/cli.js +481 -0
  3. package/dist/command-line.js +174 -0
  4. package/dist/dev-watch.js +290 -0
  5. package/dist/file-syncer.js +148 -0
  6. package/dist/index.js +1 -0
  7. package/dist/mcp/api.js +31 -0
  8. package/dist/mcp/tools.js +250 -0
  9. package/dist/mcp/types.js +37 -0
  10. package/dist/mcp.js +121 -0
  11. package/dist/options.js +162 -0
  12. package/dist/types/bin.d.ts +2 -0
  13. package/dist/types/cli.d.ts +16 -0
  14. package/dist/types/command-line.d.ts +10 -0
  15. package/dist/types/dev-watch.d.ts +27 -0
  16. package/dist/types/file-syncer.d.ts +18 -0
  17. package/dist/types/index.d.ts +1 -0
  18. package/dist/types/mcp/api.d.ts +4 -0
  19. package/dist/types/mcp/tools.d.ts +2 -0
  20. package/dist/types/mcp/types.d.ts +217 -0
  21. package/dist/types/mcp.d.ts +6 -0
  22. package/dist/types/options.d.ts +8 -0
  23. package/dist/types/types.d.ts +25 -0
  24. package/dist/types/ui-environment.d.ts +2 -0
  25. package/dist/types/ui-prompts.d.ts +12 -0
  26. package/dist/types/utils.d.ts +8 -0
  27. package/dist/types.js +1 -0
  28. package/dist/ui-environment.js +52 -0
  29. package/dist/ui-prompts.js +244 -0
  30. package/dist/utils.js +30 -0
  31. package/package.json +46 -46
  32. package/src/bin.ts +6 -93
  33. package/src/cli.ts +692 -0
  34. package/src/command-line.ts +236 -0
  35. package/src/dev-watch.ts +430 -0
  36. package/src/file-syncer.ts +205 -0
  37. package/src/index.ts +1 -85
  38. package/src/mcp.ts +190 -0
  39. package/src/options.ts +260 -0
  40. package/src/types.ts +27 -0
  41. package/src/ui-environment.ts +74 -0
  42. package/src/ui-prompts.ts +322 -0
  43. package/src/utils.ts +38 -0
  44. package/tests/command-line.test.ts +304 -0
  45. package/tests/index.test.ts +9 -0
  46. package/tests/mcp.test.ts +225 -0
  47. package/tests/options.test.ts +304 -0
  48. package/tests/setupVitest.ts +6 -0
  49. package/tests/ui-environment.test.ts +97 -0
  50. package/tests/ui-prompts.test.ts +238 -0
  51. package/tsconfig.json +17 -0
  52. package/vitest.config.js +7 -0
  53. package/dist/bin.cjs +0 -769
  54. package/dist/bin.d.cts +0 -1
  55. package/dist/bin.d.mts +0 -1
  56. package/dist/bin.mjs +0 -768
  57. package/dist/fetch-CbFFGJEw.cjs +0 -3
  58. package/dist/fetch-DG5dLrsb.cjs +0 -522
  59. package/dist/fetch-DhlVXS6S.mjs +0 -390
  60. package/dist/fetch-I_OVg8JX.mjs +0 -3
  61. package/dist/index.cjs +0 -37
  62. package/dist/index.d.cts +0 -1172
  63. package/dist/index.d.mts +0 -1172
  64. package/dist/index.mjs +0 -4
  65. package/dist/template-Szi7-AZJ.mjs +0 -2202
  66. package/dist/template-lWrIZhCQ.cjs +0 -2314
  67. package/src/api/fetch.test.ts +0 -114
  68. package/src/api/fetch.ts +0 -278
  69. package/src/cache/index.ts +0 -89
  70. package/src/commands/create.ts +0 -470
  71. package/src/commands/mcp.test.ts +0 -152
  72. package/src/commands/mcp.ts +0 -211
  73. package/src/engine/compile-with-addons.test.ts +0 -302
  74. package/src/engine/compile.test.ts +0 -404
  75. package/src/engine/compile.ts +0 -569
  76. package/src/engine/config-file.test.ts +0 -118
  77. package/src/engine/config-file.ts +0 -61
  78. package/src/engine/custom-addons/integration.ts +0 -323
  79. package/src/engine/custom-addons/shared.test.ts +0 -98
  80. package/src/engine/custom-addons/shared.ts +0 -281
  81. package/src/engine/custom-addons/template.test.ts +0 -288
  82. package/src/engine/custom-addons/template.ts +0 -124
  83. package/src/engine/template.test.ts +0 -256
  84. package/src/engine/template.ts +0 -269
  85. package/src/engine/types.ts +0 -336
  86. package/src/parse-gitignore.d.ts +0 -5
  87. package/src/templates/base.ts +0 -883
@@ -0,0 +1,250 @@
1
+ import { z } from 'zod';
2
+ import { fetchDocContent, fetchLibraries, fetchPartners } from './api.js';
3
+ // Algolia config (public read-only keys)
4
+ const ALGOLIA_APP_ID = 'FQ0DQ6MA3C';
5
+ const ALGOLIA_API_KEY = '10c34d6a5c89f6048cf644d601e65172';
6
+ const ALGOLIA_INDEX = 'tanstack-test';
7
+ const GROUP_KEYS = ['state', 'headlessUI', 'performance', 'tooling'];
8
+ function jsonResult(data) {
9
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
10
+ }
11
+ function errorResult(error) {
12
+ return { content: [{ type: 'text', text: `Error: ${error}` }], isError: true };
13
+ }
14
+ export function registerDocTools(server) {
15
+ // Tool: tanstack_list_libraries
16
+ server.tool('tanstack_list_libraries', 'List TanStack libraries with metadata, frameworks, and docs URLs.', {
17
+ group: z
18
+ .enum(GROUP_KEYS)
19
+ .optional()
20
+ .describe('Filter libraries by group. Options: state, headlessUI, performance, tooling'),
21
+ }, async ({ group }) => {
22
+ try {
23
+ const data = await fetchLibraries();
24
+ let libraries = data.libraries;
25
+ if (group && data.groups[group]) {
26
+ const groupIds = data.groups[group];
27
+ libraries = libraries.filter((lib) => groupIds.includes(lib.id));
28
+ }
29
+ const groupName = group ? data.groupNames[group] || group : 'All Libraries';
30
+ return jsonResult({
31
+ group: groupName,
32
+ count: libraries.length,
33
+ libraries: libraries.map((lib) => ({
34
+ id: lib.id,
35
+ name: lib.name,
36
+ tagline: lib.tagline,
37
+ description: lib.description,
38
+ frameworks: lib.frameworks,
39
+ latestVersion: lib.latestVersion,
40
+ docsUrl: lib.docsUrl,
41
+ githubUrl: lib.githubUrl,
42
+ })),
43
+ });
44
+ }
45
+ catch (error) {
46
+ return errorResult(String(error));
47
+ }
48
+ });
49
+ // Tool: tanstack_doc
50
+ server.tool('tanstack_doc', 'Fetch a TanStack documentation page by library and path.', {
51
+ library: z.string().describe('Library ID (e.g., query, router, table, form)'),
52
+ path: z.string().describe('Documentation path (e.g., framework/react/overview)'),
53
+ version: z.string().optional().describe('Version (e.g., v5, v1). Defaults to latest'),
54
+ }, async ({ library: libraryId, path, version = 'latest' }) => {
55
+ try {
56
+ const data = await fetchLibraries();
57
+ const library = data.libraries.find((l) => l.id === libraryId);
58
+ if (!library) {
59
+ return errorResult(`Library "${libraryId}" not found. Use tanstack_list_libraries to see available libraries.`);
60
+ }
61
+ if (version !== 'latest' && !library.availableVersions.includes(version)) {
62
+ return errorResult(`Version "${version}" not found for ${library.name}. Available: ${library.availableVersions.join(', ')}`);
63
+ }
64
+ // Resolve branch
65
+ const branch = version === 'latest' || version === library.latestVersion
66
+ ? library.latestBranch || 'main'
67
+ : version;
68
+ const docsRoot = library.docsRoot || 'docs';
69
+ const filePath = `${docsRoot}/${path}.md`;
70
+ const content = await fetchDocContent(library.repo, branch, filePath);
71
+ if (!content) {
72
+ return errorResult(`Document not found: ${library.name} / ${path} (version: ${version})`);
73
+ }
74
+ // Extract frontmatter title if present
75
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
76
+ let title = path.split('/').pop() || 'Untitled';
77
+ let docContent = content;
78
+ if (frontmatterMatch && frontmatterMatch[1]) {
79
+ const frontmatter = frontmatterMatch[1];
80
+ const titleMatch = frontmatter.match(/title:\s*['"]?([^'"\n]+)['"]?/);
81
+ if (titleMatch && titleMatch[1]) {
82
+ title = titleMatch[1];
83
+ }
84
+ docContent = content.slice(frontmatterMatch[0].length).trim();
85
+ }
86
+ return jsonResult({
87
+ title,
88
+ content: docContent,
89
+ url: `https://tanstack.com/${libraryId}/${version}/docs/${path}`,
90
+ library: library.name,
91
+ version: version === 'latest' ? library.latestVersion : version,
92
+ });
93
+ }
94
+ catch (error) {
95
+ return errorResult(String(error));
96
+ }
97
+ });
98
+ // Tool: tanstack_search_docs
99
+ server.tool('tanstack_search_docs', 'Search TanStack documentation. Returns matching pages with snippets.', {
100
+ query: z.string().describe('Search query'),
101
+ library: z.string().optional().describe('Filter to specific library (e.g., query, router)'),
102
+ framework: z
103
+ .string()
104
+ .optional()
105
+ .describe('Filter to specific framework (e.g., react, vue, solid)'),
106
+ limit: z
107
+ .number()
108
+ .min(1)
109
+ .max(50)
110
+ .optional()
111
+ .describe('Maximum number of results (default: 10, max: 50)'),
112
+ }, async ({ query, library, framework, limit = 10 }) => {
113
+ try {
114
+ const ALL_LIBRARIES = [
115
+ 'config', 'form', 'optimistic', 'pacer', 'query', 'ranger',
116
+ 'react-charts', 'router', 'start', 'store', 'table', 'virtual', 'db', 'devtools',
117
+ ];
118
+ const ALL_FRAMEWORKS = ['react', 'vue', 'solid', 'svelte', 'angular'];
119
+ // Build filters
120
+ const filterParts = ['version:latest'];
121
+ if (library) {
122
+ const otherLibraries = ALL_LIBRARIES.filter((l) => l !== library);
123
+ const exclusions = otherLibraries.map((l) => `NOT library:${l}`).join(' AND ');
124
+ if (exclusions)
125
+ filterParts.push(`(${exclusions})`);
126
+ }
127
+ if (framework) {
128
+ const otherFrameworks = ALL_FRAMEWORKS.filter((f) => f !== framework);
129
+ const exclusions = otherFrameworks.map((f) => `NOT framework:${f}`).join(' AND ');
130
+ if (exclusions)
131
+ filterParts.push(`(${exclusions})`);
132
+ }
133
+ // Call Algolia REST API directly
134
+ const searchParams = {
135
+ requests: [
136
+ {
137
+ indexName: ALGOLIA_INDEX,
138
+ query,
139
+ hitsPerPage: Math.min(limit, 50),
140
+ filters: filterParts.join(' AND '),
141
+ attributesToRetrieve: ['hierarchy', 'url', 'content', 'library'],
142
+ attributesToSnippet: ['content:80'],
143
+ },
144
+ ],
145
+ };
146
+ const response = await fetch(`https://${ALGOLIA_APP_ID}-dsn.algolia.net/1/indexes/*/queries`, {
147
+ method: 'POST',
148
+ headers: {
149
+ 'Content-Type': 'application/json',
150
+ 'X-Algolia-Application-Id': ALGOLIA_APP_ID,
151
+ 'X-Algolia-API-Key': ALGOLIA_API_KEY,
152
+ },
153
+ body: JSON.stringify(searchParams),
154
+ });
155
+ if (!response.ok) {
156
+ return errorResult(`Algolia search failed: ${response.statusText}`);
157
+ }
158
+ const searchResponse = await response.json();
159
+ const searchResult = searchResponse.results[0];
160
+ if (!searchResult) {
161
+ return jsonResult({ query, totalHits: 0, results: [] });
162
+ }
163
+ const results = searchResult.hits.map((hit) => {
164
+ const breadcrumb = Object.values(hit.hierarchy).filter((v) => Boolean(v));
165
+ return {
166
+ title: hit.hierarchy.lvl1 || hit.hierarchy.lvl0 || 'Untitled',
167
+ url: hit.url,
168
+ snippet: hit._snippetResult?.content?.value || hit.content || '',
169
+ library: hit.library || 'unknown',
170
+ breadcrumb,
171
+ };
172
+ });
173
+ return jsonResult({
174
+ query,
175
+ totalHits: searchResult.nbHits || results.length,
176
+ results,
177
+ });
178
+ }
179
+ catch (error) {
180
+ return errorResult(String(error));
181
+ }
182
+ });
183
+ // Tool: tanstack_ecosystem
184
+ server.tool('tanstack_ecosystem', 'Ecosystem partner recommendations. Filter by category (database, auth, deployment, monitoring, cms, api, data-grid) or library.', {
185
+ category: z
186
+ .string()
187
+ .optional()
188
+ .describe('Filter by category: database, auth, deployment, monitoring, cms, api, data-grid, code-review, learning'),
189
+ library: z
190
+ .string()
191
+ .optional()
192
+ .describe('Filter by TanStack library (e.g., start, router, query, table)'),
193
+ }, async ({ category, library }) => {
194
+ try {
195
+ const data = await fetchPartners();
196
+ // Category aliases
197
+ const categoryAliases = {
198
+ db: 'database',
199
+ postgres: 'database',
200
+ sql: 'database',
201
+ login: 'auth',
202
+ authentication: 'auth',
203
+ hosting: 'deployment',
204
+ deploy: 'deployment',
205
+ serverless: 'deployment',
206
+ errors: 'monitoring',
207
+ logging: 'monitoring',
208
+ content: 'cms',
209
+ 'api-keys': 'api',
210
+ grid: 'data-grid',
211
+ review: 'code-review',
212
+ courses: 'learning',
213
+ };
214
+ let resolvedCategory;
215
+ if (category) {
216
+ const normalized = category.toLowerCase().trim();
217
+ resolvedCategory = categoryAliases[normalized] || normalized;
218
+ if (!data.categories.includes(resolvedCategory)) {
219
+ resolvedCategory = undefined;
220
+ }
221
+ }
222
+ const lib = library?.toLowerCase().trim();
223
+ const partners = data.partners
224
+ .filter((p) => !resolvedCategory || p.category === resolvedCategory)
225
+ .filter((p) => !lib || p.libraries.some((l) => l === lib))
226
+ .map((p) => ({
227
+ id: p.id,
228
+ name: p.name,
229
+ tagline: p.tagline,
230
+ description: p.description,
231
+ category: p.category,
232
+ categoryLabel: p.categoryLabel,
233
+ url: p.url,
234
+ libraries: p.libraries,
235
+ }));
236
+ return jsonResult({
237
+ query: {
238
+ category,
239
+ categoryResolved: resolvedCategory,
240
+ library,
241
+ },
242
+ count: partners.length,
243
+ partners,
244
+ });
245
+ }
246
+ catch (error) {
247
+ return errorResult(String(error));
248
+ }
249
+ });
250
+ }
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ // API response types from tanstack.com
3
+ export const LibrarySchema = z.object({
4
+ id: z.string(),
5
+ name: z.string(),
6
+ tagline: z.string(),
7
+ description: z.string().optional(),
8
+ frameworks: z.array(z.string()),
9
+ latestVersion: z.string(),
10
+ latestBranch: z.string().optional(),
11
+ availableVersions: z.array(z.string()),
12
+ repo: z.string(),
13
+ docsRoot: z.string().optional(),
14
+ defaultDocs: z.string().optional(),
15
+ docsUrl: z.string().optional(),
16
+ githubUrl: z.string().optional(),
17
+ });
18
+ export const LibrariesResponseSchema = z.object({
19
+ libraries: z.array(LibrarySchema),
20
+ groups: z.record(z.array(z.string())),
21
+ groupNames: z.record(z.string()),
22
+ });
23
+ export const PartnerSchema = z.object({
24
+ id: z.string(),
25
+ name: z.string(),
26
+ tagline: z.string().optional(),
27
+ description: z.string(),
28
+ category: z.string(),
29
+ categoryLabel: z.string(),
30
+ libraries: z.array(z.string()),
31
+ url: z.string(),
32
+ });
33
+ export const PartnersResponseSchema = z.object({
34
+ partners: z.array(PartnerSchema),
35
+ categories: z.array(z.string()),
36
+ categoryLabels: z.record(z.string()),
37
+ });
package/dist/mcp.js ADDED
@@ -0,0 +1,121 @@
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
+ import { createApp, createDefaultEnvironment, finalizeAddOns, getFrameworkByName, getFrameworks, populateAddOnOptionsDefaults, } from '@tanstack/create';
7
+ import { registerDocTools } from './mcp/tools.js';
8
+ function createServer({ appName, forcedAddOns = [], }) {
9
+ const server = new McpServer({
10
+ name: `${appName} Application Builder`,
11
+ version: '1.0.0',
12
+ });
13
+ const frameworks = getFrameworks();
14
+ const frameworkNames = frameworks.map((framework) => framework.name);
15
+ server.tool('listTanStackAddOns', 'List the available add-ons for creating TanStack applications', {
16
+ framework: z
17
+ .string()
18
+ .describe(`The framework to use. Available frameworks: ${frameworkNames.join(', ')}`),
19
+ }, ({ framework: frameworkName }) => {
20
+ const framework = getFrameworkByName(frameworkName);
21
+ return {
22
+ content: [
23
+ {
24
+ type: 'text',
25
+ text: JSON.stringify(framework
26
+ .getAddOns()
27
+ .filter((addOn) => addOn.modes.includes('file-router'))
28
+ .map((addOn) => ({
29
+ id: addOn.id,
30
+ name: addOn.name,
31
+ description: addOn.description,
32
+ options: addOn.options,
33
+ dependsOn: addOn.dependsOn,
34
+ }))),
35
+ },
36
+ ],
37
+ };
38
+ });
39
+ server.tool('createTanStackApplication', 'Create a new TanStack application', {
40
+ framework: z
41
+ .string()
42
+ .describe(`The framework to use. Available frameworks: ${frameworkNames.join(', ')}`),
43
+ projectName: z
44
+ .string()
45
+ .describe('The package.json module name of the application (will also be the directory name)'),
46
+ cwd: z.string().describe('The directory to create the application in'),
47
+ 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"]'),
48
+ 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.'),
49
+ targetDir: z
50
+ .string()
51
+ .describe('The directory to create the application in. Use the absolute path of the directory you want the application to be created in'),
52
+ }, async ({ framework: frameworkName, projectName, addOns, addOnOptions, cwd, targetDir, }) => {
53
+ const framework = getFrameworkByName(frameworkName);
54
+ try {
55
+ process.chdir(cwd);
56
+ try {
57
+ const chosenAddOns = await finalizeAddOns(framework, 'file-router', Array.from(new Set([
58
+ ...addOns,
59
+ ...forcedAddOns,
60
+ ])));
61
+ await createApp(createDefaultEnvironment(), {
62
+ projectName: projectName.replace(/^\//, './'),
63
+ targetDir,
64
+ framework,
65
+ typescript: true,
66
+ tailwind: true,
67
+ packageManager: 'pnpm',
68
+ mode: 'file-router',
69
+ chosenAddOns,
70
+ addOnOptions: addOnOptions || populateAddOnOptionsDefaults(chosenAddOns),
71
+ git: true,
72
+ });
73
+ }
74
+ catch (error) {
75
+ console.error(error);
76
+ return {
77
+ content: [
78
+ { type: 'text', text: `Error creating application: ${error}` },
79
+ ],
80
+ };
81
+ }
82
+ return {
83
+ content: [{ type: 'text', text: 'Application created successfully' }],
84
+ };
85
+ }
86
+ catch (error) {
87
+ return {
88
+ content: [
89
+ { type: 'text', text: `Error creating application: ${error}` },
90
+ ],
91
+ };
92
+ }
93
+ });
94
+ // Register doc/ecosystem tools from TanStack API
95
+ registerDocTools(server);
96
+ return server;
97
+ }
98
+ export async function runMCPServer(sse, { forcedAddOns, appName, name, }) {
99
+ let transport = null;
100
+ const server = createServer({ appName, forcedAddOns, name });
101
+ if (sse) {
102
+ const app = express();
103
+ app.get('/sse', (req, res) => {
104
+ transport = new SSEServerTransport('/messages', res);
105
+ server.connect(transport);
106
+ });
107
+ app.post('/messages', (req, res) => {
108
+ if (transport) {
109
+ transport.handlePostMessage(req, res);
110
+ }
111
+ });
112
+ const port = process.env.PORT || 8080;
113
+ app.listen(port, () => {
114
+ console.log(`Server is running on port http://localhost:${port}/sse`);
115
+ });
116
+ }
117
+ else {
118
+ const transport = new StdioServerTransport();
119
+ await server.connect(transport);
120
+ }
121
+ }
@@ -0,0 +1,162 @@
1
+ import fs from 'node:fs';
2
+ import { cancel, confirm, intro, isCancel } from '@clack/prompts';
3
+ import { finalizeAddOns, getFrameworkById, getPackageManager, populateAddOnOptionsDefaults, readConfigFile, } from '@tanstack/create';
4
+ import { getProjectName, promptForAddOnOptions, selectAddOns, selectDeployment, selectGit, selectPackageManager, selectRouterType, selectTailwind, selectToolchain, selectTypescript, } from './ui-prompts.js';
5
+ import { getCurrentDirectoryName, sanitizePackageName, validateProjectName, } from './utils.js';
6
+ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], forcedMode, showDeploymentOptions = false, }) {
7
+ const options = {};
8
+ options.framework = getFrameworkById(cliOptions.framework || 'react-cra');
9
+ // Validate project name
10
+ if (cliOptions.projectName) {
11
+ // Handle "." as project name - use sanitized current directory name
12
+ if (cliOptions.projectName === '.') {
13
+ options.projectName = sanitizePackageName(getCurrentDirectoryName());
14
+ }
15
+ else {
16
+ options.projectName = cliOptions.projectName;
17
+ }
18
+ const { valid, error } = validateProjectName(options.projectName);
19
+ if (!valid) {
20
+ console.error(error);
21
+ process.exit(1);
22
+ }
23
+ }
24
+ else {
25
+ options.projectName = await getProjectName();
26
+ }
27
+ // Check if target directory is empty
28
+ if (!cliOptions.force &&
29
+ fs.existsSync(options.projectName) &&
30
+ fs.readdirSync(options.projectName).length > 0) {
31
+ const shouldContinue = await confirm({
32
+ message: `Target directory ${options.projectName} is not empty. Do you want to continue?`,
33
+ initialValue: true,
34
+ });
35
+ if (isCancel(shouldContinue) || !shouldContinue) {
36
+ cancel('Operation cancelled.');
37
+ process.exit(0);
38
+ }
39
+ }
40
+ // Router type selection
41
+ if (forcedMode) {
42
+ options.mode = forcedMode;
43
+ }
44
+ else if (cliOptions.template) {
45
+ options.mode =
46
+ cliOptions.template === 'file-router' ? 'file-router' : 'code-router';
47
+ }
48
+ else {
49
+ options.mode = await selectRouterType();
50
+ }
51
+ // TypeScript selection (if using Code Router)
52
+ // TODO: Make this declarative
53
+ options.typescript =
54
+ options.mode === 'file-router' || options.framework.id === 'solid';
55
+ if (forcedMode &&
56
+ options.framework.supportedModes[forcedMode].forceTypescript) {
57
+ options.typescript = true;
58
+ }
59
+ if (!options.typescript && options.mode === 'code-router') {
60
+ options.typescript = await selectTypescript();
61
+ }
62
+ // Package manager selection
63
+ if (cliOptions.packageManager) {
64
+ options.packageManager = cliOptions.packageManager;
65
+ }
66
+ else {
67
+ const detectedPackageManager = await getPackageManager();
68
+ options.packageManager =
69
+ detectedPackageManager || (await selectPackageManager());
70
+ }
71
+ // Toolchain selection
72
+ const toolchain = await selectToolchain(options.framework, cliOptions.toolchain);
73
+ // Deployment selection
74
+ const deployment = showDeploymentOptions
75
+ ? await selectDeployment(options.framework, cliOptions.deployment)
76
+ : undefined;
77
+ // Add-ons selection
78
+ const addOns = new Set();
79
+ if (toolchain) {
80
+ addOns.add(toolchain);
81
+ }
82
+ if (deployment) {
83
+ addOns.add(deployment);
84
+ }
85
+ for (const addOn of forcedAddOns) {
86
+ addOns.add(addOn);
87
+ }
88
+ if (Array.isArray(cliOptions.addOns)) {
89
+ for (const addOn of cliOptions.addOns) {
90
+ addOns.add(addOn);
91
+ }
92
+ }
93
+ else {
94
+ for (const addOn of await selectAddOns(options.framework, options.mode, 'add-on', 'What add-ons would you like for your project?', forcedAddOns)) {
95
+ addOns.add(addOn);
96
+ }
97
+ for (const addOn of await selectAddOns(options.framework, options.mode, 'example', 'Would you like an example?', forcedAddOns, false)) {
98
+ addOns.add(addOn);
99
+ }
100
+ }
101
+ options.chosenAddOns = Array.from(await finalizeAddOns(options.framework, options.mode, Array.from(addOns)));
102
+ if (options.chosenAddOns.length) {
103
+ options.typescript = true;
104
+ }
105
+ // Tailwind selection
106
+ // Only treat add-ons as requiring tailwind if they explicitly have "tailwind": true
107
+ const addOnsRequireTailwind = options.chosenAddOns.some((addOn) => addOn.tailwind === true);
108
+ if (addOnsRequireTailwind) {
109
+ // If any add-on explicitly requires tailwind, enable it automatically
110
+ options.tailwind = true;
111
+ }
112
+ else if (cliOptions.tailwind !== undefined) {
113
+ // User explicitly provided a CLI flag, respect it
114
+ options.tailwind = !!cliOptions.tailwind;
115
+ }
116
+ else if (options.framework.id === 'react-cra') {
117
+ // Only show prompt for react-cra when no CLI flag and no add-ons require it
118
+ options.tailwind = await selectTailwind();
119
+ }
120
+ else {
121
+ // For other frameworks (like solid), default to true
122
+ options.tailwind = true;
123
+ }
124
+ // Prompt for add-on options in interactive mode
125
+ if (Array.isArray(cliOptions.addOns)) {
126
+ // Non-interactive mode: use defaults
127
+ options.addOnOptions = populateAddOnOptionsDefaults(options.chosenAddOns);
128
+ }
129
+ else {
130
+ // Interactive mode: prompt for options
131
+ const userOptions = await promptForAddOnOptions(options.chosenAddOns.map((a) => a.id), options.framework);
132
+ const defaultOptions = populateAddOnOptionsDefaults(options.chosenAddOns);
133
+ // Merge user options with defaults
134
+ options.addOnOptions = { ...defaultOptions, ...userOptions };
135
+ }
136
+ options.git = cliOptions.git || (await selectGit());
137
+ if (cliOptions.install === false) {
138
+ options.install = false;
139
+ }
140
+ return options;
141
+ }
142
+ export async function promptForAddOns() {
143
+ const config = await readConfigFile(process.cwd());
144
+ if (!config) {
145
+ console.error('No config file found');
146
+ process.exit(1);
147
+ }
148
+ const framework = getFrameworkById(config.framework);
149
+ if (!framework) {
150
+ console.error(`Unknown framework: ${config.framework}`);
151
+ process.exit(1);
152
+ }
153
+ intro(`Adding new add-ons to '${config.projectName}'`);
154
+ const addOns = new Set();
155
+ for (const addOn of await selectAddOns(framework, config.mode, 'add-on', 'What add-ons would you like for your project?', config.chosenAddOns)) {
156
+ addOns.add(addOn);
157
+ }
158
+ for (const addOn of await selectAddOns(framework, config.mode, 'example', 'Would you like any examples?', config.chosenAddOns)) {
159
+ addOns.add(addOn);
160
+ }
161
+ return Array.from(addOns);
162
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,16 @@
1
+ import type { TemplateOptions } from './types.js';
2
+ import type { FrameworkDefinition } from '@tanstack/create';
3
+ export declare function cli({ name, appName, forcedMode, forcedAddOns, defaultTemplate, forcedDeployment, defaultFramework, craCompatible, webBase, frameworkDefinitionInitializers, showDeploymentOptions, legacyAutoCreate, }: {
4
+ name: string;
5
+ appName: string;
6
+ forcedMode?: string;
7
+ forcedAddOns?: Array<string>;
8
+ forcedDeployment?: string;
9
+ defaultTemplate?: TemplateOptions;
10
+ defaultFramework?: string;
11
+ craCompatible?: boolean;
12
+ webBase?: string;
13
+ frameworkDefinitionInitializers?: Array<() => FrameworkDefinition>;
14
+ showDeploymentOptions?: boolean;
15
+ legacyAutoCreate?: boolean;
16
+ }): void;
@@ -0,0 +1,10 @@
1
+ import type { Options } from '@tanstack/create';
2
+ import type { CliOptions } from './types.js';
3
+ export declare function normalizeOptions(cliOptions: CliOptions, forcedMode?: string, forcedAddOns?: Array<string>, opts?: {
4
+ disableNameCheck?: boolean;
5
+ forcedDeployment?: string;
6
+ }): Promise<Options | undefined>;
7
+ export declare function validateDevWatchOptions(cliOptions: CliOptions): {
8
+ valid: boolean;
9
+ error?: string;
10
+ };
@@ -0,0 +1,27 @@
1
+ import type { Environment, Framework, FrameworkDefinition, Options } from '@tanstack/create';
2
+ export interface DevWatchOptions {
3
+ watchPath: string;
4
+ targetDir: string;
5
+ framework: Framework;
6
+ cliOptions: Options;
7
+ packageManager: string;
8
+ environment: Environment;
9
+ frameworkDefinitionInitializers?: Array<() => FrameworkDefinition>;
10
+ }
11
+ export declare class DevWatchManager {
12
+ private options;
13
+ private watcher;
14
+ private debounceQueue;
15
+ private syncer;
16
+ private tempDir;
17
+ private isBuilding;
18
+ private buildCount;
19
+ constructor(options: DevWatchOptions);
20
+ start(): Promise<void>;
21
+ stop(): Promise<void>;
22
+ private startWatcher;
23
+ private handleChange;
24
+ private rebuild;
25
+ private cleanup;
26
+ private log;
27
+ }
@@ -0,0 +1,18 @@
1
+ export interface FileUpdate {
2
+ path: string;
3
+ diff?: string;
4
+ }
5
+ export interface SyncResult {
6
+ updated: Array<FileUpdate>;
7
+ skipped: Array<string>;
8
+ created: Array<string>;
9
+ errors: Array<string>;
10
+ }
11
+ export declare class FileSyncer {
12
+ sync(sourceDir: string, targetDir: string): Promise<SyncResult>;
13
+ private syncDirectory;
14
+ private shouldUpdateFile;
15
+ private calculateHash;
16
+ private shouldSkipDirectory;
17
+ private shouldSkipFile;
18
+ }
@@ -0,0 +1 @@
1
+ export { cli } from './cli.js';
@@ -0,0 +1,4 @@
1
+ import type { LibrariesResponse, PartnersResponse } from './types.js';
2
+ export declare function fetchLibraries(): Promise<LibrariesResponse>;
3
+ export declare function fetchPartners(): Promise<PartnersResponse>;
4
+ export declare function fetchDocContent(repo: string, branch: string, filePath: string): Promise<string | null>;
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerDocTools(server: McpServer): void;