@tanstack/cta-cli 0.10.0-alpha.16

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-present Tanner Linsley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cli.js ADDED
@@ -0,0 +1,149 @@
1
+ import { Command, InvalidArgumentError } from 'commander';
2
+ import { intro, log } from '@clack/prompts';
3
+ import chalk from 'chalk';
4
+ import { SUPPORTED_PACKAGE_MANAGERS, addToApp, createApp, getAllAddOns, getFrameworkById, getFrameworkByName, getFrameworks, } from '@tanstack/cta-engine';
5
+ import { initAddOn } from '@tanstack/cta-custom-add-on';
6
+ import { runMCPServer } from '@tanstack/cta-mcp';
7
+ import { launchUI } from '@tanstack/cta-ui';
8
+ import { normalizeOptions, promptForOptions } from './options.js';
9
+ import { createUIEnvironment } from './ui-environment.js';
10
+ async function listAddOns(options, { forcedMode, forcedAddOns = [], }) {
11
+ const addOns = await getAllAddOns(getFrameworkById(options.framework || 'react-cra'), forcedMode || options.template || 'typescript');
12
+ for (const addOn of addOns.filter((a) => !forcedAddOns.includes(a.id))) {
13
+ console.log(`${chalk.bold(addOn.id)}: ${addOn.description}`);
14
+ }
15
+ }
16
+ export function cli({ name, appName, forcedMode, forcedAddOns, }) {
17
+ const environment = createUIEnvironment();
18
+ const program = new Command();
19
+ const availableFrameworks = getFrameworks().map((f) => f.name);
20
+ const toolchains = new Set();
21
+ for (const framework of getFrameworks()) {
22
+ for (const addOn of framework.getAddOns()) {
23
+ if (addOn.type === 'toolchain') {
24
+ toolchains.add(addOn.id);
25
+ }
26
+ }
27
+ }
28
+ program.name(name).description(`CLI to create a new ${appName} application`);
29
+ program
30
+ .command('add')
31
+ .argument('add-on', 'Name of the add-on (or add-ons separated by commas)')
32
+ .action(async (addOn) => {
33
+ await addToApp(addOn.split(',').map((addon) => addon.trim()), {
34
+ silent: false,
35
+ }, environment);
36
+ });
37
+ const addOnCommand = program.command('add-on');
38
+ addOnCommand
39
+ .command('update')
40
+ .description('Create or update an add-on from the current project')
41
+ .action(async () => {
42
+ await initAddOn('add-on', environment);
43
+ });
44
+ addOnCommand
45
+ .command('ui')
46
+ .description('Show the add-on developer UI')
47
+ .action(async () => {
48
+ launchUI();
49
+ });
50
+ program
51
+ .command('update-starter')
52
+ .description('Create or update a project starter from the current project')
53
+ .action(async () => {
54
+ await initAddOn('starter', environment);
55
+ });
56
+ program.argument('[project-name]', 'name of the project');
57
+ if (!forcedMode) {
58
+ program.option('--template <type>', 'project template (typescript, javascript, file-router)', (value) => {
59
+ if (value !== 'typescript' &&
60
+ value !== 'javascript' &&
61
+ value !== 'file-router') {
62
+ throw new InvalidArgumentError(`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`);
63
+ }
64
+ return value;
65
+ });
66
+ }
67
+ program
68
+ .option('--framework <type>', `project framework (${availableFrameworks.join(', ')})`, (value) => {
69
+ if (!availableFrameworks.includes(value)) {
70
+ throw new InvalidArgumentError(`Invalid framework: ${value}. Only the following are allowed: ${availableFrameworks.join(', ')}`);
71
+ }
72
+ return value;
73
+ }, 'react')
74
+ .option('--starter [url]', 'initialize this project from a starter URL', false)
75
+ .option(`--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`, `Explicitly tell the CLI to use this package manager`, (value) => {
76
+ if (!SUPPORTED_PACKAGE_MANAGERS.includes(value)) {
77
+ throw new InvalidArgumentError(`Invalid package manager: ${value}. The following are allowed: ${SUPPORTED_PACKAGE_MANAGERS.join(', ')}`);
78
+ }
79
+ return value;
80
+ })
81
+ .option(`--toolchain <${Array.from(toolchains).join('|')}>`, `Explicitly tell the CLI to use this toolchain`, (value) => {
82
+ if (!toolchains.has(value)) {
83
+ throw new InvalidArgumentError(`Invalid toolchain: ${value}. The following are allowed: ${Array.from(toolchains).join(', ')}`);
84
+ }
85
+ return value;
86
+ })
87
+ .option('--tailwind', 'add Tailwind CSS', false)
88
+ .option('--add-ons [...add-ons]', 'pick from a list of available add-ons (comma separated list)', (value) => {
89
+ let addOns = !!value;
90
+ if (typeof value === 'string') {
91
+ addOns = value.split(',').map((addon) => addon.trim());
92
+ }
93
+ return addOns;
94
+ })
95
+ .option('--list-add-ons', 'list all available add-ons', false)
96
+ .option('--no-git', 'do not create a git repository')
97
+ .option('--target-dir <path>', 'the target directory for the application root')
98
+ .option('--mcp', 'run the MCP server', false)
99
+ .option('--mcp-sse', 'run the MCP server in SSE mode', false);
100
+ program.action(async (projectName, options) => {
101
+ if (options.listAddOns) {
102
+ await listAddOns(options, {
103
+ forcedMode: forcedMode,
104
+ forcedAddOns,
105
+ });
106
+ }
107
+ else if (options.mcp || options.mcpSse) {
108
+ await runMCPServer(!!options.mcpSse, {
109
+ forcedMode: forcedMode,
110
+ forcedAddOns,
111
+ appName,
112
+ });
113
+ }
114
+ else {
115
+ try {
116
+ const cliOptions = {
117
+ projectName,
118
+ ...options,
119
+ };
120
+ cliOptions.framework = getFrameworkByName(options.framework || 'react').id;
121
+ if (forcedMode) {
122
+ cliOptions.template = forcedMode;
123
+ }
124
+ let finalOptions = await normalizeOptions(cliOptions, forcedMode, forcedAddOns);
125
+ if (finalOptions) {
126
+ intro(`Creating a new ${appName} app in ${projectName}...`);
127
+ }
128
+ else {
129
+ intro(`Let's configure your ${appName} application`);
130
+ finalOptions = await promptForOptions(cliOptions, {
131
+ forcedMode: forcedMode,
132
+ forcedAddOns,
133
+ });
134
+ }
135
+ await createApp(finalOptions, {
136
+ environment: createUIEnvironment(),
137
+ cwd: options.targetDir || undefined,
138
+ name,
139
+ appName,
140
+ });
141
+ }
142
+ catch (error) {
143
+ log.error(error instanceof Error ? error.message : 'An unknown error occurred');
144
+ process.exit(1);
145
+ }
146
+ }
147
+ });
148
+ program.parse();
149
+ }
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { cli } from './cli.js';
@@ -0,0 +1,358 @@
1
+ import { cancel, confirm, isCancel, multiselect, select, text, } from '@clack/prompts';
2
+ import { CODE_ROUTER, DEFAULT_PACKAGE_MANAGER, FILE_ROUTER, SUPPORTED_PACKAGE_MANAGERS, finalizeAddOns, getAllAddOns, getFrameworkById, getPackageManager, loadRemoteAddOn, } from '@tanstack/cta-engine';
3
+ // If all CLI options are provided, use them directly
4
+ export async function normalizeOptions(cliOptions, forcedMode, forcedAddOns) {
5
+ // in some cases, if you use windows/powershell, the argument for addons
6
+ // if sepparated by comma is not really passed as an array, but as a string
7
+ // with spaces, We need to normalize this edge case.
8
+ if (Array.isArray(cliOptions.addOns) && cliOptions.addOns.length === 1) {
9
+ const parseSeparatedArgs = cliOptions.addOns[0].split(' ');
10
+ if (parseSeparatedArgs.length > 1) {
11
+ cliOptions.addOns = parseSeparatedArgs;
12
+ }
13
+ }
14
+ if (cliOptions.projectName) {
15
+ let typescript = cliOptions.template === 'typescript' ||
16
+ cliOptions.template === 'file-router' ||
17
+ cliOptions.framework === 'solid';
18
+ let tailwind = !!cliOptions.tailwind;
19
+ if (cliOptions.framework === 'solid') {
20
+ tailwind = true;
21
+ }
22
+ let mode = cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER;
23
+ const starter = cliOptions.starter
24
+ ? (await loadRemoteAddOn(cliOptions.starter))
25
+ : undefined;
26
+ if (starter) {
27
+ tailwind = starter.tailwind;
28
+ typescript = starter.typescript;
29
+ cliOptions.framework = starter.framework;
30
+ mode = starter.mode;
31
+ }
32
+ let addOns = false;
33
+ let chosenAddOns = [];
34
+ if (Array.isArray(cliOptions.addOns) ||
35
+ starter?.dependsOn ||
36
+ forcedAddOns ||
37
+ cliOptions.toolchain) {
38
+ addOns = true;
39
+ let finalAddOns = Array.from(new Set([...(starter?.dependsOn || []), ...(forcedAddOns || [])]));
40
+ if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
41
+ finalAddOns = Array.from(new Set([
42
+ ...(forcedAddOns || []),
43
+ ...finalAddOns,
44
+ ...cliOptions.addOns,
45
+ ]));
46
+ }
47
+ const framework = getFrameworkById(cliOptions.framework || 'react-cra');
48
+ if (cliOptions.toolchain) {
49
+ finalAddOns.push(cliOptions.toolchain);
50
+ }
51
+ chosenAddOns = await finalizeAddOns(framework, forcedMode || cliOptions.template === 'file-router'
52
+ ? FILE_ROUTER
53
+ : CODE_ROUTER, finalAddOns);
54
+ tailwind = true;
55
+ typescript = true;
56
+ }
57
+ return {
58
+ // TODO: This is a bit to fix the default framework
59
+ framework: getFrameworkById(cliOptions.framework || 'react-cra'),
60
+ projectName: cliOptions.projectName,
61
+ typescript,
62
+ tailwind,
63
+ packageManager: cliOptions.packageManager ||
64
+ getPackageManager() ||
65
+ DEFAULT_PACKAGE_MANAGER,
66
+ mode,
67
+ git: !!cliOptions.git,
68
+ addOns,
69
+ chosenAddOns,
70
+ variableValues: {},
71
+ starter,
72
+ };
73
+ }
74
+ }
75
+ async function collectVariables(variables) {
76
+ const responses = {};
77
+ for (const variable of variables) {
78
+ if (variable.type === 'string') {
79
+ const response = await text({
80
+ message: variable.description,
81
+ initialValue: variable.default,
82
+ });
83
+ if (isCancel(response)) {
84
+ cancel('Operation cancelled.');
85
+ process.exit(0);
86
+ }
87
+ responses[variable.name] = response;
88
+ }
89
+ else if (variable.type === 'number') {
90
+ const response = await text({
91
+ message: variable.description,
92
+ initialValue: variable.default.toString(),
93
+ });
94
+ if (isCancel(response)) {
95
+ cancel('Operation cancelled.');
96
+ process.exit(0);
97
+ }
98
+ responses[variable.name] = Number(response);
99
+ }
100
+ else {
101
+ const response = await confirm({
102
+ message: variable.description,
103
+ initialValue: variable.default === true,
104
+ });
105
+ if (isCancel(response)) {
106
+ cancel('Operation cancelled.');
107
+ process.exit(0);
108
+ }
109
+ responses[variable.name] = response;
110
+ }
111
+ }
112
+ return responses;
113
+ }
114
+ export async function promptForOptions(cliOptions, { forcedAddOns = [], forcedMode, }) {
115
+ const options = {};
116
+ const framework = getFrameworkById(cliOptions.framework || 'react-cra');
117
+ options.framework = framework;
118
+ // TODO: This is a bit of a hack to ensure that the framework is solid
119
+ if (options.framework.id === 'solid') {
120
+ options.typescript = true;
121
+ options.tailwind = true;
122
+ }
123
+ if (cliOptions.addOns) {
124
+ options.typescript = true;
125
+ }
126
+ if (!cliOptions.projectName) {
127
+ const value = await text({
128
+ message: 'What would you like to name your project?',
129
+ defaultValue: 'my-app',
130
+ validate(value) {
131
+ if (!value) {
132
+ return 'Please enter a name';
133
+ }
134
+ },
135
+ });
136
+ if (isCancel(value)) {
137
+ cancel('Operation cancelled.');
138
+ process.exit(0);
139
+ }
140
+ options.projectName = value;
141
+ }
142
+ // Router type selection
143
+ if (!cliOptions.template && !forcedMode) {
144
+ const routerType = await select({
145
+ message: 'Select the router type:',
146
+ options: [
147
+ {
148
+ value: FILE_ROUTER,
149
+ label: 'File Router - File-based routing structure',
150
+ },
151
+ {
152
+ value: CODE_ROUTER,
153
+ label: 'Code Router - Traditional code-based routing',
154
+ },
155
+ ],
156
+ initialValue: FILE_ROUTER,
157
+ });
158
+ if (isCancel(routerType)) {
159
+ cancel('Operation cancelled.');
160
+ process.exit(0);
161
+ }
162
+ options.mode = routerType;
163
+ }
164
+ else if (forcedMode) {
165
+ options.mode = forcedMode === 'file-router' ? FILE_ROUTER : CODE_ROUTER;
166
+ options.typescript = options.mode === FILE_ROUTER;
167
+ }
168
+ else {
169
+ options.mode =
170
+ cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER;
171
+ if (options.mode === FILE_ROUTER) {
172
+ options.typescript = true;
173
+ }
174
+ }
175
+ // TypeScript selection (if using Code Router)
176
+ if (!options.typescript) {
177
+ if (options.mode === CODE_ROUTER) {
178
+ const typescriptEnable = await confirm({
179
+ message: 'Would you like to use TypeScript?',
180
+ initialValue: true,
181
+ });
182
+ if (isCancel(typescriptEnable)) {
183
+ cancel('Operation cancelled.');
184
+ process.exit(0);
185
+ }
186
+ options.typescript = typescriptEnable;
187
+ }
188
+ else {
189
+ options.typescript = true;
190
+ }
191
+ }
192
+ // Tailwind selection
193
+ if (!cliOptions.tailwind && options.framework.id === 'react-cra') {
194
+ const tailwind = await confirm({
195
+ message: 'Would you like to use Tailwind CSS?',
196
+ initialValue: true,
197
+ });
198
+ if (isCancel(tailwind)) {
199
+ cancel('Operation cancelled.');
200
+ process.exit(0);
201
+ }
202
+ options.tailwind = tailwind;
203
+ }
204
+ else {
205
+ // TODO: This is a bit of a hack to ensure that the framework is solid
206
+ options.tailwind = options.framework.id === 'solid' || !!cliOptions.tailwind;
207
+ }
208
+ // Package manager selection
209
+ if (cliOptions.packageManager === undefined) {
210
+ const detectedPackageManager = getPackageManager();
211
+ if (!detectedPackageManager) {
212
+ const pm = await select({
213
+ message: 'Select package manager:',
214
+ options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
215
+ value: pm,
216
+ label: pm,
217
+ })),
218
+ initialValue: DEFAULT_PACKAGE_MANAGER,
219
+ });
220
+ if (isCancel(pm)) {
221
+ cancel('Operation cancelled.');
222
+ process.exit(0);
223
+ }
224
+ options.packageManager = pm;
225
+ }
226
+ else {
227
+ options.packageManager = detectedPackageManager;
228
+ }
229
+ }
230
+ else {
231
+ options.packageManager = cliOptions.packageManager;
232
+ }
233
+ // Toolchain selection
234
+ let toolchain = undefined;
235
+ if (cliOptions.toolchain === undefined) {
236
+ const toolchains = new Set();
237
+ for (const addOn of framework.getAddOns()) {
238
+ if (addOn.type === 'toolchain') {
239
+ toolchains.add(addOn);
240
+ }
241
+ }
242
+ const tc = await select({
243
+ message: 'Select toolchain',
244
+ options: [
245
+ {
246
+ value: undefined,
247
+ label: 'None',
248
+ },
249
+ ...Array.from(toolchains).map((tc) => ({
250
+ value: tc,
251
+ label: tc.name,
252
+ })),
253
+ ],
254
+ initialValue: undefined,
255
+ });
256
+ if (isCancel(tc)) {
257
+ cancel('Operation cancelled.');
258
+ process.exit(0);
259
+ }
260
+ toolchain = tc;
261
+ }
262
+ else {
263
+ for (const addOn of framework.getAddOns()) {
264
+ if (addOn.type === 'toolchain' && addOn.id === cliOptions.toolchain) {
265
+ toolchain = addOn;
266
+ }
267
+ }
268
+ }
269
+ options.chosenAddOns = toolchain ? [toolchain] : [];
270
+ if (Array.isArray(cliOptions.addOns)) {
271
+ options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, Array.from(new Set([...cliOptions.addOns, ...forcedAddOns, toolchain?.id])).filter(Boolean));
272
+ options.tailwind = true;
273
+ }
274
+ else if (cliOptions.addOns) {
275
+ // Select any add-ons
276
+ const allAddOns = await getAllAddOns(options.framework, options.mode);
277
+ const addOns = allAddOns.filter((addOn) => addOn.type === 'add-on');
278
+ let selectedAddOns = [];
279
+ if (options.typescript && addOns.length > 0) {
280
+ const value = await multiselect({
281
+ message: 'What add-ons would you like for your project:',
282
+ options: addOns
283
+ .filter((addOn) => !forcedAddOns.includes(addOn.id))
284
+ .map((addOn) => ({
285
+ value: addOn.id,
286
+ label: addOn.name,
287
+ hint: addOn.description,
288
+ })),
289
+ required: false,
290
+ });
291
+ if (isCancel(value)) {
292
+ cancel('Operation cancelled.');
293
+ process.exit(0);
294
+ }
295
+ selectedAddOns = value;
296
+ }
297
+ // Select any examples
298
+ let selectedExamples = [];
299
+ const examples = allAddOns.filter((addOn) => addOn.type === 'example');
300
+ if (options.typescript && examples.length > 0) {
301
+ const value = await multiselect({
302
+ message: 'Would you like any examples?',
303
+ options: examples
304
+ .filter((addOn) => !forcedAddOns.includes(addOn.id))
305
+ .map((addOn) => ({
306
+ value: addOn.id,
307
+ label: addOn.name,
308
+ hint: addOn.description,
309
+ })),
310
+ required: false,
311
+ });
312
+ if (isCancel(value)) {
313
+ cancel('Operation cancelled.');
314
+ process.exit(0);
315
+ }
316
+ selectedExamples = value;
317
+ }
318
+ if (selectedAddOns.length > 0 ||
319
+ selectedExamples.length > 0 ||
320
+ forcedAddOns.length > 0 ||
321
+ toolchain) {
322
+ options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, Array.from(new Set([
323
+ ...selectedAddOns,
324
+ ...selectedExamples,
325
+ ...forcedAddOns,
326
+ toolchain?.id,
327
+ ])).filter(Boolean));
328
+ options.tailwind = true;
329
+ }
330
+ }
331
+ else if (forcedAddOns.length > 0) {
332
+ options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, Array.from(new Set([...forcedAddOns, toolchain?.id])).filter(Boolean));
333
+ }
334
+ // Collect variables
335
+ const variables = [];
336
+ for (const addOn of options.chosenAddOns) {
337
+ for (const variable of addOn.variables ?? []) {
338
+ variables.push(variable);
339
+ }
340
+ }
341
+ options.variableValues = await collectVariables(variables);
342
+ // Git selection
343
+ if (cliOptions.git === undefined) {
344
+ const git = await confirm({
345
+ message: 'Would you like to initialize a new git repository?',
346
+ initialValue: true,
347
+ });
348
+ if (isCancel(git)) {
349
+ cancel('Operation cancelled.');
350
+ process.exit(0);
351
+ }
352
+ options.git = git;
353
+ }
354
+ else {
355
+ options.git = !!cliOptions.git;
356
+ }
357
+ return options;
358
+ }
@@ -0,0 +1,7 @@
1
+ import type { Mode } from '@tanstack/cta-engine';
2
+ export declare function cli({ name, appName, forcedMode, forcedAddOns, }: {
3
+ name: string;
4
+ appName: string;
5
+ forcedMode?: Mode;
6
+ forcedAddOns?: Array<string>;
7
+ }): void;
@@ -0,0 +1 @@
1
+ export { cli } from './cli.js';
@@ -0,0 +1,7 @@
1
+ import type { Mode, Options, TemplateOptions } from '@tanstack/cta-engine';
2
+ import type { CliOptions } from './types.js';
3
+ export declare function normalizeOptions(cliOptions: CliOptions, forcedMode?: Mode, forcedAddOns?: Array<string>): Promise<Options | undefined>;
4
+ export declare function promptForOptions(cliOptions: CliOptions, { forcedAddOns, forcedMode, }: {
5
+ forcedAddOns?: Array<string>;
6
+ forcedMode?: TemplateOptions;
7
+ }): Promise<Required<Options>>;
@@ -0,0 +1,16 @@
1
+ import type { PackageManager, TemplateOptions } from '@tanstack/cta-engine';
2
+ export interface CliOptions {
3
+ template?: TemplateOptions;
4
+ framework?: string;
5
+ tailwind?: boolean;
6
+ packageManager?: PackageManager;
7
+ toolchain?: string;
8
+ projectName?: string;
9
+ git?: boolean;
10
+ addOns?: Array<string> | boolean;
11
+ listAddOns?: boolean;
12
+ mcp?: boolean;
13
+ mcpSse?: boolean;
14
+ starter?: string;
15
+ targetDir?: string;
16
+ }
@@ -0,0 +1,2 @@
1
+ import type { Environment } from '@tanstack/cta-engine';
2
+ export declare function createUIEnvironment(): Environment;
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ import { cancel, confirm, intro, isCancel, log, outro, spinner, } from '@clack/prompts';
2
+ import chalk from 'chalk';
3
+ import { createDefaultEnvironment } from '@tanstack/cta-engine';
4
+ export function createUIEnvironment() {
5
+ const defaultEnvironment = createDefaultEnvironment();
6
+ return {
7
+ ...defaultEnvironment,
8
+ intro: (message) => {
9
+ intro(message);
10
+ },
11
+ outro: (message) => {
12
+ outro(message);
13
+ },
14
+ info: (title, message) => {
15
+ console.log('info', title, message);
16
+ log.info(`${title ? chalk.red(title) : ''}${message ? chalk.green(message) : ''}`);
17
+ },
18
+ error: (title, message) => {
19
+ console.log('error', title, message);
20
+ log.error(`${title ? `${title}: ` : ''}${message}`);
21
+ },
22
+ warn: (title, message) => {
23
+ console.log('warn', title, message);
24
+ log.warn(`${title ? `${title}: ` : ''}${message}`);
25
+ },
26
+ confirm: async (message) => {
27
+ console.log('confirm', message);
28
+ const shouldContinue = await confirm({
29
+ message,
30
+ });
31
+ if (isCancel(shouldContinue)) {
32
+ cancel('Operation cancelled.');
33
+ process.exit(0);
34
+ }
35
+ return shouldContinue;
36
+ },
37
+ spinner: () => {
38
+ const s = spinner();
39
+ return {
40
+ start: (message) => {
41
+ s.start(message);
42
+ },
43
+ stop: (message) => {
44
+ s.stop(message);
45
+ },
46
+ };
47
+ },
48
+ };
49
+ }
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@tanstack/cta-cli",
3
+ "version": "0.10.0-alpha.16",
4
+ "description": "Tanstack Application Builder CLI",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/types/index.d.ts",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/TanStack/create-tsrouter-app.git"
11
+ },
12
+ "homepage": "https://tanstack.com/router",
13
+ "funding": {
14
+ "type": "github",
15
+ "url": "https://github.com/sponsors/tannerlinsley"
16
+ },
17
+ "keywords": [
18
+ "react",
19
+ "tanstack",
20
+ "router",
21
+ "create-react-app"
22
+ ],
23
+ "author": "Jack Herrington <jherr@pobox.com>",
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "@clack/prompts": "^0.10.0",
27
+ "chalk": "^5.4.1",
28
+ "commander": "^13.1.0",
29
+ "@tanstack/cta-engine": "0.10.0-alpha.16",
30
+ "@tanstack/cta-mcp": "0.10.0-alpha.16",
31
+ "@tanstack/cta-ui": "0.10.0-alpha.16",
32
+ "@tanstack/cta-custom-add-on": "0.10.0-alpha.16"
33
+ },
34
+ "devDependencies": {
35
+ "@tanstack/config": "^0.16.2",
36
+ "@types/node": "^22.13.4",
37
+ "eslint": "^9.20.0",
38
+ "typescript": "^5.6.3",
39
+ "vitest": "^3.0.8"
40
+ },
41
+ "scripts": {}
42
+ }