assistant-ui 0.0.80 → 0.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +34 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/create.d.ts +13 -45
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +347 -102
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/create-project.d.ts +12 -0
- package/dist/lib/create-project.d.ts.map +1 -0
- package/dist/lib/create-project.js +273 -0
- package/dist/lib/create-project.js.map +1 -0
- package/package.json +7 -4
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/skills/assistant-ui/SKILL.md +167 -0
- package/src/commands/agent.ts +36 -0
- package/src/commands/create.ts +402 -129
- package/src/commands/init.ts +2 -2
- package/src/index.ts +2 -0
- package/src/lib/create-project.ts +367 -0
- package/dist/lib/create-from-example.d.ts +0 -9
- package/dist/lib/create-from-example.d.ts.map +0 -1
- package/dist/lib/create-from-example.js +0 -325
- package/dist/lib/create-from-example.js.map +0 -1
- package/src/lib/create-from-example.ts +0 -426
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Passing `--preset` to `init` also forwards to `create` for compatibility.
|
|
|
20
20
|
|
|
21
21
|
Use the `create` command to scaffold a new Next.js project with assistant-ui.
|
|
22
22
|
|
|
23
|
-
The `create` command
|
|
23
|
+
The `create` command scaffolds a project from assistant-ui starter templates or examples.
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
npx assistant-ui@latest create my-app
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,eAAO,MAAM,KAAK,SAcd,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { resolve, dirname } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { launch } from "@assistant-ui/agent-launcher";
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
function getPluginPath() {
|
|
8
|
+
// In dist/, plugin is at ../../plugin relative to dist/commands/agent.js
|
|
9
|
+
// In dev (src/), plugin is at ../../plugin relative to src/commands/
|
|
10
|
+
const candidates = [
|
|
11
|
+
resolve(__dirname, "..", "..", "plugin"),
|
|
12
|
+
resolve(__dirname, "..", "plugin"),
|
|
13
|
+
];
|
|
14
|
+
for (const candidate of candidates) {
|
|
15
|
+
if (existsSync(candidate))
|
|
16
|
+
return candidate;
|
|
17
|
+
}
|
|
18
|
+
return candidates[0];
|
|
19
|
+
}
|
|
20
|
+
export const agent = new Command()
|
|
21
|
+
.name("agent")
|
|
22
|
+
.description("launch Claude Code with assistant-ui skills")
|
|
23
|
+
.argument("<prompt...>", "prompt for the agent")
|
|
24
|
+
.option("--dry", "print the command instead of running it")
|
|
25
|
+
.action((promptParts, opts) => {
|
|
26
|
+
const prompt = promptParts.join(" ");
|
|
27
|
+
launch({
|
|
28
|
+
pluginDir: getPluginPath(),
|
|
29
|
+
skillName: "assistant-ui",
|
|
30
|
+
prompt,
|
|
31
|
+
dry: opts.dry,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAEtD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,SAAS,aAAa;IACpB,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,UAAU,GAAG;QACjB,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC;QACxC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC;KACnC,CAAC;IACF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAC9C,CAAC;IACD,OAAO,UAAU,CAAC,CAAC,CAAE,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,EAAE;KAC/B,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,6CAA6C,CAAC;KAC1D,QAAQ,CAAC,aAAa,EAAE,sBAAsB,CAAC;KAC/C,MAAM,CAAC,OAAO,EAAE,yCAAyC,CAAC;KAC1D,MAAM,CAAC,CAAC,WAAqB,EAAE,IAAI,EAAE,EAAE;IACtC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAErC,MAAM,CAAC;QACL,SAAS,EAAE,aAAa,EAAE;QAC1B,SAAS,EAAE,cAAc;QACzB,MAAM;QACN,GAAG,EAAE,IAAI,CAAC,GAAG;KACd,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,57 +1,25 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import * as p from "@clack/prompts";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
readonly cloud: {
|
|
15
|
-
readonly url: "https://github.com/assistant-ui/assistant-cloud-starter";
|
|
16
|
-
readonly label: "Cloud";
|
|
17
|
-
readonly hint: "Cloud-backed persistence starter";
|
|
18
|
-
};
|
|
19
|
-
readonly "cloud-clerk": {
|
|
20
|
-
readonly url: "https://github.com/assistant-ui/assistant-ui-starter-cloud-clerk";
|
|
21
|
-
readonly label: "Cloud + Clerk";
|
|
22
|
-
readonly hint: "Cloud-backed starter with Clerk auth";
|
|
23
|
-
};
|
|
24
|
-
readonly langgraph: {
|
|
25
|
-
readonly url: "https://github.com/assistant-ui/assistant-ui-starter-langgraph";
|
|
26
|
-
readonly label: "LangGraph";
|
|
27
|
-
readonly hint: "LangGraph starter template";
|
|
28
|
-
};
|
|
29
|
-
readonly mcp: {
|
|
30
|
-
readonly url: "https://github.com/assistant-ui/assistant-ui-starter-mcp";
|
|
31
|
-
readonly label: "MCP";
|
|
32
|
-
readonly hint: "MCP starter template";
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
type TemplateName = keyof typeof templates;
|
|
36
|
-
export declare function resolveCreateTemplateName(params: {
|
|
3
|
+
export interface ProjectMetadata {
|
|
4
|
+
name: string;
|
|
5
|
+
label: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
category: "template" | "example";
|
|
8
|
+
path: string;
|
|
9
|
+
hasLocalComponents: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const PROJECT_METADATA: ProjectMetadata[];
|
|
12
|
+
export declare function resolveProject(params: {
|
|
37
13
|
template?: string;
|
|
14
|
+
example?: string;
|
|
38
15
|
stdinIsTTY?: boolean;
|
|
39
16
|
select?: typeof p.select;
|
|
40
17
|
isCancel?: typeof p.isCancel;
|
|
41
|
-
}): Promise<
|
|
42
|
-
export declare function buildCreateNextAppArgs(params: {
|
|
43
|
-
projectDirectory?: string;
|
|
44
|
-
useNpm?: boolean;
|
|
45
|
-
usePnpm?: boolean;
|
|
46
|
-
useYarn?: boolean;
|
|
47
|
-
useBun?: boolean;
|
|
48
|
-
skipInstall?: boolean;
|
|
49
|
-
templateUrl: string;
|
|
50
|
-
}): string[];
|
|
18
|
+
}): Promise<ProjectMetadata | null>;
|
|
51
19
|
export declare function resolveCreateProjectDirectory(params: {
|
|
52
20
|
projectDirectory?: string;
|
|
53
21
|
stdinIsTTY?: boolean;
|
|
54
22
|
}): string | undefined;
|
|
23
|
+
export declare function resolvePresetUrl(preset: string): string;
|
|
55
24
|
export declare const create: Command;
|
|
56
|
-
export {};
|
|
57
25
|
//# sourceMappingURL=create.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AAWpC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,eAAO,MAAM,gBAAgB,EAAE,eAAe,EA2K7C,CAAC;AAUF,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC;CAC9B,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAyElC;AAiCD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,MAAM,GAAG,SAAS,CAMrB;AAkBD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED,eAAO,MAAM,MAAM,SAwMf,CAAC"}
|
package/dist/commands/create.js
CHANGED
|
@@ -1,65 +1,242 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { spawn } from "cross-spawn";
|
|
4
|
+
import fs from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import * as p from "@clack/prompts";
|
|
6
7
|
import { logger } from "../lib/utils/logger.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
import { dlxCommand, downloadProject, resolveLatestReleaseRef, resolvePackageManagerName, transformProject, } from "../lib/create-project.js";
|
|
9
|
+
export const PROJECT_METADATA = [
|
|
10
|
+
// Templates
|
|
11
|
+
{
|
|
12
|
+
name: "default",
|
|
12
13
|
label: "Default",
|
|
13
|
-
|
|
14
|
+
description: "Default template with Vercel AI SDK",
|
|
15
|
+
category: "template",
|
|
16
|
+
path: "templates/default",
|
|
17
|
+
hasLocalComponents: true,
|
|
14
18
|
},
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
{
|
|
20
|
+
name: "minimal",
|
|
17
21
|
label: "Minimal",
|
|
18
|
-
|
|
22
|
+
description: "Bare-bones starting point",
|
|
23
|
+
category: "template",
|
|
24
|
+
path: "templates/minimal",
|
|
25
|
+
hasLocalComponents: true,
|
|
19
26
|
},
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
{
|
|
28
|
+
name: "cloud",
|
|
22
29
|
label: "Cloud",
|
|
23
|
-
|
|
30
|
+
description: "Cloud-backed persistence starter",
|
|
31
|
+
category: "template",
|
|
32
|
+
path: "templates/cloud",
|
|
33
|
+
hasLocalComponents: true,
|
|
24
34
|
},
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
{
|
|
36
|
+
name: "cloud-clerk",
|
|
27
37
|
label: "Cloud + Clerk",
|
|
28
|
-
|
|
38
|
+
description: "Cloud-backed starter with Clerk auth",
|
|
39
|
+
category: "template",
|
|
40
|
+
path: "templates/cloud-clerk",
|
|
41
|
+
hasLocalComponents: true,
|
|
29
42
|
},
|
|
30
|
-
|
|
31
|
-
|
|
43
|
+
{
|
|
44
|
+
name: "langgraph",
|
|
32
45
|
label: "LangGraph",
|
|
33
|
-
|
|
46
|
+
description: "LangGraph starter template",
|
|
47
|
+
category: "template",
|
|
48
|
+
path: "templates/langgraph",
|
|
49
|
+
hasLocalComponents: true,
|
|
34
50
|
},
|
|
35
|
-
|
|
36
|
-
|
|
51
|
+
{
|
|
52
|
+
name: "mcp",
|
|
37
53
|
label: "MCP",
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
description: "MCP starter template",
|
|
55
|
+
category: "template",
|
|
56
|
+
path: "templates/mcp",
|
|
57
|
+
hasLocalComponents: true,
|
|
58
|
+
},
|
|
59
|
+
// Examples
|
|
60
|
+
{
|
|
61
|
+
name: "with-ag-ui",
|
|
62
|
+
label: "AG-UI",
|
|
63
|
+
description: "AG-UI protocol integration",
|
|
64
|
+
category: "example",
|
|
65
|
+
path: "examples/with-ag-ui",
|
|
66
|
+
hasLocalComponents: false,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "with-ai-sdk-v6",
|
|
70
|
+
label: "AI SDK v6",
|
|
71
|
+
description: "Vercel AI SDK v6",
|
|
72
|
+
category: "example",
|
|
73
|
+
path: "examples/with-ai-sdk-v6",
|
|
74
|
+
hasLocalComponents: false,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "with-artifacts",
|
|
78
|
+
label: "Artifacts",
|
|
79
|
+
description: "Artifact rendering",
|
|
80
|
+
category: "example",
|
|
81
|
+
path: "examples/with-artifacts",
|
|
82
|
+
hasLocalComponents: false,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "with-assistant-transport",
|
|
86
|
+
label: "Assistant Transport",
|
|
87
|
+
description: "Assistant transport protocol",
|
|
88
|
+
category: "example",
|
|
89
|
+
path: "examples/with-assistant-transport",
|
|
90
|
+
hasLocalComponents: false,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "with-chain-of-thought",
|
|
94
|
+
label: "Chain of Thought",
|
|
95
|
+
description: "Chain-of-thought rendering",
|
|
96
|
+
category: "example",
|
|
97
|
+
path: "examples/with-chain-of-thought",
|
|
98
|
+
hasLocalComponents: false,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "with-cloud",
|
|
102
|
+
label: "Cloud Example",
|
|
103
|
+
description: "Cloud integration example",
|
|
104
|
+
category: "example",
|
|
105
|
+
path: "examples/with-cloud",
|
|
106
|
+
hasLocalComponents: false,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "with-custom-thread-list",
|
|
110
|
+
label: "Custom Thread List",
|
|
111
|
+
description: "Custom thread list UI",
|
|
112
|
+
category: "example",
|
|
113
|
+
path: "examples/with-custom-thread-list",
|
|
114
|
+
hasLocalComponents: false,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "with-elevenlabs-scribe",
|
|
118
|
+
label: "ElevenLabs Scribe",
|
|
119
|
+
description: "Audio/speech integration",
|
|
120
|
+
category: "example",
|
|
121
|
+
path: "examples/with-elevenlabs-scribe",
|
|
122
|
+
hasLocalComponents: false,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "with-external-store",
|
|
126
|
+
label: "External Store",
|
|
127
|
+
description: "Custom message store",
|
|
128
|
+
category: "example",
|
|
129
|
+
path: "examples/with-external-store",
|
|
130
|
+
hasLocalComponents: false,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "with-ffmpeg",
|
|
134
|
+
label: "FFmpeg",
|
|
135
|
+
description: "File processing",
|
|
136
|
+
category: "example",
|
|
137
|
+
path: "examples/with-ffmpeg",
|
|
138
|
+
hasLocalComponents: false,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "with-langgraph",
|
|
142
|
+
label: "LangGraph Example",
|
|
143
|
+
description: "LangGraph integration",
|
|
144
|
+
category: "example",
|
|
145
|
+
path: "examples/with-langgraph",
|
|
146
|
+
hasLocalComponents: false,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "with-parent-id-grouping",
|
|
150
|
+
label: "Parent ID Grouping",
|
|
151
|
+
description: "Message grouping strategy",
|
|
152
|
+
category: "example",
|
|
153
|
+
path: "examples/with-parent-id-grouping",
|
|
154
|
+
hasLocalComponents: false,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "with-react-hook-form",
|
|
158
|
+
label: "React Hook Form",
|
|
159
|
+
description: "Form integration",
|
|
160
|
+
category: "example",
|
|
161
|
+
path: "examples/with-react-hook-form",
|
|
162
|
+
hasLocalComponents: false,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "with-react-router",
|
|
166
|
+
label: "React Router",
|
|
167
|
+
description: "React Router v7 + Vite",
|
|
168
|
+
category: "example",
|
|
169
|
+
path: "examples/with-react-router",
|
|
170
|
+
hasLocalComponents: false,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: "with-tanstack",
|
|
174
|
+
label: "TanStack",
|
|
175
|
+
description: "TanStack/React Router + Vite",
|
|
176
|
+
category: "example",
|
|
177
|
+
path: "examples/with-tanstack",
|
|
178
|
+
hasLocalComponents: false,
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
const templateNames = PROJECT_METADATA.filter((m) => m.category === "template").map((m) => m.name);
|
|
182
|
+
const exampleNames = PROJECT_METADATA.filter((m) => m.category === "example").map((m) => m.name);
|
|
183
|
+
export async function resolveProject(params) {
|
|
184
|
+
const { template, example, stdinIsTTY = process.stdin.isTTY, select = p.select, isCancel = p.isCancel, } = params;
|
|
49
185
|
if (template) {
|
|
50
|
-
|
|
186
|
+
const meta = PROJECT_METADATA.find((m) => m.name === template && m.category === "template");
|
|
187
|
+
if (!meta) {
|
|
188
|
+
logger.error(`Unknown template: ${template}`);
|
|
189
|
+
logger.info(`Available templates: ${templateNames.join(", ")}`);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
return meta;
|
|
193
|
+
}
|
|
194
|
+
if (example) {
|
|
195
|
+
const meta = PROJECT_METADATA.find((m) => m.name === example && m.category === "example");
|
|
196
|
+
if (!meta) {
|
|
197
|
+
logger.error(`Unknown example: ${example}`);
|
|
198
|
+
logger.info(`Available examples: ${exampleNames.join(", ")}`);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
return meta;
|
|
51
202
|
}
|
|
52
203
|
if (!stdinIsTTY) {
|
|
53
|
-
return "default";
|
|
204
|
+
return PROJECT_METADATA.find((m) => m.name === "default");
|
|
54
205
|
}
|
|
55
206
|
const selected = await select({
|
|
56
|
-
message: "Select a
|
|
57
|
-
options:
|
|
207
|
+
message: "Select a project to scaffold:",
|
|
208
|
+
options: [
|
|
209
|
+
{
|
|
210
|
+
value: "_separator",
|
|
211
|
+
label: "────── Starter Templates ──────",
|
|
212
|
+
disabled: true,
|
|
213
|
+
},
|
|
214
|
+
...PROJECT_METADATA.filter((m) => m.category === "template").map((m) => ({
|
|
215
|
+
value: m.name,
|
|
216
|
+
label: m.label,
|
|
217
|
+
...(m.description ? { hint: m.description } : {}),
|
|
218
|
+
})),
|
|
219
|
+
{
|
|
220
|
+
value: "_separator",
|
|
221
|
+
label: "────── Feature Examples ──────",
|
|
222
|
+
disabled: true,
|
|
223
|
+
},
|
|
224
|
+
...PROJECT_METADATA.filter((m) => m.category === "example").map((m) => ({
|
|
225
|
+
value: m.name,
|
|
226
|
+
label: m.label,
|
|
227
|
+
...(m.description ? { hint: m.description } : {}),
|
|
228
|
+
})),
|
|
229
|
+
],
|
|
58
230
|
});
|
|
59
231
|
if (isCancel(selected)) {
|
|
60
232
|
return null;
|
|
61
233
|
}
|
|
62
|
-
|
|
234
|
+
const meta = PROJECT_METADATA.find((m) => m.name === selected);
|
|
235
|
+
if (!meta) {
|
|
236
|
+
logger.error(`Unknown selection: ${String(selected)}`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
return meta;
|
|
63
240
|
}
|
|
64
241
|
class SpawnExitError extends Error {
|
|
65
242
|
code;
|
|
@@ -85,24 +262,6 @@ async function runSpawn(command, args, cwd) {
|
|
|
85
262
|
});
|
|
86
263
|
});
|
|
87
264
|
}
|
|
88
|
-
export function buildCreateNextAppArgs(params) {
|
|
89
|
-
const { projectDirectory, useNpm, usePnpm, useYarn, useBun, skipInstall, templateUrl, } = params;
|
|
90
|
-
const args = ["create-next-app@latest"];
|
|
91
|
-
if (projectDirectory)
|
|
92
|
-
args.push(projectDirectory);
|
|
93
|
-
if (useNpm)
|
|
94
|
-
args.push("--use-npm");
|
|
95
|
-
if (usePnpm)
|
|
96
|
-
args.push("--use-pnpm");
|
|
97
|
-
if (useYarn)
|
|
98
|
-
args.push("--use-yarn");
|
|
99
|
-
if (useBun)
|
|
100
|
-
args.push("--use-bun");
|
|
101
|
-
if (skipInstall)
|
|
102
|
-
args.push("--skip-install");
|
|
103
|
-
args.push("-e", templateUrl);
|
|
104
|
-
return args;
|
|
105
|
-
}
|
|
106
265
|
export function resolveCreateProjectDirectory(params) {
|
|
107
266
|
const { projectDirectory, stdinIsTTY = process.stdin.isTTY } = params;
|
|
108
267
|
if (projectDirectory)
|
|
@@ -111,8 +270,23 @@ export function resolveCreateProjectDirectory(params) {
|
|
|
111
270
|
return "my-aui-app";
|
|
112
271
|
return undefined;
|
|
113
272
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
273
|
+
function resolvePackageManager(opts) {
|
|
274
|
+
if (opts.useNpm)
|
|
275
|
+
return "npm";
|
|
276
|
+
if (opts.usePnpm)
|
|
277
|
+
return "pnpm";
|
|
278
|
+
if (opts.useYarn)
|
|
279
|
+
return "yarn";
|
|
280
|
+
if (opts.useBun)
|
|
281
|
+
return "bun";
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
const PLAYGROUND_PRESET_BASE_URL = "https://www.assistant-ui.com/playground/init";
|
|
285
|
+
export function resolvePresetUrl(preset) {
|
|
286
|
+
if (preset.startsWith("http://") || preset.startsWith("https://")) {
|
|
287
|
+
return preset;
|
|
288
|
+
}
|
|
289
|
+
return `${PLAYGROUND_PRESET_BASE_URL}?preset=${encodeURIComponent(preset)}`;
|
|
116
290
|
}
|
|
117
291
|
export const create = new Command()
|
|
118
292
|
.name("create")
|
|
@@ -120,80 +294,151 @@ export const create = new Command()
|
|
|
120
294
|
.argument("[project-directory]")
|
|
121
295
|
.usage(`${chalk.green("[project-directory]")} [options]`)
|
|
122
296
|
.option("-t, --template <template>", `template to use (${templateNames.join(", ")})`)
|
|
123
|
-
.option("-e, --example <example>",
|
|
124
|
-
.option("-p, --preset <url>", "preset
|
|
297
|
+
.option("-e, --example <example>", `create from an example (${exampleNames.join(", ")})`)
|
|
298
|
+
.option("-p, --preset <name-or-url>", "preset name or URL (e.g., chatgpt or https://www.assistant-ui.com/playground/init?preset=chatgpt)")
|
|
125
299
|
.option("--use-npm", "explicitly use npm")
|
|
126
300
|
.option("--use-pnpm", "explicitly use pnpm")
|
|
127
301
|
.option("--use-yarn", "explicitly use yarn")
|
|
128
302
|
.option("--use-bun", "explicitly use bun")
|
|
129
303
|
.option("--skip-install", "skip installing packages")
|
|
130
304
|
.action(async (projectDirectory, opts) => {
|
|
131
|
-
const resolvedProjectDirectory = resolveCreateProjectDirectory({
|
|
132
|
-
projectDirectory,
|
|
133
|
-
});
|
|
134
305
|
if (opts.example && opts.preset) {
|
|
135
306
|
logger.error("Cannot use --preset with --example.");
|
|
136
307
|
process.exit(1);
|
|
137
308
|
}
|
|
138
|
-
if (opts.
|
|
139
|
-
logger.error("
|
|
309
|
+
if (opts.template && opts.example) {
|
|
310
|
+
logger.error("Cannot use both --template and --example.");
|
|
140
311
|
process.exit(1);
|
|
141
312
|
}
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
313
|
+
// Start release ref resolution early (runs during user prompts)
|
|
314
|
+
const refPromise = resolveLatestReleaseRef();
|
|
315
|
+
// 1. Resolve project directory
|
|
316
|
+
let resolvedProjectDirectory = resolveCreateProjectDirectory({
|
|
317
|
+
projectDirectory,
|
|
318
|
+
});
|
|
319
|
+
if (!resolvedProjectDirectory) {
|
|
320
|
+
const result = await p.text({
|
|
321
|
+
message: "Project name:",
|
|
322
|
+
placeholder: "my-aui-app",
|
|
323
|
+
defaultValue: "my-aui-app",
|
|
324
|
+
validate: (value) => {
|
|
325
|
+
const name = (value ?? "").trim();
|
|
326
|
+
if (!name)
|
|
327
|
+
return "Project name cannot be empty";
|
|
328
|
+
if (name === "." || name === "..")
|
|
329
|
+
return "Project name cannot be . or ..";
|
|
330
|
+
if (name.includes("/") || name.includes("\\"))
|
|
331
|
+
return "Project name cannot contain path separators";
|
|
332
|
+
return undefined;
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
if (p.isCancel(result)) {
|
|
336
|
+
p.cancel("Project creation cancelled.");
|
|
337
|
+
process.exit(0);
|
|
338
|
+
}
|
|
339
|
+
resolvedProjectDirectory = result;
|
|
340
|
+
}
|
|
341
|
+
// Check directory
|
|
342
|
+
const absoluteProjectDir = path.resolve(resolvedProjectDirectory);
|
|
343
|
+
try {
|
|
344
|
+
const files = fs.readdirSync(absoluteProjectDir);
|
|
345
|
+
if (files.length > 0) {
|
|
346
|
+
logger.error(`Directory ${resolvedProjectDirectory} already exists and is not empty`);
|
|
146
347
|
process.exit(1);
|
|
147
348
|
}
|
|
148
|
-
await createFromExample(resolvedProjectDirectory, opts.example, {
|
|
149
|
-
skipInstall: opts.skipInstall,
|
|
150
|
-
useNpm: opts.useNpm,
|
|
151
|
-
usePnpm: opts.usePnpm,
|
|
152
|
-
useYarn: opts.useYarn,
|
|
153
|
-
useBun: opts.useBun,
|
|
154
|
-
});
|
|
155
|
-
return;
|
|
156
349
|
}
|
|
157
|
-
|
|
158
|
-
|
|
350
|
+
catch (err) {
|
|
351
|
+
if (err.code === "ENOENT") {
|
|
352
|
+
// Directory doesn't exist — good, proceed
|
|
353
|
+
}
|
|
354
|
+
else if (err.code === "ENOTDIR") {
|
|
355
|
+
logger.error(`${resolvedProjectDirectory} already exists and is not a directory`);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
logger.error(`Cannot access ${resolvedProjectDirectory}: ${err.message}`);
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// 2. Resolve scaffold target
|
|
364
|
+
const project = await resolveProject({
|
|
159
365
|
template: opts.template,
|
|
366
|
+
example: opts.example,
|
|
160
367
|
});
|
|
161
|
-
if (!
|
|
368
|
+
if (!project) {
|
|
162
369
|
p.cancel("Project creation cancelled.");
|
|
163
370
|
process.exit(0);
|
|
164
371
|
}
|
|
165
|
-
|
|
166
|
-
if (!templateUrl) {
|
|
167
|
-
logger.error(`Unknown template: ${opts.template}`);
|
|
168
|
-
logger.info(`Available templates: ${templateNames.join(", ")}`);
|
|
169
|
-
process.exit(1);
|
|
170
|
-
}
|
|
171
|
-
logger.info(`Creating project with template: ${templateName}`);
|
|
372
|
+
logger.info(`Creating project from ${project.category}: ${project.label}`);
|
|
172
373
|
logger.break();
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
...(opts.useYarn ? { useYarn: true } : {}),
|
|
180
|
-
...(opts.useBun ? { useBun: true } : {}),
|
|
181
|
-
...(opts.skipInstall ? { skipInstall: true } : {}),
|
|
182
|
-
templateUrl,
|
|
183
|
-
});
|
|
374
|
+
const pm = await resolvePackageManagerName(absoluteProjectDir, resolvePackageManager(opts));
|
|
375
|
+
// Clean up partial project directory on unexpected exit (e.g. Ctrl+C)
|
|
376
|
+
const cleanupOnExit = () => {
|
|
377
|
+
fs.rmSync(absoluteProjectDir, { recursive: true, force: true });
|
|
378
|
+
};
|
|
379
|
+
process.once("exit", cleanupOnExit);
|
|
184
380
|
try {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
381
|
+
// 3. Resolve latest release ref (started before prompts)
|
|
382
|
+
logger.step("Resolving latest release...");
|
|
383
|
+
const ref = await refPromise;
|
|
384
|
+
if (!ref) {
|
|
385
|
+
logger.warn("Could not resolve latest release, downloading from HEAD");
|
|
386
|
+
}
|
|
387
|
+
// 4. Download project
|
|
388
|
+
logger.step("Downloading project...");
|
|
389
|
+
try {
|
|
390
|
+
await downloadProject(project.path, absoluteProjectDir, ref);
|
|
391
|
+
// If the template didn't exist at the release tag, retry from HEAD
|
|
392
|
+
if (ref &&
|
|
393
|
+
!fs.existsSync(path.join(absoluteProjectDir, "package.json"))) {
|
|
394
|
+
fs.rmSync(absoluteProjectDir, { recursive: true, force: true });
|
|
395
|
+
logger.warn("Template not found at release tag, downloading from HEAD");
|
|
396
|
+
await downloadProject(project.path, absoluteProjectDir);
|
|
190
397
|
}
|
|
398
|
+
// 5. Run transform pipeline
|
|
399
|
+
await transformProject(absoluteProjectDir, {
|
|
400
|
+
hasLocalComponents: project.hasLocalComponents,
|
|
401
|
+
skipInstall: opts.skipInstall,
|
|
402
|
+
packageManager: pm,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
catch (err) {
|
|
406
|
+
// Clean up partially created project directory
|
|
407
|
+
fs.rmSync(absoluteProjectDir, { recursive: true, force: true });
|
|
408
|
+
throw err;
|
|
409
|
+
}
|
|
410
|
+
// 6. Apply preset if provided
|
|
411
|
+
if (opts.preset) {
|
|
412
|
+
const presetUrl = resolvePresetUrl(opts.preset);
|
|
191
413
|
logger.info("Applying preset configuration...");
|
|
192
414
|
logger.break();
|
|
193
|
-
|
|
415
|
+
const [dlxCmd, dlxArgs] = dlxCommand(pm);
|
|
416
|
+
try {
|
|
417
|
+
await runSpawn(dlxCmd, [
|
|
418
|
+
...dlxArgs,
|
|
419
|
+
"shadcn@latest",
|
|
420
|
+
"add",
|
|
421
|
+
"--yes",
|
|
422
|
+
"--overwrite",
|
|
423
|
+
presetUrl,
|
|
424
|
+
], absoluteProjectDir);
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
logger.warn(`Preset application failed. You can retry manually with:\n ${dlxCmd} ${[...dlxArgs, "shadcn@latest", "add", presetUrl].join(" ")}`);
|
|
428
|
+
}
|
|
194
429
|
}
|
|
430
|
+
process.removeListener("exit", cleanupOnExit);
|
|
195
431
|
logger.break();
|
|
196
432
|
logger.success("Project created successfully!");
|
|
433
|
+
logger.break();
|
|
434
|
+
const runCmd = pm === "npm" ? "npm run" : pm;
|
|
435
|
+
logger.info("Next steps:");
|
|
436
|
+
logger.info(` cd ${resolvedProjectDirectory}`);
|
|
437
|
+
if (opts.skipInstall) {
|
|
438
|
+
logger.info(` ${pm} install`);
|
|
439
|
+
}
|
|
440
|
+
logger.info(" # Set up your environment variables in .env.local");
|
|
441
|
+
logger.info(` ${runCmd} dev`);
|
|
197
442
|
}
|
|
198
443
|
catch (error) {
|
|
199
444
|
if (error instanceof SpawnExitError) {
|