assistant-ui 0.0.114 → 0.0.116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/codemods/v0-12/assistant-api-to-aui.js +1 -0
  2. package/dist/codemods/v0-12/assistant-api-to-aui.js.map +1 -1
  3. package/dist/codemods/v0-12/primitive-if-to-aui-if.d.ts.map +1 -1
  4. package/dist/codemods/v0-12/primitive-if-to-aui-if.js +7 -5
  5. package/dist/codemods/v0-12/primitive-if-to-aui-if.js.map +1 -1
  6. package/dist/commands/add.d.ts +1 -1
  7. package/dist/commands/add.d.ts.map +1 -1
  8. package/dist/commands/add.js +8 -7
  9. package/dist/commands/add.js.map +1 -1
  10. package/dist/commands/create.d.ts +9 -1
  11. package/dist/commands/create.d.ts.map +1 -1
  12. package/dist/commands/create.js +25 -8
  13. package/dist/commands/create.js.map +1 -1
  14. package/dist/commands/info.d.ts.map +1 -1
  15. package/dist/commands/info.js +0 -1
  16. package/dist/commands/info.js.map +1 -1
  17. package/dist/commands/init.js +2 -2
  18. package/dist/commands/init.js.map +1 -1
  19. package/dist/commands/mcp.d.ts.map +1 -1
  20. package/dist/commands/mcp.js +0 -1
  21. package/dist/commands/mcp.js.map +1 -1
  22. package/dist/lib/create-project.d.ts +7 -1
  23. package/dist/lib/create-project.d.ts.map +1 -1
  24. package/dist/lib/create-project.js +27 -18
  25. package/dist/lib/create-project.js.map +1 -1
  26. package/dist/lib/utils/registry.d.ts +4 -2
  27. package/dist/lib/utils/registry.d.ts.map +1 -1
  28. package/dist/lib/utils/registry.js +13 -2
  29. package/dist/lib/utils/registry.js.map +1 -1
  30. package/package.json +6 -6
  31. package/src/codemods/v0-12/__tests__/primitive-if-to-aui-if.test.ts +48 -1
  32. package/src/codemods/v0-12/assistant-api-to-aui.ts +4 -1
  33. package/src/codemods/v0-12/primitive-if-to-aui-if.ts +18 -7
  34. package/src/commands/add.ts +10 -6
  35. package/src/commands/create.ts +47 -8
  36. package/src/commands/info.ts +0 -1
  37. package/src/commands/init.ts +1 -1
  38. package/src/commands/mcp.ts +0 -3
  39. package/src/lib/create-project.ts +35 -20
  40. package/src/lib/run-spawn.test.ts +0 -1
  41. package/src/lib/utils/registry.test.ts +56 -0
  42. package/src/lib/utils/registry.ts +35 -0
  43. package/src/run.test.ts +1 -5
@@ -52,6 +52,7 @@ const migrateAssistantApiToAui = createTransformer(({ j, root, markAsChanged })
52
52
  if (j.ExportNamedDeclaration.check(statement) || j.ExportDefaultDeclaration.check(statement)) return declaredApi(statement.declaration);
53
53
  if (j.VariableDeclaration.check(statement)) {
54
54
  for (const declarator of statement.declarations) {
55
+ if (!j.VariableDeclarator.check(declarator)) continue;
55
56
  if (j.Identifier.check(declarator.id) && declarator.id.name === "api") return declarator.id;
56
57
  if (patternBindsApi(declarator.id)) return "foreign";
57
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-api-to-aui.js","names":[],"sources":["../../../src/codemods/v0-12/assistant-api-to-aui.ts"],"sourcesContent":["import { createTransformer } from \"../utils/createTransformer\";\n\n// Map of old hook names to new hook names\nconst hookRenamingMap: Record<string, string> = {\n useAssistantApi: \"useAui\",\n useAssistantState: \"useAuiState\",\n useAssistantEvent: \"useAuiEvent\",\n};\n\n// Map of old component names to new component names\nconst componentRenamingMap: Record<string, string> = {\n AssistantIf: \"AuiIf\",\n AssistantProvider: \"AuiProvider\",\n};\n\nconst isUseAuiCall = (j: any, node: any): boolean => {\n return (\n node &&\n j.CallExpression.check(node) &&\n j.Identifier.check(node.callee) &&\n (node.callee.name === \"useAui\" || node.callee.name === \"useAssistantApi\")\n );\n};\n\nconst migrateAssistantApiToAui = createTransformer(\n ({ j, root, markAsChanged }) => {\n // 1. Update imports\n root.find(j.ImportDeclaration).forEach((path: any) => {\n const source = path.value.source.value;\n\n // Only process imports from @assistant-ui packages\n if (typeof source === \"string\" && source.startsWith(\"@assistant-ui/\")) {\n path.value.specifiers?.forEach((specifier: any) => {\n if (j.ImportSpecifier.check(specifier)) {\n const oldName = specifier.imported.name as string;\n\n // Rename hooks\n if (hookRenamingMap[oldName]) {\n const newName = hookRenamingMap[oldName];\n specifier.imported.name = newName;\n if (specifier.local && specifier.local.name === oldName) {\n specifier.local.name = newName;\n }\n markAsChanged();\n }\n\n // Rename components\n if (componentRenamingMap[oldName]) {\n const newName = componentRenamingMap[oldName];\n specifier.imported.name = newName;\n if (specifier.local && specifier.local.name === oldName) {\n specifier.local.name = newName;\n }\n markAsChanged();\n }\n }\n });\n }\n });\n\n // 2. Collect `api` declarators initialized from useAui / useAssistantApi.\n // References are renamed by binding resolution, so an `api` bound\n // elsewhere (function params, `const { api } = other()`) is never touched.\n const renamedDeclaratorIds = new Set<any>();\n root.find(j.VariableDeclarator).forEach((path: any) => {\n if (\n isUseAuiCall(j, path.value.init) &&\n j.Identifier.check(path.value.id) &&\n path.value.id.name === \"api\"\n ) {\n renamedDeclaratorIds.add(path.value.id);\n }\n });\n\n // 3. Rename references governed by one of those declarators. Resolution\n // is lexical (nearest enclosing declaration wins) rather than via\n // ast-types scopes, which have no block granularity: a block-scoped\n // `const api = other()` inside the same function must shadow.\n if (renamedDeclaratorIds.size > 0) {\n const patternBindsApi = (id: any): boolean => {\n if (\n id &&\n (id.type === \"TSParameterProperty\" ||\n j.TSParameterProperty?.check?.(id))\n ) {\n return patternBindsApi(id.parameter);\n }\n if (j.Identifier.check(id)) return id.name === \"api\";\n if (j.ObjectPattern.check(id)) {\n return id.properties.some((prop: any) =>\n patternBindsApi(prop.value ?? prop.argument ?? prop),\n );\n }\n if (j.ArrayPattern.check(id)) {\n return id.elements.some((el: any) => el && patternBindsApi(el));\n }\n if (j.AssignmentPattern.check(id)) return patternBindsApi(id.left);\n if (j.RestElement.check(id)) return patternBindsApi(id.argument);\n return false;\n };\n\n // What a statement-level node declares for `api`: the declarator id\n // node when it is a plain `const/let/var api = ...`, \"foreign\" for any\n // other binding of the name (patterns, functions, classes, enums), or\n // undefined when it does not bind `api` at all.\n const declaredApi = (statement: any): any => {\n if (!statement) return undefined;\n if (\n j.ExportNamedDeclaration.check(statement) ||\n j.ExportDefaultDeclaration.check(statement)\n ) {\n return declaredApi(statement.declaration);\n }\n if (j.VariableDeclaration.check(statement)) {\n for (const declarator of statement.declarations) {\n if (\n j.Identifier.check(declarator.id) &&\n declarator.id.name === \"api\"\n )\n return declarator.id;\n if (patternBindsApi(declarator.id)) return \"foreign\";\n }\n return undefined;\n }\n // Type-only declarations do not shadow the value binding.\n if (\n statement.type === \"TSTypeAliasDeclaration\" ||\n statement.type === \"TSInterfaceDeclaration\" ||\n statement.type === \"TSDeclareFunction\"\n )\n return undefined;\n // FunctionDeclaration, ClassDeclaration, TS enums/namespaces, …\n if (\n statement.id &&\n j.Identifier.check(statement.id) &&\n statement.id.name === \"api\"\n )\n return \"foreign\";\n return undefined;\n };\n\n const scanStatements = (statements: any[]): any => {\n for (const statement of statements) {\n const found = declaredApi(statement);\n if (found !== undefined) return found;\n }\n return undefined;\n };\n\n // Returns the declarator id node governing `api` here, or \"foreign\"\n // when any other binding of the name shadows it first.\n const governingApiBinding = (path: any): any => {\n let current = path.parent;\n while (current) {\n const node = current.value;\n\n // Anything function-like (declarations, expressions, arrows,\n // object/class methods) binds its params.\n if (Array.isArray(node.params) && node.params.some(patternBindsApi))\n return \"foreign\";\n // A named function/class expression binds its own name in its body.\n if (\n (j.FunctionExpression.check(node) ||\n j.ClassExpression.check(node)) &&\n node.id?.name === \"api\"\n )\n return \"foreign\";\n if (\n j.CatchClause.check(node) &&\n node.param &&\n patternBindsApi(node.param)\n )\n return \"foreign\";\n\n let found: any;\n if (j.BlockStatement.check(node) || j.Program.check(node)) {\n found = scanStatements(node.body);\n } else if (j.ForStatement.check(node)) {\n found = declaredApi(node.init);\n } else if (\n j.ForOfStatement.check(node) ||\n j.ForInStatement.check(node)\n ) {\n found = declaredApi(node.left);\n } else if (j.SwitchStatement.check(node)) {\n found = scanStatements(\n node.cases.flatMap((c: any) => c.consequent),\n );\n } else if (j.StaticBlock?.check?.(node)) {\n found = scanStatements(node.body);\n }\n if (found !== undefined) return found;\n\n current = current.parent;\n }\n return undefined;\n };\n\n const bindsToRenamedApi = (path: any): boolean => {\n const governing = governingApiBinding(path);\n return governing !== \"foreign\" && renamedDeclaratorIds.has(governing);\n };\n\n const referencePaths: any[] = [];\n root.find(j.Identifier, { name: \"api\" }).forEach((path: any) => {\n const parent = path.parent.value;\n if (j.ImportSpecifier.check(parent)) return;\n // Declaration names (variable, function, class, type alias,\n // interface) and TS type positions are not value references.\n if (parent.id === path.value) return;\n if (j.TSTypeReference?.check?.(parent)) return;\n if (j.TSQualifiedName?.check?.(parent)) return;\n // Any non-computed key position is a name, not a reference: object\n // properties, object/class methods, class properties, TS signatures.\n // Esprima-style shorthand reuses one node as key and value, so the\n // value position must survive the guard.\n if (\n parent.key === path.value &&\n !parent.computed &&\n parent.value !== path.value\n )\n return;\n if (\n j.MemberExpression.check(parent) &&\n parent.property === path.value &&\n !parent.computed\n )\n return;\n // JSXIdentifier extends Identifier, so JSX positions land here too:\n // member properties (<config.api/>), namespace names, and lowercase\n // element names (<api/> is an intrinsic tag) are not references.\n if (\n j.JSXMemberExpression?.check?.(parent) &&\n parent.property === path.value\n )\n return;\n if (j.JSXNamespacedName?.check?.(parent)) return;\n if (\n (j.JSXOpeningElement?.check?.(parent) ||\n j.JSXClosingElement?.check?.(parent)) &&\n parent.name === path.value\n )\n return;\n if (j.JSXAttribute.check(parent)) return;\n // The exported name of `export { api }` is the public alias, not a\n // reference; only the local side is renamed (to `aui as api`). A\n // source-bearing re-export binds in the other module, never here.\n if (j.ExportSpecifier.check(parent)) {\n const grandparent = path.parent.parent?.value;\n if (\n j.ExportNamedDeclaration.check(grandparent) &&\n grandparent.source != null\n )\n return;\n if (\n grandparent?.exportKind === \"type\" ||\n parent.exportKind === \"type\"\n )\n return;\n if (parent.exported === path.value && parent.local !== path.value)\n return;\n }\n if (!bindsToRenamedApi(path)) return;\n referencePaths.push(path);\n });\n\n for (const path of referencePaths) {\n const parent = path.parent.value;\n if (\n (j.Property.check(parent) || j.ObjectProperty.check(parent)) &&\n parent.shorthand &&\n parent.value === path.value\n ) {\n // `{ api }` in an object literal: keep the key, rename the value\n parent.shorthand = false;\n parent.key = j.identifier(\"api\");\n parent.value = j.identifier(\"aui\");\n } else if (\n j.ExportSpecifier.check(parent) &&\n parent.local === path.value\n ) {\n // `export { api }` / `export { api as name }`: rename the local\n // binding, keep the public name. Replaced wholesale — recast keeps\n // the shorthand form (dropping the alias) when only the fields of\n // the original node change.\n path.parent.replace(\n j.exportSpecifier.from({\n local: j.identifier(\"aui\"),\n exported: j.Identifier.check(parent.exported)\n ? j.identifier(parent.exported.name)\n : parent.exported,\n }),\n );\n } else {\n path.value.name = \"aui\";\n }\n markAsChanged();\n }\n for (const idNode of renamedDeclaratorIds) {\n idNode.name = \"aui\";\n markAsChanged();\n }\n }\n\n // 4. Update hook call references (in case they're used as values)\n Object.entries(hookRenamingMap).forEach(([oldName, newName]) => {\n root.find(j.Identifier).forEach((path: any) => {\n if (path.value.name === oldName) {\n // Skip if already handled in imports\n if (j.ImportSpecifier.check(path.parent.value)) {\n return;\n }\n\n // This might be a reference to the hook as a value\n path.value.name = newName;\n markAsChanged();\n }\n });\n });\n\n // 5. Update JSX component names\n Object.entries(componentRenamingMap).forEach(([oldName, newName]) => {\n // Update JSX opening elements\n root.find(j.JSXOpeningElement).forEach((path: any) => {\n if (\n j.JSXIdentifier.check(path.value.name) &&\n path.value.name.name === oldName\n ) {\n path.value.name.name = newName;\n markAsChanged();\n }\n });\n\n // Update JSX closing elements\n root.find(j.JSXClosingElement).forEach((path: any) => {\n if (\n j.JSXIdentifier.check(path.value.name) &&\n path.value.name.name === oldName\n ) {\n path.value.name.name = newName;\n markAsChanged();\n }\n });\n\n // Update regular identifier references (for component references)\n root.find(j.Identifier).forEach((path: any) => {\n if (path.value.name === oldName) {\n // Skip if already handled in imports\n if (j.ImportSpecifier.check(path.parent.value)) {\n return;\n }\n\n // Skip JSX identifiers (already handled above)\n if (j.JSXIdentifier.check(path.value)) {\n return;\n }\n\n // This might be a reference to the component as a value\n path.value.name = newName;\n markAsChanged();\n }\n });\n });\n },\n);\n\nexport default migrateAssistantApiToAui;\n"],"mappings":"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"}
1
+ {"version":3,"file":"assistant-api-to-aui.js","names":[],"sources":["../../../src/codemods/v0-12/assistant-api-to-aui.ts"],"sourcesContent":["import { createTransformer } from \"../utils/createTransformer\";\n\n// Map of old hook names to new hook names\nconst hookRenamingMap: Record<string, string> = {\n useAssistantApi: \"useAui\",\n useAssistantState: \"useAuiState\",\n useAssistantEvent: \"useAuiEvent\",\n};\n\n// Map of old component names to new component names\nconst componentRenamingMap: Record<string, string> = {\n AssistantIf: \"AuiIf\",\n AssistantProvider: \"AuiProvider\",\n};\n\nconst isUseAuiCall = (j: any, node: any): boolean => {\n return (\n node &&\n j.CallExpression.check(node) &&\n j.Identifier.check(node.callee) &&\n (node.callee.name === \"useAui\" || node.callee.name === \"useAssistantApi\")\n );\n};\n\nconst migrateAssistantApiToAui = createTransformer(\n ({ j, root, markAsChanged }) => {\n // 1. Update imports\n root.find(j.ImportDeclaration).forEach((path: any) => {\n const source = path.value.source.value;\n\n // Only process imports from @assistant-ui packages\n if (typeof source === \"string\" && source.startsWith(\"@assistant-ui/\")) {\n path.value.specifiers?.forEach((specifier: any) => {\n if (j.ImportSpecifier.check(specifier)) {\n const oldName = specifier.imported.name as string;\n\n // Rename hooks\n if (hookRenamingMap[oldName]) {\n const newName = hookRenamingMap[oldName];\n specifier.imported.name = newName;\n if (specifier.local && specifier.local.name === oldName) {\n specifier.local.name = newName;\n }\n markAsChanged();\n }\n\n // Rename components\n if (componentRenamingMap[oldName]) {\n const newName = componentRenamingMap[oldName];\n specifier.imported.name = newName;\n if (specifier.local && specifier.local.name === oldName) {\n specifier.local.name = newName;\n }\n markAsChanged();\n }\n }\n });\n }\n });\n\n // 2. Collect `api` declarators initialized from useAui / useAssistantApi.\n // References are renamed by binding resolution, so an `api` bound\n // elsewhere (function params, `const { api } = other()`) is never touched.\n const renamedDeclaratorIds = new Set<any>();\n root.find(j.VariableDeclarator).forEach((path: any) => {\n if (\n isUseAuiCall(j, path.value.init) &&\n j.Identifier.check(path.value.id) &&\n path.value.id.name === \"api\"\n ) {\n renamedDeclaratorIds.add(path.value.id);\n }\n });\n\n // 3. Rename references governed by one of those declarators. Resolution\n // is lexical (nearest enclosing declaration wins) rather than via\n // ast-types scopes, which have no block granularity: a block-scoped\n // `const api = other()` inside the same function must shadow.\n if (renamedDeclaratorIds.size > 0) {\n const patternBindsApi = (id: any): boolean => {\n if (\n id &&\n (id.type === \"TSParameterProperty\" ||\n j.TSParameterProperty?.check?.(id))\n ) {\n return patternBindsApi(id.parameter);\n }\n if (j.Identifier.check(id)) return id.name === \"api\";\n if (j.ObjectPattern.check(id)) {\n return id.properties.some((prop: any) =>\n patternBindsApi(prop.value ?? prop.argument ?? prop),\n );\n }\n if (j.ArrayPattern.check(id)) {\n return id.elements.some((el: any) => el && patternBindsApi(el));\n }\n if (j.AssignmentPattern.check(id)) return patternBindsApi(id.left);\n if (j.RestElement.check(id)) return patternBindsApi(id.argument);\n return false;\n };\n\n // What a statement-level node declares for `api`: the declarator id\n // node when it is a plain `const/let/var api = ...`, \"foreign\" for any\n // other binding of the name (patterns, functions, classes, enums), or\n // undefined when it does not bind `api` at all.\n const declaredApi = (statement: any): any => {\n if (!statement) return undefined;\n if (\n j.ExportNamedDeclaration.check(statement) ||\n j.ExportDefaultDeclaration.check(statement)\n ) {\n return declaredApi(statement.declaration);\n }\n if (j.VariableDeclaration.check(statement)) {\n for (const declarator of statement.declarations) {\n if (!j.VariableDeclarator.check(declarator)) continue;\n if (\n j.Identifier.check(declarator.id) &&\n declarator.id.name === \"api\"\n )\n return declarator.id;\n if (patternBindsApi(declarator.id)) return \"foreign\";\n }\n return undefined;\n }\n // Type-only declarations do not shadow the value binding.\n if (\n statement.type === \"TSTypeAliasDeclaration\" ||\n statement.type === \"TSInterfaceDeclaration\" ||\n statement.type === \"TSDeclareFunction\"\n )\n return undefined;\n // FunctionDeclaration, ClassDeclaration, TS enums/namespaces, …\n if (\n statement.id &&\n j.Identifier.check(statement.id) &&\n statement.id.name === \"api\"\n )\n return \"foreign\";\n return undefined;\n };\n\n const scanStatements = (statements: any[]): any => {\n for (const statement of statements) {\n const found = declaredApi(statement);\n if (found !== undefined) return found;\n }\n return undefined;\n };\n\n // Returns the declarator id node governing `api` here, or \"foreign\"\n // when any other binding of the name shadows it first.\n const governingApiBinding = (path: any): any => {\n let current = path.parent;\n while (current) {\n const node = current.value;\n\n // Anything function-like (declarations, expressions, arrows,\n // object/class methods) binds its params.\n if (Array.isArray(node.params) && node.params.some(patternBindsApi))\n return \"foreign\";\n // A named function/class expression binds its own name in its body.\n if (\n (j.FunctionExpression.check(node) ||\n j.ClassExpression.check(node)) &&\n node.id?.name === \"api\"\n )\n return \"foreign\";\n if (\n j.CatchClause.check(node) &&\n node.param &&\n patternBindsApi(node.param)\n )\n return \"foreign\";\n\n let found: any;\n if (j.BlockStatement.check(node) || j.Program.check(node)) {\n found = scanStatements(node.body);\n } else if (j.ForStatement.check(node)) {\n found = declaredApi(node.init);\n } else if (\n j.ForOfStatement.check(node) ||\n j.ForInStatement.check(node)\n ) {\n found = declaredApi(node.left);\n } else if (j.SwitchStatement.check(node)) {\n found = scanStatements(\n node.cases.flatMap((c: any) => c.consequent),\n );\n } else if (j.StaticBlock?.check?.(node)) {\n found = scanStatements(node.body);\n }\n if (found !== undefined) return found;\n\n current = current.parent;\n }\n return undefined;\n };\n\n const bindsToRenamedApi = (path: any): boolean => {\n const governing = governingApiBinding(path);\n return governing !== \"foreign\" && renamedDeclaratorIds.has(governing);\n };\n\n const referencePaths: any[] = [];\n root.find(j.Identifier, { name: \"api\" }).forEach((path: any) => {\n const parent = path.parent.value;\n if (j.ImportSpecifier.check(parent)) return;\n // Declaration names (variable, function, class, type alias,\n // interface) and TS type positions are not value references.\n if (parent.id === path.value) return;\n if (j.TSTypeReference?.check?.(parent)) return;\n if (j.TSQualifiedName?.check?.(parent)) return;\n // Any non-computed key position is a name, not a reference: object\n // properties, object/class methods, class properties, TS signatures.\n // Esprima-style shorthand reuses one node as key and value, so the\n // value position must survive the guard.\n if (\n parent.key === path.value &&\n !parent.computed &&\n parent.value !== path.value\n )\n return;\n if (\n j.MemberExpression.check(parent) &&\n parent.property === path.value &&\n !parent.computed\n )\n return;\n // JSXIdentifier extends Identifier, so JSX positions land here too:\n // member properties (<config.api/>), namespace names, and lowercase\n // element names (<api/> is an intrinsic tag) are not references.\n if (\n j.JSXMemberExpression?.check?.(parent) &&\n parent.property === path.value\n )\n return;\n if (j.JSXNamespacedName?.check?.(parent)) return;\n if (\n (j.JSXOpeningElement?.check?.(parent) ||\n j.JSXClosingElement?.check?.(parent)) &&\n parent.name === path.value\n )\n return;\n if (j.JSXAttribute.check(parent)) return;\n // The exported name of `export { api }` is the public alias, not a\n // reference; only the local side is renamed (to `aui as api`). A\n // source-bearing re-export binds in the other module, never here.\n if (j.ExportSpecifier.check(parent)) {\n const grandparent = path.parent.parent?.value;\n if (\n j.ExportNamedDeclaration.check(grandparent) &&\n grandparent.source != null\n )\n return;\n // Babel emits exportKind on ExportSpecifier for inline\n // `export { type api }`; ast-types' typings omit it.\n if (\n grandparent?.exportKind === \"type\" ||\n (parent as { exportKind?: string }).exportKind === \"type\"\n )\n return;\n if (parent.exported === path.value && parent.local !== path.value)\n return;\n }\n if (!bindsToRenamedApi(path)) return;\n referencePaths.push(path);\n });\n\n for (const path of referencePaths) {\n const parent = path.parent.value;\n if (\n (j.Property.check(parent) || j.ObjectProperty.check(parent)) &&\n parent.shorthand &&\n parent.value === path.value\n ) {\n // `{ api }` in an object literal: keep the key, rename the value\n parent.shorthand = false;\n parent.key = j.identifier(\"api\");\n parent.value = j.identifier(\"aui\");\n } else if (\n j.ExportSpecifier.check(parent) &&\n parent.local === path.value\n ) {\n // `export { api }` / `export { api as name }`: rename the local\n // binding, keep the public name. Replaced wholesale — recast keeps\n // the shorthand form (dropping the alias) when only the fields of\n // the original node change.\n path.parent.replace(\n j.exportSpecifier.from({\n local: j.identifier(\"aui\"),\n exported: j.Identifier.check(parent.exported)\n ? j.identifier(parent.exported.name)\n : parent.exported,\n }),\n );\n } else {\n path.value.name = \"aui\";\n }\n markAsChanged();\n }\n for (const idNode of renamedDeclaratorIds) {\n idNode.name = \"aui\";\n markAsChanged();\n }\n }\n\n // 4. Update hook call references (in case they're used as values)\n Object.entries(hookRenamingMap).forEach(([oldName, newName]) => {\n root.find(j.Identifier).forEach((path: any) => {\n if (path.value.name === oldName) {\n // Skip if already handled in imports\n if (j.ImportSpecifier.check(path.parent.value)) {\n return;\n }\n\n // This might be a reference to the hook as a value\n path.value.name = newName;\n markAsChanged();\n }\n });\n });\n\n // 5. Update JSX component names\n Object.entries(componentRenamingMap).forEach(([oldName, newName]) => {\n // Update JSX opening elements\n root.find(j.JSXOpeningElement).forEach((path: any) => {\n if (\n j.JSXIdentifier.check(path.value.name) &&\n path.value.name.name === oldName\n ) {\n path.value.name.name = newName;\n markAsChanged();\n }\n });\n\n // Update JSX closing elements\n root.find(j.JSXClosingElement).forEach((path: any) => {\n if (\n j.JSXIdentifier.check(path.value.name) &&\n path.value.name.name === oldName\n ) {\n path.value.name.name = newName;\n markAsChanged();\n }\n });\n\n // Update regular identifier references (for component references)\n root.find(j.Identifier).forEach((path: any) => {\n if (path.value.name === oldName) {\n // Skip if already handled in imports\n if (j.ImportSpecifier.check(path.parent.value)) {\n return;\n }\n\n // Skip JSX identifiers (already handled above)\n if (j.JSXIdentifier.check(path.value)) {\n return;\n }\n\n // This might be a reference to the component as a value\n path.value.name = newName;\n markAsChanged();\n }\n });\n });\n },\n);\n\nexport default migrateAssistantApiToAui;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"primitive-if-to-aui-if.d.ts","names":[],"sources":["../../../src/codemods/v0-12/primitive-if-to-aui-if.ts"],"mappings":";cA2KM,4BAAyB,yCAAA,UAAA,oCAAA,KAAA"}
1
+ {"version":3,"file":"primitive-if-to-aui-if.d.ts","names":[],"sources":["../../../src/codemods/v0-12/primitive-if-to-aui-if.ts"],"mappings":";cAsLM,4BAAyB,yCAAA,UAAA,oCAAA,KAAA"}
@@ -106,10 +106,12 @@ const getAttrValue = (j, attr) => {
106
106
  if (j.StringLiteral.check(attr.value) || j.Literal.check(attr.value)) return attr.value.value;
107
107
  return UNSUPPORTED_VALUE;
108
108
  };
109
- const buildConditionString = (fragments) => {
110
- const parts = fragments.map((f) => f.negated ? `!${f.expression}` : f.expression);
111
- if (parts.length === 1) return parts[0];
112
- return parts.join(" && ");
109
+ const buildConditionString = (j, fragments) => {
110
+ const parseExpression = (source) => j(`${source};`).find(j.ExpressionStatement).nodes()[0].expression;
111
+ return j(fragments.map((f) => {
112
+ const expression = parseExpression(f.expression);
113
+ return f.negated ? j.unaryExpression("!", expression) : expression;
114
+ }).reduce((left, right) => j.logicalExpression("&&", left, right))).toSource();
113
115
  };
114
116
  const migratePrimitiveIfToAuiIf = createTransformer(({ j, root, markAsChanged }) => {
115
117
  let needsAuiIfImport = false;
@@ -183,7 +185,7 @@ const migratePrimitiveIfToAuiIf = createTransformer(({ j, root, markAsChanged })
183
185
  if (fragment) fragments.push(fragment);
184
186
  }
185
187
  if (hasUnknownProp || fragments.length === 0) return;
186
- convertElementToAuiIf(path, buildConditionString(fragments));
188
+ convertElementToAuiIf(path, buildConditionString(j, fragments));
187
189
  });
188
190
  if (needsAuiIfImport) {
189
191
  let hasAuiIfImport = false;
@@ -1 +1 @@
1
- {"version":3,"file":"primitive-if-to-aui-if.js","names":[],"sources":["../../../src/codemods/v0-12/primitive-if-to-aui-if.ts"],"sourcesContent":["import { createTransformer } from \"../utils/createTransformer\";\n\ntype ConditionFragment = {\n expression: string;\n negated: boolean;\n};\n\n// Map ThreadPrimitive.If props to condition expressions\nconst threadPropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n empty: (v) => ({\n expression: \"s.thread.isEmpty\",\n negated: v === false,\n }),\n running: (v) => ({\n expression: \"s.thread.isRunning\",\n negated: v === false,\n }),\n disabled: (v) => ({\n expression: \"s.thread.isDisabled\",\n negated: v === false,\n }),\n};\n\n// Map MessagePrimitive.If props to condition expressions\nconst messagePropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n user: () => ({ expression: 's.message.role === \"user\"', negated: false }),\n assistant: () => ({\n expression: 's.message.role === \"assistant\"',\n negated: false,\n }),\n system: () => ({\n expression: 's.message.role === \"system\"',\n negated: false,\n }),\n hasBranches: () => ({\n expression: \"s.message.branchCount >= 2\",\n negated: false,\n }),\n copied: (v) => ({\n expression: \"s.message.isCopied\",\n negated: v === false,\n }),\n last: (v) => ({\n expression: \"s.message.isLast\",\n negated: v === false,\n }),\n lastOrHover: () => ({\n expression: \"s.message.isHovering || s.message.isLast\",\n negated: false,\n }),\n speaking: (v) => ({\n expression: \"s.message.speech != null\",\n negated: v === false,\n }),\n hasAttachments: (v) =>\n v === true\n ? {\n expression:\n 's.message.role === \"user\" && !!s.message.attachments?.length',\n negated: false,\n }\n : {\n expression:\n 's.message.role !== \"user\" || !s.message.attachments?.length',\n negated: false,\n },\n hasContent: (v) => ({\n expression: \"s.message.parts.length > 0\",\n negated: v === false,\n }),\n submittedFeedback: (v) => {\n if (v === null) {\n return {\n expression:\n \"(s.message.metadata.submittedFeedback?.type ?? null) === null\",\n negated: false,\n };\n }\n return {\n expression: `s.message.metadata.submittedFeedback?.type === \"${v}\"`,\n negated: false,\n };\n },\n};\n\n// Map ComposerPrimitive.If props to condition expressions\nconst composerPropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n editing: (v) => ({\n expression: \"s.composer.isEditing\",\n negated: v === false,\n }),\n dictation: (v) => ({\n expression: \"s.composer.dictation != null\",\n negated: v === false,\n }),\n};\n\nconst primitiveMap: Record<\n string,\n Record<string, (value: unknown) => ConditionFragment | null>\n> = {\n ThreadPrimitive: threadPropMap,\n MessagePrimitive: messagePropMap,\n ComposerPrimitive: composerPropMap,\n};\n\n// Map of XPrimitive.Component → fixed condition (no props needed)\nconst fixedConditionMap: Record<string, Record<string, string>> = {\n ThreadPrimitive: {\n Empty: \"s.thread.isEmpty\",\n },\n};\n\n// A prop value the maps cannot faithfully express as a static condition\n// (dynamic expressions, `{undefined}`); elements carrying one are skipped\n// so runtime behavior is never silently changed.\nconst UNSUPPORTED_VALUE: unique symbol = Symbol(\"unsupported\");\n\n/**\n * Extract the value of a JSX attribute.\n * - Boolean prop (no value): `<X.If user>` → `true`\n * - `{true}` / `{false}`: → `true` / `false`\n * - `{\"positive\"}`: → `\"positive\"`\n * - `{null}`: → `null`\n * - anything else (dynamic expressions): → UNSUPPORTED_VALUE\n */\nconst getAttrValue = (j: any, attr: any): unknown => {\n // Boolean attribute (no value), e.g. `<X.If user>`\n if (attr.value === null || attr.value === undefined) {\n return true;\n }\n\n // JSX expression container: `{true}`, `{false}`, `{\"positive\"}`, `{null}`\n if (j.JSXExpressionContainer.check(attr.value)) {\n const expr = attr.value.expression;\n if (j.BooleanLiteral.check(expr)) return expr.value;\n // NullLiteral bases Literal in ast-types but carries no `value` field,\n // so it must be recognized before the generic Literal branch.\n if (j.NullLiteral.check(expr)) return null;\n if (j.Literal.check(expr)) {\n if (expr.value === null) return null;\n return expr.value;\n }\n return UNSUPPORTED_VALUE;\n }\n\n // String literal\n if (j.StringLiteral.check(attr.value) || j.Literal.check(attr.value)) {\n return attr.value.value;\n }\n\n return UNSUPPORTED_VALUE;\n};\n\nconst buildConditionString = (fragments: ConditionFragment[]): string => {\n const parts = fragments.map((f) =>\n f.negated ? `!${f.expression}` : f.expression,\n );\n if (parts.length === 1) return parts[0]!;\n return parts.join(\" && \");\n};\n\nconst migratePrimitiveIfToAuiIf = createTransformer(\n ({ j, root, markAsChanged }) => {\n let needsAuiIfImport = false;\n\n // Track which primitive namespaces are imported\n const importedPrimitives = new Set<string>();\n root.find(j.ImportDeclaration).forEach((path: any) => {\n const source = path.value.source.value;\n if (typeof source === \"string\" && source.startsWith(\"@assistant-ui/\")) {\n path.value.specifiers?.forEach((specifier: any) => {\n if (j.ImportSpecifier.check(specifier)) {\n const name = String(\n specifier.local?.name ?? specifier.imported.name,\n );\n if (primitiveMap[name] || fixedConditionMap[name]) {\n importedPrimitives.add(name);\n }\n }\n });\n }\n });\n\n if (importedPrimitives.size === 0) return;\n\n // Opening and closing tags are rewritten together per element, so a\n // skipped element can never be left with a mismatched closing tag.\n const convertElementToAuiIf = (elementPath: any, conditionBody: string) => {\n const arrowFnAst = j(`(s) => ${conditionBody}`)\n .find(j.ArrowFunctionExpression)\n .paths()[0]!.value;\n\n const opening = elementPath.value.openingElement;\n opening.name = j.jsxIdentifier(\"AuiIf\");\n opening.attributes = [\n j.jsxAttribute(\n j.jsxIdentifier(\"condition\"),\n j.jsxExpressionContainer(arrowFnAst),\n ),\n ];\n if (elementPath.value.closingElement) {\n elementPath.value.closingElement.name = j.jsxIdentifier(\"AuiIf\");\n }\n\n needsAuiIfImport = true;\n markAsChanged();\n };\n\n // Process fixed-condition components: <ThreadPrimitive.Empty> → <AuiIf condition={...}>\n root.find(j.JSXElement).forEach((path: any) => {\n const name = path.value.openingElement.name;\n if (!j.JSXMemberExpression.check(name)) return;\n if (!j.JSXIdentifier.check(name.object)) return;\n if (!j.JSXIdentifier.check(name.property)) return;\n\n const primitiveName = name.object.name as string;\n const propertyName = name.property.name as string;\n const conditionBody = fixedConditionMap[primitiveName]?.[propertyName];\n if (!conditionBody) return;\n if (!importedPrimitives.has(primitiveName)) return;\n\n // Only transform if there are no props (other than children, which are implicit)\n const attrs: any[] = path.value.openingElement.attributes || [];\n if (attrs.length > 0) return;\n\n convertElementToAuiIf(path, conditionBody);\n });\n\n // Process JSX elements: <ThreadPrimitive.If ...> → <AuiIf condition={...}>\n root.find(j.JSXElement).forEach((path: any) => {\n const name = path.value.openingElement.name;\n\n // Check for `<XPrimitive.If ...>`\n if (!j.JSXMemberExpression.check(name)) return;\n if (!j.JSXIdentifier.check(name.object)) return;\n if (!j.JSXIdentifier.check(name.property)) return;\n if (name.property.name !== \"If\") return;\n\n const primitiveName = name.object.name;\n const propMap = primitiveMap[primitiveName];\n if (!propMap) return;\n if (!importedPrimitives.has(primitiveName)) return;\n\n // Extract props\n const attrs: any[] = path.value.openingElement.attributes || [];\n const fragments: ConditionFragment[] = [];\n let hasUnknownProp = false;\n\n for (const attr of attrs) {\n if (!j.JSXAttribute.check(attr)) {\n // JSX spread attributes — can't migrate\n hasUnknownProp = true;\n continue;\n }\n const propName =\n typeof attr.name.name === \"string\" ? attr.name.name : null;\n if (!propName) {\n // e.g. JSXNamespacedName — not expressible as a condition\n hasUnknownProp = true;\n continue;\n }\n\n const mapper = propMap[propName];\n if (!mapper) {\n hasUnknownProp = true;\n continue;\n }\n\n const value = getAttrValue(j, attr);\n if (value === UNSUPPORTED_VALUE) {\n hasUnknownProp = true;\n continue;\n }\n const fragment = mapper(value);\n if (fragment) {\n fragments.push(fragment);\n }\n }\n\n // If we couldn't map all props, skip this element\n if (hasUnknownProp || fragments.length === 0) return;\n\n convertElementToAuiIf(path, buildConditionString(fragments));\n });\n\n // Add AuiIf import if needed\n if (needsAuiIfImport) {\n let hasAuiIfImport = false;\n let assistantUiImport: any = null;\n\n root.find(j.ImportDeclaration).forEach((path: any) => {\n const source = path.value.source.value;\n if (typeof source === \"string\" && source.startsWith(\"@assistant-ui/\")) {\n assistantUiImport = path;\n path.value.specifiers?.forEach((specifier: any) => {\n if (\n j.ImportSpecifier.check(specifier) &&\n (specifier.imported.name === \"AuiIf\" ||\n specifier.local?.name === \"AuiIf\")\n ) {\n hasAuiIfImport = true;\n }\n });\n }\n });\n\n if (!hasAuiIfImport && assistantUiImport) {\n assistantUiImport.value.specifiers.push(\n j.importSpecifier(j.identifier(\"AuiIf\")),\n );\n markAsChanged();\n }\n }\n },\n);\n\nexport default migratePrimitiveIfToAuiIf;\n"],"mappings":"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"}
1
+ {"version":3,"file":"primitive-if-to-aui-if.js","names":[],"sources":["../../../src/codemods/v0-12/primitive-if-to-aui-if.ts"],"sourcesContent":["import { createTransformer } from \"../utils/createTransformer\";\n\ntype ConditionFragment = {\n expression: string;\n negated: boolean;\n};\n\n// Map ThreadPrimitive.If props to condition expressions\nconst threadPropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n empty: (v) => ({\n expression: \"s.thread.isEmpty\",\n negated: v === false,\n }),\n running: (v) => ({\n expression: \"s.thread.isRunning\",\n negated: v === false,\n }),\n disabled: (v) => ({\n expression: \"s.thread.isDisabled\",\n negated: v === false,\n }),\n};\n\n// Map MessagePrimitive.If props to condition expressions\nconst messagePropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n user: () => ({ expression: 's.message.role === \"user\"', negated: false }),\n assistant: () => ({\n expression: 's.message.role === \"assistant\"',\n negated: false,\n }),\n system: () => ({\n expression: 's.message.role === \"system\"',\n negated: false,\n }),\n hasBranches: () => ({\n expression: \"s.message.branchCount >= 2\",\n negated: false,\n }),\n copied: (v) => ({\n expression: \"s.message.isCopied\",\n negated: v === false,\n }),\n last: (v) => ({\n expression: \"s.message.isLast\",\n negated: v === false,\n }),\n lastOrHover: () => ({\n expression: \"s.message.isHovering || s.message.isLast\",\n negated: false,\n }),\n speaking: (v) => ({\n expression: \"s.message.speech != null\",\n negated: v === false,\n }),\n hasAttachments: (v) =>\n v === true\n ? {\n expression:\n 's.message.role === \"user\" && !!s.message.attachments?.length',\n negated: false,\n }\n : {\n expression:\n 's.message.role !== \"user\" || !s.message.attachments?.length',\n negated: false,\n },\n hasContent: (v) => ({\n expression: \"s.message.parts.length > 0\",\n negated: v === false,\n }),\n submittedFeedback: (v) => {\n if (v === null) {\n return {\n expression:\n \"(s.message.metadata.submittedFeedback?.type ?? null) === null\",\n negated: false,\n };\n }\n return {\n expression: `s.message.metadata.submittedFeedback?.type === \"${v}\"`,\n negated: false,\n };\n },\n};\n\n// Map ComposerPrimitive.If props to condition expressions\nconst composerPropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n editing: (v) => ({\n expression: \"s.composer.isEditing\",\n negated: v === false,\n }),\n dictation: (v) => ({\n expression: \"s.composer.dictation != null\",\n negated: v === false,\n }),\n};\n\nconst primitiveMap: Record<\n string,\n Record<string, (value: unknown) => ConditionFragment | null>\n> = {\n ThreadPrimitive: threadPropMap,\n MessagePrimitive: messagePropMap,\n ComposerPrimitive: composerPropMap,\n};\n\n// Map of XPrimitive.Component → fixed condition (no props needed)\nconst fixedConditionMap: Record<string, Record<string, string>> = {\n ThreadPrimitive: {\n Empty: \"s.thread.isEmpty\",\n },\n};\n\n// A prop value the maps cannot faithfully express as a static condition\n// (dynamic expressions, `{undefined}`); elements carrying one are skipped\n// so runtime behavior is never silently changed.\nconst UNSUPPORTED_VALUE: unique symbol = Symbol(\"unsupported\");\n\n/**\n * Extract the value of a JSX attribute.\n * - Boolean prop (no value): `<X.If user>` → `true`\n * - `{true}` / `{false}`: → `true` / `false`\n * - `{\"positive\"}`: → `\"positive\"`\n * - `{null}`: → `null`\n * - anything else (dynamic expressions): → UNSUPPORTED_VALUE\n */\nconst getAttrValue = (j: any, attr: any): unknown => {\n // Boolean attribute (no value), e.g. `<X.If user>`\n if (attr.value === null || attr.value === undefined) {\n return true;\n }\n\n // JSX expression container: `{true}`, `{false}`, `{\"positive\"}`, `{null}`\n if (j.JSXExpressionContainer.check(attr.value)) {\n const expr = attr.value.expression;\n if (j.BooleanLiteral.check(expr)) return expr.value;\n // NullLiteral bases Literal in ast-types but carries no `value` field,\n // so it must be recognized before the generic Literal branch.\n if (j.NullLiteral.check(expr)) return null;\n if (j.Literal.check(expr)) {\n if (expr.value === null) return null;\n return expr.value;\n }\n return UNSUPPORTED_VALUE;\n }\n\n // String literal\n if (j.StringLiteral.check(attr.value) || j.Literal.check(attr.value)) {\n return attr.value.value;\n }\n\n return UNSUPPORTED_VALUE;\n};\n\n// Composed as a syntax tree so the printer parenthesizes by precedence;\n// concatenated text lets `!` and `&&` reassociate a fragment's own operators.\nconst buildConditionString = (\n j: any,\n fragments: ConditionFragment[],\n): string => {\n const parseExpression = (source: string) =>\n j(`${source};`).find(j.ExpressionStatement).nodes()[0]!.expression;\n\n const condition = fragments\n .map((f) => {\n const expression = parseExpression(f.expression);\n return f.negated ? j.unaryExpression(\"!\", expression) : expression;\n })\n .reduce((left: any, right: any) => j.logicalExpression(\"&&\", left, right));\n\n return j(condition).toSource();\n};\n\nconst migratePrimitiveIfToAuiIf = createTransformer(\n ({ j, root, markAsChanged }) => {\n let needsAuiIfImport = false;\n\n // Track which primitive namespaces are imported\n const importedPrimitives = new Set<string>();\n root.find(j.ImportDeclaration).forEach((path: any) => {\n const source = path.value.source.value;\n if (typeof source === \"string\" && source.startsWith(\"@assistant-ui/\")) {\n path.value.specifiers?.forEach((specifier: any) => {\n if (j.ImportSpecifier.check(specifier)) {\n const name = String(\n specifier.local?.name ?? specifier.imported.name,\n );\n if (primitiveMap[name] || fixedConditionMap[name]) {\n importedPrimitives.add(name);\n }\n }\n });\n }\n });\n\n if (importedPrimitives.size === 0) return;\n\n // Opening and closing tags are rewritten together per element, so a\n // skipped element can never be left with a mismatched closing tag.\n const convertElementToAuiIf = (elementPath: any, conditionBody: string) => {\n const arrowFnAst = j(`(s) => ${conditionBody}`)\n .find(j.ArrowFunctionExpression)\n .paths()[0]!.value;\n\n const opening = elementPath.value.openingElement;\n opening.name = j.jsxIdentifier(\"AuiIf\");\n opening.attributes = [\n j.jsxAttribute(\n j.jsxIdentifier(\"condition\"),\n j.jsxExpressionContainer(arrowFnAst),\n ),\n ];\n if (elementPath.value.closingElement) {\n elementPath.value.closingElement.name = j.jsxIdentifier(\"AuiIf\");\n }\n\n needsAuiIfImport = true;\n markAsChanged();\n };\n\n // Process fixed-condition components: <ThreadPrimitive.Empty> → <AuiIf condition={...}>\n root.find(j.JSXElement).forEach((path: any) => {\n const name = path.value.openingElement.name;\n if (!j.JSXMemberExpression.check(name)) return;\n if (!j.JSXIdentifier.check(name.object)) return;\n if (!j.JSXIdentifier.check(name.property)) return;\n\n const primitiveName = name.object.name as string;\n const propertyName = name.property.name as string;\n const conditionBody = fixedConditionMap[primitiveName]?.[propertyName];\n if (!conditionBody) return;\n if (!importedPrimitives.has(primitiveName)) return;\n\n // Only transform if there are no props (other than children, which are implicit)\n const attrs: any[] = path.value.openingElement.attributes || [];\n if (attrs.length > 0) return;\n\n convertElementToAuiIf(path, conditionBody);\n });\n\n // Process JSX elements: <ThreadPrimitive.If ...> → <AuiIf condition={...}>\n root.find(j.JSXElement).forEach((path: any) => {\n const name = path.value.openingElement.name;\n\n // Check for `<XPrimitive.If ...>`\n if (!j.JSXMemberExpression.check(name)) return;\n if (!j.JSXIdentifier.check(name.object)) return;\n if (!j.JSXIdentifier.check(name.property)) return;\n if (name.property.name !== \"If\") return;\n\n const primitiveName = name.object.name;\n const propMap = primitiveMap[primitiveName];\n if (!propMap) return;\n if (!importedPrimitives.has(primitiveName)) return;\n\n // Extract props\n const attrs: any[] = path.value.openingElement.attributes || [];\n const fragments: ConditionFragment[] = [];\n let hasUnknownProp = false;\n\n for (const attr of attrs) {\n if (!j.JSXAttribute.check(attr)) {\n // JSX spread attributes — can't migrate\n hasUnknownProp = true;\n continue;\n }\n const propName =\n typeof attr.name.name === \"string\" ? attr.name.name : null;\n if (!propName) {\n // e.g. JSXNamespacedName — not expressible as a condition\n hasUnknownProp = true;\n continue;\n }\n\n const mapper = propMap[propName];\n if (!mapper) {\n hasUnknownProp = true;\n continue;\n }\n\n const value = getAttrValue(j, attr);\n if (value === UNSUPPORTED_VALUE) {\n hasUnknownProp = true;\n continue;\n }\n const fragment = mapper(value);\n if (fragment) {\n fragments.push(fragment);\n }\n }\n\n // If we couldn't map all props, skip this element\n if (hasUnknownProp || fragments.length === 0) return;\n\n convertElementToAuiIf(path, buildConditionString(j, fragments));\n });\n\n // Add AuiIf import if needed\n if (needsAuiIfImport) {\n let hasAuiIfImport = false;\n let assistantUiImport: any = null;\n\n root.find(j.ImportDeclaration).forEach((path: any) => {\n const source = path.value.source.value;\n if (typeof source === \"string\" && source.startsWith(\"@assistant-ui/\")) {\n assistantUiImport = path;\n path.value.specifiers?.forEach((specifier: any) => {\n if (\n j.ImportSpecifier.check(specifier) &&\n (specifier.imported.name === \"AuiIf\" ||\n specifier.local?.name === \"AuiIf\")\n ) {\n hasAuiIfImport = true;\n }\n });\n }\n });\n\n if (!hasAuiIfImport && assistantUiImport) {\n assistantUiImport.value.specifiers.push(\n j.importSpecifier(j.identifier(\"AuiIf\")),\n );\n markAsChanged();\n }\n }\n },\n);\n\nexport default migratePrimitiveIfToAuiIf;\n"],"mappings":"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"}
@@ -10,9 +10,9 @@ declare function createAddComponentsPlan(params: {
10
10
  packageManager: PackageManagerName;
11
11
  yes?: boolean;
12
12
  overwrite?: boolean;
13
- cwd?: string;
14
13
  path?: string;
15
14
  style?: string;
15
+ platform?: "web" | "native";
16
16
  }): AddComponentsPlan;
17
17
  declare const add: Command;
18
18
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"add.d.ts","names":[],"sources":["../../src/commands/add.ts"],"mappings":";;;UAciB;EACf;EACA;;iBAGc,wBAAwB;EACtC;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;IACE;cAqBS,KAAG"}
1
+ {"version":3,"file":"add.d.ts","names":[],"sources":["../../src/commands/add.ts"],"mappings":";;;UAeiB;EACf;EACA;;iBAGc,wBAAwB;EACtC;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;IACE;cAoBS,KAAG"}
@@ -1,15 +1,15 @@
1
1
  import { SpawnExitError, SpawnSignalError, runSpawn } from "../lib/run-spawn.js";
2
2
  import { logger } from "../lib/utils/logger.js";
3
3
  import { resolvePackageManagerForCwd } from "../lib/utils/package-manager.js";
4
+ import { detectRegistryPlatform, getComponentsJsonStyle, resolveRegistryItemUrl } from "../lib/utils/registry.js";
4
5
  import { dlxCommand, resolvePackageManager } from "../lib/create-project.js";
5
6
  import { hasConfig } from "../lib/utils/config.js";
6
- import { getComponentsJsonStyle, resolveRegistryItemUrl } from "../lib/utils/registry.js";
7
7
  import { Command } from "commander";
8
8
  //#region src/commands/add.ts
9
9
  function createAddComponentsPlan(params) {
10
10
  const componentsToAdd = params.components.map((c) => {
11
11
  if (!/^[a-zA-Z0-9-/]+$/.test(c)) throw new Error(`Invalid component name: ${c}`);
12
- return resolveRegistryItemUrl(c, params.style);
12
+ return resolveRegistryItemUrl(c, params.style, params.platform);
13
13
  });
14
14
  const [command, dlxArgs] = dlxCommand(params.packageManager);
15
15
  const args = [
@@ -20,7 +20,6 @@ function createAddComponentsPlan(params) {
20
20
  ];
21
21
  if (params.yes) args.push("--yes");
22
22
  if (params.overwrite) args.push("--overwrite");
23
- if (params.cwd) args.push("--cwd", params.cwd);
24
23
  if (params.path) args.push("--path", params.path);
25
24
  return {
26
25
  command,
@@ -28,21 +27,23 @@ function createAddComponentsPlan(params) {
28
27
  };
29
28
  }
30
29
  const add = new Command().name("add").description("add a component to your project").argument("<components...>", "the components to add").option("-y, --yes", "skip confirmation prompt.", true).option("-o, --overwrite", "overwrite existing files.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("-p, --path <path>", "the path to add the component to.").option("--use-npm", "explicitly use npm").option("--use-pnpm", "explicitly use pnpm").option("--use-yarn", "explicitly use yarn").option("--use-bun", "explicitly use bun").action(async (components, opts) => {
30
+ const platform = detectRegistryPlatform(opts.cwd);
31
31
  if (!hasConfig(opts.cwd)) {
32
- logger.warn("It looks like you haven't initialized your project yet — defaulting to Base UI flavored components. Run 'assistant-ui init' first for a configured setup.");
32
+ logger.warn(`It looks like you haven't initialized your project yet. Defaulting to ${platform === "native" ? "the native component tree" : "Base UI flavored components"}. Run 'assistant-ui init' first for a configured setup.`);
33
33
  logger.break();
34
34
  }
35
+ logger.info(`Using the ${platform} registry tree.`);
35
36
  logger.step(`Adding ${components.length} component(s)...`);
36
37
  const packageManager = await resolvePackageManagerForCwd(opts.cwd, resolvePackageManager(opts));
37
- const style = getComponentsJsonStyle(opts.cwd);
38
+ const style = platform === "web" ? getComponentsJsonStyle(opts.cwd) : void 0;
38
39
  const { command, args } = createAddComponentsPlan({
39
40
  components,
40
41
  packageManager,
41
42
  yes: opts.yes,
42
43
  overwrite: opts.overwrite,
43
- cwd: opts.cwd,
44
44
  path: opts.path,
45
- ...style === void 0 ? {} : { style }
45
+ ...style === void 0 ? {} : { style },
46
+ platform
46
47
  });
47
48
  try {
48
49
  await runSpawn(command, args, opts.cwd);
@@ -1 +1 @@
1
- {"version":3,"file":"add.js","names":[],"sources":["../../src/commands/add.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { logger } from \"../lib/utils/logger\";\nimport { hasConfig } from \"../lib/utils/config\";\nimport {\n getComponentsJsonStyle,\n resolveRegistryItemUrl,\n} from \"../lib/utils/registry\";\nimport { dlxCommand, resolvePackageManager } from \"../lib/create-project\";\nimport { runSpawn, SpawnExitError, SpawnSignalError } from \"../lib/run-spawn\";\nimport {\n type PackageManagerName,\n resolvePackageManagerForCwd,\n} from \"../lib/utils/package-manager\";\n\nexport interface AddComponentsPlan {\n command: string;\n args: string[];\n}\n\nexport function createAddComponentsPlan(params: {\n components: string[];\n packageManager: PackageManagerName;\n yes?: boolean;\n overwrite?: boolean;\n cwd?: string;\n path?: string;\n style?: string;\n}): AddComponentsPlan {\n const componentsToAdd = params.components.map((c) => {\n if (!/^[a-zA-Z0-9-/]+$/.test(c)) {\n throw new Error(`Invalid component name: ${c}`);\n }\n return resolveRegistryItemUrl(c, params.style);\n });\n\n const [command, dlxArgs] = dlxCommand(params.packageManager);\n const args = [...dlxArgs, \"shadcn@latest\", \"add\", ...componentsToAdd];\n\n // For npm, dlxArgs may already include `--yes` for npx auto-install.\n // This flag is for shadcn's own confirmation prompt.\n if (params.yes) args.push(\"--yes\");\n if (params.overwrite) args.push(\"--overwrite\");\n if (params.cwd) args.push(\"--cwd\", params.cwd);\n if (params.path) args.push(\"--path\", params.path);\n\n return { command, args };\n}\n\nexport const add = new Command()\n .name(\"add\")\n .description(\"add a component to your project\")\n .argument(\"<components...>\", \"the components to add\")\n .option(\"-y, --yes\", \"skip confirmation prompt.\", true)\n .option(\"-o, --overwrite\", \"overwrite existing files.\", false)\n .option(\n \"-c, --cwd <cwd>\",\n \"the working directory. defaults to the current directory.\",\n process.cwd(),\n )\n .option(\"-p, --path <path>\", \"the path to add the component to.\")\n .option(\"--use-npm\", \"explicitly use npm\")\n .option(\"--use-pnpm\", \"explicitly use pnpm\")\n .option(\"--use-yarn\", \"explicitly use yarn\")\n .option(\"--use-bun\", \"explicitly use bun\")\n .action(async (components: string[], opts) => {\n // Check if project is initialized\n if (!hasConfig(opts.cwd)) {\n logger.warn(\n \"It looks like you haven't initialized your project yet — defaulting to Base UI flavored components. Run 'assistant-ui init' first for a configured setup.\",\n );\n logger.break();\n }\n\n logger.step(`Adding ${components.length} component(s)...`);\n\n const packageManager = await resolvePackageManagerForCwd(\n opts.cwd,\n resolvePackageManager(opts),\n );\n const style = getComponentsJsonStyle(opts.cwd);\n const { command, args } = createAddComponentsPlan({\n components,\n packageManager,\n yes: opts.yes,\n overwrite: opts.overwrite,\n cwd: opts.cwd,\n path: opts.path,\n ...(style === undefined ? {} : { style }),\n });\n\n try {\n await runSpawn(command, args, opts.cwd);\n } catch (error) {\n if (error instanceof SpawnSignalError) throw error;\n if (error instanceof SpawnExitError) {\n logger.error(`Process exited with code ${error.code}`);\n process.exit(error.code);\n }\n const message = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to add components: ${message}`);\n process.exit(1);\n }\n\n logger.success(\"Components added successfully!\");\n });\n"],"mappings":";;;;;;;;AAmBA,SAAgB,wBAAwB,QAQlB;CACpB,MAAM,kBAAkB,OAAO,WAAW,KAAK,MAAM;EACnD,IAAI,CAAC,mBAAmB,KAAK,CAAC,GAC5B,MAAM,IAAI,MAAM,2BAA2B,GAAG;EAEhD,OAAO,uBAAuB,GAAG,OAAO,KAAK;CAC/C,CAAC;CAED,MAAM,CAAC,SAAS,WAAW,WAAW,OAAO,cAAc;CAC3D,MAAM,OAAO;EAAC,GAAG;EAAS;EAAiB;EAAO,GAAG;CAAe;CAIpE,IAAI,OAAO,KAAK,KAAK,KAAK,OAAO;CACjC,IAAI,OAAO,WAAW,KAAK,KAAK,aAAa;CAC7C,IAAI,OAAO,KAAK,KAAK,KAAK,SAAS,OAAO,GAAG;CAC7C,IAAI,OAAO,MAAM,KAAK,KAAK,UAAU,OAAO,IAAI;CAEhD,OAAO;EAAE;EAAS;CAAK;AACzB;AAEA,MAAa,MAAM,IAAI,QAAQ,CAAC,CAC7B,KAAK,KAAK,CAAC,CACX,YAAY,iCAAiC,CAAC,CAC9C,SAAS,mBAAmB,uBAAuB,CAAC,CACpD,OAAO,aAAa,6BAA6B,IAAI,CAAC,CACtD,OAAO,mBAAmB,6BAA6B,KAAK,CAAC,CAC7D,OACC,mBACA,6DACA,QAAQ,IAAI,CACd,CAAC,CACA,OAAO,qBAAqB,mCAAmC,CAAC,CAChE,OAAO,aAAa,oBAAoB,CAAC,CACzC,OAAO,cAAc,qBAAqB,CAAC,CAC3C,OAAO,cAAc,qBAAqB,CAAC,CAC3C,OAAO,aAAa,oBAAoB,CAAC,CACzC,OAAO,OAAO,YAAsB,SAAS;CAE5C,IAAI,CAAC,UAAU,KAAK,GAAG,GAAG;EACxB,OAAO,KACL,2JACF;EACA,OAAO,MAAM;CACf;CAEA,OAAO,KAAK,UAAU,WAAW,OAAO,iBAAiB;CAEzD,MAAM,iBAAiB,MAAM,4BAC3B,KAAK,KACL,sBAAsB,IAAI,CAC5B;CACA,MAAM,QAAQ,uBAAuB,KAAK,GAAG;CAC7C,MAAM,EAAE,SAAS,SAAS,wBAAwB;EAChD;EACA;EACA,KAAK,KAAK;EACV,WAAW,KAAK;EAChB,KAAK,KAAK;EACV,MAAM,KAAK;EACX,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;CACzC,CAAC;CAED,IAAI;EACF,MAAM,SAAS,SAAS,MAAM,KAAK,GAAG;CACxC,SAAS,OAAO;EACd,IAAI,iBAAiB,kBAAkB,MAAM;EAC7C,IAAI,iBAAiB,gBAAgB;GACnC,OAAO,MAAM,4BAA4B,MAAM,MAAM;GACrD,QAAQ,KAAK,MAAM,IAAI;EACzB;EACA,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,OAAO,MAAM,6BAA6B,SAAS;EACnD,QAAQ,KAAK,CAAC;CAChB;CAEA,OAAO,QAAQ,gCAAgC;AACjD,CAAC"}
1
+ {"version":3,"file":"add.js","names":[],"sources":["../../src/commands/add.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { logger } from \"../lib/utils/logger\";\nimport { hasConfig } from \"../lib/utils/config\";\nimport {\n detectRegistryPlatform,\n getComponentsJsonStyle,\n resolveRegistryItemUrl,\n} from \"../lib/utils/registry\";\nimport { dlxCommand, resolvePackageManager } from \"../lib/create-project\";\nimport { runSpawn, SpawnExitError, SpawnSignalError } from \"../lib/run-spawn\";\nimport {\n type PackageManagerName,\n resolvePackageManagerForCwd,\n} from \"../lib/utils/package-manager\";\n\nexport interface AddComponentsPlan {\n command: string;\n args: string[];\n}\n\nexport function createAddComponentsPlan(params: {\n components: string[];\n packageManager: PackageManagerName;\n yes?: boolean;\n overwrite?: boolean;\n path?: string;\n style?: string;\n platform?: \"web\" | \"native\";\n}): AddComponentsPlan {\n const componentsToAdd = params.components.map((c) => {\n if (!/^[a-zA-Z0-9-/]+$/.test(c)) {\n throw new Error(`Invalid component name: ${c}`);\n }\n return resolveRegistryItemUrl(c, params.style, params.platform);\n });\n\n const [command, dlxArgs] = dlxCommand(params.packageManager);\n const args = [...dlxArgs, \"shadcn@latest\", \"add\", ...componentsToAdd];\n\n // For npm, dlxArgs may already include `--yes` for npx auto-install.\n // This flag is for shadcn's own confirmation prompt.\n if (params.yes) args.push(\"--yes\");\n if (params.overwrite) args.push(\"--overwrite\");\n if (params.path) args.push(\"--path\", params.path);\n\n return { command, args };\n}\n\nexport const add = new Command()\n .name(\"add\")\n .description(\"add a component to your project\")\n .argument(\"<components...>\", \"the components to add\")\n .option(\"-y, --yes\", \"skip confirmation prompt.\", true)\n .option(\"-o, --overwrite\", \"overwrite existing files.\", false)\n .option(\n \"-c, --cwd <cwd>\",\n \"the working directory. defaults to the current directory.\",\n process.cwd(),\n )\n .option(\"-p, --path <path>\", \"the path to add the component to.\")\n .option(\"--use-npm\", \"explicitly use npm\")\n .option(\"--use-pnpm\", \"explicitly use pnpm\")\n .option(\"--use-yarn\", \"explicitly use yarn\")\n .option(\"--use-bun\", \"explicitly use bun\")\n .action(async (components: string[], opts) => {\n const platform = detectRegistryPlatform(opts.cwd);\n\n // Check if project is initialized\n if (!hasConfig(opts.cwd)) {\n logger.warn(\n `It looks like you haven't initialized your project yet. Defaulting to ${platform === \"native\" ? \"the native component tree\" : \"Base UI flavored components\"}. Run 'assistant-ui init' first for a configured setup.`,\n );\n logger.break();\n }\n\n logger.info(`Using the ${platform} registry tree.`);\n logger.step(`Adding ${components.length} component(s)...`);\n\n const packageManager = await resolvePackageManagerForCwd(\n opts.cwd,\n resolvePackageManager(opts),\n );\n const style =\n platform === \"web\" ? getComponentsJsonStyle(opts.cwd) : undefined;\n const { command, args } = createAddComponentsPlan({\n components,\n packageManager,\n yes: opts.yes,\n overwrite: opts.overwrite,\n path: opts.path,\n ...(style === undefined ? {} : { style }),\n platform,\n });\n\n try {\n await runSpawn(command, args, opts.cwd);\n } catch (error) {\n if (error instanceof SpawnSignalError) throw error;\n if (error instanceof SpawnExitError) {\n logger.error(`Process exited with code ${error.code}`);\n process.exit(error.code);\n }\n const message = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to add components: ${message}`);\n process.exit(1);\n }\n\n logger.success(\"Components added successfully!\");\n });\n"],"mappings":";;;;;;;;AAoBA,SAAgB,wBAAwB,QAQlB;CACpB,MAAM,kBAAkB,OAAO,WAAW,KAAK,MAAM;EACnD,IAAI,CAAC,mBAAmB,KAAK,CAAC,GAC5B,MAAM,IAAI,MAAM,2BAA2B,GAAG;EAEhD,OAAO,uBAAuB,GAAG,OAAO,OAAO,OAAO,QAAQ;CAChE,CAAC;CAED,MAAM,CAAC,SAAS,WAAW,WAAW,OAAO,cAAc;CAC3D,MAAM,OAAO;EAAC,GAAG;EAAS;EAAiB;EAAO,GAAG;CAAe;CAIpE,IAAI,OAAO,KAAK,KAAK,KAAK,OAAO;CACjC,IAAI,OAAO,WAAW,KAAK,KAAK,aAAa;CAC7C,IAAI,OAAO,MAAM,KAAK,KAAK,UAAU,OAAO,IAAI;CAEhD,OAAO;EAAE;EAAS;CAAK;AACzB;AAEA,MAAa,MAAM,IAAI,QAAQ,CAAC,CAC7B,KAAK,KAAK,CAAC,CACX,YAAY,iCAAiC,CAAC,CAC9C,SAAS,mBAAmB,uBAAuB,CAAC,CACpD,OAAO,aAAa,6BAA6B,IAAI,CAAC,CACtD,OAAO,mBAAmB,6BAA6B,KAAK,CAAC,CAC7D,OACC,mBACA,6DACA,QAAQ,IAAI,CACd,CAAC,CACA,OAAO,qBAAqB,mCAAmC,CAAC,CAChE,OAAO,aAAa,oBAAoB,CAAC,CACzC,OAAO,cAAc,qBAAqB,CAAC,CAC3C,OAAO,cAAc,qBAAqB,CAAC,CAC3C,OAAO,aAAa,oBAAoB,CAAC,CACzC,OAAO,OAAO,YAAsB,SAAS;CAC5C,MAAM,WAAW,uBAAuB,KAAK,GAAG;CAGhD,IAAI,CAAC,UAAU,KAAK,GAAG,GAAG;EACxB,OAAO,KACL,yEAAyE,aAAa,WAAW,8BAA8B,8BAA8B,wDAC/J;EACA,OAAO,MAAM;CACf;CAEA,OAAO,KAAK,aAAa,SAAS,gBAAgB;CAClD,OAAO,KAAK,UAAU,WAAW,OAAO,iBAAiB;CAEzD,MAAM,iBAAiB,MAAM,4BAC3B,KAAK,KACL,sBAAsB,IAAI,CAC5B;CACA,MAAM,QACJ,aAAa,QAAQ,uBAAuB,KAAK,GAAG,IAAI,KAAA;CAC1D,MAAM,EAAE,SAAS,SAAS,wBAAwB;EAChD;EACA;EACA,KAAK,KAAK;EACV,WAAW,KAAK;EAChB,MAAM,KAAK;EACX,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACF,CAAC;CAED,IAAI;EACF,MAAM,SAAS,SAAS,MAAM,KAAK,GAAG;CACxC,SAAS,OAAO;EACd,IAAI,iBAAiB,kBAAkB,MAAM;EAC7C,IAAI,iBAAiB,gBAAgB;GACnC,OAAO,MAAM,4BAA4B,MAAM,MAAM;GACrD,QAAQ,KAAK,MAAM,IAAI;EACzB;EACA,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,OAAO,MAAM,6BAA6B,SAAS;EACnD,QAAQ,KAAK,CAAC;CAChB;CAEA,OAAO,QAAQ,gCAAgC;AACjD,CAAC"}
@@ -21,6 +21,14 @@ declare function resolveCreateProjectDirectory(params: {
21
21
  projectDirectory?: string;
22
22
  stdinIsTTY?: boolean;
23
23
  }): string | undefined;
24
+ declare function resolveProjectDirectoryGuidance(params: {
25
+ absoluteProjectDir: string;
26
+ cwd?: string;
27
+ platform?: NodeJS.Platform;
28
+ }): {
29
+ display: string;
30
+ cdCommand: string;
31
+ };
24
32
  declare function resolvePresetUrl(preset: string): string;
25
33
  interface ScaffoldSelectorOptions {
26
34
  template?: string;
@@ -37,5 +45,5 @@ interface ResolvedScaffoldSelector {
37
45
  declare function resolveScaffoldSelector(opts: ScaffoldSelectorOptions): ResolvedScaffoldSelector;
38
46
  declare const create: Command;
39
47
  //#endregion
40
- export { PROJECT_METADATA, ProjectMetadata, ResolvedScaffoldSelector, ScaffoldSelectorOptions, create, resolveCreateProjectDirectory, resolvePresetUrl, resolveProject, resolveScaffoldSelector };
48
+ export { PROJECT_METADATA, ProjectMetadata, ResolvedScaffoldSelector, ScaffoldSelectorOptions, create, resolveCreateProjectDirectory, resolvePresetUrl, resolveProject, resolveProjectDirectoryGuidance, resolveScaffoldSelector };
41
49
  //# sourceMappingURL=create.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","names":[],"sources":["../../src/commands/create.ts"],"mappings":";;;UA2BiB;EACf;EACA;EACA;EACA;EACA;EACA;;cAGW,kBAAkB;iBAsQT,eAAe;EACnC;EACA;EACA;EACA,gBAAgB,EAAE;EAClB,kBAAkB,EAAE;IAClB,QAAQ;iBA2EI,8BAA8B;EAC5C;EACA;;iBAYc,iBAAiB;UAOhB;EACf;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;;iBAac,wBACd,MAAM,0BACL;cAoCU,QAAM"}
1
+ {"version":3,"file":"create.d.ts","names":[],"sources":["../../src/commands/create.ts"],"mappings":";;;UA2BiB;EACf;EACA;EACA;EACA;EACA;EACA;;cAGW,kBAAkB;iBAoQT,eAAe;EACnC;EACA;EACA;EACA,gBAAgB,EAAE;EAClB,kBAAkB,EAAE;IAClB,QAAQ;iBA2EI,8BAA8B;EAC5C;EACA;;iBASc,gCAAgC;EAC9C;EACA;EACA,WAAW,OAAO;;EACd;EAAiB;;iBAmCP,iBAAiB;UAOhB;EACf;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;;iBAac,wBACd,MAAM,0BACL;cAoCU,QAAM"}
@@ -168,7 +168,7 @@ const PROJECT_METADATA = [
168
168
  description: "Expo / React Native",
169
169
  category: "example",
170
170
  path: "examples/with-expo",
171
- hasLocalComponents: true
171
+ hasLocalComponents: false
172
172
  },
173
173
  {
174
174
  name: "with-interactables",
@@ -312,6 +312,19 @@ function resolveCreateProjectDirectory(params) {
312
312
  if (projectDirectory) return projectDirectory;
313
313
  if (!stdinIsTTY) return "my-aui-app";
314
314
  }
315
+ function resolveProjectDirectoryGuidance(params) {
316
+ const { absoluteProjectDir, cwd = process.cwd(), platform = process.platform } = params;
317
+ const isWindows = platform === "win32";
318
+ const pathApi = isWindows ? path.win32 : path.posix;
319
+ const relative = pathApi.relative(cwd, absoluteProjectDir);
320
+ const escapesCwd = relative === ".." || relative.startsWith(`..${pathApi.sep}`);
321
+ const display = relative && !escapesCwd && !pathApi.isAbsolute(relative) ? relative : absoluteProjectDir;
322
+ const target = display.startsWith("-") ? `.${pathApi.sep}${display}` : display;
323
+ return {
324
+ display,
325
+ cdCommand: `cd ${(isWindows ? /^[\w@.:/\\+-]+$/ : /^[\w@./+-]+$/).test(target) ? target : isWindows ? `"${target}"` : `'${target.replaceAll("'", "'\\''")}'`}`
326
+ };
327
+ }
315
328
  const PLAYGROUND_PRESET_BASE_URL = "https://www.assistant-ui.com/playground/init";
316
329
  function resolvePresetUrl(preset) {
317
330
  if (preset.startsWith("http://") || preset.startsWith("https://")) return preset;
@@ -377,19 +390,20 @@ const create = new Command().name("create").description("create a new project").
377
390
  resolvedProjectDirectory = result;
378
391
  }
379
392
  const absoluteProjectDir = path.resolve(resolvedProjectDirectory);
393
+ const { display: displayProjectDir, cdCommand } = resolveProjectDirectoryGuidance({ absoluteProjectDir });
380
394
  try {
381
395
  if (fs.readdirSync(absoluteProjectDir).length > 0) {
382
- logger.error(`Directory ${resolvedProjectDirectory} already exists and is not empty`);
396
+ logger.error(`Directory ${displayProjectDir} already exists and is not empty`);
383
397
  process.exit(1);
384
398
  }
385
399
  } catch (err) {
386
400
  const code = err instanceof Error ? err.code : void 0;
387
401
  if (code === "ENOENT") {} else if (code === "ENOTDIR") {
388
- logger.error(`${resolvedProjectDirectory} already exists and is not a directory`);
402
+ logger.error(`${displayProjectDir} already exists and is not a directory`);
389
403
  process.exit(1);
390
404
  } else {
391
405
  const message = err instanceof Error ? err.message : String(err);
392
- logger.error(`Cannot access ${resolvedProjectDirectory}: ${message}`);
406
+ logger.error(`Cannot access ${displayProjectDir}: ${message}`);
393
407
  process.exit(1);
394
408
  }
395
409
  }
@@ -489,7 +503,7 @@ const create = new Command().name("create").description("create a new project").
489
503
  logger.break();
490
504
  logger.error("Project created with missing components.");
491
505
  logger.info("Retry the component install with:");
492
- logger.info(` cd ${resolvedProjectDirectory}`);
506
+ logger.info(` ${cdCommand}`);
493
507
  logger.info(` ${transformResult.registryInstallFailure.retryCommand}`);
494
508
  process.exit(1);
495
509
  }
@@ -530,8 +544,11 @@ const create = new Command().name("create").description("create a new project").
530
544
  envFile = scaffoldedPkg.dependencies?.next ? ".env.local" : ".env";
531
545
  } catch {}
532
546
  logger.info("Next steps:");
533
- logger.info(` cd ${resolvedProjectDirectory}`);
534
- if (opts.skipInstall) logger.info(` ${pm} install`);
547
+ logger.info(` ${cdCommand}`);
548
+ if (opts.skipInstall) {
549
+ logger.info(` ${pm} install`);
550
+ if (transformResult.registryInstallCommand) logger.info(` ${transformResult.registryInstallCommand}`);
551
+ }
535
552
  logger.info(` # Set up your environment variables in ${envFile}`);
536
553
  logger.info(` ${runCmd} ${devScript}`);
537
554
  } catch (error) {
@@ -550,6 +567,6 @@ const create = new Command().name("create").description("create a new project").
550
567
  }
551
568
  });
552
569
  //#endregion
553
- export { PROJECT_METADATA, create, resolveCreateProjectDirectory, resolvePresetUrl, resolveProject, resolveScaffoldSelector };
570
+ export { PROJECT_METADATA, create, resolveCreateProjectDirectory, resolvePresetUrl, resolveProject, resolveProjectDirectoryGuidance, resolveScaffoldSelector };
554
571
 
555
572
  //# sourceMappingURL=create.js.map