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.
@@ -0,0 +1,367 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { downloadTemplate } from "giget";
4
+ import { spawn } from "cross-spawn";
5
+ import { sync as globSync } from "glob";
6
+ import { detect } from "detect-package-manager";
7
+ import { logger } from "./utils/logger";
8
+
9
+ export type PackageManagerName = "npm" | "pnpm" | "yarn" | "bun";
10
+
11
+ export function dlxCommand(pm: PackageManagerName): [string, string[]] {
12
+ switch (pm) {
13
+ case "pnpm":
14
+ return ["pnpm", ["dlx"]];
15
+ case "yarn":
16
+ return ["yarn", ["dlx"]];
17
+ case "bun":
18
+ return ["bunx", []];
19
+ case "npm":
20
+ return ["npx", ["--yes"]];
21
+ }
22
+ }
23
+
24
+ export interface TransformOptions {
25
+ hasLocalComponents: boolean;
26
+ skipInstall?: boolean;
27
+ packageManager?: PackageManagerName;
28
+ }
29
+
30
+ export async function resolveLatestReleaseRef(): Promise<string | undefined> {
31
+ try {
32
+ const res = await fetch(
33
+ "https://api.github.com/repos/assistant-ui/assistant-ui/releases/latest",
34
+ );
35
+ if (!res.ok) return undefined;
36
+ const release = (await res.json()) as { tag_name: string };
37
+ return release.tag_name || undefined;
38
+ } catch {
39
+ return undefined;
40
+ }
41
+ }
42
+
43
+ const DOWNLOAD_TIMEOUT_MS = 30_000;
44
+
45
+ export async function downloadProject(
46
+ repoPath: string,
47
+ destDir: string,
48
+ ref?: string,
49
+ ): Promise<void> {
50
+ const source = ref
51
+ ? `gh:assistant-ui/assistant-ui/${repoPath}#${ref}`
52
+ : `gh:assistant-ui/assistant-ui/${repoPath}`;
53
+
54
+ // Suppress giget's console.debug output
55
+ const origDebug = console.debug;
56
+ console.debug = () => {};
57
+ try {
58
+ const downloadPromise = downloadTemplate(source, {
59
+ dir: destDir,
60
+ force: true,
61
+ silent: true,
62
+ });
63
+
64
+ let timer: ReturnType<typeof setTimeout>;
65
+ const timeoutPromise = new Promise<never>((_, reject) => {
66
+ timer = setTimeout(
67
+ () =>
68
+ reject(
69
+ new Error(
70
+ "Download timed out. This may be due to GitHub rate limiting or a network issue. Try again in a few minutes.",
71
+ ),
72
+ ),
73
+ DOWNLOAD_TIMEOUT_MS,
74
+ );
75
+ });
76
+
77
+ try {
78
+ await Promise.race([downloadPromise, timeoutPromise]);
79
+ } finally {
80
+ clearTimeout(timer!);
81
+ }
82
+ } finally {
83
+ console.debug = origDebug;
84
+ }
85
+ }
86
+
87
+ export async function resolvePackageManagerName(
88
+ projectDir: string,
89
+ packageManager?: PackageManagerName,
90
+ ): Promise<PackageManagerName> {
91
+ if (packageManager) return packageManager;
92
+ try {
93
+ return await detect({ cwd: path.dirname(projectDir) });
94
+ } catch {
95
+ return "npm";
96
+ }
97
+ }
98
+
99
+ export async function transformProject(
100
+ projectDir: string,
101
+ opts: TransformOptions,
102
+ ): Promise<void> {
103
+ // 1. Transform package.json (always)
104
+ logger.step("Transforming package.json...");
105
+ await transformPackageJson(projectDir);
106
+
107
+ let assistantUI: string[] | undefined;
108
+ let shadcnUI: string[] | undefined;
109
+
110
+ if (!opts.hasLocalComponents) {
111
+ logger.step("Transforming project files...");
112
+
113
+ // 2–5. Transform tsconfig, CSS, scan components, and remove workspace
114
+ // components — all independent, run in parallel
115
+ const [, , components] = await Promise.all([
116
+ transformTsConfig(projectDir),
117
+ transformCssFiles(projectDir),
118
+ scanRequiredComponents(projectDir),
119
+ removeWorkspaceComponents(projectDir),
120
+ ]);
121
+ assistantUI = components.assistantUI;
122
+ shadcnUI = components.shadcnUI;
123
+ }
124
+
125
+ // 6. Install dependencies
126
+ const pm =
127
+ opts.packageManager ?? (await resolvePackageManagerName(projectDir));
128
+ if (!opts.skipInstall) {
129
+ logger.step("Installing dependencies...");
130
+ await installDependencies(projectDir, pm);
131
+ }
132
+
133
+ if (!opts.hasLocalComponents && shadcnUI && assistantUI) {
134
+ // 7. Install shadcn UI components
135
+ const allShadcn = shadcnUI.includes("utils")
136
+ ? shadcnUI
137
+ : [...shadcnUI, "utils"];
138
+ logger.step(`Installing shadcn UI components: ${allShadcn.join(", ")}...`);
139
+ await installShadcnRegistry(projectDir, allShadcn, "shadcn components", pm);
140
+
141
+ // 8. Install assistant-ui components
142
+ if (assistantUI.length > 0) {
143
+ const auiComponents = assistantUI.map((c) => `@assistant-ui/${c}`);
144
+ logger.step(
145
+ `Installing assistant-ui components: ${assistantUI.join(", ")}...`,
146
+ );
147
+ await installShadcnRegistry(projectDir, auiComponents, "components", pm);
148
+ }
149
+ }
150
+ }
151
+
152
+ async function transformPackageJson(projectDir: string): Promise<void> {
153
+ const pkgPath = path.join(projectDir, "package.json");
154
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
155
+
156
+ // Remove @assistant-ui/ui (workspace-only package)
157
+ if (pkg.dependencies?.["@assistant-ui/ui"]) {
158
+ delete pkg.dependencies["@assistant-ui/ui"];
159
+ }
160
+
161
+ // Transform workspace dependencies to latest
162
+ for (const depType of ["dependencies", "devDependencies"] as const) {
163
+ const deps = pkg[depType];
164
+ if (!deps) continue;
165
+
166
+ for (const [name, version] of Object.entries(deps)) {
167
+ if (String(version).includes("workspace:")) {
168
+ deps[name] = "latest";
169
+ }
170
+ }
171
+ }
172
+
173
+ // Remove devDependencies that are workspace-only
174
+ if (pkg.devDependencies?.["@assistant-ui/x-buildutils"]) {
175
+ delete pkg.devDependencies["@assistant-ui/x-buildutils"];
176
+ }
177
+
178
+ // Update package name to be unique
179
+ const dirName = path.basename(projectDir);
180
+ pkg.name = dirName;
181
+
182
+ fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
183
+ }
184
+
185
+ async function transformTsConfig(projectDir: string): Promise<void> {
186
+ const tsconfigPath = path.join(projectDir, "tsconfig.json");
187
+
188
+ if (!fs.existsSync(tsconfigPath)) {
189
+ return;
190
+ }
191
+
192
+ const content = fs.readFileSync(tsconfigPath, "utf-8");
193
+ const tsconfig = JSON.parse(content);
194
+
195
+ // Remove workspace paths
196
+ if (tsconfig.compilerOptions?.paths) {
197
+ delete tsconfig.compilerOptions.paths["@/components/assistant-ui/*"];
198
+ delete tsconfig.compilerOptions.paths["@/components/ui/*"];
199
+ delete tsconfig.compilerOptions.paths["@/lib/utils"];
200
+ delete tsconfig.compilerOptions.paths["@assistant-ui/ui/*"];
201
+
202
+ if (Object.keys(tsconfig.compilerOptions.paths).length === 0) {
203
+ delete tsconfig.compilerOptions.paths;
204
+ }
205
+ }
206
+
207
+ // If extends uses @assistant-ui/x-buildutils, replace with inline config
208
+ if (tsconfig.extends?.includes("@assistant-ui/x-buildutils")) {
209
+ const isNext = tsconfig.extends.includes("ts/next");
210
+ delete tsconfig.extends;
211
+
212
+ const inlinedCompilerOptions = {
213
+ target: "ESNext",
214
+ lib: ["dom", "dom.iterable", "ES2023"],
215
+ skipLibCheck: true,
216
+ strict: true,
217
+ noEmit: true,
218
+ esModuleInterop: true,
219
+ module: "ESNext",
220
+ moduleResolution: "bundler",
221
+ resolveJsonModule: true,
222
+ isolatedModules: true,
223
+ jsx: "react-jsx",
224
+ ...(isNext ? { plugins: [{ name: "next" }] } : {}),
225
+ };
226
+
227
+ tsconfig.compilerOptions = {
228
+ ...inlinedCompilerOptions,
229
+ ...tsconfig.compilerOptions,
230
+ paths: {
231
+ "@/*": ["./*"],
232
+ ...(tsconfig.compilerOptions?.paths || {}),
233
+ },
234
+ };
235
+ }
236
+
237
+ fs.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`);
238
+ }
239
+
240
+ async function transformCssFiles(projectDir: string): Promise<void> {
241
+ const cssFiles = globSync("**/*.css", {
242
+ cwd: projectDir,
243
+ ignore: ["**/node_modules/**", "**/dist/**", "**/.next/**"],
244
+ });
245
+
246
+ for (const file of cssFiles) {
247
+ const fullPath = path.join(projectDir, file);
248
+ try {
249
+ let content = fs.readFileSync(fullPath, "utf-8");
250
+
251
+ content = content.replace(
252
+ /@source\s+["'][^"']*packages\/ui\/src[^"']*["'];\s*\n?/g,
253
+ "",
254
+ );
255
+
256
+ fs.writeFileSync(fullPath, content);
257
+ } catch {
258
+ // Ignore files that cannot be read/written
259
+ }
260
+ }
261
+ }
262
+
263
+ interface RequiredComponents {
264
+ assistantUI: string[];
265
+ shadcnUI: string[];
266
+ }
267
+
268
+ async function scanRequiredComponents(
269
+ projectDir: string,
270
+ ): Promise<RequiredComponents> {
271
+ const files = globSync("**/*.{ts,tsx}", {
272
+ cwd: projectDir,
273
+ ignore: ["**/node_modules/**", "**/dist/**", "**/.next/**"],
274
+ });
275
+
276
+ const assistantUIComponents = new Set<string>();
277
+ const shadcnUIComponents = new Set<string>();
278
+
279
+ for (const file of files) {
280
+ const fullPath = path.join(projectDir, file);
281
+ try {
282
+ const content = fs.readFileSync(fullPath, "utf-8");
283
+
284
+ const assistantUIRegex =
285
+ /from\s+["']@\/components\/assistant-ui\/([^"']+)["']/g;
286
+ let match;
287
+ while ((match = assistantUIRegex.exec(content)) !== null) {
288
+ assistantUIComponents.add(match[1]!);
289
+ }
290
+
291
+ const uiRegex = /from\s+["']@\/components\/ui\/([^"']+)["']/g;
292
+ while ((match = uiRegex.exec(content)) !== null) {
293
+ shadcnUIComponents.add(match[1]!);
294
+ }
295
+ } catch {
296
+ // Ignore files that cannot be read
297
+ }
298
+ }
299
+
300
+ return {
301
+ assistantUI: Array.from(assistantUIComponents),
302
+ shadcnUI: Array.from(shadcnUIComponents),
303
+ };
304
+ }
305
+
306
+ async function removeWorkspaceComponents(projectDir: string): Promise<void> {
307
+ const componentsDir = path.join(projectDir, "components", "assistant-ui");
308
+ fs.rmSync(componentsDir, { recursive: true, force: true });
309
+ }
310
+
311
+ async function installDependencies(
312
+ projectDir: string,
313
+ pm: PackageManagerName,
314
+ ): Promise<void> {
315
+ const args = pm === "yarn" ? [] : ["install"];
316
+
317
+ return new Promise((resolve, reject) => {
318
+ const child = spawn(pm, args, {
319
+ cwd: projectDir,
320
+ stdio: "inherit",
321
+ });
322
+
323
+ child.on("error", (error) => {
324
+ reject(new Error(`Failed to install dependencies: ${error.message}`));
325
+ });
326
+
327
+ child.on("close", (code) => {
328
+ if (code !== 0) {
329
+ reject(new Error(`${pm} install exited with code ${code}`));
330
+ } else {
331
+ resolve();
332
+ }
333
+ });
334
+ });
335
+ }
336
+
337
+ async function installShadcnRegistry(
338
+ projectDir: string,
339
+ components: string[],
340
+ label: string,
341
+ pm: PackageManagerName,
342
+ ): Promise<void> {
343
+ const [cmd, dlxArgs] = dlxCommand(pm);
344
+ return new Promise((resolve, reject) => {
345
+ const child = spawn(
346
+ cmd,
347
+ [...dlxArgs, "shadcn@latest", "add", ...components, "--yes"],
348
+ {
349
+ cwd: projectDir,
350
+ stdio: "inherit",
351
+ },
352
+ );
353
+
354
+ child.on("error", (error) => {
355
+ reject(new Error(`Failed to install ${label}: ${error.message}`));
356
+ });
357
+
358
+ child.on("close", (code) => {
359
+ if (code !== 0) {
360
+ logger.warn(
361
+ `shadcn exited with code ${code}. Run the following to retry:\n ${cmd} ${[...dlxArgs, "shadcn@latest", "add", ...components].join(" ")}`,
362
+ );
363
+ }
364
+ resolve();
365
+ });
366
+ });
367
+ }
@@ -1,9 +0,0 @@
1
- export interface CreateFromExampleOptions {
2
- skipInstall?: boolean;
3
- useNpm?: boolean;
4
- usePnpm?: boolean;
5
- useYarn?: boolean;
6
- useBun?: boolean;
7
- }
8
- export declare function createFromExample(projectDir: string, exampleName: string, opts: CreateFromExampleOptions): Promise<void>;
9
- //# sourceMappingURL=create-from-example.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-from-example.d.ts","sourceRoot":"","sources":["../../src/lib/create-from-example.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAoBD,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAgFf"}
@@ -1,325 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { spawn } from "node:child_process";
4
- import { sync as globSync } from "glob";
5
- import { detect } from "detect-package-manager";
6
- import { logger } from "./utils/logger.js";
7
- const VALID_EXAMPLES = [
8
- "with-ag-ui",
9
- "with-ai-sdk-v6",
10
- "with-artifacts",
11
- "with-assistant-transport",
12
- "with-chain-of-thought",
13
- "with-cloud",
14
- "with-custom-thread-list",
15
- "with-elevenlabs-scribe",
16
- "with-external-store",
17
- "with-ffmpeg",
18
- "with-langgraph",
19
- "with-parent-id-grouping",
20
- "with-react-hook-form",
21
- "with-react-router",
22
- "with-tanstack",
23
- ];
24
- export async function createFromExample(projectDir, exampleName, opts) {
25
- // 1. Validate example name
26
- if (!VALID_EXAMPLES.includes(exampleName)) {
27
- logger.error(`Unknown example: ${exampleName}`);
28
- logger.info(`Available examples: ${VALID_EXAMPLES.join(", ")}`);
29
- process.exit(1);
30
- }
31
- const absoluteProjectDir = path.resolve(projectDir);
32
- // Check if directory already exists
33
- if (fs.existsSync(absoluteProjectDir)) {
34
- const files = fs.readdirSync(absoluteProjectDir);
35
- if (files.length > 0) {
36
- logger.error(`Directory ${projectDir} already exists and is not empty`);
37
- process.exit(1);
38
- }
39
- }
40
- logger.info(`Creating project from example: ${exampleName}`);
41
- logger.break();
42
- // 2. Download example using degit
43
- logger.step("Downloading example...");
44
- await downloadExample(exampleName, absoluteProjectDir);
45
- // 3. Transform package.json
46
- logger.step("Transforming package.json...");
47
- await transformPackageJson(absoluteProjectDir);
48
- // 4. Transform tsconfig.json
49
- logger.step("Transforming tsconfig.json...");
50
- await transformTsConfig(absoluteProjectDir);
51
- // 5. Transform CSS files (remove monorepo-specific @source directives)
52
- logger.step("Transforming CSS files...");
53
- await transformCssFiles(absoluteProjectDir);
54
- // 6. Scan for required components
55
- logger.step("Scanning for required components...");
56
- const { assistantUI, shadcnUI } = await scanRequiredComponents(absoluteProjectDir);
57
- // 7. Remove workspace components directory
58
- logger.step("Cleaning up workspace components...");
59
- await removeWorkspaceComponents(absoluteProjectDir);
60
- // 8. Install dependencies first (needed for shadcn)
61
- if (!opts.skipInstall) {
62
- logger.step("Installing dependencies...");
63
- await installDependencies(absoluteProjectDir, opts);
64
- }
65
- // 9. Install shadcn UI components (standard shadcn components like button, tooltip, etc.)
66
- // Always include "utils" since assistant-ui components import cn from @/lib/utils
67
- // and shadcn does not declare it as a registryDependency of button/tooltip/etc.
68
- if (!shadcnUI.includes("utils")) {
69
- shadcnUI.push("utils");
70
- }
71
- logger.step(`Installing shadcn UI components: ${shadcnUI.join(", ")}...`);
72
- await installShadcnComponents(absoluteProjectDir, shadcnUI);
73
- // 10. Install assistant-ui components
74
- if (assistantUI.length > 0) {
75
- logger.step(`Installing assistant-ui components: ${assistantUI.join(", ")}...`);
76
- await installComponents(absoluteProjectDir, assistantUI, opts);
77
- }
78
- logger.break();
79
- logger.success("Project created successfully!");
80
- logger.break();
81
- logger.info("Next steps:");
82
- logger.info(` cd ${projectDir}`);
83
- if (opts.skipInstall) {
84
- logger.info(" npm install");
85
- }
86
- logger.info(" # Set up your environment variables in .env.local");
87
- logger.info(" npm run dev");
88
- }
89
- async function downloadExample(exampleName, destDir) {
90
- const degitPath = `assistant-ui/assistant-ui/examples/${exampleName}`;
91
- return new Promise((resolve, reject) => {
92
- const child = spawn("npx", ["degit", degitPath, destDir, "--force"], {
93
- stdio: "inherit",
94
- shell: true,
95
- });
96
- child.on("error", (error) => {
97
- reject(new Error(`Failed to download example: ${error.message}`));
98
- });
99
- child.on("close", (code) => {
100
- if (code !== 0) {
101
- reject(new Error(`degit exited with code ${code}`));
102
- }
103
- else {
104
- resolve();
105
- }
106
- });
107
- });
108
- }
109
- async function transformPackageJson(projectDir) {
110
- const pkgPath = path.join(projectDir, "package.json");
111
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
112
- // Remove @assistant-ui/ui (workspace-only package)
113
- if (pkg.dependencies?.["@assistant-ui/ui"]) {
114
- delete pkg.dependencies["@assistant-ui/ui"];
115
- }
116
- // Transform workspace dependencies to latest
117
- for (const depType of ["dependencies", "devDependencies"]) {
118
- const deps = pkg[depType];
119
- if (!deps)
120
- continue;
121
- for (const [name, version] of Object.entries(deps)) {
122
- if (String(version).includes("workspace:")) {
123
- deps[name] = "latest";
124
- }
125
- }
126
- }
127
- // Remove devDependencies that are workspace-only
128
- if (pkg.devDependencies?.["@assistant-ui/x-buildutils"]) {
129
- delete pkg.devDependencies["@assistant-ui/x-buildutils"];
130
- }
131
- // Update package name to be unique
132
- const dirName = path.basename(projectDir);
133
- pkg.name = dirName;
134
- fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
135
- }
136
- async function transformCssFiles(projectDir) {
137
- // Find all CSS files
138
- const cssFiles = globSync("**/*.css", {
139
- cwd: projectDir,
140
- ignore: ["**/node_modules/**", "**/dist/**", "**/.next/**"],
141
- });
142
- for (const file of cssFiles) {
143
- const fullPath = path.join(projectDir, file);
144
- try {
145
- let content = fs.readFileSync(fullPath, "utf-8");
146
- // Remove @source lines that point to monorepo packages directory
147
- // These are only needed in the monorepo context
148
- content = content.replace(/@source\s+["'][^"']*packages\/ui\/src[^"']*["'];\s*\n?/g, "");
149
- fs.writeFileSync(fullPath, content);
150
- }
151
- catch {
152
- // Ignore files that cannot be read/written
153
- }
154
- }
155
- }
156
- async function transformTsConfig(projectDir) {
157
- const tsconfigPath = path.join(projectDir, "tsconfig.json");
158
- if (!fs.existsSync(tsconfigPath)) {
159
- return;
160
- }
161
- const content = fs.readFileSync(tsconfigPath, "utf-8");
162
- const tsconfig = JSON.parse(content);
163
- // Remove workspace paths
164
- if (tsconfig.compilerOptions?.paths) {
165
- delete tsconfig.compilerOptions.paths["@/components/assistant-ui/*"];
166
- delete tsconfig.compilerOptions.paths["@/components/ui/*"];
167
- delete tsconfig.compilerOptions.paths["@/lib/utils"];
168
- delete tsconfig.compilerOptions.paths["@assistant-ui/ui/*"];
169
- // If paths is empty, remove it
170
- if (Object.keys(tsconfig.compilerOptions.paths).length === 0) {
171
- delete tsconfig.compilerOptions.paths;
172
- }
173
- }
174
- // If extends uses @assistant-ui/x-buildutils, replace with inline config
175
- if (tsconfig.extends?.includes("@assistant-ui/x-buildutils")) {
176
- delete tsconfig.extends;
177
- // Add necessary compiler options that were in the extended config
178
- tsconfig.compilerOptions = {
179
- ...{
180
- target: "ES2017",
181
- lib: ["dom", "dom.iterable", "esnext"],
182
- allowJs: true,
183
- skipLibCheck: true,
184
- strict: true,
185
- noEmit: true,
186
- esModuleInterop: true,
187
- module: "esnext",
188
- moduleResolution: "bundler",
189
- resolveJsonModule: true,
190
- isolatedModules: true,
191
- jsx: "preserve",
192
- incremental: true,
193
- plugins: [{ name: "next" }],
194
- },
195
- ...tsconfig.compilerOptions,
196
- paths: {
197
- "@/*": ["./*"],
198
- ...(tsconfig.compilerOptions?.paths || {}),
199
- },
200
- };
201
- }
202
- fs.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`);
203
- }
204
- async function scanRequiredComponents(projectDir) {
205
- const files = globSync("**/*.{ts,tsx}", {
206
- cwd: projectDir,
207
- ignore: ["**/node_modules/**", "**/dist/**", "**/.next/**"],
208
- });
209
- const assistantUIComponents = new Set();
210
- const shadcnUIComponents = new Set();
211
- for (const file of files) {
212
- const fullPath = path.join(projectDir, file);
213
- try {
214
- const content = fs.readFileSync(fullPath, "utf-8");
215
- // Match imports from "@/components/assistant-ui/xxx"
216
- const assistantUIRegex = /from\s+["']@\/components\/assistant-ui\/([^"']+)["']/g;
217
- let match;
218
- while ((match = assistantUIRegex.exec(content)) !== null) {
219
- assistantUIComponents.add(match[1]);
220
- }
221
- // Match imports from "@/components/ui/xxx"
222
- const uiRegex = /from\s+["']@\/components\/ui\/([^"']+)["']/g;
223
- while ((match = uiRegex.exec(content)) !== null) {
224
- shadcnUIComponents.add(match[1]);
225
- }
226
- }
227
- catch {
228
- // Ignore files that cannot be read
229
- }
230
- }
231
- return {
232
- assistantUI: Array.from(assistantUIComponents),
233
- shadcnUI: Array.from(shadcnUIComponents),
234
- };
235
- }
236
- async function removeWorkspaceComponents(projectDir) {
237
- const componentsDir = path.join(projectDir, "components", "assistant-ui");
238
- if (fs.existsSync(componentsDir)) {
239
- fs.rmSync(componentsDir, { recursive: true });
240
- }
241
- }
242
- async function installShadcnComponents(projectDir, components) {
243
- return new Promise((resolve, reject) => {
244
- const child = spawn("npx", ["shadcn@latest", "add", ...components, "--yes"], {
245
- cwd: projectDir,
246
- stdio: "inherit",
247
- shell: true,
248
- });
249
- child.on("error", (error) => {
250
- reject(new Error(`Failed to install shadcn components: ${error.message}`));
251
- });
252
- child.on("close", (code) => {
253
- if (code !== 0) {
254
- // Don't fail if shadcn has issues, just warn
255
- logger.warn(`shadcn exited with code ${code}, components may need manual installation`);
256
- }
257
- resolve();
258
- });
259
- });
260
- }
261
- async function installComponents(projectDir, components, _opts) {
262
- // Format component names for shadcn registry
263
- const componentArgs = components.map((c) => `@assistant-ui/${c}`);
264
- return new Promise((resolve, reject) => {
265
- const child = spawn("npx", ["shadcn@latest", "add", ...componentArgs, "--yes"], {
266
- cwd: projectDir,
267
- stdio: "inherit",
268
- shell: true,
269
- });
270
- child.on("error", (error) => {
271
- reject(new Error(`Failed to install components: ${error.message}`));
272
- });
273
- child.on("close", (code) => {
274
- if (code !== 0) {
275
- // Don't fail if shadcn has issues, just warn
276
- logger.warn(`shadcn exited with code ${code}, components may need manual installation`);
277
- }
278
- resolve();
279
- });
280
- });
281
- }
282
- async function installDependencies(projectDir, opts) {
283
- let pm;
284
- if (opts.useNpm) {
285
- pm = "npm";
286
- }
287
- else if (opts.usePnpm) {
288
- pm = "pnpm";
289
- }
290
- else if (opts.useYarn) {
291
- pm = "yarn";
292
- }
293
- else if (opts.useBun) {
294
- pm = "bun";
295
- }
296
- else {
297
- // Detect from parent directory or default to npm
298
- try {
299
- pm = await detect({ cwd: path.dirname(projectDir) });
300
- }
301
- catch {
302
- pm = "npm";
303
- }
304
- }
305
- const args = pm === "yarn" ? [] : ["install"];
306
- return new Promise((resolve, reject) => {
307
- const child = spawn(pm, args, {
308
- cwd: projectDir,
309
- stdio: "inherit",
310
- shell: true,
311
- });
312
- child.on("error", (error) => {
313
- reject(new Error(`Failed to install dependencies: ${error.message}`));
314
- });
315
- child.on("close", (code) => {
316
- if (code !== 0) {
317
- reject(new Error(`${pm} install exited with code ${code}`));
318
- }
319
- else {
320
- resolve();
321
- }
322
- });
323
- });
324
- }
325
- //# sourceMappingURL=create-from-example.js.map