assistant-ui 0.0.116 → 0.0.118
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/utils/createTransformer.d.ts +13 -16
- package/dist/codemods/utils/createTransformer.d.ts.map +1 -1
- package/dist/codemods/utils/resolveBinding.d.ts +3 -0
- package/dist/codemods/utils/resolveBinding.d.ts.map +1 -0
- package/dist/codemods/utils/resolveBinding.js +78 -0
- package/dist/codemods/utils/resolveBinding.js.map +1 -0
- package/dist/codemods/v0-11/content-part-to-message-part.d.ts +1 -3
- 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 +1 -3
- package/dist/codemods/v0-12/assistant-api-to-aui.d.ts.map +1 -1
- package/dist/codemods/v0-12/assistant-api-to-aui.js +51 -99
- package/dist/codemods/v0-12/assistant-api-to-aui.js.map +1 -1
- package/dist/codemods/v0-12/event-names-to-camelcase.d.ts +1 -3
- 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 +1 -3
- package/dist/codemods/v0-12/primitive-if-to-aui-if.d.ts.map +1 -1
- package/dist/codemods/v0-15/aui-accessor-calls-to-properties.d.ts +1 -3
- package/dist/codemods/v0-15/aui-accessor-calls-to-properties.d.ts.map +1 -1
- package/dist/codemods/v0-15/aui-accessor-calls-to-properties.js +33 -5
- package/dist/codemods/v0-15/aui-accessor-calls-to-properties.js.map +1 -1
- package/dist/codemods/v0-8/ui-package-split.d.ts +1 -3
- package/dist/codemods/v0-8/ui-package-split.d.ts.map +1 -1
- package/dist/codemods/v0-9/edge-package-split.d.ts +1 -3
- package/dist/codemods/v0-9/edge-package-split.d.ts.map +1 -1
- package/dist/commands/add.d.ts +13 -16
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +3 -3
- package/dist/commands/agent.d.ts +1 -4
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +16 -13
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/create.d.ts +43 -39
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +40 -26
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/doctor.d.ts +17 -20
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/info.d.ts +4 -6
- package/dist/commands/info.d.ts.map +1 -1
- package/dist/commands/info.js +3 -3
- package/dist/commands/init.d.ts +8 -10
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +3 -4
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -4
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/update.d.ts +1 -4
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +1 -1
- package/dist/commands/upgrade.d.ts +7 -10
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/lib/agent-skill.d.ts +11 -10
- package/dist/lib/agent-skill.d.ts.map +1 -1
- package/dist/lib/agent-skill.js +78 -2
- package/dist/lib/agent-skill.js.map +1 -1
- package/dist/lib/create-project.d.ts +33 -32
- package/dist/lib/create-project.d.ts.map +1 -1
- package/dist/lib/create-project.js +91 -5
- package/dist/lib/create-project.js.map +1 -1
- package/dist/lib/handle-cli-error.d.ts +1 -4
- package/dist/lib/handle-cli-error.d.ts.map +1 -1
- package/dist/lib/install-ai-sdk-lib.d.ts +1 -4
- package/dist/lib/install-ai-sdk-lib.d.ts.map +1 -1
- package/dist/lib/install-edge-lib.d.ts +1 -4
- package/dist/lib/install-edge-lib.d.ts.map +1 -1
- package/dist/lib/run-spawn.d.ts +17 -19
- package/dist/lib/run-spawn.d.ts.map +1 -1
- package/dist/lib/run-spawn.js +25 -10
- package/dist/lib/run-spawn.js.map +1 -1
- package/dist/lib/transform-options.d.ts +5 -8
- package/dist/lib/transform-options.d.ts.map +1 -1
- package/dist/lib/transform.d.ts +13 -16
- package/dist/lib/transform.d.ts.map +1 -1
- package/dist/lib/transform.js +19 -35
- package/dist/lib/transform.js.map +1 -1
- package/dist/lib/upgrade.d.ts +3 -6
- package/dist/lib/upgrade.d.ts.map +1 -1
- package/dist/lib/upgrade.js +6 -11
- package/dist/lib/upgrade.js.map +1 -1
- package/dist/lib/utils/config.d.ts +18 -21
- package/dist/lib/utils/config.d.ts.map +1 -1
- package/dist/lib/utils/file-scanner.d.ts +12 -15
- package/dist/lib/utils/file-scanner.d.ts.map +1 -1
- package/dist/lib/utils/logger.d.ts +7 -10
- package/dist/lib/utils/logger.d.ts.map +1 -1
- package/dist/lib/utils/package-installer.d.ts +7 -10
- package/dist/lib/utils/package-installer.d.ts.map +1 -1
- package/dist/lib/utils/package-json.d.ts +1 -4
- package/dist/lib/utils/package-json.d.ts.map +1 -1
- package/dist/lib/utils/package-manager.d.ts +9 -12
- package/dist/lib/utils/package-manager.d.ts.map +1 -1
- package/dist/lib/utils/package-manager.js +13 -2
- package/dist/lib/utils/package-manager.js.map +1 -1
- package/dist/lib/utils/registry.d.ts +5 -8
- package/dist/lib/utils/registry.d.ts.map +1 -1
- package/dist/lib/utils/workspace.d.ts +2 -5
- package/dist/lib/utils/workspace.d.ts.map +1 -1
- package/dist/program.d.ts +1 -4
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +6 -6
- package/dist/run.d.ts +1 -4
- package/dist/run.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/codemods/utils/resolveBinding.test.ts +107 -0
- package/src/codemods/utils/resolveBinding.ts +130 -0
- package/src/codemods/v0-12/__tests__/assistant-api-to-aui.test.ts +256 -0
- package/src/codemods/v0-12/assistant-api-to-aui.ts +125 -225
- package/src/codemods/v0-15/__tests__/aui-accessor-calls-to-properties.test.ts +101 -0
- package/src/codemods/v0-15/aui-accessor-calls-to-properties.ts +60 -5
- package/src/commands/agent.ts +15 -22
- package/src/commands/create.ts +53 -20
- package/src/commands/init.ts +1 -2
- package/src/lib/agent-skill.ts +103 -1
- package/src/lib/create-project.ts +99 -4
- package/src/lib/run-spawn.test.ts +47 -0
- package/src/lib/run-spawn.ts +29 -17
- package/src/lib/transform-files.test.ts +71 -0
- package/src/lib/transform.test.ts +69 -1
- package/src/lib/transform.ts +23 -38
- package/src/lib/upgrade.ts +9 -13
- package/src/lib/utils/package-manager.ts +24 -1
- package/plugin/.claude-plugin/plugin.json +0 -8
- package/plugin/skills/assistant-ui/SKILL.md +0 -199
- package/plugin/skills/assistant-ui/shipables.json +0 -22
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createTransformer } from "../utils/createTransformer";
|
|
2
|
+
import { resolveBinding } from "../utils/resolveBinding";
|
|
2
3
|
|
|
3
4
|
// Map of old hook names to new hook names
|
|
4
5
|
const hookRenamingMap: Record<string, string> = {
|
|
@@ -13,62 +14,136 @@ const componentRenamingMap: Record<string, string> = {
|
|
|
13
14
|
AssistantProvider: "AuiProvider",
|
|
14
15
|
};
|
|
15
16
|
|
|
16
|
-
const isUseAuiCall = (j: any, node: any): boolean => {
|
|
17
|
-
return (
|
|
18
|
-
node &&
|
|
19
|
-
j.CallExpression.check(node) &&
|
|
20
|
-
j.Identifier.check(node.callee) &&
|
|
21
|
-
(node.callee.name === "useAui" || node.callee.name === "useAssistantApi")
|
|
22
|
-
);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
17
|
const migrateAssistantApiToAui = createTransformer(
|
|
26
18
|
({ j, root, markAsChanged }) => {
|
|
27
|
-
// 1. Update imports
|
|
28
19
|
root.find(j.ImportDeclaration).forEach((path: any) => {
|
|
29
20
|
const source = path.value.source.value;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
21
|
+
if (typeof source !== "string" || !source.startsWith("@assistant-ui/"))
|
|
22
|
+
return;
|
|
23
|
+
path.value.specifiers?.forEach((specifier: any, index: number) => {
|
|
24
|
+
if (
|
|
25
|
+
!j.ImportSpecifier.check(specifier) ||
|
|
26
|
+
!j.Identifier.check(specifier.imported)
|
|
27
|
+
)
|
|
28
|
+
return;
|
|
29
|
+
const oldName = specifier.imported.name;
|
|
30
|
+
const importKind = (specifier as { importKind?: string }).importKind;
|
|
31
|
+
const newName =
|
|
32
|
+
hookRenamingMap[oldName] ?? componentRenamingMap[oldName];
|
|
33
|
+
if (!newName) return;
|
|
34
|
+
if (
|
|
35
|
+
specifier.local?.name === oldName &&
|
|
36
|
+
path.value.importKind !== "type" &&
|
|
37
|
+
importKind !== "type"
|
|
38
|
+
) {
|
|
39
|
+
const references = root
|
|
40
|
+
.find(j.Identifier, { name: oldName })
|
|
41
|
+
.paths()
|
|
42
|
+
.filter((reference: any) => {
|
|
43
|
+
const parent = reference.parent.value;
|
|
44
|
+
const node = reference.value;
|
|
45
|
+
if (parent.type.startsWith("Import") || parent.id === node)
|
|
46
|
+
return false;
|
|
47
|
+
if (
|
|
48
|
+
parent.key === node &&
|
|
49
|
+
!parent.computed &&
|
|
50
|
+
parent.value !== node
|
|
51
|
+
)
|
|
52
|
+
return false;
|
|
53
|
+
if (parent.property === node && !parent.computed) return false;
|
|
54
|
+
if (j.TSQualifiedName.check(parent) && parent.right === node)
|
|
55
|
+
return false;
|
|
56
|
+
if (
|
|
57
|
+
j.JSXAttribute.check(parent) ||
|
|
58
|
+
j.JSXNamespacedName.check(parent)
|
|
59
|
+
)
|
|
60
|
+
return false;
|
|
61
|
+
if (j.ExportSpecifier.check(parent)) {
|
|
62
|
+
if (
|
|
63
|
+
reference.parent.parent.value.source ||
|
|
64
|
+
parent.local !== node
|
|
65
|
+
)
|
|
66
|
+
return false;
|
|
43
67
|
}
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
return resolveBinding(j, reference, oldName) === specifier.local;
|
|
69
|
+
});
|
|
70
|
+
const canRename =
|
|
71
|
+
!resolveBinding(j, path, newName) &&
|
|
72
|
+
references.every(
|
|
73
|
+
(reference: any) => !resolveBinding(j, reference, newName),
|
|
74
|
+
);
|
|
75
|
+
if (canRename) {
|
|
76
|
+
for (const reference of references) {
|
|
77
|
+
const parent = reference.parent.value;
|
|
78
|
+
if (
|
|
79
|
+
(j.Property.check(parent) || j.ObjectProperty.check(parent)) &&
|
|
80
|
+
parent.shorthand
|
|
81
|
+
) {
|
|
82
|
+
parent.shorthand = false;
|
|
83
|
+
parent.key = j.identifier(oldName);
|
|
84
|
+
parent.value = j.identifier(newName);
|
|
85
|
+
} else if (j.ExportSpecifier.check(parent)) {
|
|
86
|
+
reference.parent.replace(
|
|
87
|
+
j.exportSpecifier.from({
|
|
88
|
+
local: j.identifier(newName),
|
|
89
|
+
exported: j.Identifier.check(parent.exported)
|
|
90
|
+
? j.identifier(parent.exported.name)
|
|
91
|
+
: parent.exported,
|
|
92
|
+
}),
|
|
93
|
+
);
|
|
94
|
+
} else {
|
|
95
|
+
reference.value.name = newName;
|
|
53
96
|
}
|
|
54
|
-
markAsChanged();
|
|
55
97
|
}
|
|
98
|
+
specifier.local.name = newName;
|
|
56
99
|
}
|
|
57
|
-
}
|
|
100
|
+
}
|
|
101
|
+
const replacement: any = j.importSpecifier(
|
|
102
|
+
j.identifier(newName),
|
|
103
|
+
j.Identifier.check(specifier.local)
|
|
104
|
+
? j.identifier(specifier.local.name)
|
|
105
|
+
: null,
|
|
106
|
+
);
|
|
107
|
+
replacement.importKind = importKind;
|
|
108
|
+
replacement.comments = specifier.comments;
|
|
109
|
+
path.get("specifiers", index).replace(replacement);
|
|
110
|
+
markAsChanged();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const hookBindings = new Set<any>();
|
|
115
|
+
root.find(j.ImportDeclaration).forEach((path) => {
|
|
116
|
+
if (
|
|
117
|
+
!String(path.value.source.value).startsWith("@assistant-ui/") ||
|
|
118
|
+
path.value.importKind === "type"
|
|
119
|
+
)
|
|
120
|
+
return;
|
|
121
|
+
for (const specifier of path.value.specifiers ?? []) {
|
|
122
|
+
if (
|
|
123
|
+
j.ImportSpecifier.check(specifier) &&
|
|
124
|
+
j.Identifier.check(specifier.imported) &&
|
|
125
|
+
specifier.imported.name === "useAui" &&
|
|
126
|
+
(specifier as { importKind?: string }).importKind !== "type" &&
|
|
127
|
+
j.Identifier.check(specifier.local)
|
|
128
|
+
)
|
|
129
|
+
hookBindings.add(specifier.local);
|
|
58
130
|
}
|
|
59
131
|
});
|
|
60
132
|
|
|
61
|
-
// 2. Collect `api` declarators initialized from useAui / useAssistantApi.
|
|
62
|
-
// References are renamed by binding resolution, so an `api` bound
|
|
63
|
-
// elsewhere (function params, `const { api } = other()`) is never touched.
|
|
64
133
|
const renamedDeclaratorIds = new Set<any>();
|
|
65
134
|
root.find(j.VariableDeclarator).forEach((path: any) => {
|
|
135
|
+
const { id, init } = path.value;
|
|
66
136
|
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
137
|
+
j.Identifier.check(id) &&
|
|
138
|
+
id.name === "api" &&
|
|
139
|
+
j.CallExpression.check(init) &&
|
|
140
|
+
j.Identifier.check(init.callee) &&
|
|
141
|
+
hookBindings.has(
|
|
142
|
+
resolveBinding(j, path.get("init", "callee"), init.callee.name),
|
|
143
|
+
) &&
|
|
144
|
+
!resolveBinding(j, path, "aui")
|
|
70
145
|
) {
|
|
71
|
-
renamedDeclaratorIds.add(
|
|
146
|
+
renamedDeclaratorIds.add(id);
|
|
72
147
|
}
|
|
73
148
|
});
|
|
74
149
|
|
|
@@ -77,130 +152,8 @@ const migrateAssistantApiToAui = createTransformer(
|
|
|
77
152
|
// ast-types scopes, which have no block granularity: a block-scoped
|
|
78
153
|
// `const api = other()` inside the same function must shadow.
|
|
79
154
|
if (renamedDeclaratorIds.size > 0) {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
id &&
|
|
83
|
-
(id.type === "TSParameterProperty" ||
|
|
84
|
-
j.TSParameterProperty?.check?.(id))
|
|
85
|
-
) {
|
|
86
|
-
return patternBindsApi(id.parameter);
|
|
87
|
-
}
|
|
88
|
-
if (j.Identifier.check(id)) return id.name === "api";
|
|
89
|
-
if (j.ObjectPattern.check(id)) {
|
|
90
|
-
return id.properties.some((prop: any) =>
|
|
91
|
-
patternBindsApi(prop.value ?? prop.argument ?? prop),
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
if (j.ArrayPattern.check(id)) {
|
|
95
|
-
return id.elements.some((el: any) => el && patternBindsApi(el));
|
|
96
|
-
}
|
|
97
|
-
if (j.AssignmentPattern.check(id)) return patternBindsApi(id.left);
|
|
98
|
-
if (j.RestElement.check(id)) return patternBindsApi(id.argument);
|
|
99
|
-
return false;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
// What a statement-level node declares for `api`: the declarator id
|
|
103
|
-
// node when it is a plain `const/let/var api = ...`, "foreign" for any
|
|
104
|
-
// other binding of the name (patterns, functions, classes, enums), or
|
|
105
|
-
// undefined when it does not bind `api` at all.
|
|
106
|
-
const declaredApi = (statement: any): any => {
|
|
107
|
-
if (!statement) return undefined;
|
|
108
|
-
if (
|
|
109
|
-
j.ExportNamedDeclaration.check(statement) ||
|
|
110
|
-
j.ExportDefaultDeclaration.check(statement)
|
|
111
|
-
) {
|
|
112
|
-
return declaredApi(statement.declaration);
|
|
113
|
-
}
|
|
114
|
-
if (j.VariableDeclaration.check(statement)) {
|
|
115
|
-
for (const declarator of statement.declarations) {
|
|
116
|
-
if (!j.VariableDeclarator.check(declarator)) continue;
|
|
117
|
-
if (
|
|
118
|
-
j.Identifier.check(declarator.id) &&
|
|
119
|
-
declarator.id.name === "api"
|
|
120
|
-
)
|
|
121
|
-
return declarator.id;
|
|
122
|
-
if (patternBindsApi(declarator.id)) return "foreign";
|
|
123
|
-
}
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
// Type-only declarations do not shadow the value binding.
|
|
127
|
-
if (
|
|
128
|
-
statement.type === "TSTypeAliasDeclaration" ||
|
|
129
|
-
statement.type === "TSInterfaceDeclaration" ||
|
|
130
|
-
statement.type === "TSDeclareFunction"
|
|
131
|
-
)
|
|
132
|
-
return undefined;
|
|
133
|
-
// FunctionDeclaration, ClassDeclaration, TS enums/namespaces, …
|
|
134
|
-
if (
|
|
135
|
-
statement.id &&
|
|
136
|
-
j.Identifier.check(statement.id) &&
|
|
137
|
-
statement.id.name === "api"
|
|
138
|
-
)
|
|
139
|
-
return "foreign";
|
|
140
|
-
return undefined;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const scanStatements = (statements: any[]): any => {
|
|
144
|
-
for (const statement of statements) {
|
|
145
|
-
const found = declaredApi(statement);
|
|
146
|
-
if (found !== undefined) return found;
|
|
147
|
-
}
|
|
148
|
-
return undefined;
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
// Returns the declarator id node governing `api` here, or "foreign"
|
|
152
|
-
// when any other binding of the name shadows it first.
|
|
153
|
-
const governingApiBinding = (path: any): any => {
|
|
154
|
-
let current = path.parent;
|
|
155
|
-
while (current) {
|
|
156
|
-
const node = current.value;
|
|
157
|
-
|
|
158
|
-
// Anything function-like (declarations, expressions, arrows,
|
|
159
|
-
// object/class methods) binds its params.
|
|
160
|
-
if (Array.isArray(node.params) && node.params.some(patternBindsApi))
|
|
161
|
-
return "foreign";
|
|
162
|
-
// A named function/class expression binds its own name in its body.
|
|
163
|
-
if (
|
|
164
|
-
(j.FunctionExpression.check(node) ||
|
|
165
|
-
j.ClassExpression.check(node)) &&
|
|
166
|
-
node.id?.name === "api"
|
|
167
|
-
)
|
|
168
|
-
return "foreign";
|
|
169
|
-
if (
|
|
170
|
-
j.CatchClause.check(node) &&
|
|
171
|
-
node.param &&
|
|
172
|
-
patternBindsApi(node.param)
|
|
173
|
-
)
|
|
174
|
-
return "foreign";
|
|
175
|
-
|
|
176
|
-
let found: any;
|
|
177
|
-
if (j.BlockStatement.check(node) || j.Program.check(node)) {
|
|
178
|
-
found = scanStatements(node.body);
|
|
179
|
-
} else if (j.ForStatement.check(node)) {
|
|
180
|
-
found = declaredApi(node.init);
|
|
181
|
-
} else if (
|
|
182
|
-
j.ForOfStatement.check(node) ||
|
|
183
|
-
j.ForInStatement.check(node)
|
|
184
|
-
) {
|
|
185
|
-
found = declaredApi(node.left);
|
|
186
|
-
} else if (j.SwitchStatement.check(node)) {
|
|
187
|
-
found = scanStatements(
|
|
188
|
-
node.cases.flatMap((c: any) => c.consequent),
|
|
189
|
-
);
|
|
190
|
-
} else if (j.StaticBlock?.check?.(node)) {
|
|
191
|
-
found = scanStatements(node.body);
|
|
192
|
-
}
|
|
193
|
-
if (found !== undefined) return found;
|
|
194
|
-
|
|
195
|
-
current = current.parent;
|
|
196
|
-
}
|
|
197
|
-
return undefined;
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
const bindsToRenamedApi = (path: any): boolean => {
|
|
201
|
-
const governing = governingApiBinding(path);
|
|
202
|
-
return governing !== "foreign" && renamedDeclaratorIds.has(governing);
|
|
203
|
-
};
|
|
155
|
+
const bindsToRenamedApi = (path: any): boolean =>
|
|
156
|
+
renamedDeclaratorIds.has(resolveBinding(j, path, "api"));
|
|
204
157
|
|
|
205
158
|
const referencePaths: any[] = [];
|
|
206
159
|
root.find(j.Identifier, { name: "api" }).forEach((path: any) => {
|
|
@@ -268,6 +221,13 @@ const migrateAssistantApiToAui = createTransformer(
|
|
|
268
221
|
});
|
|
269
222
|
|
|
270
223
|
for (const path of referencePaths) {
|
|
224
|
+
if (resolveBinding(j, path, "aui")) {
|
|
225
|
+
renamedDeclaratorIds.delete(resolveBinding(j, path, "api"));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
for (const path of referencePaths) {
|
|
230
|
+
if (!bindsToRenamedApi(path)) continue;
|
|
271
231
|
const parent = path.parent.value;
|
|
272
232
|
if (
|
|
273
233
|
(j.Property.check(parent) || j.ObjectProperty.check(parent)) &&
|
|
@@ -304,66 +264,6 @@ const migrateAssistantApiToAui = createTransformer(
|
|
|
304
264
|
markAsChanged();
|
|
305
265
|
}
|
|
306
266
|
}
|
|
307
|
-
|
|
308
|
-
// 4. Update hook call references (in case they're used as values)
|
|
309
|
-
Object.entries(hookRenamingMap).forEach(([oldName, newName]) => {
|
|
310
|
-
root.find(j.Identifier).forEach((path: any) => {
|
|
311
|
-
if (path.value.name === oldName) {
|
|
312
|
-
// Skip if already handled in imports
|
|
313
|
-
if (j.ImportSpecifier.check(path.parent.value)) {
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// This might be a reference to the hook as a value
|
|
318
|
-
path.value.name = newName;
|
|
319
|
-
markAsChanged();
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// 5. Update JSX component names
|
|
325
|
-
Object.entries(componentRenamingMap).forEach(([oldName, newName]) => {
|
|
326
|
-
// Update JSX opening elements
|
|
327
|
-
root.find(j.JSXOpeningElement).forEach((path: any) => {
|
|
328
|
-
if (
|
|
329
|
-
j.JSXIdentifier.check(path.value.name) &&
|
|
330
|
-
path.value.name.name === oldName
|
|
331
|
-
) {
|
|
332
|
-
path.value.name.name = newName;
|
|
333
|
-
markAsChanged();
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
// Update JSX closing elements
|
|
338
|
-
root.find(j.JSXClosingElement).forEach((path: any) => {
|
|
339
|
-
if (
|
|
340
|
-
j.JSXIdentifier.check(path.value.name) &&
|
|
341
|
-
path.value.name.name === oldName
|
|
342
|
-
) {
|
|
343
|
-
path.value.name.name = newName;
|
|
344
|
-
markAsChanged();
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
// Update regular identifier references (for component references)
|
|
349
|
-
root.find(j.Identifier).forEach((path: any) => {
|
|
350
|
-
if (path.value.name === oldName) {
|
|
351
|
-
// Skip if already handled in imports
|
|
352
|
-
if (j.ImportSpecifier.check(path.parent.value)) {
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Skip JSX identifiers (already handled above)
|
|
357
|
-
if (j.JSXIdentifier.check(path.value)) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// This might be a reference to the component as a value
|
|
362
|
-
path.value.name = newName;
|
|
363
|
-
markAsChanged();
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
267
|
},
|
|
368
268
|
);
|
|
369
269
|
|
|
@@ -21,6 +21,86 @@ function applyTransform(source: string): string | null {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
describe("aui-accessor-calls-to-properties", () => {
|
|
24
|
+
it("resolves parameter defaults outside the function body", () => {
|
|
25
|
+
const output = applyTransform(`const client = useAui();
|
|
26
|
+
function worker(value = client.thread()) { var client = other(); return client.thread(); }`);
|
|
27
|
+
expect(output).toContain("value = client.thread)");
|
|
28
|
+
expect(output).toContain("return client.thread();");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("does not let a body client capture an unrelated parameter default", () => {
|
|
32
|
+
const output = applyTransform(`const client = other();
|
|
33
|
+
function worker(value = client.thread()) { var client = useAui(); return client.thread(); }`);
|
|
34
|
+
expect(output).toContain("value = client.thread())");
|
|
35
|
+
expect(output).toContain("return client.thread;");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("preserves unrelated namespace bindings", () => {
|
|
39
|
+
const output = applyTransform(`const client = useAui();
|
|
40
|
+
namespace Local { export const client = other(); client.thread(); }
|
|
41
|
+
client.thread();`);
|
|
42
|
+
expect(output).toContain("export const client = other(); client.thread();");
|
|
43
|
+
expect(output).toContain("\nclient.thread;");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it.each([
|
|
47
|
+
"const read = ({ aui }) => aui.thread();",
|
|
48
|
+
"const read = (aui = fallback) => aui.thread();",
|
|
49
|
+
"class Reader { constructor(private aui: AssistantClient) { aui.thread(); } }",
|
|
50
|
+
])("retains supported parameter wrappers: %s", (source) => {
|
|
51
|
+
expect(applyTransform(source)).toContain("aui.thread;");
|
|
52
|
+
});
|
|
53
|
+
it.each([
|
|
54
|
+
"function worker(client: { thread(): string }) { return client.thread(); }",
|
|
55
|
+
"function worker() { const client = other(); return client.thread(); }",
|
|
56
|
+
"{ const client = other(); client.thread(); }",
|
|
57
|
+
"{ const { client } = other(); client.thread(); }",
|
|
58
|
+
"for (const client of others) { client.thread(); }",
|
|
59
|
+
"for (const client of client.thread()) { client.thread(); }",
|
|
60
|
+
"for (let client of client.thread()) { client.thread(); }",
|
|
61
|
+
"for (const client in client.thread()) { client.thread(); }",
|
|
62
|
+
"for (let client in client.thread()) { client.thread(); }",
|
|
63
|
+
"try {} catch (client) { client.thread(); }",
|
|
64
|
+
"const worker = { run(client) { return client.thread(); } };",
|
|
65
|
+
"function worker() { if (ready) { var client = other(); } return client.thread(); }",
|
|
66
|
+
"switch (kind) { case 1: const client = other(); client.thread(); }",
|
|
67
|
+
])("preserves a different binding: %s", (unrelated) => {
|
|
68
|
+
const output = applyTransform(
|
|
69
|
+
`const client = useAui();\n${unrelated}\nclient.thread();`,
|
|
70
|
+
);
|
|
71
|
+
expect(output).toContain(unrelated);
|
|
72
|
+
expect(output).toContain("\nclient.thread;");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("does not treat a known unrelated aui binding as an implicit client", () => {
|
|
76
|
+
expect(applyTransform("const aui = other(); aui.thread();")).toBeNull();
|
|
77
|
+
expect(
|
|
78
|
+
applyTransform(
|
|
79
|
+
"function worker(aui: OtherClient) { return aui.thread(); }",
|
|
80
|
+
),
|
|
81
|
+
).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("keeps references to a recognized client in nested closures", () => {
|
|
85
|
+
expect(
|
|
86
|
+
applyTransform(
|
|
87
|
+
"const client = useAui(); const read = () => client.thread();",
|
|
88
|
+
),
|
|
89
|
+
).toContain("() => client.thread;");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("recognizes an aliased assistant hook without matching a foreign hook", () => {
|
|
93
|
+
const input = `import { useAui as useClient } from "@assistant-ui/react";
|
|
94
|
+
import { useAui } from "./other";
|
|
95
|
+
const client = useClient();
|
|
96
|
+
const other = useAui();
|
|
97
|
+
client.thread();
|
|
98
|
+
other.thread();`;
|
|
99
|
+
const output = applyTransform(input);
|
|
100
|
+
expect(output).toContain("client.thread;");
|
|
101
|
+
expect(output).toContain("other.thread();");
|
|
102
|
+
});
|
|
103
|
+
|
|
24
104
|
it("rewrites nullary accessor calls on a useAui variable", () => {
|
|
25
105
|
const input = `
|
|
26
106
|
const client = useAui();
|
|
@@ -33,6 +113,27 @@ client.thread.cancelRun();
|
|
|
33
113
|
const state = client.composer.getState();
|
|
34
114
|
`;
|
|
35
115
|
expect(applyTransform(input)?.trim()).toBe(expected.trim());
|
|
116
|
+
expect(applyTransform(expected)).toBeNull();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("resolves the switch discriminant outside the case declarations", () => {
|
|
120
|
+
const output = applyTransform(`const client = useAui();
|
|
121
|
+
switch (client.thread()) { case 1: const client = other(); client.thread(); }`);
|
|
122
|
+
expect(output).toContain("switch (client.thread)");
|
|
123
|
+
expect(output).toContain("const client = other(); client.thread();");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("migrates typed method parameters without touching another method", () => {
|
|
127
|
+
const output = applyTransform(`const readers = {
|
|
128
|
+
chat(client: AssistantClient) { return client.thread(); },
|
|
129
|
+
other(client: Other) { return client.thread(); },
|
|
130
|
+
};`);
|
|
131
|
+
expect(output).toContain(
|
|
132
|
+
"chat(client: AssistantClient) { return client.thread; }",
|
|
133
|
+
);
|
|
134
|
+
expect(output).toContain(
|
|
135
|
+
"other(client: Other) { return client.thread(); }",
|
|
136
|
+
);
|
|
36
137
|
});
|
|
37
138
|
|
|
38
139
|
it("rewrites only the accessor call in chained expressions", () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createTransformer } from "../utils/createTransformer";
|
|
2
|
+
import { resolveBinding } from "../utils/resolveBinding";
|
|
2
3
|
|
|
3
4
|
// Nullary scope accessors that became properties in v0.15. Parameterized
|
|
4
5
|
// lookups (e.g. `aui.thread.message({ id })`) stay as real calls.
|
|
@@ -28,7 +29,23 @@ const AUI_HOOKS = new Set(["useAui", "useAssistantApi"]);
|
|
|
28
29
|
|
|
29
30
|
const auiAccessorCallsToProperties = createTransformer(
|
|
30
31
|
({ j, root, markAsChanged }) => {
|
|
31
|
-
const
|
|
32
|
+
const auiBindings = new Set<any>();
|
|
33
|
+
const hookBindings = new Set<any>();
|
|
34
|
+
const hookNames = new Set(AUI_HOOKS);
|
|
35
|
+
root.find(j.ImportDeclaration).forEach((path) => {
|
|
36
|
+
if (!String(path.value.source.value).startsWith("@assistant-ui/")) return;
|
|
37
|
+
for (const specifier of path.value.specifiers ?? []) {
|
|
38
|
+
if (
|
|
39
|
+
j.ImportSpecifier.check(specifier) &&
|
|
40
|
+
j.Identifier.check(specifier.imported) &&
|
|
41
|
+
AUI_HOOKS.has(specifier.imported.name)
|
|
42
|
+
) {
|
|
43
|
+
hookBindings.add(specifier.local);
|
|
44
|
+
if (j.Identifier.check(specifier.local))
|
|
45
|
+
hookNames.add(specifier.local.name);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
32
49
|
|
|
33
50
|
root.find(j.VariableDeclarator).forEach((path: any) => {
|
|
34
51
|
const { id, init } = path.value;
|
|
@@ -37,13 +54,37 @@ const auiAccessorCallsToProperties = createTransformer(
|
|
|
37
54
|
init &&
|
|
38
55
|
j.CallExpression.check(init) &&
|
|
39
56
|
j.Identifier.check(init.callee) &&
|
|
40
|
-
|
|
57
|
+
hookNames.has(init.callee.name) &&
|
|
58
|
+
(() => {
|
|
59
|
+
const binding = resolveBinding(
|
|
60
|
+
j,
|
|
61
|
+
path.get("init", "callee"),
|
|
62
|
+
init.callee.name,
|
|
63
|
+
);
|
|
64
|
+
return binding
|
|
65
|
+
? hookBindings.has(binding)
|
|
66
|
+
: AUI_HOOKS.has(init.callee.name);
|
|
67
|
+
})()
|
|
41
68
|
) {
|
|
42
|
-
|
|
69
|
+
auiBindings.add(id);
|
|
43
70
|
}
|
|
44
71
|
});
|
|
45
72
|
|
|
46
73
|
const collectParam = (param: any) => {
|
|
74
|
+
if (param?.type === "TSParameterProperty")
|
|
75
|
+
return collectParam(param.parameter);
|
|
76
|
+
if (j.AssignmentPattern.check(param)) return collectParam(param.left);
|
|
77
|
+
if (j.RestElement.check(param)) return collectParam(param.argument);
|
|
78
|
+
if (j.ObjectPattern.check(param)) {
|
|
79
|
+
param.properties.forEach((p: any) =>
|
|
80
|
+
collectParam(p.value ?? p.argument),
|
|
81
|
+
);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (j.ArrayPattern.check(param)) {
|
|
85
|
+
param.elements.forEach(collectParam);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
47
88
|
const annotation = param?.typeAnnotation?.typeAnnotation;
|
|
48
89
|
if (
|
|
49
90
|
j.Identifier.check(param) &&
|
|
@@ -52,13 +93,21 @@ const auiAccessorCallsToProperties = createTransformer(
|
|
|
52
93
|
j.Identifier.check(annotation.typeName) &&
|
|
53
94
|
annotation.typeName.name === "AssistantClient"
|
|
54
95
|
) {
|
|
55
|
-
|
|
96
|
+
auiBindings.add(param);
|
|
97
|
+
} else if (
|
|
98
|
+
j.Identifier.check(param) &&
|
|
99
|
+
param.name === "aui" &&
|
|
100
|
+
!annotation
|
|
101
|
+
) {
|
|
102
|
+
auiBindings.add(param);
|
|
56
103
|
}
|
|
57
104
|
};
|
|
58
105
|
for (const fnType of [
|
|
59
106
|
j.FunctionDeclaration,
|
|
60
107
|
j.FunctionExpression,
|
|
61
108
|
j.ArrowFunctionExpression,
|
|
109
|
+
j.ObjectMethod,
|
|
110
|
+
j.ClassMethod,
|
|
62
111
|
] as const) {
|
|
63
112
|
root.find(fnType as typeof j.FunctionDeclaration).forEach((path: any) => {
|
|
64
113
|
path.value.params.forEach(collectParam);
|
|
@@ -73,7 +122,13 @@ const auiAccessorCallsToProperties = createTransformer(
|
|
|
73
122
|
if (!j.Identifier.check(callee.property)) return;
|
|
74
123
|
if (!NULLARY_SCOPES.has(callee.property.name)) return;
|
|
75
124
|
if (!j.Identifier.check(callee.object)) return;
|
|
76
|
-
|
|
125
|
+
const binding = resolveBinding(
|
|
126
|
+
j,
|
|
127
|
+
path.get("callee", "object"),
|
|
128
|
+
callee.object.name,
|
|
129
|
+
);
|
|
130
|
+
if (binding ? !auiBindings.has(binding) : callee.object.name !== "aui")
|
|
131
|
+
return;
|
|
77
132
|
j(path).replaceWith(callee);
|
|
78
133
|
markAsChanged();
|
|
79
134
|
});
|
package/src/commands/agent.ts
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
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
2
|
import { launch } from "@assistant-ui/agent-launcher";
|
|
3
|
+
import { ensureSkillsPlugin, skillsPluginDir } from "../lib/agent-skill";
|
|
4
|
+
import { logger } from "../lib/utils/logger";
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
resolve(__dirname, "..", "plugin"),
|
|
15
|
-
];
|
|
16
|
-
for (const candidate of candidates) {
|
|
17
|
-
if (existsSync(candidate)) return candidate;
|
|
6
|
+
async function resolvePluginDir(dry: boolean): Promise<string> {
|
|
7
|
+
if (dry) return skillsPluginDir();
|
|
8
|
+
try {
|
|
9
|
+
return await ensureSkillsPlugin();
|
|
10
|
+
} catch (error) {
|
|
11
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
12
|
+
process.exit(1);
|
|
18
13
|
}
|
|
19
|
-
throw new Error(
|
|
20
|
-
`Could not locate the assistant-ui plugin directory. Checked:\n${candidates.map((c) => ` ${c}`).join("\n")}`,
|
|
21
|
-
);
|
|
22
14
|
}
|
|
23
15
|
|
|
24
16
|
export const agent = new Command()
|
|
@@ -26,13 +18,14 @@ export const agent = new Command()
|
|
|
26
18
|
.description("launch Claude Code with assistant-ui skills")
|
|
27
19
|
.argument("<prompt...>", "prompt for the agent")
|
|
28
20
|
.option("--dry", "print the command instead of running it")
|
|
29
|
-
.action((promptParts: string[], opts) => {
|
|
30
|
-
const
|
|
21
|
+
.action(async (promptParts: string[], opts: { dry?: boolean }) => {
|
|
22
|
+
const dry = opts.dry === true;
|
|
23
|
+
const pluginDir = await resolvePluginDir(dry);
|
|
31
24
|
|
|
32
25
|
launch({
|
|
33
|
-
pluginDir
|
|
26
|
+
pluginDir,
|
|
34
27
|
skillName: "assistant-ui",
|
|
35
|
-
prompt,
|
|
36
|
-
dry
|
|
28
|
+
prompt: promptParts.join(" "),
|
|
29
|
+
dry,
|
|
37
30
|
});
|
|
38
31
|
});
|