assistant-ui 0.0.81 → 0.0.82
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/README.md +1 -1
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +34 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/create.d.ts +13 -45
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +347 -101
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/create-project.d.ts +12 -0
- package/dist/lib/create-project.d.ts.map +1 -0
- package/dist/lib/create-project.js +273 -0
- package/dist/lib/create-project.js.map +1 -0
- package/package.json +5 -2
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/skills/assistant-ui/SKILL.md +167 -0
- package/src/commands/agent.ts +36 -0
- package/src/commands/create.ts +402 -128
- package/src/commands/init.ts +2 -2
- package/src/index.ts +2 -0
- package/src/lib/create-project.ts +367 -0
- package/dist/lib/create-from-example.d.ts +0 -9
- package/dist/lib/create-from-example.d.ts.map +0 -1
- package/dist/lib/create-from-example.js +0 -325
- package/dist/lib/create-from-example.js.map +0 -1
- package/src/lib/create-from-example.ts +0 -426
package/src/commands/create.ts
CHANGED
|
@@ -1,88 +1,288 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { spawn } from "cross-spawn";
|
|
4
|
+
import fs from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import * as p from "@clack/prompts";
|
|
6
7
|
import { logger } from "../lib/utils/logger";
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
dlxCommand,
|
|
10
|
+
downloadProject,
|
|
11
|
+
resolveLatestReleaseRef,
|
|
12
|
+
resolvePackageManagerName,
|
|
13
|
+
transformProject,
|
|
14
|
+
type PackageManagerName,
|
|
15
|
+
} from "../lib/create-project";
|
|
16
|
+
|
|
17
|
+
export interface ProjectMetadata {
|
|
18
|
+
name: string;
|
|
19
|
+
label: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
category: "template" | "example";
|
|
22
|
+
path: string;
|
|
23
|
+
hasLocalComponents: boolean;
|
|
24
|
+
}
|
|
8
25
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
26
|
+
export const PROJECT_METADATA: ProjectMetadata[] = [
|
|
27
|
+
// Templates
|
|
28
|
+
{
|
|
29
|
+
name: "default",
|
|
12
30
|
label: "Default",
|
|
13
|
-
|
|
31
|
+
description: "Default template with Vercel AI SDK",
|
|
32
|
+
category: "template",
|
|
33
|
+
path: "templates/default",
|
|
34
|
+
hasLocalComponents: true,
|
|
14
35
|
},
|
|
15
|
-
|
|
16
|
-
|
|
36
|
+
{
|
|
37
|
+
name: "minimal",
|
|
17
38
|
label: "Minimal",
|
|
18
|
-
|
|
39
|
+
description: "Bare-bones starting point",
|
|
40
|
+
category: "template",
|
|
41
|
+
path: "templates/minimal",
|
|
42
|
+
hasLocalComponents: true,
|
|
19
43
|
},
|
|
20
|
-
|
|
21
|
-
|
|
44
|
+
{
|
|
45
|
+
name: "cloud",
|
|
22
46
|
label: "Cloud",
|
|
23
|
-
|
|
47
|
+
description: "Cloud-backed persistence starter",
|
|
48
|
+
category: "template",
|
|
49
|
+
path: "templates/cloud",
|
|
50
|
+
hasLocalComponents: true,
|
|
24
51
|
},
|
|
25
|
-
|
|
26
|
-
|
|
52
|
+
{
|
|
53
|
+
name: "cloud-clerk",
|
|
27
54
|
label: "Cloud + Clerk",
|
|
28
|
-
|
|
55
|
+
description: "Cloud-backed starter with Clerk auth",
|
|
56
|
+
category: "template",
|
|
57
|
+
path: "templates/cloud-clerk",
|
|
58
|
+
hasLocalComponents: true,
|
|
29
59
|
},
|
|
30
|
-
|
|
31
|
-
|
|
60
|
+
{
|
|
61
|
+
name: "langgraph",
|
|
32
62
|
label: "LangGraph",
|
|
33
|
-
|
|
63
|
+
description: "LangGraph starter template",
|
|
64
|
+
category: "template",
|
|
65
|
+
path: "templates/langgraph",
|
|
66
|
+
hasLocalComponents: true,
|
|
34
67
|
},
|
|
35
|
-
|
|
36
|
-
|
|
68
|
+
{
|
|
69
|
+
name: "mcp",
|
|
37
70
|
label: "MCP",
|
|
38
|
-
|
|
71
|
+
description: "MCP starter template",
|
|
72
|
+
category: "template",
|
|
73
|
+
path: "templates/mcp",
|
|
74
|
+
hasLocalComponents: true,
|
|
75
|
+
},
|
|
76
|
+
// Examples
|
|
77
|
+
{
|
|
78
|
+
name: "with-ag-ui",
|
|
79
|
+
label: "AG-UI",
|
|
80
|
+
description: "AG-UI protocol integration",
|
|
81
|
+
category: "example",
|
|
82
|
+
path: "examples/with-ag-ui",
|
|
83
|
+
hasLocalComponents: false,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "with-ai-sdk-v6",
|
|
87
|
+
label: "AI SDK v6",
|
|
88
|
+
description: "Vercel AI SDK v6",
|
|
89
|
+
category: "example",
|
|
90
|
+
path: "examples/with-ai-sdk-v6",
|
|
91
|
+
hasLocalComponents: false,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "with-artifacts",
|
|
95
|
+
label: "Artifacts",
|
|
96
|
+
description: "Artifact rendering",
|
|
97
|
+
category: "example",
|
|
98
|
+
path: "examples/with-artifacts",
|
|
99
|
+
hasLocalComponents: false,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "with-assistant-transport",
|
|
103
|
+
label: "Assistant Transport",
|
|
104
|
+
description: "Assistant transport protocol",
|
|
105
|
+
category: "example",
|
|
106
|
+
path: "examples/with-assistant-transport",
|
|
107
|
+
hasLocalComponents: false,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "with-chain-of-thought",
|
|
111
|
+
label: "Chain of Thought",
|
|
112
|
+
description: "Chain-of-thought rendering",
|
|
113
|
+
category: "example",
|
|
114
|
+
path: "examples/with-chain-of-thought",
|
|
115
|
+
hasLocalComponents: false,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "with-cloud",
|
|
119
|
+
label: "Cloud Example",
|
|
120
|
+
description: "Cloud integration example",
|
|
121
|
+
category: "example",
|
|
122
|
+
path: "examples/with-cloud",
|
|
123
|
+
hasLocalComponents: false,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "with-custom-thread-list",
|
|
127
|
+
label: "Custom Thread List",
|
|
128
|
+
description: "Custom thread list UI",
|
|
129
|
+
category: "example",
|
|
130
|
+
path: "examples/with-custom-thread-list",
|
|
131
|
+
hasLocalComponents: false,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "with-elevenlabs-scribe",
|
|
135
|
+
label: "ElevenLabs Scribe",
|
|
136
|
+
description: "Audio/speech integration",
|
|
137
|
+
category: "example",
|
|
138
|
+
path: "examples/with-elevenlabs-scribe",
|
|
139
|
+
hasLocalComponents: false,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "with-external-store",
|
|
143
|
+
label: "External Store",
|
|
144
|
+
description: "Custom message store",
|
|
145
|
+
category: "example",
|
|
146
|
+
path: "examples/with-external-store",
|
|
147
|
+
hasLocalComponents: false,
|
|
39
148
|
},
|
|
40
|
-
|
|
149
|
+
{
|
|
150
|
+
name: "with-ffmpeg",
|
|
151
|
+
label: "FFmpeg",
|
|
152
|
+
description: "File processing",
|
|
153
|
+
category: "example",
|
|
154
|
+
path: "examples/with-ffmpeg",
|
|
155
|
+
hasLocalComponents: false,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "with-langgraph",
|
|
159
|
+
label: "LangGraph Example",
|
|
160
|
+
description: "LangGraph integration",
|
|
161
|
+
category: "example",
|
|
162
|
+
path: "examples/with-langgraph",
|
|
163
|
+
hasLocalComponents: false,
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "with-parent-id-grouping",
|
|
167
|
+
label: "Parent ID Grouping",
|
|
168
|
+
description: "Message grouping strategy",
|
|
169
|
+
category: "example",
|
|
170
|
+
path: "examples/with-parent-id-grouping",
|
|
171
|
+
hasLocalComponents: false,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "with-react-hook-form",
|
|
175
|
+
label: "React Hook Form",
|
|
176
|
+
description: "Form integration",
|
|
177
|
+
category: "example",
|
|
178
|
+
path: "examples/with-react-hook-form",
|
|
179
|
+
hasLocalComponents: false,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "with-react-router",
|
|
183
|
+
label: "React Router",
|
|
184
|
+
description: "React Router v7 + Vite",
|
|
185
|
+
category: "example",
|
|
186
|
+
path: "examples/with-react-router",
|
|
187
|
+
hasLocalComponents: false,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "with-tanstack",
|
|
191
|
+
label: "TanStack",
|
|
192
|
+
description: "TanStack/React Router + Vite",
|
|
193
|
+
category: "example",
|
|
194
|
+
path: "examples/with-tanstack",
|
|
195
|
+
hasLocalComponents: false,
|
|
196
|
+
},
|
|
197
|
+
];
|
|
41
198
|
|
|
42
|
-
|
|
43
|
-
|
|
199
|
+
const templateNames = PROJECT_METADATA.filter(
|
|
200
|
+
(m) => m.category === "template",
|
|
201
|
+
).map((m) => m.name);
|
|
44
202
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
value: name,
|
|
51
|
-
label: templates[name].label,
|
|
52
|
-
hint: templates[name].hint,
|
|
53
|
-
}));
|
|
54
|
-
|
|
55
|
-
export async function resolveCreateTemplateName(params: {
|
|
203
|
+
const exampleNames = PROJECT_METADATA.filter(
|
|
204
|
+
(m) => m.category === "example",
|
|
205
|
+
).map((m) => m.name);
|
|
206
|
+
|
|
207
|
+
export async function resolveProject(params: {
|
|
56
208
|
template?: string;
|
|
209
|
+
example?: string;
|
|
57
210
|
stdinIsTTY?: boolean;
|
|
58
211
|
select?: typeof p.select;
|
|
59
212
|
isCancel?: typeof p.isCancel;
|
|
60
|
-
}): Promise<
|
|
213
|
+
}): Promise<ProjectMetadata | null> {
|
|
61
214
|
const {
|
|
62
215
|
template,
|
|
216
|
+
example,
|
|
63
217
|
stdinIsTTY = process.stdin.isTTY,
|
|
64
218
|
select = p.select,
|
|
65
219
|
isCancel = p.isCancel,
|
|
66
220
|
} = params;
|
|
67
221
|
|
|
68
222
|
if (template) {
|
|
69
|
-
|
|
223
|
+
const meta = PROJECT_METADATA.find(
|
|
224
|
+
(m) => m.name === template && m.category === "template",
|
|
225
|
+
);
|
|
226
|
+
if (!meta) {
|
|
227
|
+
logger.error(`Unknown template: ${template}`);
|
|
228
|
+
logger.info(`Available templates: ${templateNames.join(", ")}`);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
return meta;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (example) {
|
|
235
|
+
const meta = PROJECT_METADATA.find(
|
|
236
|
+
(m) => m.name === example && m.category === "example",
|
|
237
|
+
);
|
|
238
|
+
if (!meta) {
|
|
239
|
+
logger.error(`Unknown example: ${example}`);
|
|
240
|
+
logger.info(`Available examples: ${exampleNames.join(", ")}`);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
return meta;
|
|
70
244
|
}
|
|
71
245
|
|
|
72
246
|
if (!stdinIsTTY) {
|
|
73
|
-
return "default"
|
|
247
|
+
return PROJECT_METADATA.find((m) => m.name === "default")!;
|
|
74
248
|
}
|
|
75
249
|
|
|
76
250
|
const selected = await select({
|
|
77
|
-
message: "Select a
|
|
78
|
-
options:
|
|
251
|
+
message: "Select a project to scaffold:",
|
|
252
|
+
options: [
|
|
253
|
+
{
|
|
254
|
+
value: "_separator",
|
|
255
|
+
label: "────── Starter Templates ──────",
|
|
256
|
+
disabled: true,
|
|
257
|
+
},
|
|
258
|
+
...PROJECT_METADATA.filter((m) => m.category === "template").map((m) => ({
|
|
259
|
+
value: m.name,
|
|
260
|
+
label: m.label,
|
|
261
|
+
...(m.description ? { hint: m.description } : {}),
|
|
262
|
+
})),
|
|
263
|
+
{
|
|
264
|
+
value: "_separator",
|
|
265
|
+
label: "────── Feature Examples ──────",
|
|
266
|
+
disabled: true,
|
|
267
|
+
},
|
|
268
|
+
...PROJECT_METADATA.filter((m) => m.category === "example").map((m) => ({
|
|
269
|
+
value: m.name,
|
|
270
|
+
label: m.label,
|
|
271
|
+
...(m.description ? { hint: m.description } : {}),
|
|
272
|
+
})),
|
|
273
|
+
],
|
|
79
274
|
});
|
|
80
275
|
|
|
81
276
|
if (isCancel(selected)) {
|
|
82
277
|
return null;
|
|
83
278
|
}
|
|
84
279
|
|
|
85
|
-
|
|
280
|
+
const meta = PROJECT_METADATA.find((m) => m.name === selected);
|
|
281
|
+
if (!meta) {
|
|
282
|
+
logger.error(`Unknown selection: ${String(selected)}`);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
return meta;
|
|
86
286
|
}
|
|
87
287
|
|
|
88
288
|
class SpawnExitError extends Error {
|
|
@@ -116,37 +316,6 @@ async function runSpawn(
|
|
|
116
316
|
});
|
|
117
317
|
}
|
|
118
318
|
|
|
119
|
-
export function buildCreateNextAppArgs(params: {
|
|
120
|
-
projectDirectory?: string;
|
|
121
|
-
useNpm?: boolean;
|
|
122
|
-
usePnpm?: boolean;
|
|
123
|
-
useYarn?: boolean;
|
|
124
|
-
useBun?: boolean;
|
|
125
|
-
skipInstall?: boolean;
|
|
126
|
-
templateUrl: string;
|
|
127
|
-
}): string[] {
|
|
128
|
-
const {
|
|
129
|
-
projectDirectory,
|
|
130
|
-
useNpm,
|
|
131
|
-
usePnpm,
|
|
132
|
-
useYarn,
|
|
133
|
-
useBun,
|
|
134
|
-
skipInstall,
|
|
135
|
-
templateUrl,
|
|
136
|
-
} = params;
|
|
137
|
-
|
|
138
|
-
const args = ["create-next-app@latest"];
|
|
139
|
-
if (projectDirectory) args.push(projectDirectory);
|
|
140
|
-
if (useNpm) args.push("--use-npm");
|
|
141
|
-
if (usePnpm) args.push("--use-pnpm");
|
|
142
|
-
if (useYarn) args.push("--use-yarn");
|
|
143
|
-
if (useBun) args.push("--use-bun");
|
|
144
|
-
if (skipInstall) args.push("--skip-install");
|
|
145
|
-
|
|
146
|
-
args.push("-e", templateUrl);
|
|
147
|
-
return args;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
319
|
export function resolveCreateProjectDirectory(params: {
|
|
151
320
|
projectDirectory?: string;
|
|
152
321
|
stdinIsTTY?: boolean;
|
|
@@ -158,8 +327,27 @@ export function resolveCreateProjectDirectory(params: {
|
|
|
158
327
|
return undefined;
|
|
159
328
|
}
|
|
160
329
|
|
|
161
|
-
function
|
|
162
|
-
|
|
330
|
+
function resolvePackageManager(opts: {
|
|
331
|
+
useNpm?: boolean;
|
|
332
|
+
usePnpm?: boolean;
|
|
333
|
+
useYarn?: boolean;
|
|
334
|
+
useBun?: boolean;
|
|
335
|
+
}): PackageManagerName | undefined {
|
|
336
|
+
if (opts.useNpm) return "npm";
|
|
337
|
+
if (opts.usePnpm) return "pnpm";
|
|
338
|
+
if (opts.useYarn) return "yarn";
|
|
339
|
+
if (opts.useBun) return "bun";
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const PLAYGROUND_PRESET_BASE_URL =
|
|
344
|
+
"https://www.assistant-ui.com/playground/init";
|
|
345
|
+
|
|
346
|
+
export function resolvePresetUrl(preset: string): string {
|
|
347
|
+
if (preset.startsWith("http://") || preset.startsWith("https://")) {
|
|
348
|
+
return preset;
|
|
349
|
+
}
|
|
350
|
+
return `${PLAYGROUND_PRESET_BASE_URL}?preset=${encodeURIComponent(preset)}`;
|
|
163
351
|
}
|
|
164
352
|
|
|
165
353
|
export const create = new Command()
|
|
@@ -173,11 +361,11 @@ export const create = new Command()
|
|
|
173
361
|
)
|
|
174
362
|
.option(
|
|
175
363
|
"-e, --example <example>",
|
|
176
|
-
|
|
364
|
+
`create from an example (${exampleNames.join(", ")})`,
|
|
177
365
|
)
|
|
178
366
|
.option(
|
|
179
|
-
"-p, --preset <url>",
|
|
180
|
-
"preset
|
|
367
|
+
"-p, --preset <name-or-url>",
|
|
368
|
+
"preset name or URL (e.g., chatgpt or https://www.assistant-ui.com/playground/init?preset=chatgpt)",
|
|
181
369
|
)
|
|
182
370
|
.option("--use-npm", "explicitly use npm")
|
|
183
371
|
.option("--use-pnpm", "explicitly use pnpm")
|
|
@@ -185,88 +373,174 @@ export const create = new Command()
|
|
|
185
373
|
.option("--use-bun", "explicitly use bun")
|
|
186
374
|
.option("--skip-install", "skip installing packages")
|
|
187
375
|
.action(async (projectDirectory, opts) => {
|
|
188
|
-
const resolvedProjectDirectory = resolveCreateProjectDirectory({
|
|
189
|
-
projectDirectory,
|
|
190
|
-
});
|
|
191
|
-
|
|
192
376
|
if (opts.example && opts.preset) {
|
|
193
377
|
logger.error("Cannot use --preset with --example.");
|
|
194
378
|
process.exit(1);
|
|
195
379
|
}
|
|
196
380
|
|
|
197
|
-
if (opts.
|
|
198
|
-
logger.error("
|
|
381
|
+
if (opts.template && opts.example) {
|
|
382
|
+
logger.error("Cannot use both --template and --example.");
|
|
199
383
|
process.exit(1);
|
|
200
384
|
}
|
|
201
385
|
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
386
|
+
// Start release ref resolution early (runs during user prompts)
|
|
387
|
+
const refPromise = resolveLatestReleaseRef();
|
|
388
|
+
|
|
389
|
+
// 1. Resolve project directory
|
|
390
|
+
let resolvedProjectDirectory = resolveCreateProjectDirectory({
|
|
391
|
+
projectDirectory,
|
|
392
|
+
});
|
|
208
393
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
394
|
+
if (!resolvedProjectDirectory) {
|
|
395
|
+
const result = await p.text({
|
|
396
|
+
message: "Project name:",
|
|
397
|
+
placeholder: "my-aui-app",
|
|
398
|
+
defaultValue: "my-aui-app",
|
|
399
|
+
validate: (value?: string) => {
|
|
400
|
+
const name = (value ?? "").trim();
|
|
401
|
+
if (!name) return "Project name cannot be empty";
|
|
402
|
+
if (name === "." || name === "..")
|
|
403
|
+
return "Project name cannot be . or ..";
|
|
404
|
+
if (name.includes("/") || name.includes("\\"))
|
|
405
|
+
return "Project name cannot contain path separators";
|
|
406
|
+
return undefined;
|
|
407
|
+
},
|
|
215
408
|
});
|
|
216
|
-
|
|
409
|
+
|
|
410
|
+
if (p.isCancel(result)) {
|
|
411
|
+
p.cancel("Project creation cancelled.");
|
|
412
|
+
process.exit(0);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
resolvedProjectDirectory = result;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Check directory
|
|
419
|
+
const absoluteProjectDir = path.resolve(resolvedProjectDirectory);
|
|
420
|
+
try {
|
|
421
|
+
const files = fs.readdirSync(absoluteProjectDir);
|
|
422
|
+
if (files.length > 0) {
|
|
423
|
+
logger.error(
|
|
424
|
+
`Directory ${resolvedProjectDirectory} already exists and is not empty`,
|
|
425
|
+
);
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
} catch (err: any) {
|
|
429
|
+
if (err.code === "ENOENT") {
|
|
430
|
+
// Directory doesn't exist — good, proceed
|
|
431
|
+
} else if (err.code === "ENOTDIR") {
|
|
432
|
+
logger.error(
|
|
433
|
+
`${resolvedProjectDirectory} already exists and is not a directory`,
|
|
434
|
+
);
|
|
435
|
+
process.exit(1);
|
|
436
|
+
} else {
|
|
437
|
+
logger.error(
|
|
438
|
+
`Cannot access ${resolvedProjectDirectory}: ${err.message}`,
|
|
439
|
+
);
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
217
442
|
}
|
|
218
443
|
|
|
219
|
-
//
|
|
220
|
-
const
|
|
444
|
+
// 2. Resolve scaffold target
|
|
445
|
+
const project = await resolveProject({
|
|
221
446
|
template: opts.template,
|
|
447
|
+
example: opts.example,
|
|
222
448
|
});
|
|
223
|
-
if (!
|
|
449
|
+
if (!project) {
|
|
224
450
|
p.cancel("Project creation cancelled.");
|
|
225
451
|
process.exit(0);
|
|
226
452
|
}
|
|
227
453
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (!templateUrl) {
|
|
231
|
-
logger.error(`Unknown template: ${opts.template}`);
|
|
232
|
-
logger.info(`Available templates: ${templateNames.join(", ")}`);
|
|
233
|
-
process.exit(1);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
logger.info(`Creating project with template: ${templateName}`);
|
|
454
|
+
logger.info(`Creating project from ${project.category}: ${project.label}`);
|
|
237
455
|
logger.break();
|
|
238
456
|
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
});
|
|
457
|
+
const pm = await resolvePackageManagerName(
|
|
458
|
+
absoluteProjectDir,
|
|
459
|
+
resolvePackageManager(opts),
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
// Clean up partial project directory on unexpected exit (e.g. Ctrl+C)
|
|
463
|
+
const cleanupOnExit = () => {
|
|
464
|
+
fs.rmSync(absoluteProjectDir, { recursive: true, force: true });
|
|
465
|
+
};
|
|
466
|
+
process.once("exit", cleanupOnExit);
|
|
250
467
|
|
|
251
468
|
try {
|
|
252
|
-
|
|
469
|
+
// 3. Resolve latest release ref (started before prompts)
|
|
470
|
+
logger.step("Resolving latest release...");
|
|
471
|
+
const ref = await refPromise;
|
|
472
|
+
if (!ref) {
|
|
473
|
+
logger.warn("Could not resolve latest release, downloading from HEAD");
|
|
474
|
+
}
|
|
253
475
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
476
|
+
// 4. Download project
|
|
477
|
+
logger.step("Downloading project...");
|
|
478
|
+
try {
|
|
479
|
+
await downloadProject(project.path, absoluteProjectDir, ref);
|
|
480
|
+
|
|
481
|
+
// If the template didn't exist at the release tag, retry from HEAD
|
|
482
|
+
if (
|
|
483
|
+
ref &&
|
|
484
|
+
!fs.existsSync(path.join(absoluteProjectDir, "package.json"))
|
|
485
|
+
) {
|
|
486
|
+
fs.rmSync(absoluteProjectDir, { recursive: true, force: true });
|
|
487
|
+
logger.warn(
|
|
488
|
+
"Template not found at release tag, downloading from HEAD",
|
|
489
|
+
);
|
|
490
|
+
await downloadProject(project.path, absoluteProjectDir);
|
|
258
491
|
}
|
|
492
|
+
|
|
493
|
+
// 5. Run transform pipeline
|
|
494
|
+
await transformProject(absoluteProjectDir, {
|
|
495
|
+
hasLocalComponents: project.hasLocalComponents,
|
|
496
|
+
skipInstall: opts.skipInstall,
|
|
497
|
+
packageManager: pm,
|
|
498
|
+
});
|
|
499
|
+
} catch (err) {
|
|
500
|
+
// Clean up partially created project directory
|
|
501
|
+
fs.rmSync(absoluteProjectDir, { recursive: true, force: true });
|
|
502
|
+
throw err;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// 6. Apply preset if provided
|
|
506
|
+
if (opts.preset) {
|
|
507
|
+
const presetUrl = resolvePresetUrl(opts.preset);
|
|
259
508
|
logger.info("Applying preset configuration...");
|
|
260
509
|
logger.break();
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
510
|
+
const [dlxCmd, dlxArgs] = dlxCommand(pm);
|
|
511
|
+
try {
|
|
512
|
+
await runSpawn(
|
|
513
|
+
dlxCmd,
|
|
514
|
+
[
|
|
515
|
+
...dlxArgs,
|
|
516
|
+
"shadcn@latest",
|
|
517
|
+
"add",
|
|
518
|
+
"--yes",
|
|
519
|
+
"--overwrite",
|
|
520
|
+
presetUrl,
|
|
521
|
+
],
|
|
522
|
+
absoluteProjectDir,
|
|
523
|
+
);
|
|
524
|
+
} catch {
|
|
525
|
+
logger.warn(
|
|
526
|
+
`Preset application failed. You can retry manually with:\n ${dlxCmd} ${[...dlxArgs, "shadcn@latest", "add", presetUrl].join(" ")}`,
|
|
527
|
+
);
|
|
528
|
+
}
|
|
266
529
|
}
|
|
267
530
|
|
|
531
|
+
process.removeListener("exit", cleanupOnExit);
|
|
532
|
+
|
|
268
533
|
logger.break();
|
|
269
534
|
logger.success("Project created successfully!");
|
|
535
|
+
logger.break();
|
|
536
|
+
const runCmd = pm === "npm" ? "npm run" : pm;
|
|
537
|
+
logger.info("Next steps:");
|
|
538
|
+
logger.info(` cd ${resolvedProjectDirectory}`);
|
|
539
|
+
if (opts.skipInstall) {
|
|
540
|
+
logger.info(` ${pm} install`);
|
|
541
|
+
}
|
|
542
|
+
logger.info(" # Set up your environment variables in .env.local");
|
|
543
|
+
logger.info(` ${runCmd} dev`);
|
|
270
544
|
} catch (error) {
|
|
271
545
|
if (error instanceof SpawnExitError) {
|
|
272
546
|
logger.error(`Project creation failed with code ${error.code}`);
|
package/src/commands/init.ts
CHANGED
|
@@ -89,8 +89,8 @@ export const init = new Command()
|
|
|
89
89
|
)
|
|
90
90
|
.addOption(
|
|
91
91
|
new Option(
|
|
92
|
-
"-p, --preset <url>",
|
|
93
|
-
"preset
|
|
92
|
+
"-p, --preset <name-or-url>",
|
|
93
|
+
"preset name or URL (forwarded to 'assistant-ui create')",
|
|
94
94
|
).hideHelp(),
|
|
95
95
|
)
|
|
96
96
|
.option("--use-npm", "explicitly use npm")
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { codemodCommand, upgradeCommand } from "./commands/upgrade";
|
|
|
7
7
|
import { init } from "./commands/init";
|
|
8
8
|
import { update } from "./commands/update";
|
|
9
9
|
import { mcp } from "./commands/mcp";
|
|
10
|
+
import { agent } from "./commands/agent";
|
|
10
11
|
|
|
11
12
|
process.on("SIGINT", () => process.exit(0));
|
|
12
13
|
process.on("SIGTERM", () => process.exit(0));
|
|
@@ -23,6 +24,7 @@ function main() {
|
|
|
23
24
|
program.addCommand(codemodCommand);
|
|
24
25
|
program.addCommand(upgradeCommand);
|
|
25
26
|
program.addCommand(update);
|
|
27
|
+
program.addCommand(agent);
|
|
26
28
|
|
|
27
29
|
program.parse();
|
|
28
30
|
}
|