assistant-ui 0.0.112 → 0.0.114
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/dist/codemods/v0-12/assistant-api-to-aui.d.ts.map +1 -1
- package/dist/codemods/v0-12/assistant-api-to-aui.js +87 -62
- package/dist/codemods/v0-12/assistant-api-to-aui.js.map +1 -1
- package/dist/codemods/v0-12/primitive-if-to-aui-if.d.ts.map +1 -1
- package/dist/codemods/v0-12/primitive-if-to-aui-if.js +31 -45
- package/dist/codemods/v0-12/primitive-if-to-aui-if.js.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 +4 -2
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +33 -10
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/info.d.ts.map +1 -1
- package/dist/commands/info.js +2 -1
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +4 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +52 -40
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/update.js +6 -7
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +5 -2
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-skill.d.ts +1 -1
- package/dist/lib/agent-skill.d.ts.map +1 -1
- package/dist/lib/agent-skill.js.map +1 -1
- package/dist/lib/create-project.d.ts +11 -3
- package/dist/lib/create-project.d.ts.map +1 -1
- package/dist/lib/create-project.js +127 -43
- package/dist/lib/create-project.js.map +1 -1
- package/dist/lib/handle-cli-error.d.ts +5 -0
- package/dist/lib/handle-cli-error.d.ts.map +1 -0
- package/dist/lib/handle-cli-error.js +17 -0
- package/dist/lib/handle-cli-error.js.map +1 -0
- package/dist/lib/install-ai-sdk-lib.js +8 -1
- package/dist/lib/install-ai-sdk-lib.js.map +1 -1
- package/dist/lib/install-edge-lib.js +4 -8
- package/dist/lib/install-edge-lib.js.map +1 -1
- package/dist/lib/run-spawn.d.ts +16 -2
- package/dist/lib/run-spawn.d.ts.map +1 -1
- package/dist/lib/run-spawn.js +87 -9
- package/dist/lib/run-spawn.js.map +1 -1
- package/dist/lib/transform.d.ts +1 -1
- package/dist/lib/transform.d.ts.map +1 -1
- package/dist/lib/transform.js +12 -25
- package/dist/lib/transform.js.map +1 -1
- package/dist/lib/upgrade.js +20 -16
- package/dist/lib/upgrade.js.map +1 -1
- package/dist/lib/utils/file-scanner.d.ts +9 -1
- package/dist/lib/utils/file-scanner.d.ts.map +1 -1
- package/dist/lib/utils/file-scanner.js +22 -20
- package/dist/lib/utils/file-scanner.js.map +1 -1
- package/dist/lib/utils/package-installer.js +1 -1
- package/dist/lib/utils/package-manager.d.ts +3 -1
- package/dist/lib/utils/package-manager.d.ts.map +1 -1
- package/dist/lib/utils/package-manager.js +22 -10
- package/dist/lib/utils/package-manager.js.map +1 -1
- package/package.json +7 -7
- package/plugin/skills/assistant-ui/SKILL.md +4 -4
- package/src/codemods/v0-12/__tests__/assistant-api-to-aui.test.ts +370 -0
- package/src/codemods/v0-12/__tests__/primitive-if-to-aui-if.test.ts +100 -0
- package/src/codemods/v0-12/assistant-api-to-aui.ts +227 -134
- package/src/codemods/v0-12/primitive-if-to-aui-if.ts +44 -72
- package/src/commands/add.ts +5 -5
- package/src/commands/create.ts +41 -7
- package/src/commands/info.ts +1 -0
- package/src/commands/init.ts +4 -6
- package/src/commands/mcp.ts +57 -41
- package/src/commands/update.ts +6 -8
- package/src/commands/upgrade.ts +5 -2
- package/src/index.ts +2 -7
- package/src/lib/agent-skill.ts +2 -1
- package/src/lib/create-project.test.ts +175 -0
- package/src/lib/create-project.ts +189 -59
- package/src/lib/handle-cli-error.test.ts +67 -0
- package/src/lib/handle-cli-error.ts +17 -0
- package/src/lib/install-ai-sdk-lib.ts +13 -1
- package/src/lib/install-edge-lib.ts +7 -8
- package/src/lib/run-spawn.test.ts +148 -0
- package/src/lib/run-spawn.ts +121 -11
- package/src/lib/transform.test.ts +87 -0
- package/src/lib/transform.ts +21 -37
- package/src/lib/upgrade.ts +23 -19
- package/src/lib/utils/file-scanner.ts +26 -20
- package/src/lib/utils/package-manager.ts +29 -12
|
@@ -120,12 +120,18 @@ const fixedConditionMap: Record<string, Record<string, string>> = {
|
|
|
120
120
|
},
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
+
// A prop value the maps cannot faithfully express as a static condition
|
|
124
|
+
// (dynamic expressions, `{undefined}`); elements carrying one are skipped
|
|
125
|
+
// so runtime behavior is never silently changed.
|
|
126
|
+
const UNSUPPORTED_VALUE: unique symbol = Symbol("unsupported");
|
|
127
|
+
|
|
123
128
|
/**
|
|
124
129
|
* Extract the value of a JSX attribute.
|
|
125
130
|
* - Boolean prop (no value): `<X.If user>` → `true`
|
|
126
131
|
* - `{true}` / `{false}`: → `true` / `false`
|
|
127
132
|
* - `{"positive"}`: → `"positive"`
|
|
128
133
|
* - `{null}`: → `null`
|
|
134
|
+
* - anything else (dynamic expressions): → UNSUPPORTED_VALUE
|
|
129
135
|
*/
|
|
130
136
|
const getAttrValue = (j: any, attr: any): unknown => {
|
|
131
137
|
// Boolean attribute (no value), e.g. `<X.If user>`
|
|
@@ -137,12 +143,14 @@ const getAttrValue = (j: any, attr: any): unknown => {
|
|
|
137
143
|
if (j.JSXExpressionContainer.check(attr.value)) {
|
|
138
144
|
const expr = attr.value.expression;
|
|
139
145
|
if (j.BooleanLiteral.check(expr)) return expr.value;
|
|
146
|
+
// NullLiteral bases Literal in ast-types but carries no `value` field,
|
|
147
|
+
// so it must be recognized before the generic Literal branch.
|
|
148
|
+
if (j.NullLiteral.check(expr)) return null;
|
|
140
149
|
if (j.Literal.check(expr)) {
|
|
141
150
|
if (expr.value === null) return null;
|
|
142
151
|
return expr.value;
|
|
143
152
|
}
|
|
144
|
-
|
|
145
|
-
if (j.Identifier.check(expr) && expr.name === "undefined") return undefined;
|
|
153
|
+
return UNSUPPORTED_VALUE;
|
|
146
154
|
}
|
|
147
155
|
|
|
148
156
|
// String literal
|
|
@@ -150,7 +158,7 @@ const getAttrValue = (j: any, attr: any): unknown => {
|
|
|
150
158
|
return attr.value.value;
|
|
151
159
|
}
|
|
152
160
|
|
|
153
|
-
return
|
|
161
|
+
return UNSUPPORTED_VALUE;
|
|
154
162
|
};
|
|
155
163
|
|
|
156
164
|
const buildConditionString = (fragments: ConditionFragment[]): string => {
|
|
@@ -185,61 +193,52 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
185
193
|
|
|
186
194
|
if (importedPrimitives.size === 0) return;
|
|
187
195
|
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (!j.JSXMemberExpression.check(name)) return;
|
|
192
|
-
if (!j.JSXIdentifier.check(name.object)) return;
|
|
193
|
-
if (!j.JSXIdentifier.check(name.property)) return;
|
|
194
|
-
|
|
195
|
-
const primitiveName = name.object.name as string;
|
|
196
|
-
const propertyName = name.property.name as string;
|
|
197
|
-
const fixedMap = fixedConditionMap[primitiveName];
|
|
198
|
-
if (!fixedMap) return;
|
|
199
|
-
const conditionBody = fixedMap[propertyName];
|
|
200
|
-
if (!conditionBody) return;
|
|
201
|
-
if (!importedPrimitives.has(primitiveName)) return;
|
|
202
|
-
|
|
203
|
-
// Only transform if there are no props (other than children, which are implicit)
|
|
204
|
-
const attrs: any[] = path.value.attributes || [];
|
|
205
|
-
if (attrs.length > 0) return;
|
|
206
|
-
|
|
196
|
+
// Opening and closing tags are rewritten together per element, so a
|
|
197
|
+
// skipped element can never be left with a mismatched closing tag.
|
|
198
|
+
const convertElementToAuiIf = (elementPath: any, conditionBody: string) => {
|
|
207
199
|
const arrowFnAst = j(`(s) => ${conditionBody}`)
|
|
208
200
|
.find(j.ArrowFunctionExpression)
|
|
209
201
|
.paths()[0]!.value;
|
|
210
202
|
|
|
211
|
-
|
|
212
|
-
|
|
203
|
+
const opening = elementPath.value.openingElement;
|
|
204
|
+
opening.name = j.jsxIdentifier("AuiIf");
|
|
205
|
+
opening.attributes = [
|
|
213
206
|
j.jsxAttribute(
|
|
214
207
|
j.jsxIdentifier("condition"),
|
|
215
208
|
j.jsxExpressionContainer(arrowFnAst),
|
|
216
209
|
),
|
|
217
210
|
];
|
|
211
|
+
if (elementPath.value.closingElement) {
|
|
212
|
+
elementPath.value.closingElement.name = j.jsxIdentifier("AuiIf");
|
|
213
|
+
}
|
|
218
214
|
|
|
219
215
|
needsAuiIfImport = true;
|
|
220
216
|
markAsChanged();
|
|
221
|
-
}
|
|
217
|
+
};
|
|
222
218
|
|
|
223
|
-
//
|
|
224
|
-
root.find(j.
|
|
225
|
-
const name = path.value.name;
|
|
219
|
+
// Process fixed-condition components: <ThreadPrimitive.Empty> → <AuiIf condition={...}>
|
|
220
|
+
root.find(j.JSXElement).forEach((path: any) => {
|
|
221
|
+
const name = path.value.openingElement.name;
|
|
226
222
|
if (!j.JSXMemberExpression.check(name)) return;
|
|
227
223
|
if (!j.JSXIdentifier.check(name.object)) return;
|
|
228
224
|
if (!j.JSXIdentifier.check(name.property)) return;
|
|
229
225
|
|
|
230
226
|
const primitiveName = name.object.name as string;
|
|
231
227
|
const propertyName = name.property.name as string;
|
|
232
|
-
const
|
|
233
|
-
if (!
|
|
228
|
+
const conditionBody = fixedConditionMap[primitiveName]?.[propertyName];
|
|
229
|
+
if (!conditionBody) return;
|
|
234
230
|
if (!importedPrimitives.has(primitiveName)) return;
|
|
235
231
|
|
|
236
|
-
|
|
237
|
-
|
|
232
|
+
// Only transform if there are no props (other than children, which are implicit)
|
|
233
|
+
const attrs: any[] = path.value.openingElement.attributes || [];
|
|
234
|
+
if (attrs.length > 0) return;
|
|
235
|
+
|
|
236
|
+
convertElementToAuiIf(path, conditionBody);
|
|
238
237
|
});
|
|
239
238
|
|
|
240
239
|
// Process JSX elements: <ThreadPrimitive.If ...> → <AuiIf condition={...}>
|
|
241
|
-
root.find(j.
|
|
242
|
-
const name = path.value.name;
|
|
240
|
+
root.find(j.JSXElement).forEach((path: any) => {
|
|
241
|
+
const name = path.value.openingElement.name;
|
|
243
242
|
|
|
244
243
|
// Check for `<XPrimitive.If ...>`
|
|
245
244
|
if (!j.JSXMemberExpression.check(name)) return;
|
|
@@ -253,7 +252,7 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
253
252
|
if (!importedPrimitives.has(primitiveName)) return;
|
|
254
253
|
|
|
255
254
|
// Extract props
|
|
256
|
-
const attrs: any[] = path.value.attributes || [];
|
|
255
|
+
const attrs: any[] = path.value.openingElement.attributes || [];
|
|
257
256
|
const fragments: ConditionFragment[] = [];
|
|
258
257
|
let hasUnknownProp = false;
|
|
259
258
|
|
|
@@ -265,7 +264,11 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
265
264
|
}
|
|
266
265
|
const propName =
|
|
267
266
|
typeof attr.name.name === "string" ? attr.name.name : null;
|
|
268
|
-
if (!propName)
|
|
267
|
+
if (!propName) {
|
|
268
|
+
// e.g. JSXNamespacedName — not expressible as a condition
|
|
269
|
+
hasUnknownProp = true;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
269
272
|
|
|
270
273
|
const mapper = propMap[propName];
|
|
271
274
|
if (!mapper) {
|
|
@@ -274,6 +277,10 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
274
277
|
}
|
|
275
278
|
|
|
276
279
|
const value = getAttrValue(j, attr);
|
|
280
|
+
if (value === UNSUPPORTED_VALUE) {
|
|
281
|
+
hasUnknownProp = true;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
277
284
|
const fragment = mapper(value);
|
|
278
285
|
if (fragment) {
|
|
279
286
|
fragments.push(fragment);
|
|
@@ -283,42 +290,7 @@ const migratePrimitiveIfToAuiIf = createTransformer(
|
|
|
283
290
|
// If we couldn't map all props, skip this element
|
|
284
291
|
if (hasUnknownProp || fragments.length === 0) return;
|
|
285
292
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
// Parse the arrow function as an expression to get a proper AST node
|
|
289
|
-
const arrowFnAst = j(`(s) => ${conditionBody}`)
|
|
290
|
-
.find(j.ArrowFunctionExpression)
|
|
291
|
-
.paths()[0]!.value;
|
|
292
|
-
|
|
293
|
-
// Replace <XPrimitive.If ...> with <AuiIf condition={...}>
|
|
294
|
-
path.value.name = j.jsxIdentifier("AuiIf");
|
|
295
|
-
|
|
296
|
-
// Replace all attributes with a single condition prop
|
|
297
|
-
path.value.attributes = [
|
|
298
|
-
j.jsxAttribute(
|
|
299
|
-
j.jsxIdentifier("condition"),
|
|
300
|
-
j.jsxExpressionContainer(arrowFnAst),
|
|
301
|
-
),
|
|
302
|
-
];
|
|
303
|
-
|
|
304
|
-
needsAuiIfImport = true;
|
|
305
|
-
markAsChanged();
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// Update closing elements to match
|
|
309
|
-
root.find(j.JSXClosingElement).forEach((path: any) => {
|
|
310
|
-
const name = path.value.name;
|
|
311
|
-
if (!j.JSXMemberExpression.check(name)) return;
|
|
312
|
-
if (!j.JSXIdentifier.check(name.object)) return;
|
|
313
|
-
if (!j.JSXIdentifier.check(name.property)) return;
|
|
314
|
-
if (name.property.name !== "If") return;
|
|
315
|
-
|
|
316
|
-
const primitiveName = name.object.name;
|
|
317
|
-
if (!primitiveMap[primitiveName]) return;
|
|
318
|
-
if (!importedPrimitives.has(primitiveName)) return;
|
|
319
|
-
|
|
320
|
-
path.value.name = j.jsxIdentifier("AuiIf");
|
|
321
|
-
markAsChanged();
|
|
293
|
+
convertElementToAuiIf(path, buildConditionString(fragments));
|
|
322
294
|
});
|
|
323
295
|
|
|
324
296
|
// Add AuiIf import if needed
|
package/src/commands/add.ts
CHANGED
|
@@ -5,13 +5,12 @@ import {
|
|
|
5
5
|
getComponentsJsonStyle,
|
|
6
6
|
resolveRegistryItemUrl,
|
|
7
7
|
} from "../lib/utils/registry";
|
|
8
|
+
import { dlxCommand, resolvePackageManager } from "../lib/create-project";
|
|
9
|
+
import { runSpawn, SpawnExitError, SpawnSignalError } from "../lib/run-spawn";
|
|
8
10
|
import {
|
|
9
|
-
dlxCommand,
|
|
10
|
-
resolvePackageManager,
|
|
11
|
-
resolvePackageManagerForCwd,
|
|
12
11
|
type PackageManagerName,
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
resolvePackageManagerForCwd,
|
|
13
|
+
} from "../lib/utils/package-manager";
|
|
15
14
|
|
|
16
15
|
export interface AddComponentsPlan {
|
|
17
16
|
command: string;
|
|
@@ -92,6 +91,7 @@ export const add = new Command()
|
|
|
92
91
|
try {
|
|
93
92
|
await runSpawn(command, args, opts.cwd);
|
|
94
93
|
} catch (error) {
|
|
94
|
+
if (error instanceof SpawnSignalError) throw error;
|
|
95
95
|
if (error instanceof SpawnExitError) {
|
|
96
96
|
logger.error(`Process exited with code ${error.code}`);
|
|
97
97
|
process.exit(error.code);
|
package/src/commands/create.ts
CHANGED
|
@@ -9,12 +9,17 @@ import {
|
|
|
9
9
|
downloadProject,
|
|
10
10
|
resolveLatestReleaseRef,
|
|
11
11
|
resolvePackageManager,
|
|
12
|
-
resolvePackageManagerForCwd,
|
|
13
12
|
scaffoldProject,
|
|
14
13
|
transformProject,
|
|
15
14
|
type TransformResult,
|
|
16
15
|
} from "../lib/create-project";
|
|
17
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
hasActiveSpawn,
|
|
18
|
+
runSpawn,
|
|
19
|
+
SpawnExitError,
|
|
20
|
+
SpawnSignalError,
|
|
21
|
+
} from "../lib/run-spawn";
|
|
22
|
+
import { resolvePackageManagerForCwd } from "../lib/utils/package-manager";
|
|
18
23
|
import {
|
|
19
24
|
buildSkillsAddCommand,
|
|
20
25
|
resolveSkillsInstall,
|
|
@@ -597,10 +602,31 @@ export const create = new Command()
|
|
|
597
602
|
);
|
|
598
603
|
|
|
599
604
|
// Clean up partial project directory on unexpected exit (e.g. Ctrl+C)
|
|
605
|
+
let cleanupArmed = true;
|
|
600
606
|
const cleanupOnExit = () => {
|
|
607
|
+
if (!cleanupArmed) return;
|
|
608
|
+
cleanupArmed = false;
|
|
601
609
|
fs.rmSync(absoluteProjectDir, { recursive: true, force: true });
|
|
602
610
|
};
|
|
611
|
+
const disarmCleanup = () => {
|
|
612
|
+
cleanupArmed = false;
|
|
613
|
+
process.removeListener("exit", cleanupOnExit);
|
|
614
|
+
process.removeListener("SIGINT", cleanupOnSignal);
|
|
615
|
+
process.removeListener("SIGTERM", cleanupOnSignal);
|
|
616
|
+
};
|
|
617
|
+
// Node emits no "exit" when a signal kills the process. An in-flight
|
|
618
|
+
// runSpawn forwards the signal itself, so the directory is removed on the
|
|
619
|
+
// error path once the child is reaped rather than while it is still writing.
|
|
620
|
+
const cleanupOnSignal = (signal: NodeJS.Signals) => {
|
|
621
|
+
if (hasActiveSpawn()) return;
|
|
622
|
+
cleanupOnExit();
|
|
623
|
+
disarmCleanup();
|
|
624
|
+
process.kill(process.pid, signal);
|
|
625
|
+
};
|
|
626
|
+
|
|
603
627
|
process.once("exit", cleanupOnExit);
|
|
628
|
+
process.on("SIGINT", cleanupOnSignal);
|
|
629
|
+
process.on("SIGTERM", cleanupOnSignal);
|
|
604
630
|
|
|
605
631
|
try {
|
|
606
632
|
// 3. Resolve latest release ref (started before prompts)
|
|
@@ -655,7 +681,8 @@ export const create = new Command()
|
|
|
655
681
|
});
|
|
656
682
|
try {
|
|
657
683
|
await runSpawn(skillsCmd, skillsArgs, absoluteProjectDir);
|
|
658
|
-
} catch {
|
|
684
|
+
} catch (error) {
|
|
685
|
+
if (error instanceof SpawnSignalError) throw error;
|
|
659
686
|
logger.warn(
|
|
660
687
|
`Could not add assistant-ui agent skills. You can add them later with:\n ${skillsCmd} ${skillsArgs.join(" ")}`,
|
|
661
688
|
);
|
|
@@ -663,12 +690,13 @@ export const create = new Command()
|
|
|
663
690
|
}
|
|
664
691
|
} catch (err) {
|
|
665
692
|
// Clean up partially created project directory
|
|
666
|
-
|
|
693
|
+
cleanupOnExit();
|
|
694
|
+
disarmCleanup();
|
|
667
695
|
throw err;
|
|
668
696
|
}
|
|
669
697
|
|
|
670
698
|
if (transformResult.registryInstallFailure) {
|
|
671
|
-
|
|
699
|
+
disarmCleanup();
|
|
672
700
|
logger.break();
|
|
673
701
|
logger.error("Project created with missing components.");
|
|
674
702
|
logger.info("Retry the component install with:");
|
|
@@ -696,14 +724,15 @@ export const create = new Command()
|
|
|
696
724
|
],
|
|
697
725
|
absoluteProjectDir,
|
|
698
726
|
);
|
|
699
|
-
} catch {
|
|
727
|
+
} catch (error) {
|
|
728
|
+
if (error instanceof SpawnSignalError) throw error;
|
|
700
729
|
logger.warn(
|
|
701
730
|
`Preset application failed. You can retry manually with:\n ${dlxCmd} ${[...dlxArgs, "shadcn@latest", "add", presetUrl].join(" ")}`,
|
|
702
731
|
);
|
|
703
732
|
}
|
|
704
733
|
}
|
|
705
734
|
|
|
706
|
-
|
|
735
|
+
disarmCleanup();
|
|
707
736
|
|
|
708
737
|
logger.break();
|
|
709
738
|
logger.success("Project created successfully!");
|
|
@@ -736,6 +765,11 @@ export const create = new Command()
|
|
|
736
765
|
logger.info(` # Set up your environment variables in ${envFile}`);
|
|
737
766
|
logger.info(` ${runCmd} ${devScript}`);
|
|
738
767
|
} catch (error) {
|
|
768
|
+
if (error instanceof SpawnSignalError) {
|
|
769
|
+
cleanupOnExit();
|
|
770
|
+
disarmCleanup();
|
|
771
|
+
throw error;
|
|
772
|
+
}
|
|
739
773
|
if (error instanceof SpawnExitError) {
|
|
740
774
|
logger.error(`Project creation failed with code ${error.code}`);
|
|
741
775
|
process.exit(error.code);
|
package/src/commands/info.ts
CHANGED
package/src/commands/init.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { Command, Option } from "commander";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
resolvePackageManager,
|
|
7
|
-
resolvePackageManagerForCwd,
|
|
8
|
-
} from "../lib/create-project";
|
|
9
|
-
import { runSpawn, SpawnExitError } from "../lib/run-spawn";
|
|
4
|
+
import { dlxCommand, resolvePackageManager } from "../lib/create-project";
|
|
5
|
+
import { runSpawn, SpawnExitError, SpawnSignalError } from "../lib/run-spawn";
|
|
10
6
|
import { logger } from "../lib/utils/logger";
|
|
7
|
+
import { resolvePackageManagerForCwd } from "../lib/utils/package-manager";
|
|
11
8
|
import {
|
|
12
9
|
getComponentsJsonStyle,
|
|
13
10
|
resolveQuickStartRegistryUrl,
|
|
@@ -135,6 +132,7 @@ export const init = new Command()
|
|
|
135
132
|
logger.success("Project initialized successfully!");
|
|
136
133
|
logger.info("You can now add more components with 'assistant-ui add'");
|
|
137
134
|
} catch (error) {
|
|
135
|
+
if (error instanceof SpawnSignalError) throw error;
|
|
138
136
|
if (error instanceof SpawnExitError) {
|
|
139
137
|
logger.error(`Initialization failed with code ${error.code}`);
|
|
140
138
|
process.exit(error.code);
|
package/src/commands/mcp.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { Command } from "commander";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import os from "node:os";
|
|
5
|
-
import { spawn } from "cross-spawn";
|
|
6
5
|
import { logger } from "../lib/utils/logger";
|
|
6
|
+
import { runSpawn, SpawnExitError, SpawnSignalError } from "../lib/run-spawn";
|
|
7
7
|
import * as p from "@clack/prompts";
|
|
8
8
|
|
|
9
9
|
type MCPTarget =
|
|
@@ -14,12 +14,15 @@ type MCPTarget =
|
|
|
14
14
|
| "claude-code"
|
|
15
15
|
| "claude-desktop";
|
|
16
16
|
|
|
17
|
+
const HOSTED_MCP_URL = "https://www.assistant-ui.com/mcp";
|
|
18
|
+
|
|
17
19
|
const MCP_CONFIGS: Record<
|
|
18
20
|
Exclude<MCPTarget, "claude-code">,
|
|
19
21
|
{
|
|
20
22
|
name: string;
|
|
21
23
|
getPath: () => string;
|
|
22
24
|
config: object;
|
|
25
|
+
replaceServerKey?: string;
|
|
23
26
|
postInstall?: string;
|
|
24
27
|
}
|
|
25
28
|
> = {
|
|
@@ -29,11 +32,11 @@ const MCP_CONFIGS: Record<
|
|
|
29
32
|
config: {
|
|
30
33
|
mcpServers: {
|
|
31
34
|
"assistant-ui": {
|
|
32
|
-
|
|
33
|
-
args: ["-y", "@assistant-ui/mcp-docs-server"],
|
|
35
|
+
url: HOSTED_MCP_URL,
|
|
34
36
|
},
|
|
35
37
|
},
|
|
36
38
|
},
|
|
39
|
+
replaceServerKey: "mcpServers",
|
|
37
40
|
postInstall:
|
|
38
41
|
"Open Cursor Settings → MCP → find 'assistant-ui' and click enable.",
|
|
39
42
|
},
|
|
@@ -44,11 +47,11 @@ const MCP_CONFIGS: Record<
|
|
|
44
47
|
config: {
|
|
45
48
|
mcpServers: {
|
|
46
49
|
"assistant-ui": {
|
|
47
|
-
|
|
48
|
-
args: ["-y", "@assistant-ui/mcp-docs-server"],
|
|
50
|
+
serverUrl: HOSTED_MCP_URL,
|
|
49
51
|
},
|
|
50
52
|
},
|
|
51
53
|
},
|
|
54
|
+
replaceServerKey: "mcpServers",
|
|
52
55
|
postInstall: "Fully quit and re-open Windsurf to activate.",
|
|
53
56
|
},
|
|
54
57
|
vscode: {
|
|
@@ -57,12 +60,12 @@ const MCP_CONFIGS: Record<
|
|
|
57
60
|
config: {
|
|
58
61
|
servers: {
|
|
59
62
|
"assistant-ui": {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
type: "stdio",
|
|
63
|
+
type: "http",
|
|
64
|
+
url: HOSTED_MCP_URL,
|
|
63
65
|
},
|
|
64
66
|
},
|
|
65
67
|
},
|
|
68
|
+
replaceServerKey: "servers",
|
|
66
69
|
postInstall:
|
|
67
70
|
"Enable MCP in Settings → search 'MCP' → enable 'Chat > MCP'. Use Copilot Chat in Agent mode.",
|
|
68
71
|
},
|
|
@@ -152,45 +155,42 @@ async function installForTarget(target: MCPTarget): Promise<void> {
|
|
|
152
155
|
logger.info("Installing MCP server for Claude Code...");
|
|
153
156
|
logger.break();
|
|
154
157
|
|
|
155
|
-
|
|
156
|
-
"claude",
|
|
157
|
-
|
|
158
|
+
try {
|
|
159
|
+
await runSpawn("claude", ["mcp", "remove", "assistant-ui"]).catch(
|
|
160
|
+
(error: unknown) => {
|
|
161
|
+
if (error instanceof SpawnSignalError) throw error;
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
await runSpawn("claude", [
|
|
158
165
|
"mcp",
|
|
159
166
|
"add",
|
|
167
|
+
"--transport",
|
|
168
|
+
"http",
|
|
160
169
|
"assistant-ui",
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
},
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
return new Promise((resolve, reject) => {
|
|
172
|
-
child.on("error", (error) => {
|
|
170
|
+
HOSTED_MCP_URL,
|
|
171
|
+
]);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (error instanceof SpawnSignalError) throw error;
|
|
174
|
+
if (error instanceof SpawnExitError) {
|
|
175
|
+
logger.error(`Installation failed with code ${error.code}`);
|
|
176
|
+
} else if (error instanceof Error) {
|
|
173
177
|
logger.error(`Failed to install: ${error.message}`);
|
|
174
178
|
logger.info(
|
|
175
179
|
"Make sure Claude Code CLI is installed: https://docs.anthropic.com/en/docs/claude-code",
|
|
176
180
|
);
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
179
184
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
);
|
|
190
|
-
resolve();
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
});
|
|
185
|
+
logger.break();
|
|
186
|
+
logger.success("MCP server installed for Claude Code!");
|
|
187
|
+
logger.info(
|
|
188
|
+
`Connects to the hosted assistant-ui MCP server at ${HOSTED_MCP_URL}.`,
|
|
189
|
+
);
|
|
190
|
+
logger.info(
|
|
191
|
+
"The server starts automatically. Try asking about assistant-ui!",
|
|
192
|
+
);
|
|
193
|
+
return;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
if (target === "claude-desktop" && process.platform === "linux") {
|
|
@@ -230,12 +230,27 @@ async function installForTarget(target: MCPTarget): Promise<void> {
|
|
|
230
230
|
|
|
231
231
|
const newConfig = deepMerge(existingConfig, targetConfig.config);
|
|
232
232
|
|
|
233
|
+
if (targetConfig.replaceServerKey) {
|
|
234
|
+
const key = targetConfig.replaceServerKey;
|
|
235
|
+
newConfig[key] = {
|
|
236
|
+
...newConfig[key],
|
|
237
|
+
"assistant-ui": (targetConfig.config as any)[key]["assistant-ui"],
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
233
241
|
fs.writeFileSync(configPath, `${JSON.stringify(newConfig, null, 2)}\n`);
|
|
234
242
|
|
|
235
243
|
logger.break();
|
|
236
244
|
logger.success(`MCP server installed for ${targetConfig.name}!`);
|
|
237
245
|
logger.info(`Config written to: ${configPath}`);
|
|
238
246
|
|
|
247
|
+
if (targetConfig.replaceServerKey) {
|
|
248
|
+
logger.break();
|
|
249
|
+
logger.info(
|
|
250
|
+
`Connects to the hosted assistant-ui MCP server at ${HOSTED_MCP_URL}.`,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
239
254
|
if (targetConfig.postInstall) {
|
|
240
255
|
logger.break();
|
|
241
256
|
logger.info(targetConfig.postInstall);
|
|
@@ -244,7 +259,7 @@ async function installForTarget(target: MCPTarget): Promise<void> {
|
|
|
244
259
|
|
|
245
260
|
export const mcp = new Command()
|
|
246
261
|
.name("mcp")
|
|
247
|
-
.description("
|
|
262
|
+
.description("connect your IDE to the assistant-ui MCP server")
|
|
248
263
|
.option("--cursor", "install for Cursor")
|
|
249
264
|
.option("--windsurf", "install for Windsurf")
|
|
250
265
|
.option("--vscode", "install for VSCode")
|
|
@@ -288,7 +303,8 @@ export const mcp = new Command()
|
|
|
288
303
|
for (const target of targets) {
|
|
289
304
|
try {
|
|
290
305
|
await installForTarget(target);
|
|
291
|
-
} catch {
|
|
306
|
+
} catch (error) {
|
|
307
|
+
if (error instanceof SpawnSignalError) throw error;
|
|
292
308
|
process.exit(1);
|
|
293
309
|
}
|
|
294
310
|
}
|
package/src/commands/update.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { sync as spawnSync } from "cross-spawn";
|
|
5
4
|
import { logger } from "../lib/utils/logger";
|
|
6
5
|
import { logPackageJsonParseError } from "../lib/utils/package-json";
|
|
7
6
|
import { getInstallCommand } from "../lib/utils/package-manager";
|
|
7
|
+
import { runSpawn, SpawnExitError, SpawnSignalError } from "../lib/run-spawn";
|
|
8
8
|
|
|
9
9
|
export const update = new Command()
|
|
10
10
|
.name("update")
|
|
@@ -73,14 +73,12 @@ export const update = new Command()
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
logger.step("Updating packages...");
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (result.status !== 0) {
|
|
76
|
+
try {
|
|
77
|
+
await runSpawn(installCmd.command, installCmd.args, opts.cwd);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error instanceof SpawnSignalError) throw error;
|
|
82
80
|
logger.error("Package manager update failed.");
|
|
83
|
-
process.exit(
|
|
81
|
+
process.exit(error instanceof SpawnExitError ? error.code : 1);
|
|
84
82
|
}
|
|
85
83
|
|
|
86
84
|
logger.break();
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { transform } from "../lib/transform";
|
|
3
3
|
import { upgrade } from "../lib/upgrade";
|
|
4
|
+
import { SpawnSignalError } from "../lib/run-spawn";
|
|
4
5
|
import debug from "debug";
|
|
5
6
|
|
|
6
7
|
export interface TransformOptions {
|
|
@@ -30,10 +31,11 @@ export const codemodCommand = addTransformOptions(
|
|
|
30
31
|
.description("CLI tool for running codemods")
|
|
31
32
|
.argument("<codemod>", "Codemod to run (e.g., rewrite-framework-imports)")
|
|
32
33
|
.argument("<source>", "Path to source files or directory to transform"),
|
|
33
|
-
).action((codemod, source, options: TransformOptions) => {
|
|
34
|
+
).action(async (codemod, source, options: TransformOptions) => {
|
|
34
35
|
try {
|
|
35
|
-
transform(codemod, source, options);
|
|
36
|
+
await transform(codemod, source, options);
|
|
36
37
|
} catch (err) {
|
|
38
|
+
if (err instanceof SpawnSignalError) throw err;
|
|
37
39
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
38
40
|
const errorStack = err instanceof Error ? err.stack : undefined;
|
|
39
41
|
error(`Error transforming: ${errorMessage}`);
|
|
@@ -52,6 +54,7 @@ export const upgradeCommand = addTransformOptions(
|
|
|
52
54
|
try {
|
|
53
55
|
await upgrade(options);
|
|
54
56
|
} catch (err) {
|
|
57
|
+
if (err instanceof SpawnSignalError) throw err;
|
|
55
58
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
56
59
|
const errorStack = err instanceof Error ? err.stack : undefined;
|
|
57
60
|
error(`Error upgrading: ${errorMessage}`);
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { handleCliError } from "./lib/handle-cli-error";
|
|
3
4
|
import { runCli } from "./run";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
process.on("SIGTERM", () => process.exit(0));
|
|
7
|
-
|
|
8
|
-
void runCli().catch((error: unknown) => {
|
|
9
|
-
console.error(error);
|
|
10
|
-
process.exitCode = 1;
|
|
11
|
-
});
|
|
6
|
+
void runCli().catch(handleCliError);
|
package/src/lib/agent-skill.ts
CHANGED