assistant-ui 0.0.81 → 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 -101
- 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 +5 -2
- 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 -128
- 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-ui-starter-cloud";
|
|
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,64 +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
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import { dlxCommand, downloadProject, resolveLatestReleaseRef, resolvePackageManagerName, transformProject, } from "../lib/create-project.js";
|
|
9
|
+
export const PROJECT_METADATA = [
|
|
10
|
+
// Templates
|
|
11
|
+
{
|
|
12
|
+
name: "default",
|
|
11
13
|
label: "Default",
|
|
12
|
-
|
|
14
|
+
description: "Default template with Vercel AI SDK",
|
|
15
|
+
category: "template",
|
|
16
|
+
path: "templates/default",
|
|
17
|
+
hasLocalComponents: true,
|
|
13
18
|
},
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
{
|
|
20
|
+
name: "minimal",
|
|
16
21
|
label: "Minimal",
|
|
17
|
-
|
|
22
|
+
description: "Bare-bones starting point",
|
|
23
|
+
category: "template",
|
|
24
|
+
path: "templates/minimal",
|
|
25
|
+
hasLocalComponents: true,
|
|
18
26
|
},
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
{
|
|
28
|
+
name: "cloud",
|
|
21
29
|
label: "Cloud",
|
|
22
|
-
|
|
30
|
+
description: "Cloud-backed persistence starter",
|
|
31
|
+
category: "template",
|
|
32
|
+
path: "templates/cloud",
|
|
33
|
+
hasLocalComponents: true,
|
|
23
34
|
},
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
{
|
|
36
|
+
name: "cloud-clerk",
|
|
26
37
|
label: "Cloud + Clerk",
|
|
27
|
-
|
|
38
|
+
description: "Cloud-backed starter with Clerk auth",
|
|
39
|
+
category: "template",
|
|
40
|
+
path: "templates/cloud-clerk",
|
|
41
|
+
hasLocalComponents: true,
|
|
28
42
|
},
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
{
|
|
44
|
+
name: "langgraph",
|
|
31
45
|
label: "LangGraph",
|
|
32
|
-
|
|
46
|
+
description: "LangGraph starter template",
|
|
47
|
+
category: "template",
|
|
48
|
+
path: "templates/langgraph",
|
|
49
|
+
hasLocalComponents: true,
|
|
33
50
|
},
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
{
|
|
52
|
+
name: "mcp",
|
|
36
53
|
label: "MCP",
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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;
|
|
48
185
|
if (template) {
|
|
49
|
-
|
|
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;
|
|
50
202
|
}
|
|
51
203
|
if (!stdinIsTTY) {
|
|
52
|
-
return "default";
|
|
204
|
+
return PROJECT_METADATA.find((m) => m.name === "default");
|
|
53
205
|
}
|
|
54
206
|
const selected = await select({
|
|
55
|
-
message: "Select a
|
|
56
|
-
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
|
+
],
|
|
57
230
|
});
|
|
58
231
|
if (isCancel(selected)) {
|
|
59
232
|
return null;
|
|
60
233
|
}
|
|
61
|
-
|
|
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;
|
|
62
240
|
}
|
|
63
241
|
class SpawnExitError extends Error {
|
|
64
242
|
code;
|
|
@@ -84,24 +262,6 @@ async function runSpawn(command, args, cwd) {
|
|
|
84
262
|
});
|
|
85
263
|
});
|
|
86
264
|
}
|
|
87
|
-
export function buildCreateNextAppArgs(params) {
|
|
88
|
-
const { projectDirectory, useNpm, usePnpm, useYarn, useBun, skipInstall, templateUrl, } = params;
|
|
89
|
-
const args = ["create-next-app@latest"];
|
|
90
|
-
if (projectDirectory)
|
|
91
|
-
args.push(projectDirectory);
|
|
92
|
-
if (useNpm)
|
|
93
|
-
args.push("--use-npm");
|
|
94
|
-
if (usePnpm)
|
|
95
|
-
args.push("--use-pnpm");
|
|
96
|
-
if (useYarn)
|
|
97
|
-
args.push("--use-yarn");
|
|
98
|
-
if (useBun)
|
|
99
|
-
args.push("--use-bun");
|
|
100
|
-
if (skipInstall)
|
|
101
|
-
args.push("--skip-install");
|
|
102
|
-
args.push("-e", templateUrl);
|
|
103
|
-
return args;
|
|
104
|
-
}
|
|
105
265
|
export function resolveCreateProjectDirectory(params) {
|
|
106
266
|
const { projectDirectory, stdinIsTTY = process.stdin.isTTY } = params;
|
|
107
267
|
if (projectDirectory)
|
|
@@ -110,8 +270,23 @@ export function resolveCreateProjectDirectory(params) {
|
|
|
110
270
|
return "my-aui-app";
|
|
111
271
|
return undefined;
|
|
112
272
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
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)}`;
|
|
115
290
|
}
|
|
116
291
|
export const create = new Command()
|
|
117
292
|
.name("create")
|
|
@@ -119,80 +294,151 @@ export const create = new Command()
|
|
|
119
294
|
.argument("[project-directory]")
|
|
120
295
|
.usage(`${chalk.green("[project-directory]")} [options]`)
|
|
121
296
|
.option("-t, --template <template>", `template to use (${templateNames.join(", ")})`)
|
|
122
|
-
.option("-e, --example <example>",
|
|
123
|
-
.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)")
|
|
124
299
|
.option("--use-npm", "explicitly use npm")
|
|
125
300
|
.option("--use-pnpm", "explicitly use pnpm")
|
|
126
301
|
.option("--use-yarn", "explicitly use yarn")
|
|
127
302
|
.option("--use-bun", "explicitly use bun")
|
|
128
303
|
.option("--skip-install", "skip installing packages")
|
|
129
304
|
.action(async (projectDirectory, opts) => {
|
|
130
|
-
const resolvedProjectDirectory = resolveCreateProjectDirectory({
|
|
131
|
-
projectDirectory,
|
|
132
|
-
});
|
|
133
305
|
if (opts.example && opts.preset) {
|
|
134
306
|
logger.error("Cannot use --preset with --example.");
|
|
135
307
|
process.exit(1);
|
|
136
308
|
}
|
|
137
|
-
if (opts.
|
|
138
|
-
logger.error("
|
|
309
|
+
if (opts.template && opts.example) {
|
|
310
|
+
logger.error("Cannot use both --template and --example.");
|
|
139
311
|
process.exit(1);
|
|
140
312
|
}
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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`);
|
|
145
347
|
process.exit(1);
|
|
146
348
|
}
|
|
147
|
-
await createFromExample(resolvedProjectDirectory, opts.example, {
|
|
148
|
-
skipInstall: opts.skipInstall,
|
|
149
|
-
useNpm: opts.useNpm,
|
|
150
|
-
usePnpm: opts.usePnpm,
|
|
151
|
-
useYarn: opts.useYarn,
|
|
152
|
-
useBun: opts.useBun,
|
|
153
|
-
});
|
|
154
|
-
return;
|
|
155
349
|
}
|
|
156
|
-
|
|
157
|
-
|
|
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({
|
|
158
365
|
template: opts.template,
|
|
366
|
+
example: opts.example,
|
|
159
367
|
});
|
|
160
|
-
if (!
|
|
368
|
+
if (!project) {
|
|
161
369
|
p.cancel("Project creation cancelled.");
|
|
162
370
|
process.exit(0);
|
|
163
371
|
}
|
|
164
|
-
|
|
165
|
-
if (!templateUrl) {
|
|
166
|
-
logger.error(`Unknown template: ${opts.template}`);
|
|
167
|
-
logger.info(`Available templates: ${templateNames.join(", ")}`);
|
|
168
|
-
process.exit(1);
|
|
169
|
-
}
|
|
170
|
-
logger.info(`Creating project with template: ${templateName}`);
|
|
372
|
+
logger.info(`Creating project from ${project.category}: ${project.label}`);
|
|
171
373
|
logger.break();
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
...(opts.useYarn ? { useYarn: true } : {}),
|
|
179
|
-
...(opts.useBun ? { useBun: true } : {}),
|
|
180
|
-
...(opts.skipInstall ? { skipInstall: true } : {}),
|
|
181
|
-
templateUrl,
|
|
182
|
-
});
|
|
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);
|
|
183
380
|
try {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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);
|
|
189
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);
|
|
190
413
|
logger.info("Applying preset configuration...");
|
|
191
414
|
logger.break();
|
|
192
|
-
|
|
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
|
+
}
|
|
193
429
|
}
|
|
430
|
+
process.removeListener("exit", cleanupOnExit);
|
|
194
431
|
logger.break();
|
|
195
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`);
|
|
196
442
|
}
|
|
197
443
|
catch (error) {
|
|
198
444
|
if (error instanceof SpawnExitError) {
|