assistant-ui 0.0.113 → 0.0.114

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.
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.ts","names":[],"sources":["../../src/commands/mcp.ts"],"mappings":";;cA0Oa,KAAG"}
1
+ {"version":3,"file":"mcp.d.ts","names":[],"sources":["../../src/commands/mcp.ts"],"mappings":";;cAmQa,KAAG"}
@@ -6,33 +6,30 @@ import fs from "node:fs";
6
6
  import path from "node:path";
7
7
  import * as p from "@clack/prompts";
8
8
  //#region src/commands/mcp.ts
9
+ const HOSTED_MCP_URL = "https://www.assistant-ui.com/mcp";
9
10
  const MCP_CONFIGS = {
10
11
  cursor: {
11
12
  name: "Cursor",
12
13
  getPath: () => path.join(process.cwd(), ".cursor", "mcp.json"),
13
- config: { mcpServers: { "assistant-ui": {
14
- command: "npx",
15
- args: ["-y", "@assistant-ui/mcp-docs-server"]
16
- } } },
14
+ config: { mcpServers: { "assistant-ui": { url: HOSTED_MCP_URL } } },
15
+ replaceServerKey: "mcpServers",
17
16
  postInstall: "Open Cursor Settings → MCP → find 'assistant-ui' and click enable."
18
17
  },
19
18
  windsurf: {
20
19
  name: "Windsurf",
21
20
  getPath: () => path.join(os.homedir(), ".codeium", "windsurf", "mcp_config.json"),
22
- config: { mcpServers: { "assistant-ui": {
23
- command: "npx",
24
- args: ["-y", "@assistant-ui/mcp-docs-server"]
25
- } } },
21
+ config: { mcpServers: { "assistant-ui": { serverUrl: HOSTED_MCP_URL } } },
22
+ replaceServerKey: "mcpServers",
26
23
  postInstall: "Fully quit and re-open Windsurf to activate."
27
24
  },
28
25
  vscode: {
29
26
  name: "VSCode",
30
27
  getPath: () => path.join(process.cwd(), ".vscode", "mcp.json"),
31
28
  config: { servers: { "assistant-ui": {
32
- command: "npx",
33
- args: ["-y", "@assistant-ui/mcp-docs-server"],
34
- type: "stdio"
29
+ type: "http",
30
+ url: HOSTED_MCP_URL
35
31
  } } },
32
+ replaceServerKey: "servers",
36
33
  postInstall: "Enable MCP in Settings → search 'MCP' → enable 'Chat > MCP'. Use Copilot Chat in Agent mode."
37
34
  },
38
35
  zed: {
@@ -79,14 +76,20 @@ async function installForTarget(target) {
79
76
  logger.info("Installing MCP server for Claude Code...");
80
77
  logger.break();
81
78
  try {
79
+ await runSpawn("claude", [
80
+ "mcp",
81
+ "remove",
82
+ "assistant-ui"
83
+ ]).catch((error) => {
84
+ if (error instanceof SpawnSignalError) throw error;
85
+ });
82
86
  await runSpawn("claude", [
83
87
  "mcp",
84
88
  "add",
89
+ "--transport",
90
+ "http",
85
91
  "assistant-ui",
86
- "--",
87
- "npx",
88
- "-y",
89
- "@assistant-ui/mcp-docs-server"
92
+ HOSTED_MCP_URL
90
93
  ]);
91
94
  } catch (error) {
92
95
  if (error instanceof SpawnSignalError) throw error;
@@ -99,6 +102,7 @@ async function installForTarget(target) {
99
102
  }
100
103
  logger.break();
101
104
  logger.success("MCP server installed for Claude Code!");
105
+ logger.info(`Connects to the hosted assistant-ui MCP server at ${HOSTED_MCP_URL}.`);
102
106
  logger.info("The server starts automatically. Try asking about assistant-ui!");
103
107
  return;
104
108
  }
@@ -127,16 +131,27 @@ async function installForTarget(target) {
127
131
  }
128
132
  }
129
133
  const newConfig = deepMerge(existingConfig, targetConfig.config);
134
+ if (targetConfig.replaceServerKey) {
135
+ const key = targetConfig.replaceServerKey;
136
+ newConfig[key] = {
137
+ ...newConfig[key],
138
+ "assistant-ui": targetConfig.config[key]["assistant-ui"]
139
+ };
140
+ }
130
141
  fs.writeFileSync(configPath, `${JSON.stringify(newConfig, null, 2)}\n`);
131
142
  logger.break();
132
143
  logger.success(`MCP server installed for ${targetConfig.name}!`);
133
144
  logger.info(`Config written to: ${configPath}`);
145
+ if (targetConfig.replaceServerKey) {
146
+ logger.break();
147
+ logger.info(`Connects to the hosted assistant-ui MCP server at ${HOSTED_MCP_URL}.`);
148
+ }
134
149
  if (targetConfig.postInstall) {
135
150
  logger.break();
136
151
  logger.info(targetConfig.postInstall);
137
152
  }
138
153
  }
139
- const mcp = new Command().name("mcp").description("install assistant-ui MCP docs server for your IDE").option("--cursor", "install for Cursor").option("--windsurf", "install for Windsurf").option("--vscode", "install for VSCode").option("--zed", "install for Zed").option("--claude-code", "install for Claude Code").option("--claude-desktop", "install for Claude Desktop").action(async (opts) => {
154
+ const mcp = new Command().name("mcp").description("connect your IDE to the assistant-ui MCP server").option("--cursor", "install for Cursor").option("--windsurf", "install for Windsurf").option("--vscode", "install for VSCode").option("--zed", "install for Zed").option("--claude-code", "install for Claude Code").option("--claude-desktop", "install for Claude Desktop").action(async (opts) => {
140
155
  const targets = [];
141
156
  if (opts.cursor) targets.push("cursor");
142
157
  if (opts.windsurf) targets.push("windsurf");
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.js","names":[],"sources":["../../src/commands/mcp.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { logger } from \"../lib/utils/logger\";\nimport { runSpawn, SpawnExitError, SpawnSignalError } from \"../lib/run-spawn\";\nimport * as p from \"@clack/prompts\";\n\ntype MCPTarget =\n | \"cursor\"\n | \"windsurf\"\n | \"vscode\"\n | \"zed\"\n | \"claude-code\"\n | \"claude-desktop\";\n\nconst MCP_CONFIGS: Record<\n Exclude<MCPTarget, \"claude-code\">,\n {\n name: string;\n getPath: () => string;\n config: object;\n postInstall?: string;\n }\n> = {\n cursor: {\n name: \"Cursor\",\n getPath: () => path.join(process.cwd(), \".cursor\", \"mcp.json\"),\n config: {\n mcpServers: {\n \"assistant-ui\": {\n command: \"npx\",\n args: [\"-y\", \"@assistant-ui/mcp-docs-server\"],\n },\n },\n },\n postInstall:\n \"Open Cursor Settings → MCP → find 'assistant-ui' and click enable.\",\n },\n windsurf: {\n name: \"Windsurf\",\n getPath: () =>\n path.join(os.homedir(), \".codeium\", \"windsurf\", \"mcp_config.json\"),\n config: {\n mcpServers: {\n \"assistant-ui\": {\n command: \"npx\",\n args: [\"-y\", \"@assistant-ui/mcp-docs-server\"],\n },\n },\n },\n postInstall: \"Fully quit and re-open Windsurf to activate.\",\n },\n vscode: {\n name: \"VSCode\",\n getPath: () => path.join(process.cwd(), \".vscode\", \"mcp.json\"),\n config: {\n servers: {\n \"assistant-ui\": {\n command: \"npx\",\n args: [\"-y\", \"@assistant-ui/mcp-docs-server\"],\n type: \"stdio\",\n },\n },\n },\n postInstall:\n \"Enable MCP in Settings → search 'MCP' → enable 'Chat > MCP'. Use Copilot Chat in Agent mode.\",\n },\n zed: {\n name: \"Zed\",\n getPath: () => {\n if (process.platform === \"win32\") {\n return path.join(process.env.APPDATA || \"\", \"Zed\", \"settings.json\");\n }\n if (process.platform === \"darwin\") {\n return path.join(os.homedir(), \".zed\", \"settings.json\");\n }\n return path.join(os.homedir(), \".config\", \"zed\", \"settings.json\");\n },\n config: {\n context_servers: {\n \"assistant-ui\": {\n command: {\n path: \"npx\",\n args: [\"-y\", \"@assistant-ui/mcp-docs-server\"],\n },\n },\n },\n },\n postInstall: \"The server starts automatically with the Assistant Panel.\",\n },\n \"claude-desktop\": {\n name: \"Claude Desktop\",\n getPath: () => {\n if (process.platform === \"win32\") {\n return path.join(\n process.env.APPDATA || \"\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n }\n return path.join(\n os.homedir(),\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n },\n config: {\n mcpServers: {\n \"assistant-ui\": {\n command: \"npx\",\n args: [\"-y\", \"@assistant-ui/mcp-docs-server\"],\n },\n },\n },\n postInstall: \"Restart Claude Desktop to activate.\",\n },\n};\n\nfunction deepMerge(target: any, source: any): any {\n const result = { ...target };\n for (const key of Object.keys(source)) {\n if (\n source[key] &&\n typeof source[key] === \"object\" &&\n !Array.isArray(source[key])\n ) {\n result[key] = deepMerge(result[key] || {}, source[key]);\n } else {\n result[key] = source[key];\n }\n }\n return result;\n}\n\nclass McpConfigParseError extends Error {\n constructor(targetName: string, configPath: string, flag: string) {\n super(\n `Invalid ${targetName} MCP config JSON at ${configPath}. Fix the JSON syntax, then run: assistant-ui mcp ${flag}`,\n );\n this.name = \"McpConfigParseError\";\n }\n}\n\nconst getTargetFlag = (target: Exclude<MCPTarget, \"claude-code\">) =>\n `--${target}`;\n\nasync function installForTarget(target: MCPTarget): Promise<void> {\n if (target === \"claude-code\") {\n logger.info(\"Installing MCP server for Claude Code...\");\n logger.break();\n\n try {\n await runSpawn(\"claude\", [\n \"mcp\",\n \"add\",\n \"assistant-ui\",\n \"--\",\n \"npx\",\n \"-y\",\n \"@assistant-ui/mcp-docs-server\",\n ]);\n } catch (error) {\n if (error instanceof SpawnSignalError) throw error;\n if (error instanceof SpawnExitError) {\n logger.error(`Installation failed with code ${error.code}`);\n } else if (error instanceof Error) {\n logger.error(`Failed to install: ${error.message}`);\n logger.info(\n \"Make sure Claude Code CLI is installed: https://docs.anthropic.com/en/docs/claude-code\",\n );\n }\n throw error;\n }\n\n logger.break();\n logger.success(\"MCP server installed for Claude Code!\");\n logger.info(\n \"The server starts automatically. Try asking about assistant-ui!\",\n );\n return;\n }\n\n if (target === \"claude-desktop\" && process.platform === \"linux\") {\n logger.error(\"Claude Desktop is not available on Linux.\");\n logger.info(\n \"See: https://claude.ai/download for supported operating systems.\",\n );\n throw new Error(\"Unsupported platform for Claude Desktop\");\n }\n\n const targetConfig = MCP_CONFIGS[target];\n const configPath = targetConfig.getPath();\n const configDir = path.dirname(configPath);\n\n logger.info(`Installing MCP server for ${targetConfig.name}...`);\n\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true });\n }\n\n let existingConfig: any = {};\n if (fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, \"utf-8\");\n try {\n existingConfig = JSON.parse(content);\n } catch {\n const flag = getTargetFlag(target);\n logger.error(`Could not parse ${targetConfig.name} MCP config.`);\n logger.info(`Config path: ${configPath}`);\n logger.info(\n `Fix the JSON syntax in that file, then run: assistant-ui mcp ${flag}`,\n );\n logger.info(\"No changes were written.\");\n throw new McpConfigParseError(targetConfig.name, configPath, flag);\n }\n }\n\n const newConfig = deepMerge(existingConfig, targetConfig.config);\n\n fs.writeFileSync(configPath, `${JSON.stringify(newConfig, null, 2)}\\n`);\n\n logger.break();\n logger.success(`MCP server installed for ${targetConfig.name}!`);\n logger.info(`Config written to: ${configPath}`);\n\n if (targetConfig.postInstall) {\n logger.break();\n logger.info(targetConfig.postInstall);\n }\n}\n\nexport const mcp = new Command()\n .name(\"mcp\")\n .description(\"install assistant-ui MCP docs server for your IDE\")\n .option(\"--cursor\", \"install for Cursor\")\n .option(\"--windsurf\", \"install for Windsurf\")\n .option(\"--vscode\", \"install for VSCode\")\n .option(\"--zed\", \"install for Zed\")\n .option(\"--claude-code\", \"install for Claude Code\")\n .option(\"--claude-desktop\", \"install for Claude Desktop\")\n .action(async (opts) => {\n const targets: MCPTarget[] = [];\n\n if (opts.cursor) targets.push(\"cursor\");\n if (opts.windsurf) targets.push(\"windsurf\");\n if (opts.vscode) targets.push(\"vscode\");\n if (opts.zed) targets.push(\"zed\");\n if (opts.claudeCode) targets.push(\"claude-code\");\n if (opts.claudeDesktop) targets.push(\"claude-desktop\");\n\n // If no target specified, prompt user\n if (targets.length === 0) {\n p.intro(\"assistant-ui MCP Server Installation\");\n\n const selected = await p.select({\n message: \"Select your IDE or tool:\",\n options: [\n { value: \"cursor\", label: \"Cursor\" },\n { value: \"windsurf\", label: \"Windsurf\" },\n { value: \"vscode\", label: \"VSCode\" },\n { value: \"zed\", label: \"Zed\" },\n { value: \"claude-code\", label: \"Claude Code\" },\n { value: \"claude-desktop\", label: \"Claude Desktop\" },\n ],\n });\n\n if (p.isCancel(selected)) {\n p.cancel(\"Installation cancelled.\");\n process.exit(0);\n }\n\n targets.push(selected as MCPTarget);\n }\n\n for (const target of targets) {\n try {\n await installForTarget(target);\n } catch (error) {\n if (error instanceof SpawnSignalError) throw error;\n process.exit(1);\n }\n }\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"mcp.js","names":[],"sources":["../../src/commands/mcp.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { logger } from \"../lib/utils/logger\";\nimport { runSpawn, SpawnExitError, SpawnSignalError } from \"../lib/run-spawn\";\nimport * as p from \"@clack/prompts\";\n\ntype MCPTarget =\n | \"cursor\"\n | \"windsurf\"\n | \"vscode\"\n | \"zed\"\n | \"claude-code\"\n | \"claude-desktop\";\n\nconst HOSTED_MCP_URL = \"https://www.assistant-ui.com/mcp\";\n\nconst MCP_CONFIGS: Record<\n Exclude<MCPTarget, \"claude-code\">,\n {\n name: string;\n getPath: () => string;\n config: object;\n replaceServerKey?: string;\n postInstall?: string;\n }\n> = {\n cursor: {\n name: \"Cursor\",\n getPath: () => path.join(process.cwd(), \".cursor\", \"mcp.json\"),\n config: {\n mcpServers: {\n \"assistant-ui\": {\n url: HOSTED_MCP_URL,\n },\n },\n },\n replaceServerKey: \"mcpServers\",\n postInstall:\n \"Open Cursor Settings → MCP → find 'assistant-ui' and click enable.\",\n },\n windsurf: {\n name: \"Windsurf\",\n getPath: () =>\n path.join(os.homedir(), \".codeium\", \"windsurf\", \"mcp_config.json\"),\n config: {\n mcpServers: {\n \"assistant-ui\": {\n serverUrl: HOSTED_MCP_URL,\n },\n },\n },\n replaceServerKey: \"mcpServers\",\n postInstall: \"Fully quit and re-open Windsurf to activate.\",\n },\n vscode: {\n name: \"VSCode\",\n getPath: () => path.join(process.cwd(), \".vscode\", \"mcp.json\"),\n config: {\n servers: {\n \"assistant-ui\": {\n type: \"http\",\n url: HOSTED_MCP_URL,\n },\n },\n },\n replaceServerKey: \"servers\",\n postInstall:\n \"Enable MCP in Settings → search 'MCP' → enable 'Chat > MCP'. Use Copilot Chat in Agent mode.\",\n },\n zed: {\n name: \"Zed\",\n getPath: () => {\n if (process.platform === \"win32\") {\n return path.join(process.env.APPDATA || \"\", \"Zed\", \"settings.json\");\n }\n if (process.platform === \"darwin\") {\n return path.join(os.homedir(), \".zed\", \"settings.json\");\n }\n return path.join(os.homedir(), \".config\", \"zed\", \"settings.json\");\n },\n config: {\n context_servers: {\n \"assistant-ui\": {\n command: {\n path: \"npx\",\n args: [\"-y\", \"@assistant-ui/mcp-docs-server\"],\n },\n },\n },\n },\n postInstall: \"The server starts automatically with the Assistant Panel.\",\n },\n \"claude-desktop\": {\n name: \"Claude Desktop\",\n getPath: () => {\n if (process.platform === \"win32\") {\n return path.join(\n process.env.APPDATA || \"\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n }\n return path.join(\n os.homedir(),\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n },\n config: {\n mcpServers: {\n \"assistant-ui\": {\n command: \"npx\",\n args: [\"-y\", \"@assistant-ui/mcp-docs-server\"],\n },\n },\n },\n postInstall: \"Restart Claude Desktop to activate.\",\n },\n};\n\nfunction deepMerge(target: any, source: any): any {\n const result = { ...target };\n for (const key of Object.keys(source)) {\n if (\n source[key] &&\n typeof source[key] === \"object\" &&\n !Array.isArray(source[key])\n ) {\n result[key] = deepMerge(result[key] || {}, source[key]);\n } else {\n result[key] = source[key];\n }\n }\n return result;\n}\n\nclass McpConfigParseError extends Error {\n constructor(targetName: string, configPath: string, flag: string) {\n super(\n `Invalid ${targetName} MCP config JSON at ${configPath}. Fix the JSON syntax, then run: assistant-ui mcp ${flag}`,\n );\n this.name = \"McpConfigParseError\";\n }\n}\n\nconst getTargetFlag = (target: Exclude<MCPTarget, \"claude-code\">) =>\n `--${target}`;\n\nasync function installForTarget(target: MCPTarget): Promise<void> {\n if (target === \"claude-code\") {\n logger.info(\"Installing MCP server for Claude Code...\");\n logger.break();\n\n try {\n await runSpawn(\"claude\", [\"mcp\", \"remove\", \"assistant-ui\"]).catch(\n (error: unknown) => {\n if (error instanceof SpawnSignalError) throw error;\n },\n );\n await runSpawn(\"claude\", [\n \"mcp\",\n \"add\",\n \"--transport\",\n \"http\",\n \"assistant-ui\",\n HOSTED_MCP_URL,\n ]);\n } catch (error) {\n if (error instanceof SpawnSignalError) throw error;\n if (error instanceof SpawnExitError) {\n logger.error(`Installation failed with code ${error.code}`);\n } else if (error instanceof Error) {\n logger.error(`Failed to install: ${error.message}`);\n logger.info(\n \"Make sure Claude Code CLI is installed: https://docs.anthropic.com/en/docs/claude-code\",\n );\n }\n throw error;\n }\n\n logger.break();\n logger.success(\"MCP server installed for Claude Code!\");\n logger.info(\n `Connects to the hosted assistant-ui MCP server at ${HOSTED_MCP_URL}.`,\n );\n logger.info(\n \"The server starts automatically. Try asking about assistant-ui!\",\n );\n return;\n }\n\n if (target === \"claude-desktop\" && process.platform === \"linux\") {\n logger.error(\"Claude Desktop is not available on Linux.\");\n logger.info(\n \"See: https://claude.ai/download for supported operating systems.\",\n );\n throw new Error(\"Unsupported platform for Claude Desktop\");\n }\n\n const targetConfig = MCP_CONFIGS[target];\n const configPath = targetConfig.getPath();\n const configDir = path.dirname(configPath);\n\n logger.info(`Installing MCP server for ${targetConfig.name}...`);\n\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true });\n }\n\n let existingConfig: any = {};\n if (fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, \"utf-8\");\n try {\n existingConfig = JSON.parse(content);\n } catch {\n const flag = getTargetFlag(target);\n logger.error(`Could not parse ${targetConfig.name} MCP config.`);\n logger.info(`Config path: ${configPath}`);\n logger.info(\n `Fix the JSON syntax in that file, then run: assistant-ui mcp ${flag}`,\n );\n logger.info(\"No changes were written.\");\n throw new McpConfigParseError(targetConfig.name, configPath, flag);\n }\n }\n\n const newConfig = deepMerge(existingConfig, targetConfig.config);\n\n if (targetConfig.replaceServerKey) {\n const key = targetConfig.replaceServerKey;\n newConfig[key] = {\n ...newConfig[key],\n \"assistant-ui\": (targetConfig.config as any)[key][\"assistant-ui\"],\n };\n }\n\n fs.writeFileSync(configPath, `${JSON.stringify(newConfig, null, 2)}\\n`);\n\n logger.break();\n logger.success(`MCP server installed for ${targetConfig.name}!`);\n logger.info(`Config written to: ${configPath}`);\n\n if (targetConfig.replaceServerKey) {\n logger.break();\n logger.info(\n `Connects to the hosted assistant-ui MCP server at ${HOSTED_MCP_URL}.`,\n );\n }\n\n if (targetConfig.postInstall) {\n logger.break();\n logger.info(targetConfig.postInstall);\n }\n}\n\nexport const mcp = new Command()\n .name(\"mcp\")\n .description(\"connect your IDE to the assistant-ui MCP server\")\n .option(\"--cursor\", \"install for Cursor\")\n .option(\"--windsurf\", \"install for Windsurf\")\n .option(\"--vscode\", \"install for VSCode\")\n .option(\"--zed\", \"install for Zed\")\n .option(\"--claude-code\", \"install for Claude Code\")\n .option(\"--claude-desktop\", \"install for Claude Desktop\")\n .action(async (opts) => {\n const targets: MCPTarget[] = [];\n\n if (opts.cursor) targets.push(\"cursor\");\n if (opts.windsurf) targets.push(\"windsurf\");\n if (opts.vscode) targets.push(\"vscode\");\n if (opts.zed) targets.push(\"zed\");\n if (opts.claudeCode) targets.push(\"claude-code\");\n if (opts.claudeDesktop) targets.push(\"claude-desktop\");\n\n // If no target specified, prompt user\n if (targets.length === 0) {\n p.intro(\"assistant-ui MCP Server Installation\");\n\n const selected = await p.select({\n message: \"Select your IDE or tool:\",\n options: [\n { value: \"cursor\", label: \"Cursor\" },\n { value: \"windsurf\", label: \"Windsurf\" },\n { value: \"vscode\", label: \"VSCode\" },\n { value: \"zed\", label: \"Zed\" },\n { value: \"claude-code\", label: \"Claude Code\" },\n { value: \"claude-desktop\", label: \"Claude Desktop\" },\n ],\n });\n\n if (p.isCancel(selected)) {\n p.cancel(\"Installation cancelled.\");\n process.exit(0);\n }\n\n targets.push(selected as MCPTarget);\n }\n\n for (const target of targets) {\n try {\n await installForTarget(target);\n } catch (error) {\n if (error instanceof SpawnSignalError) throw error;\n process.exit(1);\n }\n }\n });\n"],"mappings":"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"}
@@ -28,6 +28,15 @@ interface TransformResult {
28
28
  };
29
29
  }
30
30
  declare function transformProject(projectDir: string, opts: TransformOptions): Promise<TransformResult>;
31
+ /**
32
+ * Example snapshots are downloaded at a release tag while the shadcn registry
33
+ * is live, so a snapshot may import components at the legacy flat path
34
+ * (`@/components/assistant-ui/<name>`) after the registry has moved the file
35
+ * to `components/assistant-ui/elements/<name>.aui.tsx`. Resolve each legacy
36
+ * specifier against the files the registry actually installed and rewrite it
37
+ * only when the legacy path is absent and the elements layout has it.
38
+ */
39
+ declare function reconcileAssistantUIImportLayout(projectDir: string): Promise<void>;
31
40
  //#endregion
32
- export { ProjectSource, TransformOptions, TransformResult, dlxCommand, downloadProject, resolveLatestReleaseRef, resolvePackageManager, scaffoldProject, transformProject };
41
+ export { ProjectSource, TransformOptions, TransformResult, dlxCommand, downloadProject, reconcileAssistantUIImportLayout, resolveLatestReleaseRef, resolvePackageManager, scaffoldProject, transformProject };
33
42
  //# sourceMappingURL=create-project.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-project.d.ts","names":[],"sources":["../../src/lib/create-project.ts"],"mappings":";;iBAagB,WAAW,IAAI;UAad;EACf;EACA;EACA,gBAAgB;;KAGN;EAEN;EACA;;EAGA;EACA;;iBAcU,sBAAsB;EACpC;EACA;EACA;EACA;IACE;iBAmBkB,2BAA2B;iBAmB3B,gBACpB,kBACA,iBACA,eACC;iBAuDmB,gBACpB,kBACA,iBACA,QAAQ,gBACP;UA2Bc;EACf;IAA2B;;;iBAGP,iBACpB,oBACA,MAAM,mBACL,QAAQ"}
1
+ {"version":3,"file":"create-project.d.ts","names":[],"sources":["../../src/lib/create-project.ts"],"mappings":";;iBAagB,WAAW,IAAI;UAad;EACf;EACA;EACA,gBAAgB;;KAGN;EAEN;EACA;;EAGA;EACA;;iBAcU,sBAAsB;EACpC;EACA;EACA;EACA;IACE;iBAmBkB,2BAA2B;iBAmB3B,gBACpB,kBACA,iBACA,eACC;iBAuDmB,gBACpB,kBACA,iBACA,QAAQ,gBACP;UA2Bc;EACf;IAA2B;;;iBAGP,iBACpB,oBACA,MAAM,mBACL,QAAQ;;;;;;;;;iBA8OW,iCACpB,qBACC"}
@@ -110,12 +110,16 @@ async function transformProject(projectDir, opts) {
110
110
  await installDependencies(projectDir, pm);
111
111
  }
112
112
  if (!opts.skipInstall && !opts.hasLocalComponents && shadcnUI && assistantUI) {
113
- const allShadcn = shadcnUI.includes("utils") ? shadcnUI : [...shadcnUI, "utils"];
114
113
  const auiComponents = assistantUI.map((c) => `@assistant-ui/${c}`);
115
- const components = [...allShadcn, ...auiComponents];
114
+ const components = [
115
+ "@assistant-ui/utils",
116
+ ...shadcnUI,
117
+ ...auiComponents
118
+ ];
116
119
  logger.step(`Installing components: ${components.join(", ")}...`);
117
120
  const failure = await installShadcnRegistry(projectDir, components, "components", pm);
118
121
  if (failure) return { registryInstallFailure: failure };
122
+ await reconcileAssistantUIImportLayout(projectDir);
119
123
  }
120
124
  return {};
121
125
  }
@@ -202,6 +206,104 @@ function transformCssFiles(projectDir) {
202
206
  function stripImportExtension(component) {
203
207
  return component.replace(/\.[cm]?[tj]sx?$/, "");
204
208
  }
209
+ const ASSISTANT_UI_OWNED_UI = /* @__PURE__ */ new Set([
210
+ "accordion",
211
+ "badge",
212
+ "diff-viewer",
213
+ "direction",
214
+ "dot-matrix",
215
+ "number-roll",
216
+ "select",
217
+ "tabs"
218
+ ]);
219
+ const BARE_ELEMENT_ITEMS = /* @__PURE__ */ new Set([
220
+ "file",
221
+ "generative-ui",
222
+ "heat-graph",
223
+ "image",
224
+ "logos",
225
+ "markdown-text",
226
+ "syntax-highlighter",
227
+ "tooltip-icon-button"
228
+ ]);
229
+ function toAssistantUIItem(specifier) {
230
+ let name = stripImportExtension(specifier);
231
+ const inElements = name.startsWith("elements/");
232
+ if (inElements) name = name.slice(9);
233
+ else if (name.includes("/")) return null;
234
+ if (name.endsWith(".aui")) return name.slice(0, -4);
235
+ return inElements && !BARE_ELEMENT_ITEMS.has(name) ? `elements-${name}` : name;
236
+ }
237
+ /**
238
+ * Example snapshots are downloaded at a release tag while the shadcn registry
239
+ * is live, so a snapshot may import components at the legacy flat path
240
+ * (`@/components/assistant-ui/<name>`) after the registry has moved the file
241
+ * to `components/assistant-ui/elements/<name>.aui.tsx`. Resolve each legacy
242
+ * specifier against the files the registry actually installed and rewrite it
243
+ * only when the legacy path is absent and the elements layout has it.
244
+ */
245
+ async function reconcileAssistantUIImportLayout(projectDir) {
246
+ const componentRoots = ["components", "src/components"].map((dir) => path$1.join(projectDir, dir, "assistant-ui")).filter((dir) => fs$1.existsSync(dir));
247
+ if (componentRoots.length === 0) return;
248
+ const resolvesAtLegacyPath = (name) => componentRoots.some((root) => [
249
+ ".tsx",
250
+ ".ts",
251
+ "/index.tsx",
252
+ "/index.ts"
253
+ ].some((suffix) => fs$1.existsSync(path$1.join(root, `${name}${suffix}`))));
254
+ const installedByName = /* @__PURE__ */ new Map();
255
+ for (const root of componentRoots) for (const { file } of readProjectFiles("**/*.{ts,tsx}", { cwd: root })) {
256
+ const normalized = file.split(path$1.sep).join("/");
257
+ if (!normalized.includes("/")) continue;
258
+ const specifier = normalized.replace(/\.[cm]?[tj]sx?$/, "");
259
+ const name = path$1.posix.basename(specifier).replace(/\.aui$/, "");
260
+ const existing = installedByName.get(name);
261
+ if (existing === void 0 || !existing.endsWith(".aui") && specifier.endsWith(".aui")) installedByName.set(name, specifier);
262
+ }
263
+ if (installedByName.size === 0) return;
264
+ const { default: jscodeshift } = await import("jscodeshift");
265
+ const parsers = {
266
+ ts: jscodeshift.withParser("ts"),
267
+ tsx: jscodeshift.withParser("tsx")
268
+ };
269
+ for (const { fullPath, content } of readProjectFiles("**/*.{ts,tsx}", {
270
+ cwd: projectDir,
271
+ ignore: LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES
272
+ })) {
273
+ if (!content.includes("@/components/assistant-ui/")) continue;
274
+ const replacements = [];
275
+ const collectReplacement = (source) => {
276
+ if (typeof source.value !== "string" || source.start == null || source.end == null) return;
277
+ if (!source.value.startsWith("@/components/assistant-ui/")) return;
278
+ const specifier = source.value.slice(26);
279
+ if (specifier.includes("/")) return;
280
+ const name = stripImportExtension(specifier);
281
+ const installed = installedByName.get(name);
282
+ if (resolvesAtLegacyPath(name) || installed === void 0) return;
283
+ const raw = content.slice(source.start, source.end);
284
+ const quote = raw[0];
285
+ if (quote !== "\"" && quote !== "'" || raw.at(-1) !== quote) return;
286
+ replacements.push({
287
+ start: source.start,
288
+ end: source.end,
289
+ value: `${quote}@/components/assistant-ui/${installed}${quote}`
290
+ });
291
+ };
292
+ const j = fullPath.endsWith(".tsx") ? parsers.tsx : parsers.ts;
293
+ let root;
294
+ try {
295
+ root = j(content);
296
+ } catch {
297
+ continue;
298
+ }
299
+ root.find(j.ImportDeclaration).forEach(({ node }) => collectReplacement(node.source));
300
+ root.find(j.ExportNamedDeclaration).forEach(({ node }) => node.source && collectReplacement(node.source));
301
+ root.find(j.ExportAllDeclaration).forEach(({ node }) => collectReplacement(node.source));
302
+ let next = content;
303
+ for (const replacement of replacements.sort((a, b) => b.start - a.start)) next = next.slice(0, replacement.start) + replacement.value + next.slice(replacement.end);
304
+ if (next !== content) fs$1.writeFileSync(fullPath, next);
305
+ }
306
+ }
205
307
  function scanRequiredComponents(projectDir) {
206
308
  const assistantUIComponents = /* @__PURE__ */ new Set();
207
309
  const shadcnUIComponents = /* @__PURE__ */ new Set();
@@ -209,8 +311,15 @@ function scanRequiredComponents(projectDir) {
209
311
  cwd: projectDir,
210
312
  ignore: LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES
211
313
  })) {
212
- for (const match of content.matchAll(/from\s+["']@\/components\/assistant-ui\/([^"']+)["']/g)) assistantUIComponents.add(stripImportExtension(match[1]));
213
- for (const match of content.matchAll(/from\s+["']@\/components\/ui\/([^"']+)["']/g)) shadcnUIComponents.add(stripImportExtension(match[1]));
314
+ for (const match of content.matchAll(/from\s+["']@\/components\/assistant-ui\/([^"']+)["']/g)) {
315
+ const item = toAssistantUIItem(match[1]);
316
+ if (item) assistantUIComponents.add(item);
317
+ }
318
+ for (const match of content.matchAll(/from\s+["']@\/components\/ui\/([^"']+)["']/g)) {
319
+ const name = stripImportExtension(match[1]);
320
+ if (ASSISTANT_UI_OWNED_UI.has(name)) assistantUIComponents.add(name);
321
+ else shadcnUIComponents.add(name);
322
+ }
214
323
  }
215
324
  return {
216
325
  assistantUI: Array.from(assistantUIComponents),
@@ -251,6 +360,6 @@ async function installShadcnRegistry(projectDir, components, label, pm) {
251
360
  }
252
361
  }
253
362
  //#endregion
254
- export { dlxCommand, downloadProject, resolveLatestReleaseRef, resolvePackageManager, scaffoldProject, transformProject };
363
+ export { dlxCommand, downloadProject, reconcileAssistantUIImportLayout, resolveLatestReleaseRef, resolvePackageManager, scaffoldProject, transformProject };
255
364
 
256
365
  //# sourceMappingURL=create-project.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-project.js","names":["path","fs","parseJsonc"],"sources":["../../src/lib/create-project.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { downloadTemplate } from \"giget\";\nimport {\n parse as parseJsonc,\n printParseErrorCode,\n type ParseError,\n} from \"jsonc-parser\";\nimport { logger } from \"./utils/logger\";\nimport { runSpawn, SpawnExitError, SpawnSignalError } from \"./run-spawn\";\nimport { type PackageManagerName } from \"./utils/package-manager\";\nimport { readProjectFiles } from \"./utils/file-scanner\";\n\nexport function dlxCommand(pm: PackageManagerName): [string, string[]] {\n switch (pm) {\n case \"pnpm\":\n return [\"pnpm\", [\"dlx\"]];\n case \"yarn\":\n return [\"yarn\", [\"dlx\"]];\n case \"bun\":\n return [\"bunx\", []];\n case \"npm\":\n return [\"npx\", [\"--yes\"]];\n }\n}\n\nexport interface TransformOptions {\n hasLocalComponents: boolean;\n skipInstall?: boolean;\n packageManager: PackageManagerName;\n}\n\nexport type ProjectSource =\n | {\n kind: \"github\";\n ref: string | undefined;\n }\n | {\n kind: \"local\";\n rootDir: string;\n };\n\nconst LOCAL_PROJECT_ARTIFACT_DIRS: readonly string[] = [\n \"node_modules\",\n \".next\",\n \"dist\",\n \"build\",\n];\n\nconst LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES = LOCAL_PROJECT_ARTIFACT_DIRS.map(\n (dir) => `**/${dir}/**`,\n);\n\nexport function resolvePackageManager(opts: {\n useNpm?: boolean;\n usePnpm?: boolean;\n useYarn?: boolean;\n useBun?: boolean;\n}): PackageManagerName | undefined {\n if (opts.useNpm) return \"npm\";\n if (opts.usePnpm) return \"pnpm\";\n if (opts.useYarn) return \"yarn\";\n if (opts.useBun) return \"bun\";\n return undefined;\n}\n\nfunction resolveGitHubAuthToken(): string | undefined {\n const token =\n process.env.GIGET_AUTH ?? process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;\n const trimmed = token?.trim();\n return trimmed || undefined;\n}\n\nfunction toBearerAuthHeader(token: string): string {\n return token.toLowerCase().startsWith(\"bearer \") ? token : `Bearer ${token}`;\n}\n\nexport async function resolveLatestReleaseRef(): Promise<string | undefined> {\n try {\n const authToken = resolveGitHubAuthToken();\n const res = await fetch(\n \"https://api.github.com/repos/assistant-ui/assistant-ui/releases/latest\",\n authToken\n ? { headers: { Authorization: toBearerAuthHeader(authToken) } }\n : undefined,\n );\n if (!res.ok) return undefined;\n const release = (await res.json()) as { tag_name: string };\n return release.tag_name || undefined;\n } catch {\n return undefined;\n }\n}\n\nconst DOWNLOAD_TIMEOUT_MS = 30_000;\n\nexport async function downloadProject(\n repoPath: string,\n destDir: string,\n ref?: string,\n): Promise<void> {\n const source = ref\n ? `gh:assistant-ui/assistant-ui/${repoPath}#${ref}`\n : `gh:assistant-ui/assistant-ui/${repoPath}`;\n\n // Suppress giget's debug output. The `debug` package (used by the upgrade\n // command) sets process.env.DEBUG at module-load time, and giget logs to\n // console.debug whenever that env var is truthy — even for unrelated\n // namespaces. Temporarily unsetting it targets the root cause.\n const origDebug = process.env.DEBUG;\n delete process.env.DEBUG;\n try {\n const authToken = resolveGitHubAuthToken();\n const downloadPromise = downloadTemplate(source, {\n dir: destDir,\n force: true,\n silent: true,\n ...(authToken ? { auth: authToken } : {}),\n });\n\n let timer: ReturnType<typeof setTimeout>;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () =>\n reject(\n new Error(\n \"Download timed out. This may be due to GitHub rate limiting or a network issue. Try again in a few minutes.\",\n ),\n ),\n DOWNLOAD_TIMEOUT_MS,\n );\n });\n\n try {\n await Promise.race([downloadPromise, timeoutPromise]);\n } finally {\n clearTimeout(timer!);\n }\n } finally {\n if (origDebug !== undefined) {\n process.env.DEBUG = origDebug;\n }\n }\n}\n\nfunction shouldCopyLocalProjectPath(src: string, projectDir: string): boolean {\n const relative = path.relative(projectDir, src);\n if (!relative) return true;\n\n const segments = relative.split(path.sep);\n return !segments.some((segment) =>\n LOCAL_PROJECT_ARTIFACT_DIRS.includes(segment),\n );\n}\n\nexport async function scaffoldProject(\n repoPath: string,\n destDir: string,\n source: ProjectSource,\n): Promise<void> {\n if (source.kind === \"github\") {\n await downloadProject(repoPath, destDir, source.ref);\n return;\n }\n\n const localProjectDir = path.resolve(source.rootDir, repoPath);\n try {\n fs.cpSync(localProjectDir, destDir, {\n recursive: true,\n force: true,\n filter: (src) => shouldCopyLocalProjectPath(src, localProjectDir),\n });\n } catch (error) {\n const code =\n error instanceof Error\n ? (error as NodeJS.ErrnoException).code\n : undefined;\n if (code === \"ENOENT\") {\n throw new Error(\n `Local project source does not exist: ${localProjectDir}`,\n );\n }\n throw error;\n }\n}\n\nexport interface TransformResult {\n registryInstallFailure?: { retryCommand: string };\n}\n\nexport async function transformProject(\n projectDir: string,\n opts: TransformOptions,\n): Promise<TransformResult> {\n logger.step(\"Transforming package.json...\");\n transformPackageJson(projectDir);\n\n logger.step(\"Transforming project files...\");\n transformTsConfig(projectDir);\n transformCssFiles(projectDir);\n\n let assistantUI: string[] | undefined;\n let shadcnUI: string[] | undefined;\n\n if (!opts.hasLocalComponents) {\n const components = scanRequiredComponents(projectDir);\n assistantUI = components.assistantUI;\n shadcnUI = components.shadcnUI;\n }\n\n const pm = opts.packageManager;\n if (!opts.skipInstall) {\n logger.step(\"Installing dependencies...\");\n await installDependencies(projectDir, pm);\n }\n\n if (\n !opts.skipInstall &&\n !opts.hasLocalComponents &&\n shadcnUI &&\n assistantUI\n ) {\n const allShadcn = shadcnUI.includes(\"utils\")\n ? shadcnUI\n : [...shadcnUI, \"utils\"];\n const auiComponents = assistantUI.map((c) => `@assistant-ui/${c}`);\n const components = [...allShadcn, ...auiComponents];\n logger.step(`Installing components: ${components.join(\", \")}...`);\n const failure = await installShadcnRegistry(\n projectDir,\n components,\n \"components\",\n pm,\n );\n if (failure) return { registryInstallFailure: failure };\n }\n return {};\n}\n\nfunction transformPackageJson(projectDir: string): void {\n const pkgPath = path.join(projectDir, \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\"));\n\n // Remove @assistant-ui/ui dependency\n if (pkg.dependencies?.[\"@assistant-ui/ui\"]) {\n delete pkg.dependencies[\"@assistant-ui/ui\"];\n }\n\n // Transform workspace dependencies to latest\n for (const depType of [\"dependencies\", \"devDependencies\"] as const) {\n const deps = pkg[depType];\n if (!deps) continue;\n\n for (const [name, version] of Object.entries(deps)) {\n if (String(version).includes(\"workspace:\")) {\n deps[name] = \"latest\";\n }\n }\n }\n\n // Remove devDependencies that are workspace-only\n if (pkg.devDependencies?.[\"@assistant-ui/x-buildutils\"]) {\n delete pkg.devDependencies[\"@assistant-ui/x-buildutils\"];\n }\n\n // Update package name to be unique\n const dirName = path.basename(projectDir);\n pkg.name = dirName;\n\n fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n}\n\nfunction parseTsConfig(content: string): any {\n const errors: ParseError[] = [];\n const tsconfig = parseJsonc(content, errors, { allowTrailingComma: true });\n const error = errors[0];\n if (error) {\n throw new SyntaxError(\n `Invalid tsconfig.json: ${printParseErrorCode(error.error)} at offset ${error.offset}`,\n );\n }\n return tsconfig;\n}\n\nfunction transformTsConfig(projectDir: string): void {\n const tsconfigPath = path.join(projectDir, \"tsconfig.json\");\n\n if (!fs.existsSync(tsconfigPath)) {\n return;\n }\n\n const content = fs.readFileSync(tsconfigPath, \"utf-8\");\n const tsconfig = parseTsConfig(content);\n\n // Remove workspace paths\n if (tsconfig.compilerOptions?.paths) {\n const workspaceKeys = new Set([\n \"@/components/assistant-ui/*\",\n \"@/components/icons/*\",\n \"@/components/ui/*\",\n \"@/components/ui/radix/*\",\n \"@/hooks/*\",\n \"@/lib/utils\",\n \"@assistant-ui/ui/*\",\n ]);\n for (const [key, targets] of Object.entries(\n tsconfig.compilerOptions.paths as Record<string, unknown>,\n )) {\n const targetsWorkspace =\n Array.isArray(targets) &&\n targets.some(\n (target) =>\n typeof target === \"string\" &&\n (target.includes(\"packages/ui/\") || target.startsWith(\"../\")),\n );\n if (workspaceKeys.has(key) || targetsWorkspace) {\n delete tsconfig.compilerOptions.paths[key];\n }\n }\n\n if (Object.keys(tsconfig.compilerOptions.paths).length === 0) {\n delete tsconfig.compilerOptions.paths;\n }\n }\n\n // If extends uses @assistant-ui/x-buildutils, replace with inline config\n if (tsconfig.extends?.includes(\"@assistant-ui/x-buildutils\")) {\n const isNext = tsconfig.extends.includes(\"ts/next\");\n delete tsconfig.extends;\n\n const inlinedCompilerOptions = {\n target: \"ESNext\",\n lib: [\"dom\", \"dom.iterable\", \"ES2023\"],\n skipLibCheck: true,\n strict: true,\n noEmit: true,\n esModuleInterop: true,\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n resolveJsonModule: true,\n isolatedModules: true,\n jsx: \"react-jsx\",\n ...(isNext ? { plugins: [{ name: \"next\" }] } : {}),\n };\n\n tsconfig.compilerOptions = {\n ...inlinedCompilerOptions,\n ...tsconfig.compilerOptions,\n paths: {\n \"@/*\": [\"./*\"],\n ...(tsconfig.compilerOptions?.paths || {}),\n },\n };\n }\n\n fs.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\\n`);\n}\n\nfunction transformCssFiles(projectDir: string): void {\n for (const { fullPath, content } of readProjectFiles(\"**/*.css\", {\n cwd: projectDir,\n ignore: LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES,\n })) {\n try {\n const newContent = content.replace(\n /@source\\s+[\"'][^\"']*packages\\/ui\\/src[^\"']*[\"'];\\s*\\n?/g,\n \"\",\n );\n\n if (newContent !== content) {\n fs.writeFileSync(fullPath, newContent);\n }\n } catch {\n continue;\n }\n }\n}\n\ninterface RequiredComponents {\n assistantUI: string[];\n shadcnUI: string[];\n}\n\nfunction stripImportExtension(component: string): string {\n return component.replace(/\\.[cm]?[tj]sx?$/, \"\");\n}\n\nfunction scanRequiredComponents(projectDir: string): RequiredComponents {\n const assistantUIComponents = new Set<string>();\n const shadcnUIComponents = new Set<string>();\n\n for (const { content } of readProjectFiles(\"**/*.{ts,tsx}\", {\n cwd: projectDir,\n ignore: LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES,\n })) {\n const assistantUIRegex =\n /from\\s+[\"']@\\/components\\/assistant-ui\\/([^\"']+)[\"']/g;\n for (const match of content.matchAll(assistantUIRegex)) {\n assistantUIComponents.add(stripImportExtension(match[1]!));\n }\n\n const uiRegex = /from\\s+[\"']@\\/components\\/ui\\/([^\"']+)[\"']/g;\n for (const match of content.matchAll(uiRegex)) {\n shadcnUIComponents.add(stripImportExtension(match[1]!));\n }\n }\n\n return {\n assistantUI: Array.from(assistantUIComponents),\n shadcnUI: Array.from(shadcnUIComponents),\n };\n}\n\nasync function installDependencies(\n projectDir: string,\n pm: PackageManagerName,\n): Promise<void> {\n const args = pm === \"yarn\" ? [] : [\"install\"];\n try {\n await runSpawn(pm, args, projectDir);\n } catch (error) {\n if (error instanceof SpawnSignalError) {\n throw error;\n }\n if (error instanceof SpawnExitError) {\n throw new Error(`${pm} install exited with code ${error.code}`);\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to install dependencies: ${message}`);\n }\n}\n\nasync function installShadcnRegistry(\n projectDir: string,\n components: string[],\n label: string,\n pm: PackageManagerName,\n): Promise<{ retryCommand: string } | undefined> {\n const [cmd, dlxArgs] = dlxCommand(pm);\n // For npm, dlxArgs may already include `--yes` for npx auto-install.\n // The trailing `--yes` is for shadcn's own confirmation prompt.\n const retryArgs = [...dlxArgs, \"shadcn@latest\", \"add\", ...components];\n const addArgs = [...retryArgs, \"--yes\"];\n\n try {\n await runSpawn(cmd, addArgs, projectDir);\n return undefined;\n } catch (error) {\n if (error instanceof SpawnSignalError) {\n throw error;\n }\n if (error instanceof SpawnExitError) {\n logger.warn(`shadcn exited with code ${error.code}.`);\n return { retryCommand: `${cmd} ${retryArgs.join(\" \")}` };\n }\n\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to install ${label}: ${message}`);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"create-project.js","names":["path","fs","parseJsonc"],"sources":["../../src/lib/create-project.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { downloadTemplate } from \"giget\";\nimport {\n parse as parseJsonc,\n printParseErrorCode,\n type ParseError,\n} from \"jsonc-parser\";\nimport { logger } from \"./utils/logger\";\nimport { runSpawn, SpawnExitError, SpawnSignalError } from \"./run-spawn\";\nimport { type PackageManagerName } from \"./utils/package-manager\";\nimport { readProjectFiles } from \"./utils/file-scanner\";\n\nexport function dlxCommand(pm: PackageManagerName): [string, string[]] {\n switch (pm) {\n case \"pnpm\":\n return [\"pnpm\", [\"dlx\"]];\n case \"yarn\":\n return [\"yarn\", [\"dlx\"]];\n case \"bun\":\n return [\"bunx\", []];\n case \"npm\":\n return [\"npx\", [\"--yes\"]];\n }\n}\n\nexport interface TransformOptions {\n hasLocalComponents: boolean;\n skipInstall?: boolean;\n packageManager: PackageManagerName;\n}\n\nexport type ProjectSource =\n | {\n kind: \"github\";\n ref: string | undefined;\n }\n | {\n kind: \"local\";\n rootDir: string;\n };\n\nconst LOCAL_PROJECT_ARTIFACT_DIRS: readonly string[] = [\n \"node_modules\",\n \".next\",\n \"dist\",\n \"build\",\n];\n\nconst LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES = LOCAL_PROJECT_ARTIFACT_DIRS.map(\n (dir) => `**/${dir}/**`,\n);\n\nexport function resolvePackageManager(opts: {\n useNpm?: boolean;\n usePnpm?: boolean;\n useYarn?: boolean;\n useBun?: boolean;\n}): PackageManagerName | undefined {\n if (opts.useNpm) return \"npm\";\n if (opts.usePnpm) return \"pnpm\";\n if (opts.useYarn) return \"yarn\";\n if (opts.useBun) return \"bun\";\n return undefined;\n}\n\nfunction resolveGitHubAuthToken(): string | undefined {\n const token =\n process.env.GIGET_AUTH ?? process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;\n const trimmed = token?.trim();\n return trimmed || undefined;\n}\n\nfunction toBearerAuthHeader(token: string): string {\n return token.toLowerCase().startsWith(\"bearer \") ? token : `Bearer ${token}`;\n}\n\nexport async function resolveLatestReleaseRef(): Promise<string | undefined> {\n try {\n const authToken = resolveGitHubAuthToken();\n const res = await fetch(\n \"https://api.github.com/repos/assistant-ui/assistant-ui/releases/latest\",\n authToken\n ? { headers: { Authorization: toBearerAuthHeader(authToken) } }\n : undefined,\n );\n if (!res.ok) return undefined;\n const release = (await res.json()) as { tag_name: string };\n return release.tag_name || undefined;\n } catch {\n return undefined;\n }\n}\n\nconst DOWNLOAD_TIMEOUT_MS = 30_000;\n\nexport async function downloadProject(\n repoPath: string,\n destDir: string,\n ref?: string,\n): Promise<void> {\n const source = ref\n ? `gh:assistant-ui/assistant-ui/${repoPath}#${ref}`\n : `gh:assistant-ui/assistant-ui/${repoPath}`;\n\n // Suppress giget's debug output. The `debug` package (used by the upgrade\n // command) sets process.env.DEBUG at module-load time, and giget logs to\n // console.debug whenever that env var is truthy — even for unrelated\n // namespaces. Temporarily unsetting it targets the root cause.\n const origDebug = process.env.DEBUG;\n delete process.env.DEBUG;\n try {\n const authToken = resolveGitHubAuthToken();\n const downloadPromise = downloadTemplate(source, {\n dir: destDir,\n force: true,\n silent: true,\n ...(authToken ? { auth: authToken } : {}),\n });\n\n let timer: ReturnType<typeof setTimeout>;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () =>\n reject(\n new Error(\n \"Download timed out. This may be due to GitHub rate limiting or a network issue. Try again in a few minutes.\",\n ),\n ),\n DOWNLOAD_TIMEOUT_MS,\n );\n });\n\n try {\n await Promise.race([downloadPromise, timeoutPromise]);\n } finally {\n clearTimeout(timer!);\n }\n } finally {\n if (origDebug !== undefined) {\n process.env.DEBUG = origDebug;\n }\n }\n}\n\nfunction shouldCopyLocalProjectPath(src: string, projectDir: string): boolean {\n const relative = path.relative(projectDir, src);\n if (!relative) return true;\n\n const segments = relative.split(path.sep);\n return !segments.some((segment) =>\n LOCAL_PROJECT_ARTIFACT_DIRS.includes(segment),\n );\n}\n\nexport async function scaffoldProject(\n repoPath: string,\n destDir: string,\n source: ProjectSource,\n): Promise<void> {\n if (source.kind === \"github\") {\n await downloadProject(repoPath, destDir, source.ref);\n return;\n }\n\n const localProjectDir = path.resolve(source.rootDir, repoPath);\n try {\n fs.cpSync(localProjectDir, destDir, {\n recursive: true,\n force: true,\n filter: (src) => shouldCopyLocalProjectPath(src, localProjectDir),\n });\n } catch (error) {\n const code =\n error instanceof Error\n ? (error as NodeJS.ErrnoException).code\n : undefined;\n if (code === \"ENOENT\") {\n throw new Error(\n `Local project source does not exist: ${localProjectDir}`,\n );\n }\n throw error;\n }\n}\n\nexport interface TransformResult {\n registryInstallFailure?: { retryCommand: string };\n}\n\nexport async function transformProject(\n projectDir: string,\n opts: TransformOptions,\n): Promise<TransformResult> {\n logger.step(\"Transforming package.json...\");\n transformPackageJson(projectDir);\n\n logger.step(\"Transforming project files...\");\n transformTsConfig(projectDir);\n transformCssFiles(projectDir);\n\n let assistantUI: string[] | undefined;\n let shadcnUI: string[] | undefined;\n\n if (!opts.hasLocalComponents) {\n const components = scanRequiredComponents(projectDir);\n assistantUI = components.assistantUI;\n shadcnUI = components.shadcnUI;\n }\n\n const pm = opts.packageManager;\n if (!opts.skipInstall) {\n logger.step(\"Installing dependencies...\");\n await installDependencies(projectDir, pm);\n }\n\n if (\n !opts.skipInstall &&\n !opts.hasLocalComponents &&\n shadcnUI &&\n assistantUI\n ) {\n const auiComponents = assistantUI.map((c) => `@assistant-ui/${c}`);\n const components = [\"@assistant-ui/utils\", ...shadcnUI, ...auiComponents];\n logger.step(`Installing components: ${components.join(\", \")}...`);\n const failure = await installShadcnRegistry(\n projectDir,\n components,\n \"components\",\n pm,\n );\n if (failure) return { registryInstallFailure: failure };\n await reconcileAssistantUIImportLayout(projectDir);\n }\n return {};\n}\n\nfunction transformPackageJson(projectDir: string): void {\n const pkgPath = path.join(projectDir, \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\"));\n\n // Remove @assistant-ui/ui dependency\n if (pkg.dependencies?.[\"@assistant-ui/ui\"]) {\n delete pkg.dependencies[\"@assistant-ui/ui\"];\n }\n\n // Transform workspace dependencies to latest\n for (const depType of [\"dependencies\", \"devDependencies\"] as const) {\n const deps = pkg[depType];\n if (!deps) continue;\n\n for (const [name, version] of Object.entries(deps)) {\n if (String(version).includes(\"workspace:\")) {\n deps[name] = \"latest\";\n }\n }\n }\n\n // Remove devDependencies that are workspace-only\n if (pkg.devDependencies?.[\"@assistant-ui/x-buildutils\"]) {\n delete pkg.devDependencies[\"@assistant-ui/x-buildutils\"];\n }\n\n // Update package name to be unique\n const dirName = path.basename(projectDir);\n pkg.name = dirName;\n\n fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n}\n\nfunction parseTsConfig(content: string): any {\n const errors: ParseError[] = [];\n const tsconfig = parseJsonc(content, errors, { allowTrailingComma: true });\n const error = errors[0];\n if (error) {\n throw new SyntaxError(\n `Invalid tsconfig.json: ${printParseErrorCode(error.error)} at offset ${error.offset}`,\n );\n }\n return tsconfig;\n}\n\nfunction transformTsConfig(projectDir: string): void {\n const tsconfigPath = path.join(projectDir, \"tsconfig.json\");\n\n if (!fs.existsSync(tsconfigPath)) {\n return;\n }\n\n const content = fs.readFileSync(tsconfigPath, \"utf-8\");\n const tsconfig = parseTsConfig(content);\n\n // Remove workspace paths\n if (tsconfig.compilerOptions?.paths) {\n const workspaceKeys = new Set([\n \"@/components/assistant-ui/*\",\n \"@/components/icons/*\",\n \"@/components/ui/*\",\n \"@/components/ui/radix/*\",\n \"@/hooks/*\",\n \"@/lib/utils\",\n \"@assistant-ui/ui/*\",\n ]);\n for (const [key, targets] of Object.entries(\n tsconfig.compilerOptions.paths as Record<string, unknown>,\n )) {\n const targetsWorkspace =\n Array.isArray(targets) &&\n targets.some(\n (target) =>\n typeof target === \"string\" &&\n (target.includes(\"packages/ui/\") || target.startsWith(\"../\")),\n );\n if (workspaceKeys.has(key) || targetsWorkspace) {\n delete tsconfig.compilerOptions.paths[key];\n }\n }\n\n if (Object.keys(tsconfig.compilerOptions.paths).length === 0) {\n delete tsconfig.compilerOptions.paths;\n }\n }\n\n // If extends uses @assistant-ui/x-buildutils, replace with inline config\n if (tsconfig.extends?.includes(\"@assistant-ui/x-buildutils\")) {\n const isNext = tsconfig.extends.includes(\"ts/next\");\n delete tsconfig.extends;\n\n const inlinedCompilerOptions = {\n target: \"ESNext\",\n lib: [\"dom\", \"dom.iterable\", \"ES2023\"],\n skipLibCheck: true,\n strict: true,\n noEmit: true,\n esModuleInterop: true,\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n resolveJsonModule: true,\n isolatedModules: true,\n jsx: \"react-jsx\",\n ...(isNext ? { plugins: [{ name: \"next\" }] } : {}),\n };\n\n tsconfig.compilerOptions = {\n ...inlinedCompilerOptions,\n ...tsconfig.compilerOptions,\n paths: {\n \"@/*\": [\"./*\"],\n ...(tsconfig.compilerOptions?.paths || {}),\n },\n };\n }\n\n fs.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\\n`);\n}\n\nfunction transformCssFiles(projectDir: string): void {\n for (const { fullPath, content } of readProjectFiles(\"**/*.css\", {\n cwd: projectDir,\n ignore: LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES,\n })) {\n try {\n const newContent = content.replace(\n /@source\\s+[\"'][^\"']*packages\\/ui\\/src[^\"']*[\"'];\\s*\\n?/g,\n \"\",\n );\n\n if (newContent !== content) {\n fs.writeFileSync(fullPath, newContent);\n }\n } catch {\n continue;\n }\n }\n}\n\ninterface RequiredComponents {\n assistantUI: string[];\n shadcnUI: string[];\n}\n\nfunction stripImportExtension(component: string): string {\n return component.replace(/\\.[cm]?[tj]sx?$/, \"\");\n}\n\nconst ASSISTANT_UI_OWNED_UI = new Set([\n \"accordion\",\n \"badge\",\n \"diff-viewer\",\n \"direction\",\n \"dot-matrix\",\n \"number-roll\",\n \"select\",\n \"tabs\",\n]);\n\nconst BARE_ELEMENT_ITEMS = new Set([\n \"file\",\n \"generative-ui\",\n \"heat-graph\",\n \"image\",\n \"logos\",\n \"markdown-text\",\n \"syntax-highlighter\",\n \"tooltip-icon-button\",\n]);\n\nfunction toAssistantUIItem(specifier: string): string | null {\n let name = stripImportExtension(specifier);\n const inElements = name.startsWith(\"elements/\");\n if (inElements) {\n name = name.slice(\"elements/\".length);\n } else if (name.includes(\"/\")) {\n return null;\n }\n if (name.endsWith(\".aui\")) {\n return name.slice(0, -\".aui\".length);\n }\n return inElements && !BARE_ELEMENT_ITEMS.has(name)\n ? `elements-${name}`\n : name;\n}\n\n/**\n * Example snapshots are downloaded at a release tag while the shadcn registry\n * is live, so a snapshot may import components at the legacy flat path\n * (`@/components/assistant-ui/<name>`) after the registry has moved the file\n * to `components/assistant-ui/elements/<name>.aui.tsx`. Resolve each legacy\n * specifier against the files the registry actually installed and rewrite it\n * only when the legacy path is absent and the elements layout has it.\n */\nexport async function reconcileAssistantUIImportLayout(\n projectDir: string,\n): Promise<void> {\n const componentRoots = [\"components\", \"src/components\"]\n .map((dir) => path.join(projectDir, dir, \"assistant-ui\"))\n .filter((dir) => fs.existsSync(dir));\n if (componentRoots.length === 0) return;\n\n const resolvesAtLegacyPath = (name: string) =>\n componentRoots.some((root) =>\n [\".tsx\", \".ts\", \"/index.tsx\", \"/index.ts\"].some((suffix) =>\n fs.existsSync(path.join(root, `${name}${suffix}`)),\n ),\n );\n\n // Index the installed tree by import name so the rewrite follows whatever\n // layout the registry delivered — some items install as\n // elements/<name>.aui.tsx, others as elements/<name>.tsx, and a future\n // layout move should not require new knowledge here.\n const installedByName = new Map<string, string>();\n for (const root of componentRoots) {\n for (const { file } of readProjectFiles(\"**/*.{ts,tsx}\", { cwd: root })) {\n const normalized = file.split(path.sep).join(\"/\");\n if (!normalized.includes(\"/\")) continue;\n const specifier = normalized.replace(/\\.[cm]?[tj]sx?$/, \"\");\n const name = path.posix.basename(specifier).replace(/\\.aui$/, \"\");\n // A flat legacy import maps to the registry's `<name>` item, which is\n // the `.aui` file; a colliding bare file with the same basename belongs\n // to the distinct `elements-<name>` item, so the `.aui` variant wins.\n const existing = installedByName.get(name);\n if (\n existing === undefined ||\n (!existing.endsWith(\".aui\") && specifier.endsWith(\".aui\"))\n ) {\n installedByName.set(name, specifier);\n }\n }\n }\n if (installedByName.size === 0) return;\n\n const { default: jscodeshift } = await import(\"jscodeshift\");\n const parsers = {\n ts: jscodeshift.withParser(\"ts\"),\n tsx: jscodeshift.withParser(\"tsx\"),\n };\n\n for (const { fullPath, content } of readProjectFiles(\"**/*.{ts,tsx}\", {\n cwd: projectDir,\n ignore: LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES,\n })) {\n if (!content.includes(\"@/components/assistant-ui/\")) continue;\n\n const replacements: Array<{ start: number; end: number; value: string }> =\n [];\n const collectReplacement = (source: {\n value?: unknown;\n start?: number | null;\n end?: number | null;\n }) => {\n if (\n typeof source.value !== \"string\" ||\n source.start == null ||\n source.end == null\n ) {\n return;\n }\n\n const prefix = \"@/components/assistant-ui/\";\n if (!source.value.startsWith(prefix)) return;\n const specifier = source.value.slice(prefix.length);\n if (specifier.includes(\"/\")) return;\n\n const name = stripImportExtension(specifier);\n const installed = installedByName.get(name);\n if (resolvesAtLegacyPath(name) || installed === undefined) return;\n\n const raw = content.slice(source.start, source.end);\n const quote = raw[0];\n if ((quote !== '\"' && quote !== \"'\") || raw.at(-1) !== quote) return;\n replacements.push({\n start: source.start,\n end: source.end,\n value: `${quote}@/components/assistant-ui/${installed}${quote}`,\n });\n };\n\n const j = fullPath.endsWith(\".tsx\") ? parsers.tsx : parsers.ts;\n let root;\n try {\n root = j(content);\n } catch {\n continue;\n }\n root\n .find(j.ImportDeclaration)\n .forEach(({ node }) => collectReplacement(node.source));\n root\n .find(j.ExportNamedDeclaration)\n .forEach(({ node }) => node.source && collectReplacement(node.source));\n root\n .find(j.ExportAllDeclaration)\n .forEach(({ node }) => collectReplacement(node.source));\n\n let next = content;\n for (const replacement of replacements.sort((a, b) => b.start - a.start)) {\n next =\n next.slice(0, replacement.start) +\n replacement.value +\n next.slice(replacement.end);\n }\n if (next !== content) fs.writeFileSync(fullPath, next);\n }\n}\n\nfunction scanRequiredComponents(projectDir: string): RequiredComponents {\n const assistantUIComponents = new Set<string>();\n const shadcnUIComponents = new Set<string>();\n\n for (const { content } of readProjectFiles(\"**/*.{ts,tsx}\", {\n cwd: projectDir,\n ignore: LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES,\n })) {\n const assistantUIRegex =\n /from\\s+[\"']@\\/components\\/assistant-ui\\/([^\"']+)[\"']/g;\n for (const match of content.matchAll(assistantUIRegex)) {\n const item = toAssistantUIItem(match[1]!);\n if (item) assistantUIComponents.add(item);\n }\n\n const uiRegex = /from\\s+[\"']@\\/components\\/ui\\/([^\"']+)[\"']/g;\n for (const match of content.matchAll(uiRegex)) {\n const name = stripImportExtension(match[1]!);\n if (ASSISTANT_UI_OWNED_UI.has(name)) {\n assistantUIComponents.add(name);\n } else {\n shadcnUIComponents.add(name);\n }\n }\n }\n\n return {\n assistantUI: Array.from(assistantUIComponents),\n shadcnUI: Array.from(shadcnUIComponents),\n };\n}\n\nasync function installDependencies(\n projectDir: string,\n pm: PackageManagerName,\n): Promise<void> {\n const args = pm === \"yarn\" ? [] : [\"install\"];\n try {\n await runSpawn(pm, args, projectDir);\n } catch (error) {\n if (error instanceof SpawnSignalError) {\n throw error;\n }\n if (error instanceof SpawnExitError) {\n throw new Error(`${pm} install exited with code ${error.code}`);\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to install dependencies: ${message}`);\n }\n}\n\nasync function installShadcnRegistry(\n projectDir: string,\n components: string[],\n label: string,\n pm: PackageManagerName,\n): Promise<{ retryCommand: string } | undefined> {\n const [cmd, dlxArgs] = dlxCommand(pm);\n // For npm, dlxArgs may already include `--yes` for npx auto-install.\n // The trailing `--yes` is for shadcn's own confirmation prompt.\n const retryArgs = [...dlxArgs, \"shadcn@latest\", \"add\", ...components];\n const addArgs = [...retryArgs, \"--yes\"];\n\n try {\n await runSpawn(cmd, addArgs, projectDir);\n return undefined;\n } catch (error) {\n if (error instanceof SpawnSignalError) {\n throw error;\n }\n if (error instanceof SpawnExitError) {\n logger.warn(`shadcn exited with code ${error.code}.`);\n return { retryCommand: `${cmd} ${retryArgs.join(\" \")}` };\n }\n\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to install ${label}: ${message}`);\n }\n}\n"],"mappings":"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"}
@@ -83,7 +83,7 @@ async function transform(codemod, source, transformOptions, options = { logStatu
83
83
  const command = buildCommand(codemodPath, targetFiles, transformOptions);
84
84
  const result = await runSpawnCapture(command[0], command.slice(1));
85
85
  if (result.signal !== null) throw new SpawnSignalError(result.signal, false);
86
- if (!options.onProgress && result.code !== 0) throw new SpawnExitError(result.code || 1, result.stderr);
86
+ if (result.code !== 0) throw new SpawnExitError(result.code || 1, result.stderr);
87
87
  const { stdout } = result;
88
88
  if (options.onProgress) {
89
89
  const processedFiles = (stdout.match(/Processing file/g) || []).length;
@@ -1 +1 @@
1
- {"version":3,"file":"transform.js","names":["globSync","fs"],"sources":["../../src/lib/transform.ts"],"sourcesContent":["import debug from \"debug\";\nimport path from \"node:path\";\nimport type { TransformOptions } from \"./transform-options\";\nimport { fileURLToPath } from \"node:url\";\nimport * as fs from \"node:fs\";\nimport { sync as globSync } from \"glob\";\nimport { runSpawnCapture, SpawnExitError, SpawnSignalError } from \"./run-spawn\";\n\nconst log = debug(\"codemod:transform\");\nconst error = debug(\"codemod:transform:error\");\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/**\n * Gets the list of files that need to be processed in the codebase\n * Only includes files that contain \"assistant-ui\" to optimize performance\n */\nexport function getRelevantFiles(cwd: string): string[] {\n const pattern = \"**/*.{js,jsx,ts,tsx}\";\n const files = globSync(pattern, {\n cwd,\n ignore: [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/*.min.js\",\n \"**/*.bundle.js\",\n ],\n });\n\n // Filter files to only include those containing \"assistant-ui\"\n const relevantFiles = files.filter((file) => {\n try {\n const content = fs.readFileSync(path.join(cwd, file), \"utf8\");\n return content.includes(\"assistant-ui\");\n } catch {\n return false;\n }\n });\n\n return relevantFiles.map((file) => path.join(cwd, file));\n}\n\n/**\n * Counts the number of files that need to be processed\n */\nexport function countFilesToProcess(cwd: string): number {\n return getRelevantFiles(cwd).length;\n}\n\nfunction buildCommand(\n codemodPath: string,\n targetFiles: string[],\n options: TransformOptions,\n): string[] {\n const command = [\n \"npx\",\n \"jscodeshift\",\n \"-t\",\n codemodPath,\n ...targetFiles,\n \"--parser\",\n \"tsx\",\n ];\n\n if (options.dry) {\n command.push(\"--dry\");\n }\n\n if (options.print) {\n command.push(\"--print\");\n }\n\n if (options.verbose) {\n command.push(\"--verbose\");\n }\n\n if (options.jscodeshift) {\n command.push(options.jscodeshift);\n }\n\n return command;\n}\n\nexport type TransformErrors = {\n transform: string;\n filename: string;\n summary: string;\n}[];\n\nfunction parseErrors(transform: string, output: string): TransformErrors {\n const errors: TransformErrors = [];\n const errorRegex = /ERR (.+) Transformation error/g;\n const syntaxErrorRegex = /SyntaxError: .+/g;\n\n for (const match of output.matchAll(errorRegex)) {\n const filename = match[1]!;\n const syntaxErrorMatch = syntaxErrorRegex.exec(output);\n if (syntaxErrorMatch) {\n const summary = syntaxErrorMatch[0];\n errors.push({ transform, filename, summary });\n }\n }\n\n return errors;\n}\n\nexport async function transform(\n codemod: string,\n source: string,\n transformOptions: TransformOptions,\n options: {\n logStatus: boolean;\n onProgress?: (processedFiles: number) => void;\n relevantFiles?: string[];\n } = { logStatus: true },\n): Promise<TransformErrors> {\n if (options.logStatus) {\n log(`Applying codemod '${codemod}': ${source}`);\n }\n const codemodPath = path.resolve(__dirname, `../codemods/${codemod}.js`);\n\n // Use pre-computed relevant files if provided, otherwise get them\n const targetFiles = options.relevantFiles || getRelevantFiles(source);\n\n if (targetFiles.length === 0) {\n log(`No relevant files found for codemod '${codemod}'`);\n return [];\n }\n\n log(`Found ${targetFiles.length} relevant files for codemod '${codemod}'`);\n\n const command = buildCommand(codemodPath, targetFiles, transformOptions);\n\n const result = await runSpawnCapture(command[0]!, command.slice(1));\n if (result.signal !== null) {\n throw new SpawnSignalError(result.signal, false);\n }\n if (!options.onProgress && result.code !== 0) {\n throw new SpawnExitError(result.code || 1, result.stderr);\n }\n\n const { stdout } = result;\n\n if (options.onProgress) {\n const processedFiles = (stdout.match(/Processing file/g) || []).length;\n options.onProgress(processedFiles);\n }\n\n const errors = parseErrors(codemod, stdout);\n if (options.logStatus && errors.length > 0) {\n errors.forEach(({ transform, filename, summary }) => {\n error(\n `Error applying codemod [codemod=${transform}, path=${filename}, summary=${summary}]`,\n );\n });\n }\n return errors;\n}\n"],"mappings":";;;;;;;AAQA,MAAM,MAAM,MAAM,mBAAmB;AACrC,MAAM,QAAQ,MAAM,yBAAyB;AAE7C,MAAM,aAAa,cAAc,YAAY,GAAG;AAChD,MAAM,YAAY,KAAK,QAAQ,UAAU;;;;;AAMzC,SAAgB,iBAAiB,KAAuB;CAuBtD,OArBcA,KAAS,wBAAS;EAC9B;EACA,QAAQ;GACN;GACA;GACA;GACA;GACA;EACF;CACF,CAG0B,CAAC,CAAC,QAAQ,SAAS;EAC3C,IAAI;GAEF,OADgBC,KAAG,aAAa,KAAK,KAAK,KAAK,IAAI,GAAG,MACzC,CAAC,CAAC,SAAS,cAAc;EACxC,QAAQ;GACN,OAAO;EACT;CACF,CAEmB,CAAC,CAAC,KAAK,SAAS,KAAK,KAAK,KAAK,IAAI,CAAC;AACzD;;;;AAKA,SAAgB,oBAAoB,KAAqB;CACvD,OAAO,iBAAiB,GAAG,CAAC,CAAC;AAC/B;AAEA,SAAS,aACP,aACA,aACA,SACU;CACV,MAAM,UAAU;EACd;EACA;EACA;EACA;EACA,GAAG;EACH;EACA;CACF;CAEA,IAAI,QAAQ,KACV,QAAQ,KAAK,OAAO;CAGtB,IAAI,QAAQ,OACV,QAAQ,KAAK,SAAS;CAGxB,IAAI,QAAQ,SACV,QAAQ,KAAK,WAAW;CAG1B,IAAI,QAAQ,aACV,QAAQ,KAAK,QAAQ,WAAW;CAGlC,OAAO;AACT;AAQA,SAAS,YAAY,WAAmB,QAAiC;CACvE,MAAM,SAA0B,CAAC;CACjC,MAAM,aAAa;CACnB,MAAM,mBAAmB;CAEzB,KAAK,MAAM,SAAS,OAAO,SAAS,UAAU,GAAG;EAC/C,MAAM,WAAW,MAAM;EACvB,MAAM,mBAAmB,iBAAiB,KAAK,MAAM;EACrD,IAAI,kBAAkB;GACpB,MAAM,UAAU,iBAAiB;GACjC,OAAO,KAAK;IAAE;IAAW;IAAU;GAAQ,CAAC;EAC9C;CACF;CAEA,OAAO;AACT;AAEA,eAAsB,UACpB,SACA,QACA,kBACA,UAII,EAAE,WAAW,KAAK,GACI;CAC1B,IAAI,QAAQ,WACV,IAAI,qBAAqB,QAAQ,KAAK,QAAQ;CAEhD,MAAM,cAAc,KAAK,QAAQ,WAAW,eAAe,QAAQ,IAAI;CAGvE,MAAM,cAAc,QAAQ,iBAAiB,iBAAiB,MAAM;CAEpE,IAAI,YAAY,WAAW,GAAG;EAC5B,IAAI,wCAAwC,QAAQ,EAAE;EACtD,OAAO,CAAC;CACV;CAEA,IAAI,SAAS,YAAY,OAAO,+BAA+B,QAAQ,EAAE;CAEzE,MAAM,UAAU,aAAa,aAAa,aAAa,gBAAgB;CAEvE,MAAM,SAAS,MAAM,gBAAgB,QAAQ,IAAK,QAAQ,MAAM,CAAC,CAAC;CAClE,IAAI,OAAO,WAAW,MACpB,MAAM,IAAI,iBAAiB,OAAO,QAAQ,KAAK;CAEjD,IAAI,CAAC,QAAQ,cAAc,OAAO,SAAS,GACzC,MAAM,IAAI,eAAe,OAAO,QAAQ,GAAG,OAAO,MAAM;CAG1D,MAAM,EAAE,WAAW;CAEnB,IAAI,QAAQ,YAAY;EACtB,MAAM,kBAAkB,OAAO,MAAM,kBAAkB,KAAK,CAAC,EAAA,CAAG;EAChE,QAAQ,WAAW,cAAc;CACnC;CAEA,MAAM,SAAS,YAAY,SAAS,MAAM;CAC1C,IAAI,QAAQ,aAAa,OAAO,SAAS,GACvC,OAAO,SAAS,EAAE,WAAW,UAAU,cAAc;EACnD,MACE,mCAAmC,UAAU,SAAS,SAAS,YAAY,QAAQ,EACrF;CACF,CAAC;CAEH,OAAO;AACT"}
1
+ {"version":3,"file":"transform.js","names":["globSync","fs"],"sources":["../../src/lib/transform.ts"],"sourcesContent":["import debug from \"debug\";\nimport path from \"node:path\";\nimport type { TransformOptions } from \"./transform-options\";\nimport { fileURLToPath } from \"node:url\";\nimport * as fs from \"node:fs\";\nimport { sync as globSync } from \"glob\";\nimport { runSpawnCapture, SpawnExitError, SpawnSignalError } from \"./run-spawn\";\n\nconst log = debug(\"codemod:transform\");\nconst error = debug(\"codemod:transform:error\");\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/**\n * Gets the list of files that need to be processed in the codebase\n * Only includes files that contain \"assistant-ui\" to optimize performance\n */\nexport function getRelevantFiles(cwd: string): string[] {\n const pattern = \"**/*.{js,jsx,ts,tsx}\";\n const files = globSync(pattern, {\n cwd,\n ignore: [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/*.min.js\",\n \"**/*.bundle.js\",\n ],\n });\n\n // Filter files to only include those containing \"assistant-ui\"\n const relevantFiles = files.filter((file) => {\n try {\n const content = fs.readFileSync(path.join(cwd, file), \"utf8\");\n return content.includes(\"assistant-ui\");\n } catch {\n return false;\n }\n });\n\n return relevantFiles.map((file) => path.join(cwd, file));\n}\n\n/**\n * Counts the number of files that need to be processed\n */\nexport function countFilesToProcess(cwd: string): number {\n return getRelevantFiles(cwd).length;\n}\n\nfunction buildCommand(\n codemodPath: string,\n targetFiles: string[],\n options: TransformOptions,\n): string[] {\n const command = [\n \"npx\",\n \"jscodeshift\",\n \"-t\",\n codemodPath,\n ...targetFiles,\n \"--parser\",\n \"tsx\",\n ];\n\n if (options.dry) {\n command.push(\"--dry\");\n }\n\n if (options.print) {\n command.push(\"--print\");\n }\n\n if (options.verbose) {\n command.push(\"--verbose\");\n }\n\n if (options.jscodeshift) {\n command.push(options.jscodeshift);\n }\n\n return command;\n}\n\nexport type TransformErrors = {\n transform: string;\n filename: string;\n summary: string;\n}[];\n\nfunction parseErrors(transform: string, output: string): TransformErrors {\n const errors: TransformErrors = [];\n const errorRegex = /ERR (.+) Transformation error/g;\n const syntaxErrorRegex = /SyntaxError: .+/g;\n\n for (const match of output.matchAll(errorRegex)) {\n const filename = match[1]!;\n const syntaxErrorMatch = syntaxErrorRegex.exec(output);\n if (syntaxErrorMatch) {\n const summary = syntaxErrorMatch[0];\n errors.push({ transform, filename, summary });\n }\n }\n\n return errors;\n}\n\nexport async function transform(\n codemod: string,\n source: string,\n transformOptions: TransformOptions,\n options: {\n logStatus: boolean;\n onProgress?: (processedFiles: number) => void;\n relevantFiles?: string[];\n } = { logStatus: true },\n): Promise<TransformErrors> {\n if (options.logStatus) {\n log(`Applying codemod '${codemod}': ${source}`);\n }\n const codemodPath = path.resolve(__dirname, `../codemods/${codemod}.js`);\n\n // Use pre-computed relevant files if provided, otherwise get them\n const targetFiles = options.relevantFiles || getRelevantFiles(source);\n\n if (targetFiles.length === 0) {\n log(`No relevant files found for codemod '${codemod}'`);\n return [];\n }\n\n log(`Found ${targetFiles.length} relevant files for codemod '${codemod}'`);\n\n const command = buildCommand(codemodPath, targetFiles, transformOptions);\n\n const result = await runSpawnCapture(command[0]!, command.slice(1));\n if (result.signal !== null) {\n throw new SpawnSignalError(result.signal, false);\n }\n if (result.code !== 0) {\n throw new SpawnExitError(result.code || 1, result.stderr);\n }\n\n const { stdout } = result;\n\n if (options.onProgress) {\n const processedFiles = (stdout.match(/Processing file/g) || []).length;\n options.onProgress(processedFiles);\n }\n\n const errors = parseErrors(codemod, stdout);\n if (options.logStatus && errors.length > 0) {\n errors.forEach(({ transform, filename, summary }) => {\n error(\n `Error applying codemod [codemod=${transform}, path=${filename}, summary=${summary}]`,\n );\n });\n }\n return errors;\n}\n"],"mappings":";;;;;;;AAQA,MAAM,MAAM,MAAM,mBAAmB;AACrC,MAAM,QAAQ,MAAM,yBAAyB;AAE7C,MAAM,aAAa,cAAc,YAAY,GAAG;AAChD,MAAM,YAAY,KAAK,QAAQ,UAAU;;;;;AAMzC,SAAgB,iBAAiB,KAAuB;CAuBtD,OArBcA,KAAS,wBAAS;EAC9B;EACA,QAAQ;GACN;GACA;GACA;GACA;GACA;EACF;CACF,CAG0B,CAAC,CAAC,QAAQ,SAAS;EAC3C,IAAI;GAEF,OADgBC,KAAG,aAAa,KAAK,KAAK,KAAK,IAAI,GAAG,MACzC,CAAC,CAAC,SAAS,cAAc;EACxC,QAAQ;GACN,OAAO;EACT;CACF,CAEmB,CAAC,CAAC,KAAK,SAAS,KAAK,KAAK,KAAK,IAAI,CAAC;AACzD;;;;AAKA,SAAgB,oBAAoB,KAAqB;CACvD,OAAO,iBAAiB,GAAG,CAAC,CAAC;AAC/B;AAEA,SAAS,aACP,aACA,aACA,SACU;CACV,MAAM,UAAU;EACd;EACA;EACA;EACA;EACA,GAAG;EACH;EACA;CACF;CAEA,IAAI,QAAQ,KACV,QAAQ,KAAK,OAAO;CAGtB,IAAI,QAAQ,OACV,QAAQ,KAAK,SAAS;CAGxB,IAAI,QAAQ,SACV,QAAQ,KAAK,WAAW;CAG1B,IAAI,QAAQ,aACV,QAAQ,KAAK,QAAQ,WAAW;CAGlC,OAAO;AACT;AAQA,SAAS,YAAY,WAAmB,QAAiC;CACvE,MAAM,SAA0B,CAAC;CACjC,MAAM,aAAa;CACnB,MAAM,mBAAmB;CAEzB,KAAK,MAAM,SAAS,OAAO,SAAS,UAAU,GAAG;EAC/C,MAAM,WAAW,MAAM;EACvB,MAAM,mBAAmB,iBAAiB,KAAK,MAAM;EACrD,IAAI,kBAAkB;GACpB,MAAM,UAAU,iBAAiB;GACjC,OAAO,KAAK;IAAE;IAAW;IAAU;GAAQ,CAAC;EAC9C;CACF;CAEA,OAAO;AACT;AAEA,eAAsB,UACpB,SACA,QACA,kBACA,UAII,EAAE,WAAW,KAAK,GACI;CAC1B,IAAI,QAAQ,WACV,IAAI,qBAAqB,QAAQ,KAAK,QAAQ;CAEhD,MAAM,cAAc,KAAK,QAAQ,WAAW,eAAe,QAAQ,IAAI;CAGvE,MAAM,cAAc,QAAQ,iBAAiB,iBAAiB,MAAM;CAEpE,IAAI,YAAY,WAAW,GAAG;EAC5B,IAAI,wCAAwC,QAAQ,EAAE;EACtD,OAAO,CAAC;CACV;CAEA,IAAI,SAAS,YAAY,OAAO,+BAA+B,QAAQ,EAAE;CAEzE,MAAM,UAAU,aAAa,aAAa,aAAa,gBAAgB;CAEvE,MAAM,SAAS,MAAM,gBAAgB,QAAQ,IAAK,QAAQ,MAAM,CAAC,CAAC;CAClE,IAAI,OAAO,WAAW,MACpB,MAAM,IAAI,iBAAiB,OAAO,QAAQ,KAAK;CAEjD,IAAI,OAAO,SAAS,GAClB,MAAM,IAAI,eAAe,OAAO,QAAQ,GAAG,OAAO,MAAM;CAG1D,MAAM,EAAE,WAAW;CAEnB,IAAI,QAAQ,YAAY;EACtB,MAAM,kBAAkB,OAAO,MAAM,kBAAkB,KAAK,CAAC,EAAA,CAAG;EAChE,QAAQ,WAAW,cAAc;CACnC;CAEA,MAAM,SAAS,YAAY,SAAS,MAAM;CAC1C,IAAI,QAAQ,aAAa,OAAO,SAAS,GACvC,OAAO,SAAS,EAAE,WAAW,UAAU,cAAc;EACnD,MACE,mCAAmC,UAAU,SAAS,SAAS,YAAY,QAAQ,EACrF;CACF,CAAC;CAEH,OAAO;AACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assistant-ui",
3
- "version": "0.0.113",
3
+ "version": "0.0.114",
4
4
  "description": "CLI for assistant-ui",
5
5
  "keywords": [
6
6
  "cli",
@@ -26,7 +26,7 @@
26
26
  ],
27
27
  "sideEffects": false,
28
28
  "dependencies": {
29
- "@assistant-ui/agent-launcher": "0.1.13",
29
+ "@assistant-ui/agent-launcher": "^0.1.14",
30
30
  "@clack/prompts": "^1.7.0",
31
31
  "chalk": "^6.0.0",
32
32
  "cli-progress": "^3.12.0",
@@ -41,12 +41,12 @@
41
41
  "semver": "^7.8.5"
42
42
  },
43
43
  "devDependencies": {
44
- "@assistant-ui/x-buildutils": "0.0.24",
44
+ "@assistant-ui/x-buildutils": "0.0.25",
45
45
  "@types/cli-progress": "^3.11.6",
46
46
  "@types/cross-spawn": "^6.0.6",
47
47
  "@types/debug": "^4.1.13",
48
48
  "@types/jscodeshift": "^17.3.0",
49
- "@types/node": "^26.2.0",
49
+ "@types/node": "^26.4.0",
50
50
  "@types/semver": "^7.8.0",
51
51
  "@vitest/coverage-v8": "^4.1.11",
52
52
  "vitest": "^4.1.11"
@@ -99,7 +99,7 @@ Create the assistant component:
99
99
 
100
100
  import { AssistantRuntimeProvider } from "@assistant-ui/react";
101
101
  import { useChatRuntime } from "@assistant-ui/ai-sdk";
102
- import { Thread } from "@/components/assistant-ui/thread";
102
+ import { Thread } from "@/components/assistant-ui/elements/thread.aui";
103
103
 
104
104
  export const Assistant = () => {
105
105
  const runtime = useChatRuntime({
@@ -14,12 +14,15 @@ type MCPTarget =
14
14
  | "claude-code"
15
15
  | "claude-desktop";
16
16
 
17
+ const HOSTED_MCP_URL = "https://www.assistant-ui.com/mcp";
18
+
17
19
  const MCP_CONFIGS: Record<
18
20
  Exclude<MCPTarget, "claude-code">,
19
21
  {
20
22
  name: string;
21
23
  getPath: () => string;
22
24
  config: object;
25
+ replaceServerKey?: string;
23
26
  postInstall?: string;
24
27
  }
25
28
  > = {
@@ -29,11 +32,11 @@ const MCP_CONFIGS: Record<
29
32
  config: {
30
33
  mcpServers: {
31
34
  "assistant-ui": {
32
- command: "npx",
33
- args: ["-y", "@assistant-ui/mcp-docs-server"],
35
+ url: HOSTED_MCP_URL,
34
36
  },
35
37
  },
36
38
  },
39
+ replaceServerKey: "mcpServers",
37
40
  postInstall:
38
41
  "Open Cursor Settings → MCP → find 'assistant-ui' and click enable.",
39
42
  },
@@ -44,11 +47,11 @@ const MCP_CONFIGS: Record<
44
47
  config: {
45
48
  mcpServers: {
46
49
  "assistant-ui": {
47
- command: "npx",
48
- args: ["-y", "@assistant-ui/mcp-docs-server"],
50
+ serverUrl: HOSTED_MCP_URL,
49
51
  },
50
52
  },
51
53
  },
54
+ replaceServerKey: "mcpServers",
52
55
  postInstall: "Fully quit and re-open Windsurf to activate.",
53
56
  },
54
57
  vscode: {
@@ -57,12 +60,12 @@ const MCP_CONFIGS: Record<
57
60
  config: {
58
61
  servers: {
59
62
  "assistant-ui": {
60
- command: "npx",
61
- args: ["-y", "@assistant-ui/mcp-docs-server"],
62
- type: "stdio",
63
+ type: "http",
64
+ url: HOSTED_MCP_URL,
63
65
  },
64
66
  },
65
67
  },
68
+ replaceServerKey: "servers",
66
69
  postInstall:
67
70
  "Enable MCP in Settings → search 'MCP' → enable 'Chat > MCP'. Use Copilot Chat in Agent mode.",
68
71
  },
@@ -153,14 +156,18 @@ async function installForTarget(target: MCPTarget): Promise<void> {
153
156
  logger.break();
154
157
 
155
158
  try {
159
+ await runSpawn("claude", ["mcp", "remove", "assistant-ui"]).catch(
160
+ (error: unknown) => {
161
+ if (error instanceof SpawnSignalError) throw error;
162
+ },
163
+ );
156
164
  await runSpawn("claude", [
157
165
  "mcp",
158
166
  "add",
167
+ "--transport",
168
+ "http",
159
169
  "assistant-ui",
160
- "--",
161
- "npx",
162
- "-y",
163
- "@assistant-ui/mcp-docs-server",
170
+ HOSTED_MCP_URL,
164
171
  ]);
165
172
  } catch (error) {
166
173
  if (error instanceof SpawnSignalError) throw error;
@@ -177,6 +184,9 @@ async function installForTarget(target: MCPTarget): Promise<void> {
177
184
 
178
185
  logger.break();
179
186
  logger.success("MCP server installed for Claude Code!");
187
+ logger.info(
188
+ `Connects to the hosted assistant-ui MCP server at ${HOSTED_MCP_URL}.`,
189
+ );
180
190
  logger.info(
181
191
  "The server starts automatically. Try asking about assistant-ui!",
182
192
  );
@@ -220,12 +230,27 @@ async function installForTarget(target: MCPTarget): Promise<void> {
220
230
 
221
231
  const newConfig = deepMerge(existingConfig, targetConfig.config);
222
232
 
233
+ if (targetConfig.replaceServerKey) {
234
+ const key = targetConfig.replaceServerKey;
235
+ newConfig[key] = {
236
+ ...newConfig[key],
237
+ "assistant-ui": (targetConfig.config as any)[key]["assistant-ui"],
238
+ };
239
+ }
240
+
223
241
  fs.writeFileSync(configPath, `${JSON.stringify(newConfig, null, 2)}\n`);
224
242
 
225
243
  logger.break();
226
244
  logger.success(`MCP server installed for ${targetConfig.name}!`);
227
245
  logger.info(`Config written to: ${configPath}`);
228
246
 
247
+ if (targetConfig.replaceServerKey) {
248
+ logger.break();
249
+ logger.info(
250
+ `Connects to the hosted assistant-ui MCP server at ${HOSTED_MCP_URL}.`,
251
+ );
252
+ }
253
+
229
254
  if (targetConfig.postInstall) {
230
255
  logger.break();
231
256
  logger.info(targetConfig.postInstall);
@@ -234,7 +259,7 @@ async function installForTarget(target: MCPTarget): Promise<void> {
234
259
 
235
260
  export const mcp = new Command()
236
261
  .name("mcp")
237
- .description("install assistant-ui MCP docs server for your IDE")
262
+ .description("connect your IDE to the assistant-ui MCP server")
238
263
  .option("--cursor", "install for Cursor")
239
264
  .option("--windsurf", "install for Windsurf")
240
265
  .option("--vscode", "install for VSCode")
@@ -0,0 +1,175 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { reconcileAssistantUIImportLayout } from "./create-project";
6
+
7
+ describe("reconcileAssistantUIImportLayout", () => {
8
+ let projectDir: string;
9
+
10
+ beforeEach(() => {
11
+ projectDir = fs.mkdtempSync(path.join(os.tmpdir(), "aui-cli-test-"));
12
+ });
13
+
14
+ afterEach(() => {
15
+ fs.rmSync(projectDir, { recursive: true, force: true });
16
+ });
17
+
18
+ const write = (file: string, content: string) => {
19
+ const fullPath = path.join(projectDir, file);
20
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
21
+ fs.writeFileSync(fullPath, content);
22
+ };
23
+
24
+ const read = (file: string) =>
25
+ fs.readFileSync(path.join(projectDir, file), "utf8");
26
+
27
+ it("rewrites a legacy import when only the elements layout exists", async () => {
28
+ write(
29
+ "app/page.tsx",
30
+ 'import { Thread } from "@/components/assistant-ui/thread";\n' +
31
+ 'import { ThreadList } from "@/components/assistant-ui/thread-list";\n',
32
+ );
33
+ write("components/assistant-ui/elements/thread.aui.tsx", "export {};");
34
+ write("components/assistant-ui/elements/thread-list.aui.tsx", "export {};");
35
+
36
+ await reconcileAssistantUIImportLayout(projectDir);
37
+
38
+ expect(read("app/page.tsx")).toBe(
39
+ 'import { Thread } from "@/components/assistant-ui/elements/thread.aui";\n' +
40
+ 'import { ThreadList } from "@/components/assistant-ui/elements/thread-list.aui";\n',
41
+ );
42
+ });
43
+
44
+ it("rewrites module declarations without changing import-like source text", async () => {
45
+ write(
46
+ "app/page.tsx",
47
+ 'import { Thread } from "@/components/assistant-ui/thread";\n' +
48
+ 'export { ThreadList } from "@/components/assistant-ui/thread-list";\n' +
49
+ "const example = 'from \"@/components/assistant-ui/thread\"';\n" +
50
+ '// from "@/components/assistant-ui/thread-list"\n',
51
+ );
52
+ write("components/assistant-ui/elements/thread.aui.tsx", "export {};");
53
+ write("components/assistant-ui/elements/thread-list.aui.tsx", "export {};");
54
+
55
+ await reconcileAssistantUIImportLayout(projectDir);
56
+
57
+ expect(read("app/page.tsx")).toBe(
58
+ 'import { Thread } from "@/components/assistant-ui/elements/thread.aui";\n' +
59
+ 'export { ThreadList } from "@/components/assistant-ui/elements/thread-list.aui";\n' +
60
+ "const example = 'from \"@/components/assistant-ui/thread\"';\n" +
61
+ '// from "@/components/assistant-ui/thread-list"\n',
62
+ );
63
+ });
64
+
65
+ it("rewrites a legacy import to a bare elements file without the .aui segment", async () => {
66
+ write(
67
+ "app/MyThread.tsx",
68
+ 'import { MarkdownText } from "@/components/assistant-ui/markdown-text";\n' +
69
+ 'import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";\n',
70
+ );
71
+ write("components/assistant-ui/elements/markdown-text.tsx", "export {};");
72
+ write(
73
+ "components/assistant-ui/elements/tooltip-icon-button.tsx",
74
+ "export {};",
75
+ );
76
+
77
+ await reconcileAssistantUIImportLayout(projectDir);
78
+
79
+ expect(read("app/MyThread.tsx")).toBe(
80
+ 'import { MarkdownText } from "@/components/assistant-ui/elements/markdown-text";\n' +
81
+ 'import { TooltipIconButton } from "@/components/assistant-ui/elements/tooltip-icon-button";\n',
82
+ );
83
+ });
84
+
85
+ it("prefers the .aui variant when both variants share a basename", async () => {
86
+ write(
87
+ "app/page.tsx",
88
+ 'import { Reasoning } from "@/components/assistant-ui/reasoning";\n',
89
+ );
90
+ write("components/assistant-ui/elements/reasoning.tsx", "export {};");
91
+ write("components/assistant-ui/elements/reasoning.aui.tsx", "export {};");
92
+
93
+ await reconcileAssistantUIImportLayout(projectDir);
94
+
95
+ expect(read("app/page.tsx")).toBe(
96
+ 'import { Reasoning } from "@/components/assistant-ui/elements/reasoning.aui";\n',
97
+ );
98
+ });
99
+
100
+ it("supports the src/ project layout", async () => {
101
+ write(
102
+ "src/routes/index.tsx",
103
+ 'import { Thread } from "@/components/assistant-ui/thread";\n',
104
+ );
105
+ write("src/components/assistant-ui/elements/thread.aui.tsx", "export {};");
106
+
107
+ await reconcileAssistantUIImportLayout(projectDir);
108
+
109
+ expect(read("src/routes/index.tsx")).toBe(
110
+ 'import { Thread } from "@/components/assistant-ui/elements/thread.aui";\n',
111
+ );
112
+ });
113
+
114
+ it("leaves imports alone when the legacy file exists", async () => {
115
+ const source =
116
+ 'import { Thread } from "@/components/assistant-ui/thread";\n';
117
+ write("app/page.tsx", source);
118
+ write("components/assistant-ui/thread.tsx", "export {};");
119
+ write("components/assistant-ui/elements/thread.aui.tsx", "export {};");
120
+
121
+ await reconcileAssistantUIImportLayout(projectDir);
122
+
123
+ expect(read("app/page.tsx")).toBe(source);
124
+ });
125
+
126
+ it("leaves imports alone when neither layout has the file", async () => {
127
+ const source =
128
+ 'import { Custom } from "@/components/assistant-ui/custom-part";\n';
129
+ write("app/page.tsx", source);
130
+ write("components/assistant-ui/elements/thread.aui.tsx", "export {};");
131
+
132
+ await reconcileAssistantUIImportLayout(projectDir);
133
+
134
+ expect(read("app/page.tsx")).toBe(source);
135
+ });
136
+
137
+ it("leaves elements imports untouched", async () => {
138
+ const source =
139
+ 'import { Thread } from "@/components/assistant-ui/elements/thread.aui";\n';
140
+ write("app/page.tsx", source);
141
+ write("components/assistant-ui/elements/thread.aui.tsx", "export {};");
142
+
143
+ await reconcileAssistantUIImportLayout(projectDir);
144
+
145
+ expect(read("app/page.tsx")).toBe(source);
146
+ });
147
+
148
+ it("parses TypeScript files with generic arrow functions", async () => {
149
+ write(
150
+ "app/helpers.ts",
151
+ 'import { Thread } from "@/components/assistant-ui/thread";\n' +
152
+ "export const identity = <T>(value: T) => value;\n",
153
+ );
154
+ write("components/assistant-ui/elements/thread.aui.tsx", "export {};");
155
+
156
+ await reconcileAssistantUIImportLayout(projectDir);
157
+
158
+ expect(read("app/helpers.ts")).toBe(
159
+ 'import { Thread } from "@/components/assistant-ui/elements/thread.aui";\n' +
160
+ "export const identity = <T>(value: T) => value;\n",
161
+ );
162
+ });
163
+
164
+ it("leaves unparseable source files untouched", async () => {
165
+ const source =
166
+ 'import { Thread } from "@/components/assistant-ui/thread";\nconst broken = ;\n';
167
+ write("app/broken.ts", source);
168
+ write("components/assistant-ui/elements/thread.aui.tsx", "export {};");
169
+
170
+ await expect(
171
+ reconcileAssistantUIImportLayout(projectDir),
172
+ ).resolves.toBeUndefined();
173
+ expect(read("app/broken.ts")).toBe(source);
174
+ });
175
+ });
@@ -220,11 +220,8 @@ export async function transformProject(
220
220
  shadcnUI &&
221
221
  assistantUI
222
222
  ) {
223
- const allShadcn = shadcnUI.includes("utils")
224
- ? shadcnUI
225
- : [...shadcnUI, "utils"];
226
223
  const auiComponents = assistantUI.map((c) => `@assistant-ui/${c}`);
227
- const components = [...allShadcn, ...auiComponents];
224
+ const components = ["@assistant-ui/utils", ...shadcnUI, ...auiComponents];
228
225
  logger.step(`Installing components: ${components.join(", ")}...`);
229
226
  const failure = await installShadcnRegistry(
230
227
  projectDir,
@@ -233,6 +230,7 @@ export async function transformProject(
233
230
  pm,
234
231
  );
235
232
  if (failure) return { registryInstallFailure: failure };
233
+ await reconcileAssistantUIImportLayout(projectDir);
236
234
  }
237
235
  return {};
238
236
  }
@@ -385,6 +383,166 @@ function stripImportExtension(component: string): string {
385
383
  return component.replace(/\.[cm]?[tj]sx?$/, "");
386
384
  }
387
385
 
386
+ const ASSISTANT_UI_OWNED_UI = new Set([
387
+ "accordion",
388
+ "badge",
389
+ "diff-viewer",
390
+ "direction",
391
+ "dot-matrix",
392
+ "number-roll",
393
+ "select",
394
+ "tabs",
395
+ ]);
396
+
397
+ const BARE_ELEMENT_ITEMS = new Set([
398
+ "file",
399
+ "generative-ui",
400
+ "heat-graph",
401
+ "image",
402
+ "logos",
403
+ "markdown-text",
404
+ "syntax-highlighter",
405
+ "tooltip-icon-button",
406
+ ]);
407
+
408
+ function toAssistantUIItem(specifier: string): string | null {
409
+ let name = stripImportExtension(specifier);
410
+ const inElements = name.startsWith("elements/");
411
+ if (inElements) {
412
+ name = name.slice("elements/".length);
413
+ } else if (name.includes("/")) {
414
+ return null;
415
+ }
416
+ if (name.endsWith(".aui")) {
417
+ return name.slice(0, -".aui".length);
418
+ }
419
+ return inElements && !BARE_ELEMENT_ITEMS.has(name)
420
+ ? `elements-${name}`
421
+ : name;
422
+ }
423
+
424
+ /**
425
+ * Example snapshots are downloaded at a release tag while the shadcn registry
426
+ * is live, so a snapshot may import components at the legacy flat path
427
+ * (`@/components/assistant-ui/<name>`) after the registry has moved the file
428
+ * to `components/assistant-ui/elements/<name>.aui.tsx`. Resolve each legacy
429
+ * specifier against the files the registry actually installed and rewrite it
430
+ * only when the legacy path is absent and the elements layout has it.
431
+ */
432
+ export async function reconcileAssistantUIImportLayout(
433
+ projectDir: string,
434
+ ): Promise<void> {
435
+ const componentRoots = ["components", "src/components"]
436
+ .map((dir) => path.join(projectDir, dir, "assistant-ui"))
437
+ .filter((dir) => fs.existsSync(dir));
438
+ if (componentRoots.length === 0) return;
439
+
440
+ const resolvesAtLegacyPath = (name: string) =>
441
+ componentRoots.some((root) =>
442
+ [".tsx", ".ts", "/index.tsx", "/index.ts"].some((suffix) =>
443
+ fs.existsSync(path.join(root, `${name}${suffix}`)),
444
+ ),
445
+ );
446
+
447
+ // Index the installed tree by import name so the rewrite follows whatever
448
+ // layout the registry delivered — some items install as
449
+ // elements/<name>.aui.tsx, others as elements/<name>.tsx, and a future
450
+ // layout move should not require new knowledge here.
451
+ const installedByName = new Map<string, string>();
452
+ for (const root of componentRoots) {
453
+ for (const { file } of readProjectFiles("**/*.{ts,tsx}", { cwd: root })) {
454
+ const normalized = file.split(path.sep).join("/");
455
+ if (!normalized.includes("/")) continue;
456
+ const specifier = normalized.replace(/\.[cm]?[tj]sx?$/, "");
457
+ const name = path.posix.basename(specifier).replace(/\.aui$/, "");
458
+ // A flat legacy import maps to the registry's `<name>` item, which is
459
+ // the `.aui` file; a colliding bare file with the same basename belongs
460
+ // to the distinct `elements-<name>` item, so the `.aui` variant wins.
461
+ const existing = installedByName.get(name);
462
+ if (
463
+ existing === undefined ||
464
+ (!existing.endsWith(".aui") && specifier.endsWith(".aui"))
465
+ ) {
466
+ installedByName.set(name, specifier);
467
+ }
468
+ }
469
+ }
470
+ if (installedByName.size === 0) return;
471
+
472
+ const { default: jscodeshift } = await import("jscodeshift");
473
+ const parsers = {
474
+ ts: jscodeshift.withParser("ts"),
475
+ tsx: jscodeshift.withParser("tsx"),
476
+ };
477
+
478
+ for (const { fullPath, content } of readProjectFiles("**/*.{ts,tsx}", {
479
+ cwd: projectDir,
480
+ ignore: LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES,
481
+ })) {
482
+ if (!content.includes("@/components/assistant-ui/")) continue;
483
+
484
+ const replacements: Array<{ start: number; end: number; value: string }> =
485
+ [];
486
+ const collectReplacement = (source: {
487
+ value?: unknown;
488
+ start?: number | null;
489
+ end?: number | null;
490
+ }) => {
491
+ if (
492
+ typeof source.value !== "string" ||
493
+ source.start == null ||
494
+ source.end == null
495
+ ) {
496
+ return;
497
+ }
498
+
499
+ const prefix = "@/components/assistant-ui/";
500
+ if (!source.value.startsWith(prefix)) return;
501
+ const specifier = source.value.slice(prefix.length);
502
+ if (specifier.includes("/")) return;
503
+
504
+ const name = stripImportExtension(specifier);
505
+ const installed = installedByName.get(name);
506
+ if (resolvesAtLegacyPath(name) || installed === undefined) return;
507
+
508
+ const raw = content.slice(source.start, source.end);
509
+ const quote = raw[0];
510
+ if ((quote !== '"' && quote !== "'") || raw.at(-1) !== quote) return;
511
+ replacements.push({
512
+ start: source.start,
513
+ end: source.end,
514
+ value: `${quote}@/components/assistant-ui/${installed}${quote}`,
515
+ });
516
+ };
517
+
518
+ const j = fullPath.endsWith(".tsx") ? parsers.tsx : parsers.ts;
519
+ let root;
520
+ try {
521
+ root = j(content);
522
+ } catch {
523
+ continue;
524
+ }
525
+ root
526
+ .find(j.ImportDeclaration)
527
+ .forEach(({ node }) => collectReplacement(node.source));
528
+ root
529
+ .find(j.ExportNamedDeclaration)
530
+ .forEach(({ node }) => node.source && collectReplacement(node.source));
531
+ root
532
+ .find(j.ExportAllDeclaration)
533
+ .forEach(({ node }) => collectReplacement(node.source));
534
+
535
+ let next = content;
536
+ for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
537
+ next =
538
+ next.slice(0, replacement.start) +
539
+ replacement.value +
540
+ next.slice(replacement.end);
541
+ }
542
+ if (next !== content) fs.writeFileSync(fullPath, next);
543
+ }
544
+ }
545
+
388
546
  function scanRequiredComponents(projectDir: string): RequiredComponents {
389
547
  const assistantUIComponents = new Set<string>();
390
548
  const shadcnUIComponents = new Set<string>();
@@ -396,12 +554,18 @@ function scanRequiredComponents(projectDir: string): RequiredComponents {
396
554
  const assistantUIRegex =
397
555
  /from\s+["']@\/components\/assistant-ui\/([^"']+)["']/g;
398
556
  for (const match of content.matchAll(assistantUIRegex)) {
399
- assistantUIComponents.add(stripImportExtension(match[1]!));
557
+ const item = toAssistantUIItem(match[1]!);
558
+ if (item) assistantUIComponents.add(item);
400
559
  }
401
560
 
402
561
  const uiRegex = /from\s+["']@\/components\/ui\/([^"']+)["']/g;
403
562
  for (const match of content.matchAll(uiRegex)) {
404
- shadcnUIComponents.add(stripImportExtension(match[1]!));
563
+ const name = stripImportExtension(match[1]!);
564
+ if (ASSISTANT_UI_OWNED_UI.has(name)) {
565
+ assistantUIComponents.add(name);
566
+ } else {
567
+ shadcnUIComponents.add(name);
568
+ }
405
569
  }
406
570
  }
407
571
 
@@ -41,6 +41,31 @@ describe("transform", () => {
41
41
  );
42
42
  });
43
43
 
44
+ it("fails a progress-enabled codemod that exits nonzero", async () => {
45
+ mocks.runSpawnCapture.mockResolvedValue({
46
+ code: 7,
47
+ signal: null,
48
+ stdout: "Processing file app.tsx\n",
49
+ stderr: "SyntaxError: Broken input\n",
50
+ });
51
+ const onProgress = vi.fn();
52
+
53
+ const failure = transform(
54
+ "v0-8/ui-package-split",
55
+ "/tmp/project",
56
+ { dry: true },
57
+ {
58
+ logStatus: false,
59
+ onProgress,
60
+ relevantFiles: ["/tmp/project/app.tsx"],
61
+ },
62
+ );
63
+
64
+ await expect(failure).rejects.toBeInstanceOf(SpawnExitError);
65
+ await expect(failure).rejects.toThrow("SyntaxError: Broken input");
66
+ expect(onProgress).not.toHaveBeenCalled();
67
+ });
68
+
44
69
  it("surfaces the codemod's stderr when it exits nonzero", async () => {
45
70
  mocks.runSpawnCapture.mockResolvedValue({
46
71
  code: 1,
@@ -137,7 +137,7 @@ export async function transform(
137
137
  if (result.signal !== null) {
138
138
  throw new SpawnSignalError(result.signal, false);
139
139
  }
140
- if (!options.onProgress && result.code !== 0) {
140
+ if (result.code !== 0) {
141
141
  throw new SpawnExitError(result.code || 1, result.stderr);
142
142
  }
143
143