assistant-ui 0.0.106 → 0.0.107

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 (37) hide show
  1. package/dist/codemods/v0-12/assistant-api-to-aui.js.map +1 -1
  2. package/dist/codemods/v0-8/ui-package-split.js.map +1 -1
  3. package/dist/commands/add.js +1 -1
  4. package/dist/commands/add.js.map +1 -1
  5. package/dist/commands/create.d.ts.map +1 -1
  6. package/dist/commands/create.js +14 -4
  7. package/dist/commands/create.js.map +1 -1
  8. package/dist/commands/doctor.d.ts.map +1 -1
  9. package/dist/commands/doctor.js +11 -10
  10. package/dist/commands/doctor.js.map +1 -1
  11. package/dist/commands/info.d.ts +1 -1
  12. package/dist/commands/info.d.ts.map +1 -1
  13. package/dist/commands/info.js +13 -14
  14. package/dist/commands/info.js.map +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/lib/create-project.d.ts +7 -2
  17. package/dist/lib/create-project.d.ts.map +1 -1
  18. package/dist/lib/create-project.js +13 -10
  19. package/dist/lib/create-project.js.map +1 -1
  20. package/dist/lib/run-spawn.d.ts.map +1 -1
  21. package/dist/lib/utils/logger.d.ts.map +1 -1
  22. package/dist/lib/utils/registry.js +2 -1
  23. package/dist/lib/utils/registry.js.map +1 -1
  24. package/dist/lib/utils/workspace.d.ts +6 -0
  25. package/dist/lib/utils/workspace.d.ts.map +1 -0
  26. package/dist/lib/utils/workspace.js +28 -0
  27. package/dist/lib/utils/workspace.js.map +1 -0
  28. package/package.json +4 -4
  29. package/plugin/skills/assistant-ui/SKILL.md +24 -5
  30. package/src/commands/add.ts +1 -1
  31. package/src/commands/create.ts +16 -4
  32. package/src/commands/doctor.ts +17 -13
  33. package/src/commands/info.ts +21 -20
  34. package/src/lib/create-project.ts +25 -14
  35. package/src/lib/utils/registry.test.ts +8 -2
  36. package/src/lib/utils/registry.ts +5 -1
  37. package/src/lib/utils/workspace.ts +34 -0
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import chalk from "chalk";
5
5
  import { compare, valid } from "semver";
6
+ import { findWorkspaceRoot, resolveRealPath } from "../lib/utils/workspace";
6
7
 
7
8
  const ASSISTANT_UI_PACKAGE_NAMES = new Set([
8
9
  "assistant-stream",
@@ -39,13 +40,7 @@ function processPackageDir(
39
40
  results: DiscoveredPackage[],
40
41
  visited: ProcessedDir,
41
42
  ): void {
42
- const real = (() => {
43
- try {
44
- return fs.realpathSync(pkgDir);
45
- } catch {
46
- return pkgDir;
47
- }
48
- })();
43
+ const real = resolveRealPath(pkgDir);
49
44
  if (visited.set.has(real)) return;
50
45
  visited.set.add(real);
51
46
 
@@ -183,14 +178,23 @@ function walkPnpmStore(
183
178
  }
184
179
 
185
180
  // Discover every installation of an assistant-ui-family package reachable
186
- // from `cwd`. Recurses into nested node_modules so transitive copies
187
- // (the real source of duplicate-version bugs) are not missed, and scans the
188
- // pnpm `.pnpm` virtual store, which the hoisted walk cannot reach.
181
+ // from `cwd`. Node resolves packages through ancestor node_modules directories,
182
+ // so inspect each level to include workspace-hoisted installs. Nested installs
183
+ // and pnpm virtual stores are scanned to catch duplicate transitive copies.
189
184
  export function discoverInstalledPackages(cwd: string): DiscoveredPackage[] {
190
185
  const results: DiscoveredPackage[] = [];
191
186
  const visited: ProcessedDir = { set: new Set() };
192
- walkNodeModulesAt(cwd, results, visited);
193
- walkPnpmStore(cwd, results, visited);
187
+ let dir = resolveRealPath(cwd);
188
+ const scanRoot = findWorkspaceRoot(dir) ?? dir;
189
+
190
+ while (true) {
191
+ walkNodeModulesAt(dir, results, visited);
192
+ walkPnpmStore(dir, results, visited);
193
+
194
+ if (dir === scanRoot) break;
195
+ dir = path.dirname(dir);
196
+ }
197
+
194
198
  return results;
195
199
  }
196
200
 
@@ -369,7 +373,7 @@ export const doctor = new Command()
369
373
  )
370
374
  .option("--no-network", "Skip the npm registry check for latest versions.")
371
375
  .action(async (opts: { cwd: string; network: boolean }) => {
372
- const cwd = path.resolve(opts.cwd);
376
+ const cwd = resolveRealPath(opts.cwd);
373
377
  const packageJsonPath = path.join(cwd, "package.json");
374
378
 
375
379
  if (!fs.existsSync(packageJsonPath)) {
@@ -6,6 +6,9 @@ import { spawnSync } from "node:child_process";
6
6
  import chalk from "chalk";
7
7
  import { detect } from "detect-package-manager";
8
8
  import { satisfies } from "semver";
9
+ import { findWorkspaceRoot, resolveRealPath } from "../lib/utils/workspace";
10
+
11
+ export { findWorkspaceRoot };
9
12
 
10
13
  const ASSISTANT_UI_PACKAGES = [
11
14
  // Distribution
@@ -89,25 +92,6 @@ function getInstalledVersion(pkg: string, cwd: string): string | null {
89
92
  return null;
90
93
  }
91
94
 
92
- export function findWorkspaceRoot(cwd: string): string | null {
93
- let dir = cwd;
94
- const root = path.parse(dir).root;
95
- while (true) {
96
- if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml"))) return dir;
97
- const pkgPath = path.join(dir, "package.json");
98
- if (fs.existsSync(pkgPath)) {
99
- try {
100
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
101
- if (pkg.workspaces) return dir;
102
- } catch {
103
- // ignore
104
- }
105
- }
106
- if (dir === root) return null;
107
- dir = path.dirname(dir);
108
- }
109
- }
110
-
111
95
  function readProjectDeps(
112
96
  projectPkg: Record<string, unknown>,
113
97
  ): Record<string, string> {
@@ -197,6 +181,19 @@ function getOsInfo(): string {
197
181
  }
198
182
  }
199
183
 
184
+ function getCliVersion(): string {
185
+ try {
186
+ const packageJson = JSON.parse(
187
+ fs.readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
188
+ ) as { version?: unknown };
189
+ return typeof packageJson.version === "string"
190
+ ? packageJson.version
191
+ : "unknown";
192
+ } catch {
193
+ return "unknown";
194
+ }
195
+ }
196
+
200
197
  async function getPackageManagerInfo(
201
198
  cwd: string,
202
199
  ): Promise<{ name: string; version: string }> {
@@ -230,6 +227,7 @@ interface PackageInfo {
230
227
  }
231
228
 
232
229
  interface InfoData {
230
+ cliVersion: string;
233
231
  os: string;
234
232
  node: string;
235
233
  pm: { name: string; version: string };
@@ -317,6 +315,7 @@ async function collectInfo(
317
315
  const warnings = collectWarnings(packages, cwd, projectPkg);
318
316
 
319
317
  return {
318
+ cliVersion: getCliVersion(),
320
319
  os: getOsInfo(),
321
320
  node: process.version,
322
321
  pm,
@@ -344,6 +343,7 @@ function renderPlain(data: InfoData): string[] {
344
343
  const lines: string[] = [];
345
344
 
346
345
  lines.push("Environment:");
346
+ lines.push(` assistant-ui CLI: ${data.cliVersion}`);
347
347
  lines.push(` OS: ${data.os}`);
348
348
  lines.push(` Node.js: ${data.node}`);
349
349
  lines.push(` Package Manager: ${data.pm.name} ${data.pm.version}`);
@@ -370,6 +370,7 @@ function renderColored(data: InfoData): string[] {
370
370
  const lines: string[] = [];
371
371
 
372
372
  lines.push(chalk.bold("Environment:"));
373
+ lines.push(` assistant-ui CLI: ${data.cliVersion}`);
373
374
  lines.push(` OS: ${data.os}`);
374
375
  lines.push(` Node.js: ${data.node}`);
375
376
  lines.push(` Package Manager: ${data.pm.name} ${data.pm.version}`);
@@ -415,7 +416,7 @@ export const info = new Command()
415
416
  process.cwd(),
416
417
  )
417
418
  .action(async (opts) => {
418
- const cwd = path.resolve(opts.cwd);
419
+ const cwd = resolveRealPath(opts.cwd);
419
420
  const packageJsonPath = path.join(cwd, "package.json");
420
421
 
421
422
  if (!fs.existsSync(packageJsonPath)) {
@@ -210,22 +210,25 @@ export async function resolvePackageManagerForCwd(
210
210
  }
211
211
  }
212
212
 
213
+ export interface TransformResult {
214
+ registryInstallFailure?: { retryCommand: string };
215
+ }
216
+
213
217
  export async function transformProject(
214
218
  projectDir: string,
215
219
  opts: TransformOptions,
216
- ): Promise<void> {
220
+ ): Promise<TransformResult> {
217
221
  logger.step("Transforming package.json...");
218
222
  transformPackageJson(projectDir);
219
223
 
224
+ logger.step("Transforming project files...");
225
+ transformTsConfig(projectDir);
226
+ transformCssFiles(projectDir);
227
+
220
228
  let assistantUI: string[] | undefined;
221
229
  let shadcnUI: string[] | undefined;
222
230
 
223
231
  if (!opts.hasLocalComponents) {
224
- logger.step("Transforming project files...");
225
-
226
- transformTsConfig(projectDir);
227
- transformCssFiles(projectDir);
228
-
229
232
  const components = scanRequiredComponents(projectDir);
230
233
  assistantUI = components.assistantUI;
231
234
  shadcnUI = components.shadcnUI;
@@ -249,8 +252,15 @@ export async function transformProject(
249
252
  const auiComponents = assistantUI.map((c) => `@assistant-ui/${c}`);
250
253
  const components = [...allShadcn, ...auiComponents];
251
254
  logger.step(`Installing components: ${components.join(", ")}...`);
252
- await installShadcnRegistry(projectDir, components, "components", pm);
255
+ const failure = await installShadcnRegistry(
256
+ projectDir,
257
+ components,
258
+ "components",
259
+ pm,
260
+ );
261
+ if (failure) return { registryInstallFailure: failure };
253
262
  }
263
+ return {};
254
264
  }
255
265
 
256
266
  function transformPackageJson(projectDir: string): void {
@@ -326,7 +336,8 @@ function transformTsConfig(projectDir: string): void {
326
336
  Array.isArray(targets) &&
327
337
  targets.some(
328
338
  (target) =>
329
- typeof target === "string" && target.includes("packages/ui/"),
339
+ typeof target === "string" &&
340
+ (target.includes("packages/ui/") || target.startsWith("../")),
330
341
  );
331
342
  if (workspaceKeys.has(key) || targetsWorkspace) {
332
343
  delete tsconfig.compilerOptions.paths[key];
@@ -461,20 +472,20 @@ async function installShadcnRegistry(
461
472
  components: string[],
462
473
  label: string,
463
474
  pm: PackageManagerName,
464
- ): Promise<void> {
475
+ ): Promise<{ retryCommand: string } | undefined> {
465
476
  const [cmd, dlxArgs] = dlxCommand(pm);
466
477
  // For npm, dlxArgs may already include `--yes` for npx auto-install.
467
478
  // The trailing `--yes` is for shadcn's own confirmation prompt.
468
- const addArgs = [...dlxArgs, "shadcn@latest", "add", ...components, "--yes"];
479
+ const retryArgs = [...dlxArgs, "shadcn@latest", "add", ...components];
480
+ const addArgs = [...retryArgs, "--yes"];
469
481
 
470
482
  try {
471
483
  await runSpawn(cmd, addArgs, projectDir);
484
+ return undefined;
472
485
  } catch (error) {
473
486
  if (error instanceof SpawnExitError) {
474
- logger.warn(
475
- `shadcn exited with code ${error.code}. Run the following to retry:\n ${cmd} ${addArgs.slice(0, -1).join(" ")}`,
476
- );
477
- return;
487
+ logger.warn(`shadcn exited with code ${error.code}.`);
488
+ return { retryCommand: `${cmd} ${retryArgs.join(" ")}` };
478
489
  }
479
490
 
480
491
  const message = error instanceof Error ? error.message : String(error);
@@ -94,9 +94,15 @@ describe("resolveRegistryItemUrl", () => {
94
94
  );
95
95
  });
96
96
 
97
- it("uses the plain URL without a style", () => {
97
+ it("uses the base URL without a style", () => {
98
98
  expect(resolveRegistryItemUrl("thread")).toBe(
99
- "https://r.assistant-ui.com/thread.json",
99
+ "https://r.assistant-ui.com/base/thread.json",
100
+ );
101
+ });
102
+
103
+ it("uses the base URL for an explicit undefined style", () => {
104
+ expect(resolveRegistryItemUrl("thread", undefined)).toBe(
105
+ "https://r.assistant-ui.com/base/thread.json",
100
106
  );
101
107
  });
102
108
 
@@ -31,7 +31,11 @@ export function resolveRegistryItemUrl(
31
31
  component: string,
32
32
  style?: string,
33
33
  ): string {
34
- const registryUrl = style?.startsWith("base-")
34
+ if (style === undefined) {
35
+ return `${REGISTRY_BASE_URL}/base/${encodeURIComponent(component)}.json`;
36
+ }
37
+
38
+ const registryUrl = style.startsWith("base-")
35
39
  ? `${REGISTRY_BASE_URL}/styles/${encodeURIComponent(style)}`
36
40
  : REGISTRY_BASE_URL;
37
41
 
@@ -0,0 +1,34 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+
4
+ export function resolveRealPath(inputPath: string): string {
5
+ const resolved = path.resolve(inputPath);
6
+
7
+ try {
8
+ return fs.realpathSync(resolved);
9
+ } catch {
10
+ return resolved;
11
+ }
12
+ }
13
+
14
+ export function findWorkspaceRoot(cwd: string): string | null {
15
+ let dir = path.resolve(cwd);
16
+ const root = path.parse(dir).root;
17
+
18
+ while (true) {
19
+ if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml"))) return dir;
20
+
21
+ const pkgPath = path.join(dir, "package.json");
22
+ if (fs.existsSync(pkgPath)) {
23
+ try {
24
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
25
+ if (pkg.workspaces) return dir;
26
+ } catch {
27
+ // Malformed package manifests are not workspace markers.
28
+ }
29
+ }
30
+
31
+ if (dir === root) return null;
32
+ dir = path.dirname(dir);
33
+ }
34
+ }