assistant-ui 0.0.105 → 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.
- package/README.md +2 -2
- package/dist/codemods/utils/createTransformer.d.ts +0 -1
- package/dist/codemods/utils/createTransformer.d.ts.map +1 -1
- package/dist/codemods/v0-11/content-part-to-message-part.d.ts.map +1 -1
- package/dist/codemods/v0-12/assistant-api-to-aui.d.ts.map +1 -1
- package/dist/codemods/v0-12/assistant-api-to-aui.js.map +1 -1
- package/dist/codemods/v0-12/event-names-to-camelcase.d.ts.map +1 -1
- package/dist/codemods/v0-12/primitive-if-to-aui-if.d.ts.map +1 -1
- package/dist/codemods/v0-8/ui-package-split.d.ts.map +1 -1
- package/dist/codemods/v0-8/ui-package-split.js.map +1 -1
- package/dist/codemods/v0-9/edge-package-split.d.ts.map +1 -1
- package/dist/commands/add.d.ts +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +8 -5
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/agent.d.ts +0 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/create.d.ts +0 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +26 -8
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +66 -29
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/info.d.ts +3 -2
- package/dist/commands/info.d.ts.map +1 -1
- package/dist/commands/info.js +22 -26
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/init.d.ts +1 -3
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -24
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp.d.ts +0 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +14 -4
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/update.d.ts +0 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +14 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/upgrade.d.ts +0 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/lib/agent-skill.d.ts +0 -1
- package/dist/lib/agent-skill.d.ts.map +1 -1
- package/dist/lib/create-project.d.ts +7 -2
- package/dist/lib/create-project.d.ts.map +1 -1
- package/dist/lib/create-project.js +34 -17
- package/dist/lib/create-project.js.map +1 -1
- package/dist/lib/install-ai-sdk-lib.d.ts.map +1 -1
- package/dist/lib/install-edge-lib.d.ts.map +1 -1
- package/dist/lib/install-ui-lib.d.ts.map +1 -1
- package/dist/lib/run-spawn.d.ts.map +1 -1
- package/dist/lib/transform-options.d.ts.map +1 -1
- package/dist/lib/transform.d.ts +0 -1
- package/dist/lib/transform.d.ts.map +1 -1
- package/dist/lib/upgrade.d.ts +0 -1
- package/dist/lib/upgrade.d.ts.map +1 -1
- package/dist/lib/utils/config.d.ts.map +1 -1
- package/dist/lib/utils/file-scanner.d.ts.map +1 -1
- package/dist/lib/utils/logger.d.ts.map +1 -1
- package/dist/lib/utils/package-installer.d.ts.map +1 -1
- package/dist/lib/utils/package-manager.d.ts.map +1 -1
- package/dist/lib/utils/registry.d.ts +7 -0
- package/dist/lib/utils/registry.d.ts.map +1 -0
- package/dist/lib/utils/registry.js +25 -0
- package/dist/lib/utils/registry.js.map +1 -0
- package/dist/lib/utils/workspace.d.ts +6 -0
- package/dist/lib/utils/workspace.d.ts.map +1 -0
- package/dist/lib/utils/workspace.js +28 -0
- package/dist/lib/utils/workspace.js.map +1 -0
- package/dist/program.d.ts +0 -1
- package/dist/program.d.ts.map +1 -1
- package/package.json +12 -9
- package/plugin/skills/assistant-ui/SKILL.md +24 -5
- package/src/commands/add.ts +9 -4
- package/src/commands/create.ts +28 -8
- package/src/commands/doctor.ts +97 -44
- package/src/commands/info.ts +49 -42
- package/src/commands/init.ts +16 -24
- package/src/commands/mcp.ts +20 -5
- package/src/commands/update.ts +17 -1
- package/src/lib/create-project.ts +64 -20
- package/src/lib/utils/registry.test.ts +114 -0
- package/src/lib/utils/registry.ts +43 -0
- package/src/lib/utils/workspace.ts +34 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assistant-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.107",
|
|
4
4
|
"description": "CLI for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -26,27 +26,30 @@
|
|
|
26
26
|
],
|
|
27
27
|
"sideEffects": false,
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@clack/prompts": "^1.
|
|
29
|
+
"@clack/prompts": "^1.7.0",
|
|
30
30
|
"chalk": "^5.6.2",
|
|
31
31
|
"cli-progress": "^3.12.0",
|
|
32
32
|
"commander": "^15.0.0",
|
|
33
33
|
"cross-spawn": "^7.0.6",
|
|
34
34
|
"debug": "^4.4.3",
|
|
35
35
|
"detect-package-manager": "^3.0.2",
|
|
36
|
-
"giget": "^3.3.
|
|
36
|
+
"giget": "^3.3.1",
|
|
37
37
|
"glob": "^13.0.6",
|
|
38
|
-
"jscodeshift": "^17.
|
|
39
|
-
"
|
|
38
|
+
"jscodeshift": "^17.4.0",
|
|
39
|
+
"jsonc-parser": "^3.3.1",
|
|
40
|
+
"semver": "^7.8.5",
|
|
41
|
+
"@assistant-ui/agent-launcher": "0.1.9"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
44
|
"@types/cli-progress": "^3.11.6",
|
|
43
45
|
"@types/cross-spawn": "^6.0.6",
|
|
44
46
|
"@types/debug": "^4.1.13",
|
|
45
47
|
"@types/jscodeshift": "^17.3.0",
|
|
46
|
-
"@types/node": "^26.
|
|
47
|
-
"@
|
|
48
|
-
"vitest": "^4.1.
|
|
49
|
-
"
|
|
48
|
+
"@types/node": "^26.1.1",
|
|
49
|
+
"@types/semver": "^7.7.1",
|
|
50
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
51
|
+
"vitest": "^4.1.10",
|
|
52
|
+
"@assistant-ui/x-buildutils": "0.0.19"
|
|
50
53
|
},
|
|
51
54
|
"publishConfig": {
|
|
52
55
|
"access": "public",
|
|
@@ -24,20 +24,39 @@ This initializes shadcn and installs the default assistant-ui chat components.
|
|
|
24
24
|
Install components via the shadcn registry:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
npx shadcn@latest add "https://r.assistant-ui.com/chat/b/ai-sdk-quick-start/json"
|
|
27
|
+
npx shadcn@latest add "https://r.assistant-ui.com/base/chat/b/ai-sdk-quick-start/json"
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
For Radix-styled projects (`components.json` style not starting with `base-`), use `https://r.assistant-ui.com/chat/b/ai-sdk-quick-start/json` instead.
|
|
31
|
+
|
|
30
32
|
Available component presets:
|
|
31
33
|
|
|
32
34
|
| Preset | Registry URL |
|
|
33
35
|
|--------|-------------|
|
|
34
|
-
| AI SDK Quick Start | `https://r.assistant-ui.com/chat/b/ai-sdk-quick-start/json` |
|
|
36
|
+
| AI SDK Quick Start | `https://r.assistant-ui.com/base/chat/b/ai-sdk-quick-start/json` |
|
|
37
|
+
|
|
38
|
+
You can also add individual assistant-ui components with the assistant-ui CLI, which picks the right flavor from `components.json`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx assistant-ui add thread
|
|
42
|
+
npx assistant-ui add markdown-text
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
To use the shadcn CLI directly instead, add the assistant-ui registry to `components.json`:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"registries": {
|
|
50
|
+
"@assistant-ui": "https://r.assistant-ui.com/styles/{style}/{name}.json"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
35
54
|
|
|
36
|
-
|
|
55
|
+
Then add components with the namespaced form:
|
|
37
56
|
|
|
38
57
|
```bash
|
|
39
|
-
npx shadcn@latest add assistant-ui/thread
|
|
40
|
-
npx shadcn@latest add assistant-ui/markdown-text
|
|
58
|
+
npx shadcn@latest add @assistant-ui/thread
|
|
59
|
+
npx shadcn@latest add @assistant-ui/markdown-text
|
|
41
60
|
```
|
|
42
61
|
|
|
43
62
|
## Step 3: Runtime Setup
|
package/src/commands/add.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { logger } from "../lib/utils/logger";
|
|
3
3
|
import { hasConfig } from "../lib/utils/config";
|
|
4
|
+
import {
|
|
5
|
+
getComponentsJsonStyle,
|
|
6
|
+
resolveRegistryItemUrl,
|
|
7
|
+
} from "../lib/utils/registry";
|
|
4
8
|
import {
|
|
5
9
|
dlxCommand,
|
|
6
10
|
resolvePackageManager,
|
|
@@ -9,8 +13,6 @@ import {
|
|
|
9
13
|
} from "../lib/create-project";
|
|
10
14
|
import { runSpawn, SpawnExitError } from "../lib/run-spawn";
|
|
11
15
|
|
|
12
|
-
const REGISTRY_BASE_URL = "https://r.assistant-ui.com";
|
|
13
|
-
|
|
14
16
|
export interface AddComponentsPlan {
|
|
15
17
|
command: string;
|
|
16
18
|
args: string[];
|
|
@@ -23,12 +25,13 @@ export function createAddComponentsPlan(params: {
|
|
|
23
25
|
overwrite?: boolean;
|
|
24
26
|
cwd?: string;
|
|
25
27
|
path?: string;
|
|
28
|
+
style?: string;
|
|
26
29
|
}): AddComponentsPlan {
|
|
27
30
|
const componentsToAdd = params.components.map((c) => {
|
|
28
31
|
if (!/^[a-zA-Z0-9-/]+$/.test(c)) {
|
|
29
32
|
throw new Error(`Invalid component name: ${c}`);
|
|
30
33
|
}
|
|
31
|
-
return
|
|
34
|
+
return resolveRegistryItemUrl(c, params.style);
|
|
32
35
|
});
|
|
33
36
|
|
|
34
37
|
const [command, dlxArgs] = dlxCommand(params.packageManager);
|
|
@@ -64,7 +67,7 @@ export const add = new Command()
|
|
|
64
67
|
// Check if project is initialized
|
|
65
68
|
if (!hasConfig(opts.cwd)) {
|
|
66
69
|
logger.warn(
|
|
67
|
-
"It looks like you haven't initialized your project yet. Run 'assistant-ui init' first.",
|
|
70
|
+
"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.",
|
|
68
71
|
);
|
|
69
72
|
logger.break();
|
|
70
73
|
}
|
|
@@ -75,6 +78,7 @@ export const add = new Command()
|
|
|
75
78
|
opts.cwd,
|
|
76
79
|
resolvePackageManager(opts),
|
|
77
80
|
);
|
|
81
|
+
const style = getComponentsJsonStyle(opts.cwd);
|
|
78
82
|
const { command, args } = createAddComponentsPlan({
|
|
79
83
|
components,
|
|
80
84
|
packageManager,
|
|
@@ -82,6 +86,7 @@ export const add = new Command()
|
|
|
82
86
|
overwrite: opts.overwrite,
|
|
83
87
|
cwd: opts.cwd,
|
|
84
88
|
path: opts.path,
|
|
89
|
+
...(style === undefined ? {} : { style }),
|
|
85
90
|
});
|
|
86
91
|
|
|
87
92
|
try {
|
package/src/commands/create.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
resolvePackageManagerForCwd,
|
|
13
13
|
scaffoldProject,
|
|
14
14
|
transformProject,
|
|
15
|
+
type TransformResult,
|
|
15
16
|
} from "../lib/create-project";
|
|
16
17
|
import { runSpawn, SpawnExitError } from "../lib/run-spawn";
|
|
17
18
|
import {
|
|
@@ -104,11 +105,11 @@ export const PROJECT_METADATA: ProjectMetadata[] = [
|
|
|
104
105
|
hasLocalComponents: false,
|
|
105
106
|
},
|
|
106
107
|
{
|
|
107
|
-
name: "with-ai-sdk-
|
|
108
|
-
label: "AI SDK
|
|
109
|
-
description: "Vercel AI SDK
|
|
108
|
+
name: "with-ai-sdk-v7",
|
|
109
|
+
label: "AI SDK v7",
|
|
110
|
+
description: "Vercel AI SDK v7",
|
|
110
111
|
category: "example",
|
|
111
|
-
path: "examples/with-ai-sdk-
|
|
112
|
+
path: "examples/with-ai-sdk-v7",
|
|
112
113
|
hasLocalComponents: false,
|
|
113
114
|
},
|
|
114
115
|
{
|
|
@@ -165,7 +166,7 @@ export const PROJECT_METADATA: ProjectMetadata[] = [
|
|
|
165
166
|
description: "Realtime voice with ElevenLabs",
|
|
166
167
|
category: "example",
|
|
167
168
|
path: "examples/with-elevenlabs-conversational",
|
|
168
|
-
hasLocalComponents:
|
|
169
|
+
hasLocalComponents: false,
|
|
169
170
|
},
|
|
170
171
|
{
|
|
171
172
|
name: "with-elevenlabs-scribe",
|
|
@@ -181,7 +182,7 @@ export const PROJECT_METADATA: ProjectMetadata[] = [
|
|
|
181
182
|
description: "Realtime voice with LiveKit",
|
|
182
183
|
category: "example",
|
|
183
184
|
path: "examples/with-livekit",
|
|
184
|
-
hasLocalComponents:
|
|
185
|
+
hasLocalComponents: false,
|
|
185
186
|
},
|
|
186
187
|
{
|
|
187
188
|
name: "with-expo",
|
|
@@ -197,7 +198,7 @@ export const PROJECT_METADATA: ProjectMetadata[] = [
|
|
|
197
198
|
description: "AI-driven interactive UI components",
|
|
198
199
|
category: "example",
|
|
199
200
|
path: "examples/with-interactables",
|
|
200
|
-
hasLocalComponents:
|
|
201
|
+
hasLocalComponents: false,
|
|
201
202
|
},
|
|
202
203
|
{
|
|
203
204
|
name: "with-external-store",
|
|
@@ -255,6 +256,14 @@ export const PROJECT_METADATA: ProjectMetadata[] = [
|
|
|
255
256
|
path: "examples/with-tanstack",
|
|
256
257
|
hasLocalComponents: false,
|
|
257
258
|
},
|
|
259
|
+
{
|
|
260
|
+
name: "with-resumable-stream",
|
|
261
|
+
label: "Resumable Stream",
|
|
262
|
+
description: "Resumable LLM stream that survives reload mid-response",
|
|
263
|
+
category: "example",
|
|
264
|
+
path: "examples/with-resumable-stream",
|
|
265
|
+
hasLocalComponents: false,
|
|
266
|
+
},
|
|
258
267
|
];
|
|
259
268
|
|
|
260
269
|
// Examples that exist in the monorepo but are intentionally excluded from the CLI:
|
|
@@ -601,6 +610,7 @@ export const create = new Command()
|
|
|
601
610
|
? `Copying project from local source: ${localSourceRoot}`
|
|
602
611
|
: "Downloading project...",
|
|
603
612
|
);
|
|
613
|
+
let transformResult: TransformResult;
|
|
604
614
|
try {
|
|
605
615
|
const source = localSourceRoot
|
|
606
616
|
? { kind: "local" as const, rootDir: localSourceRoot }
|
|
@@ -624,7 +634,7 @@ export const create = new Command()
|
|
|
624
634
|
}
|
|
625
635
|
|
|
626
636
|
// 5. Run transform pipeline
|
|
627
|
-
await transformProject(absoluteProjectDir, {
|
|
637
|
+
transformResult = await transformProject(absoluteProjectDir, {
|
|
628
638
|
hasLocalComponents: project.hasLocalComponents,
|
|
629
639
|
skipInstall: opts.skipInstall,
|
|
630
640
|
packageManager: pm,
|
|
@@ -649,6 +659,16 @@ export const create = new Command()
|
|
|
649
659
|
throw err;
|
|
650
660
|
}
|
|
651
661
|
|
|
662
|
+
if (transformResult.registryInstallFailure) {
|
|
663
|
+
process.removeListener("exit", cleanupOnExit);
|
|
664
|
+
logger.break();
|
|
665
|
+
logger.error("Project created with missing components.");
|
|
666
|
+
logger.info("Retry the component install with:");
|
|
667
|
+
logger.info(` cd ${resolvedProjectDirectory}`);
|
|
668
|
+
logger.info(` ${transformResult.registryInstallFailure.retryCommand}`);
|
|
669
|
+
process.exit(1);
|
|
670
|
+
}
|
|
671
|
+
|
|
652
672
|
// 6. Apply preset if provided
|
|
653
673
|
if (scaffoldSelector.preset) {
|
|
654
674
|
const presetUrl = resolvePresetUrl(scaffoldSelector.preset);
|
package/src/commands/doctor.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Command } from "commander";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
+
import { compare, valid } from "semver";
|
|
6
|
+
import { findWorkspaceRoot, resolveRealPath } from "../lib/utils/workspace";
|
|
5
7
|
|
|
6
8
|
const ASSISTANT_UI_PACKAGE_NAMES = new Set([
|
|
7
9
|
"assistant-stream",
|
|
@@ -38,13 +40,7 @@ function processPackageDir(
|
|
|
38
40
|
results: DiscoveredPackage[],
|
|
39
41
|
visited: ProcessedDir,
|
|
40
42
|
): void {
|
|
41
|
-
const real = (
|
|
42
|
-
try {
|
|
43
|
-
return fs.realpathSync(pkgDir);
|
|
44
|
-
} catch {
|
|
45
|
-
return pkgDir;
|
|
46
|
-
}
|
|
47
|
-
})();
|
|
43
|
+
const real = resolveRealPath(pkgDir);
|
|
48
44
|
if (visited.set.has(real)) return;
|
|
49
45
|
visited.set.add(real);
|
|
50
46
|
|
|
@@ -106,13 +102,99 @@ function walkNodeModulesAt(
|
|
|
106
102
|
}
|
|
107
103
|
}
|
|
108
104
|
|
|
105
|
+
// pnpm keeps every real package dir in the `.pnpm` virtual store as
|
|
106
|
+
// `node_modules/.pnpm/<pkg>@<ver>[_<peerhash>]/node_modules/<pkg>` and never
|
|
107
|
+
// nests a package's deps inside its own dir, so the hoisted-layout walk above
|
|
108
|
+
// only ever reaches the project's direct deps. The store dir name encodes the
|
|
109
|
+
// package, so filtering by a tracked prefix keeps this pass O(tracked entries).
|
|
110
|
+
const TRACKED_PNPM_ENTRY_PREFIXES = [
|
|
111
|
+
"@assistant-ui+",
|
|
112
|
+
"assistant-ui@",
|
|
113
|
+
"assistant-stream@",
|
|
114
|
+
"assistant-cloud@",
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
function isTrackedPnpmEntry(dirName: string): boolean {
|
|
118
|
+
return TRACKED_PNPM_ENTRY_PREFIXES.some((p) => dirName.startsWith(p));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function processTrackedPackagesIn(
|
|
122
|
+
nm: string,
|
|
123
|
+
results: DiscoveredPackage[],
|
|
124
|
+
visited: ProcessedDir,
|
|
125
|
+
): void {
|
|
126
|
+
let entries: fs.Dirent[];
|
|
127
|
+
try {
|
|
128
|
+
entries = fs.readdirSync(nm, { withFileTypes: true });
|
|
129
|
+
} catch {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for (const entry of entries) {
|
|
134
|
+
if (entry.name.startsWith(".")) continue;
|
|
135
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
136
|
+
|
|
137
|
+
if (entry.name.startsWith("@")) {
|
|
138
|
+
const scopeDir = path.join(nm, entry.name);
|
|
139
|
+
let scoped: fs.Dirent[];
|
|
140
|
+
try {
|
|
141
|
+
scoped = fs.readdirSync(scopeDir, { withFileTypes: true });
|
|
142
|
+
} catch {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
for (const s of scoped) {
|
|
146
|
+
if (!s.isDirectory() && !s.isSymbolicLink()) continue;
|
|
147
|
+
if (!isTrackedPackage(`${entry.name}/${s.name}`)) continue;
|
|
148
|
+
processPackageDir(path.join(scopeDir, s.name), results, visited);
|
|
149
|
+
}
|
|
150
|
+
} else if (isTrackedPackage(entry.name)) {
|
|
151
|
+
processPackageDir(path.join(nm, entry.name), results, visited);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function walkPnpmStore(
|
|
157
|
+
cwd: string,
|
|
158
|
+
results: DiscoveredPackage[],
|
|
159
|
+
visited: ProcessedDir,
|
|
160
|
+
): void {
|
|
161
|
+
const storeDir = path.join(cwd, "node_modules", ".pnpm");
|
|
162
|
+
let entries: fs.Dirent[];
|
|
163
|
+
try {
|
|
164
|
+
entries = fs.readdirSync(storeDir, { withFileTypes: true });
|
|
165
|
+
} catch {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
for (const entry of entries) {
|
|
170
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
171
|
+
if (!isTrackedPnpmEntry(entry.name)) continue;
|
|
172
|
+
processTrackedPackagesIn(
|
|
173
|
+
path.join(storeDir, entry.name, "node_modules"),
|
|
174
|
+
results,
|
|
175
|
+
visited,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
109
180
|
// Discover every installation of an assistant-ui-family package reachable
|
|
110
|
-
// from `cwd`.
|
|
111
|
-
//
|
|
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.
|
|
112
184
|
export function discoverInstalledPackages(cwd: string): DiscoveredPackage[] {
|
|
113
185
|
const results: DiscoveredPackage[] = [];
|
|
114
186
|
const visited: ProcessedDir = { set: new Set() };
|
|
115
|
-
|
|
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
|
+
|
|
116
198
|
return results;
|
|
117
199
|
}
|
|
118
200
|
|
|
@@ -177,40 +259,11 @@ async function fetchAllLatestVersions(
|
|
|
177
259
|
return new Map(entries);
|
|
178
260
|
}
|
|
179
261
|
|
|
180
|
-
interface SemverParts {
|
|
181
|
-
major: number;
|
|
182
|
-
minor: number;
|
|
183
|
-
patch: number;
|
|
184
|
-
prerelease: string;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function parseSemver(v: string): SemverParts | null {
|
|
188
|
-
const m = /^(\d+)\.(\d+)\.(\d+)(?:-([^+\s]+))?/.exec(v);
|
|
189
|
-
if (!m) return null;
|
|
190
|
-
return {
|
|
191
|
-
major: parseInt(m[1]!, 10),
|
|
192
|
-
minor: parseInt(m[2]!, 10),
|
|
193
|
-
patch: parseInt(m[3]!, 10),
|
|
194
|
-
prerelease: m[4] ?? "",
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
262
|
export function compareSemver(a: string, b: string): number {
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
if (!
|
|
202
|
-
|
|
203
|
-
if (pa.minor !== pb.minor) return pa.minor - pb.minor;
|
|
204
|
-
if (pa.patch !== pb.patch) return pa.patch - pb.patch;
|
|
205
|
-
|
|
206
|
-
// Per SemVer §11: a version with a prerelease tag is *less than* the
|
|
207
|
-
// same x.y.z without one. We compare tags lexically for a stable
|
|
208
|
-
// ordering across prereleases — good enough for doctor's "is X older
|
|
209
|
-
// than the npm latest" check.
|
|
210
|
-
if (pa.prerelease === pb.prerelease) return 0;
|
|
211
|
-
if (!pa.prerelease) return 1;
|
|
212
|
-
if (!pb.prerelease) return -1;
|
|
213
|
-
return pa.prerelease.localeCompare(pb.prerelease);
|
|
263
|
+
const validA = valid(a);
|
|
264
|
+
const validB = valid(b);
|
|
265
|
+
if (!validA || !validB) return a.localeCompare(b);
|
|
266
|
+
return compare(validA, validB);
|
|
214
267
|
}
|
|
215
268
|
|
|
216
269
|
export interface OutdatedPackage {
|
|
@@ -320,7 +373,7 @@ export const doctor = new Command()
|
|
|
320
373
|
)
|
|
321
374
|
.option("--no-network", "Skip the npm registry check for latest versions.")
|
|
322
375
|
.action(async (opts: { cwd: string; network: boolean }) => {
|
|
323
|
-
const cwd =
|
|
376
|
+
const cwd = resolveRealPath(opts.cwd);
|
|
324
377
|
const packageJsonPath = path.join(cwd, "package.json");
|
|
325
378
|
|
|
326
379
|
if (!fs.existsSync(packageJsonPath)) {
|
package/src/commands/info.ts
CHANGED
|
@@ -5,6 +5,10 @@ import * as path from "node:path";
|
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import { detect } from "detect-package-manager";
|
|
8
|
+
import { satisfies } from "semver";
|
|
9
|
+
import { findWorkspaceRoot, resolveRealPath } from "../lib/utils/workspace";
|
|
10
|
+
|
|
11
|
+
export { findWorkspaceRoot };
|
|
8
12
|
|
|
9
13
|
const ASSISTANT_UI_PACKAGES = [
|
|
10
14
|
// Distribution
|
|
@@ -88,25 +92,6 @@ function getInstalledVersion(pkg: string, cwd: string): string | null {
|
|
|
88
92
|
return null;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
function findWorkspaceRoot(cwd: string): string | null {
|
|
92
|
-
let dir = path.dirname(cwd);
|
|
93
|
-
const root = path.parse(dir).root;
|
|
94
|
-
while (dir !== root) {
|
|
95
|
-
if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml"))) return dir;
|
|
96
|
-
const pkgPath = path.join(dir, "package.json");
|
|
97
|
-
if (fs.existsSync(pkgPath)) {
|
|
98
|
-
try {
|
|
99
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
100
|
-
if (pkg.workspaces) return dir;
|
|
101
|
-
} catch {
|
|
102
|
-
// ignore
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
dir = path.dirname(dir);
|
|
106
|
-
}
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
95
|
function readProjectDeps(
|
|
111
96
|
projectPkg: Record<string, unknown>,
|
|
112
97
|
): Record<string, string> {
|
|
@@ -116,6 +101,16 @@ function readProjectDeps(
|
|
|
116
101
|
};
|
|
117
102
|
}
|
|
118
103
|
|
|
104
|
+
function getAssistantUiPackageNames(
|
|
105
|
+
projectPkg: Record<string, unknown>,
|
|
106
|
+
): string[] {
|
|
107
|
+
const declaredPackages = Object.keys(readProjectDeps(projectPkg))
|
|
108
|
+
.filter((name) => name.startsWith("@assistant-ui/"))
|
|
109
|
+
.sort();
|
|
110
|
+
|
|
111
|
+
return [...new Set([...ASSISTANT_UI_PACKAGES, ...declaredPackages])];
|
|
112
|
+
}
|
|
113
|
+
|
|
119
114
|
function getSpecifiedRange(
|
|
120
115
|
pkg: string,
|
|
121
116
|
projectPkg: Record<string, unknown>,
|
|
@@ -186,6 +181,19 @@ function getOsInfo(): string {
|
|
|
186
181
|
}
|
|
187
182
|
}
|
|
188
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
|
+
|
|
189
197
|
async function getPackageManagerInfo(
|
|
190
198
|
cwd: string,
|
|
191
199
|
): Promise<{ name: string; version: string }> {
|
|
@@ -198,27 +206,18 @@ async function getPackageManagerInfo(
|
|
|
198
206
|
return { name: pm, version };
|
|
199
207
|
}
|
|
200
208
|
|
|
201
|
-
function satisfiesRange(version: string, range: string): boolean {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const rangeMajor = major(range);
|
|
214
|
-
const versionMajor = major(version);
|
|
215
|
-
|
|
216
|
-
if (Number.isNaN(rangeMajor) || Number.isNaN(versionMajor)) return true;
|
|
217
|
-
|
|
218
|
-
if (range.startsWith("^")) return versionMajor >= rangeMajor;
|
|
219
|
-
if (range.startsWith(">=")) return versionMajor >= rangeMajor;
|
|
220
|
-
|
|
221
|
-
return versionMajor >= rangeMajor;
|
|
209
|
+
export function satisfiesRange(version: string, range: string): boolean {
|
|
210
|
+
const normalizedRange = range.startsWith("workspace:")
|
|
211
|
+
? range.slice("workspace:".length)
|
|
212
|
+
: range;
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
normalizedRange === "" ||
|
|
216
|
+
normalizedRange === "^" ||
|
|
217
|
+
normalizedRange === "~" ||
|
|
218
|
+
normalizedRange === "any" ||
|
|
219
|
+
satisfies(version, normalizedRange, { includePrerelease: true })
|
|
220
|
+
);
|
|
222
221
|
}
|
|
223
222
|
|
|
224
223
|
interface PackageInfo {
|
|
@@ -228,6 +227,7 @@ interface PackageInfo {
|
|
|
228
227
|
}
|
|
229
228
|
|
|
230
229
|
interface InfoData {
|
|
230
|
+
cliVersion: string;
|
|
231
231
|
os: string;
|
|
232
232
|
node: string;
|
|
233
233
|
pm: { name: string; version: string };
|
|
@@ -306,11 +306,16 @@ async function collectInfo(
|
|
|
306
306
|
projectPkg: Record<string, unknown>,
|
|
307
307
|
): Promise<InfoData> {
|
|
308
308
|
const pm = await getPackageManagerInfo(cwd);
|
|
309
|
-
const packages = collectPackages(
|
|
309
|
+
const packages = collectPackages(
|
|
310
|
+
getAssistantUiPackageNames(projectPkg),
|
|
311
|
+
cwd,
|
|
312
|
+
projectPkg,
|
|
313
|
+
);
|
|
310
314
|
const ecosystem = collectPackages(ECOSYSTEM_PACKAGES, cwd, projectPkg);
|
|
311
315
|
const warnings = collectWarnings(packages, cwd, projectPkg);
|
|
312
316
|
|
|
313
317
|
return {
|
|
318
|
+
cliVersion: getCliVersion(),
|
|
314
319
|
os: getOsInfo(),
|
|
315
320
|
node: process.version,
|
|
316
321
|
pm,
|
|
@@ -338,6 +343,7 @@ function renderPlain(data: InfoData): string[] {
|
|
|
338
343
|
const lines: string[] = [];
|
|
339
344
|
|
|
340
345
|
lines.push("Environment:");
|
|
346
|
+
lines.push(` assistant-ui CLI: ${data.cliVersion}`);
|
|
341
347
|
lines.push(` OS: ${data.os}`);
|
|
342
348
|
lines.push(` Node.js: ${data.node}`);
|
|
343
349
|
lines.push(` Package Manager: ${data.pm.name} ${data.pm.version}`);
|
|
@@ -364,6 +370,7 @@ function renderColored(data: InfoData): string[] {
|
|
|
364
370
|
const lines: string[] = [];
|
|
365
371
|
|
|
366
372
|
lines.push(chalk.bold("Environment:"));
|
|
373
|
+
lines.push(` assistant-ui CLI: ${data.cliVersion}`);
|
|
367
374
|
lines.push(` OS: ${data.os}`);
|
|
368
375
|
lines.push(` Node.js: ${data.node}`);
|
|
369
376
|
lines.push(` Package Manager: ${data.pm.name} ${data.pm.version}`);
|
|
@@ -409,7 +416,7 @@ export const info = new Command()
|
|
|
409
416
|
process.cwd(),
|
|
410
417
|
)
|
|
411
418
|
.action(async (opts) => {
|
|
412
|
-
const cwd =
|
|
419
|
+
const cwd = resolveRealPath(opts.cwd);
|
|
413
420
|
const packageJsonPath = path.join(cwd, "package.json");
|
|
414
421
|
|
|
415
422
|
if (!fs.existsSync(packageJsonPath)) {
|
package/src/commands/init.ts
CHANGED
|
@@ -8,35 +8,28 @@ import {
|
|
|
8
8
|
} from "../lib/create-project";
|
|
9
9
|
import { runSpawn, SpawnExitError } from "../lib/run-spawn";
|
|
10
10
|
import { logger } from "../lib/utils/logger";
|
|
11
|
+
import {
|
|
12
|
+
getComponentsJsonStyle,
|
|
13
|
+
resolveQuickStartRegistryUrl,
|
|
14
|
+
} from "../lib/utils/registry";
|
|
11
15
|
import { create } from "./create";
|
|
12
16
|
|
|
13
|
-
const DEFAULT_REGISTRY_URL =
|
|
14
|
-
"https://r.assistant-ui.com/chat/b/ai-sdk-quick-start/json";
|
|
15
|
-
|
|
16
17
|
interface ExistingProjectInitPlan {
|
|
17
|
-
initArgs: string[]
|
|
18
|
+
initArgs: string[];
|
|
18
19
|
addArgs: string[];
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export function createExistingProjectInitPlan(params: {
|
|
22
23
|
yes: boolean;
|
|
23
24
|
overwrite: boolean;
|
|
24
|
-
registryUrl: string;
|
|
25
25
|
}): ExistingProjectInitPlan {
|
|
26
|
-
const { yes, overwrite
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return { initArgs: null, addArgs };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const initArgs = [`shadcn@latest`, "init", "--defaults", "--yes"];
|
|
36
|
-
|
|
37
|
-
const addArgs = [`shadcn@latest`, "add", "--yes"];
|
|
26
|
+
const { yes, overwrite } = params;
|
|
27
|
+
const initArgs = yes
|
|
28
|
+
? [`shadcn@latest`, "init", "--defaults", "--yes"]
|
|
29
|
+
: [`shadcn@latest`, "init"];
|
|
30
|
+
const addArgs = [`shadcn@latest`, "add"];
|
|
31
|
+
if (yes) addArgs.push("--yes");
|
|
38
32
|
if (overwrite) addArgs.push("--overwrite");
|
|
39
|
-
addArgs.push(registryUrl);
|
|
40
33
|
|
|
41
34
|
return { initArgs, addArgs };
|
|
42
35
|
}
|
|
@@ -106,7 +99,6 @@ export const init = new Command()
|
|
|
106
99
|
return;
|
|
107
100
|
}
|
|
108
101
|
|
|
109
|
-
const registryUrl = DEFAULT_REGISTRY_URL;
|
|
110
102
|
logger.info("Initializing assistant-ui in existing project...");
|
|
111
103
|
logger.break();
|
|
112
104
|
|
|
@@ -131,13 +123,13 @@ export const init = new Command()
|
|
|
131
123
|
const { initArgs, addArgs } = createExistingProjectInitPlan({
|
|
132
124
|
yes: opts.yes,
|
|
133
125
|
overwrite: opts.overwrite,
|
|
134
|
-
registryUrl,
|
|
135
126
|
});
|
|
136
127
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
128
|
+
await runSpawn(dlxCmd, [...dlxArgs, ...initArgs], targetDir);
|
|
129
|
+
const registryUrl = resolveQuickStartRegistryUrl(
|
|
130
|
+
getComponentsJsonStyle(targetDir),
|
|
131
|
+
);
|
|
132
|
+
await runSpawn(dlxCmd, [...dlxArgs, ...addArgs, registryUrl], targetDir);
|
|
141
133
|
|
|
142
134
|
logger.break();
|
|
143
135
|
logger.success("Project initialized successfully!");
|