byteplan-cli 1.3.2 → 1.3.3
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 +22 -33
- package/package.json +1 -1
- package/src/commands/skills.js +117 -213
package/README.md
CHANGED
|
@@ -45,55 +45,44 @@ byteplan data query <modelCode>
|
|
|
45
45
|
|
|
46
46
|
### Skills Commands
|
|
47
47
|
|
|
48
|
-
BytePlan CLI 提供了一系列
|
|
48
|
+
BytePlan CLI 提供了一系列 Skills,可以安装到多个 AI CLI 平台。
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
**命令列表**:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
+
# 查看可用的 Skills
|
|
53
54
|
byteplan skills list
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### 安装 Skills
|
|
57
|
-
|
|
58
|
-
支持多平台安装,使用 `-p` 或 `--platform` 参数指定目标平台:
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
# 安装到 Claude Code
|
|
62
|
-
byteplan skills install byteplan-api -p claude-code
|
|
63
|
-
|
|
64
|
-
# 安装到多个平台
|
|
65
|
-
byteplan skills install -p claude-code,codex,openclaw
|
|
66
55
|
|
|
67
56
|
# 安装所有 Skills
|
|
68
|
-
byteplan skills install
|
|
69
|
-
```
|
|
57
|
+
byteplan skills install -p claude-code
|
|
70
58
|
|
|
71
|
-
|
|
59
|
+
# 查看已安装的 Skills
|
|
60
|
+
byteplan skills installed -p claude-code
|
|
61
|
+
```
|
|
72
62
|
|
|
73
|
-
|
|
74
|
-
|----------|------|------|
|
|
75
|
-
| `claude-code` (alias: `claude`) | `~/.claude/commands` | Claude Code CLI |
|
|
76
|
-
| `codex` | `~/.codex` | OpenAI Codex CLI |
|
|
77
|
-
| `openclaw` | `~/.openclaw/skills` | OpenClaw |
|
|
63
|
+
**支持的平台**:
|
|
78
64
|
|
|
79
|
-
|
|
65
|
+
| Platform | 目录 |
|
|
66
|
+
|----------|------|
|
|
67
|
+
| `claude-code` (alias: `claude`) | `~/.claude/commands` |
|
|
68
|
+
| `codex` | `~/.codex` |
|
|
69
|
+
| `openclaw` | `~/.openclaw/skills` |
|
|
80
70
|
|
|
81
|
-
|
|
71
|
+
**安装示例**:
|
|
82
72
|
|
|
83
73
|
```bash
|
|
84
|
-
#
|
|
85
|
-
byteplan skills install
|
|
74
|
+
# 安装到单个平台
|
|
75
|
+
byteplan skills install -p claude-code
|
|
86
76
|
|
|
87
|
-
#
|
|
88
|
-
byteplan skills install
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### 查看已安装 Skills
|
|
77
|
+
# 安装到多个平台
|
|
78
|
+
byteplan skills install -p claude-code,codex,openclaw
|
|
92
79
|
|
|
93
|
-
|
|
94
|
-
byteplan skills
|
|
80
|
+
# 使用自定义目录
|
|
81
|
+
byteplan skills install -d ~/.claude/commands
|
|
95
82
|
```
|
|
96
83
|
|
|
84
|
+
**Windows 用户**:`~` 路径会自动展开,也可使用 `%USERPROFILE%` 环境变量。
|
|
85
|
+
|
|
97
86
|
## Options
|
|
98
87
|
|
|
99
88
|
- `-e, --env <environment>` - Environment (uat, prod), default: uat
|
package/package.json
CHANGED
package/src/commands/skills.js
CHANGED
|
@@ -12,6 +12,23 @@ import { createRequire } from 'module';
|
|
|
12
12
|
|
|
13
13
|
const require = createRequire(import.meta.url);
|
|
14
14
|
|
|
15
|
+
// 展开 ~ 路径,支持跨平台
|
|
16
|
+
function expandHomeDir(dirPath) {
|
|
17
|
+
if (!dirPath) return dirPath;
|
|
18
|
+
|
|
19
|
+
// 处理 ~ 开头的路径
|
|
20
|
+
if (dirPath.startsWith('~')) {
|
|
21
|
+
return path.join(homedir(), dirPath.slice(1));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Windows: 处理 %USERPROFILE% 环境变量
|
|
25
|
+
if (dirPath.includes('%USERPROFILE%')) {
|
|
26
|
+
return dirPath.replace('%USERPROFILE%', homedir());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return dirPath;
|
|
30
|
+
}
|
|
31
|
+
|
|
15
32
|
// 平台默认目录映射
|
|
16
33
|
const PLATFORM_DIRS = {
|
|
17
34
|
'claude-code': path.join(homedir(), '.claude', 'commands'),
|
|
@@ -22,24 +39,20 @@ const PLATFORM_DIRS = {
|
|
|
22
39
|
|
|
23
40
|
// Skills 源目录(在 npm 包中)
|
|
24
41
|
const getSkillsSourceDir = () => {
|
|
25
|
-
// 获取当前模块所在目录,然后向上找到项目根目录
|
|
26
42
|
const currentDir = path.dirname(new URL(import.meta.url).pathname);
|
|
27
|
-
// 从 src/commands/skills.js -> src -> 项目根目录 -> skills
|
|
28
43
|
return path.join(currentDir, '..', '..', 'skills');
|
|
29
44
|
};
|
|
30
45
|
|
|
31
|
-
//
|
|
46
|
+
// 解析平台和目录参数
|
|
32
47
|
function resolveTargetDirs(platformArg, dirArg) {
|
|
33
48
|
const targets = [];
|
|
49
|
+
const expandedDirArg = expandHomeDir(dirArg);
|
|
34
50
|
|
|
35
51
|
if (platformArg) {
|
|
36
|
-
// 解析平台列表(逗号分隔)
|
|
37
52
|
const platforms = platformArg.split(',').map(p => p.trim());
|
|
38
|
-
|
|
39
53
|
for (const platform of platforms) {
|
|
40
54
|
const normalizedName = platform.toLowerCase();
|
|
41
55
|
const defaultDir = PLATFORM_DIRS[normalizedName];
|
|
42
|
-
|
|
43
56
|
if (!defaultDir) {
|
|
44
57
|
return {
|
|
45
58
|
error: true,
|
|
@@ -47,20 +60,17 @@ function resolveTargetDirs(platformArg, dirArg) {
|
|
|
47
60
|
available: Object.keys(PLATFORM_DIRS).filter(k => !k.includes('-')),
|
|
48
61
|
};
|
|
49
62
|
}
|
|
50
|
-
|
|
51
63
|
targets.push({
|
|
52
64
|
platform: normalizedName,
|
|
53
|
-
targetDir:
|
|
65
|
+
targetDir: expandedDirArg || defaultDir,
|
|
54
66
|
});
|
|
55
67
|
}
|
|
56
|
-
} else if (
|
|
57
|
-
// 向后兼容:只指定 -d 的情况
|
|
68
|
+
} else if (expandedDirArg) {
|
|
58
69
|
targets.push({
|
|
59
70
|
platform: 'custom',
|
|
60
|
-
targetDir:
|
|
71
|
+
targetDir: expandedDirArg,
|
|
61
72
|
});
|
|
62
73
|
} else {
|
|
63
|
-
// 都不指定,返回错误
|
|
64
74
|
return {
|
|
65
75
|
error: true,
|
|
66
76
|
message: 'Must specify at least one of --platform or --dir',
|
|
@@ -82,17 +92,11 @@ function printError(error) {
|
|
|
82
92
|
// 获取 skill 信息
|
|
83
93
|
function getSkillInfo(skillPath) {
|
|
84
94
|
const skillMdPath = path.join(skillPath, 'SKILL.md');
|
|
85
|
-
if (!existsSync(skillMdPath))
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
95
|
+
if (!existsSync(skillMdPath)) return null;
|
|
88
96
|
|
|
89
97
|
const content = readFileSync(skillMdPath, 'utf-8');
|
|
90
|
-
|
|
91
|
-
// 解析 frontmatter
|
|
92
98
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
93
|
-
if (!frontmatterMatch)
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
99
|
+
if (!frontmatterMatch) return null;
|
|
96
100
|
|
|
97
101
|
const frontmatter = frontmatterMatch[1];
|
|
98
102
|
const nameMatch = frontmatter.match(/name:\s*(.+)/);
|
|
@@ -101,283 +105,183 @@ function getSkillInfo(skillPath) {
|
|
|
101
105
|
return {
|
|
102
106
|
name: nameMatch ? nameMatch[1].trim() : path.basename(skillPath),
|
|
103
107
|
description: descMatch ? descMatch[1].trim() : '',
|
|
104
|
-
path: skillPath,
|
|
105
108
|
};
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
// 获取所有可用 skills
|
|
109
112
|
function getAvailableSkills() {
|
|
110
113
|
const sourceDir = getSkillsSourceDir();
|
|
111
|
-
if (!existsSync(sourceDir))
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
+
if (!existsSync(sourceDir)) return [];
|
|
114
115
|
|
|
115
116
|
const skills = [];
|
|
116
117
|
const dirs = readdirSync(sourceDir, { withFileTypes: true });
|
|
117
|
-
|
|
118
118
|
for (const dir of dirs) {
|
|
119
119
|
if (dir.isDirectory()) {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
if (info) {
|
|
123
|
-
skills.push(info);
|
|
124
|
-
}
|
|
120
|
+
const info = getSkillInfo(path.join(sourceDir, dir.name));
|
|
121
|
+
if (info) skills.push(info);
|
|
125
122
|
}
|
|
126
123
|
}
|
|
127
|
-
|
|
128
124
|
return skills;
|
|
129
125
|
}
|
|
130
126
|
|
|
131
|
-
// 获取已安装的 skills
|
|
127
|
+
// 获取已安装的 skills
|
|
132
128
|
function getInstalledSkills(targetDir) {
|
|
133
|
-
if (!existsSync(targetDir))
|
|
134
|
-
return [];
|
|
135
|
-
}
|
|
129
|
+
if (!existsSync(targetDir)) return [];
|
|
136
130
|
|
|
137
131
|
const skills = [];
|
|
138
132
|
const dirs = readdirSync(targetDir, { withFileTypes: true });
|
|
139
|
-
|
|
140
133
|
for (const dir of dirs) {
|
|
141
134
|
if (dir.isDirectory() && dir.name.startsWith('byteplan-')) {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
if (info) {
|
|
145
|
-
skills.push(info);
|
|
146
|
-
}
|
|
135
|
+
const info = getSkillInfo(path.join(targetDir, dir.name));
|
|
136
|
+
if (info) skills.push(info);
|
|
147
137
|
}
|
|
148
138
|
}
|
|
149
|
-
|
|
150
139
|
return skills;
|
|
151
140
|
}
|
|
152
141
|
|
|
142
|
+
// 安装所有 skills
|
|
143
|
+
function installAllSkills(targetDirs) {
|
|
144
|
+
const sourceDir = getSkillsSourceDir();
|
|
145
|
+
const availableSkills = getAvailableSkills();
|
|
146
|
+
const toInstall = availableSkills.map(s => s.name);
|
|
147
|
+
const results = {};
|
|
148
|
+
|
|
149
|
+
for (const target of targetDirs) {
|
|
150
|
+
if (!existsSync(target.targetDir)) {
|
|
151
|
+
mkdirSync(target.targetDir, { recursive: true });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const installed = [];
|
|
155
|
+
for (const skillName of toInstall) {
|
|
156
|
+
cpSync(
|
|
157
|
+
path.join(sourceDir, skillName),
|
|
158
|
+
path.join(target.targetDir, skillName),
|
|
159
|
+
{ recursive: true }
|
|
160
|
+
);
|
|
161
|
+
installed.push(skillName);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
results[target.platform] = {
|
|
165
|
+
targetDir: target.targetDir,
|
|
166
|
+
installed: installed,
|
|
167
|
+
count: installed.length,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return { results, totalSkills: toInstall.length };
|
|
172
|
+
}
|
|
173
|
+
|
|
153
174
|
// Skills 命令
|
|
154
175
|
const skillsCmd = new Command('skills')
|
|
155
|
-
.description('Manage BytePlan
|
|
156
|
-
.option('-
|
|
157
|
-
.option('-
|
|
176
|
+
.description('Manage BytePlan skills (install, list, check installed)')
|
|
177
|
+
.option('-p, --platform <platforms>', 'Target platform(s): claude-code, codex, openclaw (comma-separated)')
|
|
178
|
+
.option('-d, --dir <directory>', 'Custom target directory')
|
|
158
179
|
.addHelpText('after', `
|
|
159
180
|
Commands:
|
|
160
|
-
skills
|
|
161
|
-
skills
|
|
162
|
-
skills
|
|
163
|
-
skills install-all Install all BytePlan skills (requires -p or -d)
|
|
181
|
+
skills install Install all BytePlan skills
|
|
182
|
+
skills list List all available skills
|
|
183
|
+
skills installed List installed skills
|
|
164
184
|
|
|
165
|
-
|
|
166
|
-
claude-code (
|
|
167
|
-
codex
|
|
168
|
-
openclaw
|
|
185
|
+
Platforms:
|
|
186
|
+
claude-code (claude) ~/.claude/commands
|
|
187
|
+
codex ~/.codex
|
|
188
|
+
openclaw ~/.openclaw/skills
|
|
169
189
|
|
|
170
190
|
Examples:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
$ byteplan skills install byteplan-api byteplan-analysis -p claude-code,codex
|
|
175
|
-
$ byteplan skills install-all -p claude-code,openclaw
|
|
176
|
-
$ byteplan skills install byteplan-api -d ~/.claude/commands
|
|
177
|
-
$ byteplan skills install byteplan-api -p claude-code -d /custom/path
|
|
191
|
+
byteplan skills install -p claude-code
|
|
192
|
+
byteplan skills install -p claude-code,codex,openclaw
|
|
193
|
+
byteplan skills list
|
|
178
194
|
`);
|
|
179
195
|
|
|
180
|
-
// skills
|
|
196
|
+
// skills install 子命令 - 安装全部
|
|
181
197
|
skillsCmd
|
|
182
|
-
.command('
|
|
183
|
-
.description('
|
|
184
|
-
.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
printJSON({
|
|
189
|
-
skills: skills.map(s => ({
|
|
190
|
-
name: s.name,
|
|
191
|
-
description: s.description,
|
|
192
|
-
})),
|
|
193
|
-
total: skills.length,
|
|
194
|
-
sourceDir: getSkillsSourceDir(),
|
|
195
|
-
});
|
|
196
|
-
} catch (error) {
|
|
197
|
-
printError(error);
|
|
198
|
-
}
|
|
199
|
-
});
|
|
198
|
+
.command('install')
|
|
199
|
+
.description('Install all BytePlan skills to target platform(s)')
|
|
200
|
+
.addHelpText('after', `
|
|
201
|
+
Options (from parent):
|
|
202
|
+
-p, --platform Target platform(s): claude-code, codex, openclaw
|
|
203
|
+
-d, --dir Custom target directory
|
|
200
204
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
Examples:
|
|
206
|
+
byteplan skills install -p claude-code
|
|
207
|
+
byteplan skills install -p claude-code,codex
|
|
208
|
+
byteplan skills install -d ~/.claude/commands
|
|
209
|
+
`)
|
|
205
210
|
.action(() => {
|
|
206
211
|
try {
|
|
207
212
|
const parentOptions = skillsCmd.opts();
|
|
208
|
-
const
|
|
209
|
-
const dirArg = parentOptions.dir;
|
|
213
|
+
const resolved = resolveTargetDirs(parentOptions.platform, parentOptions.dir);
|
|
210
214
|
|
|
211
|
-
const resolved = resolveTargetDirs(platformArg, dirArg);
|
|
212
215
|
if (resolved.error) {
|
|
213
216
|
printJSON(resolved);
|
|
214
217
|
process.exit(1);
|
|
215
218
|
}
|
|
216
219
|
|
|
217
|
-
const results =
|
|
218
|
-
for (const target of resolved.targets) {
|
|
219
|
-
const skills = getInstalledSkills(target.targetDir);
|
|
220
|
-
results[target.platform] = {
|
|
221
|
-
targetDir: target.targetDir,
|
|
222
|
-
skills: skills.map(s => ({
|
|
223
|
-
name: s.name,
|
|
224
|
-
description: s.description,
|
|
225
|
-
})),
|
|
226
|
-
count: skills.length,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
220
|
+
const { results, totalSkills } = installAllSkills(resolved.targets);
|
|
229
221
|
|
|
230
222
|
printJSON({
|
|
223
|
+
success: true,
|
|
231
224
|
platforms: results,
|
|
232
225
|
totalPlatforms: resolved.targets.length,
|
|
226
|
+
totalSkills: totalSkills,
|
|
233
227
|
});
|
|
234
228
|
} catch (error) {
|
|
235
229
|
printError(error);
|
|
236
230
|
}
|
|
237
231
|
});
|
|
238
232
|
|
|
239
|
-
// skills
|
|
233
|
+
// skills list 子命令
|
|
240
234
|
skillsCmd
|
|
241
|
-
.command('
|
|
242
|
-
.description('
|
|
243
|
-
.
|
|
244
|
-
.addHelpText('after', `
|
|
245
|
-
Examples:
|
|
246
|
-
$ byteplan skills install byteplan-api -p claude-code
|
|
247
|
-
$ byteplan skills install byteplan-api byteplan-analysis -p claude-code,codex
|
|
248
|
-
$ byteplan skills install --all -p claude-code
|
|
249
|
-
$ byteplan skills install byteplan-api -d ~/.claude/commands
|
|
250
|
-
$ byteplan skills install byteplan-api -p claude-code -d /custom/path
|
|
251
|
-
`)
|
|
252
|
-
.action(async (names, options) => {
|
|
235
|
+
.command('list')
|
|
236
|
+
.description('List all available BytePlan skills')
|
|
237
|
+
.action(() => {
|
|
253
238
|
try {
|
|
254
|
-
const
|
|
255
|
-
const platformArg = parentOptions.platform;
|
|
256
|
-
const dirArg = parentOptions.dir;
|
|
257
|
-
|
|
258
|
-
const resolved = resolveTargetDirs(platformArg, dirArg);
|
|
259
|
-
if (resolved.error) {
|
|
260
|
-
printJSON(resolved);
|
|
261
|
-
process.exit(1);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const sourceDir = getSkillsSourceDir();
|
|
265
|
-
|
|
266
|
-
// 获取要安装的 skills
|
|
267
|
-
let toInstall = names || [];
|
|
268
|
-
if (options.all || toInstall.length === 0) {
|
|
269
|
-
const available = getAvailableSkills();
|
|
270
|
-
toInstall = available.map(s => s.name);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// 验证 skills 存在
|
|
274
|
-
const availableSkills = getAvailableSkills();
|
|
275
|
-
const availableNames = availableSkills.map(s => s.name);
|
|
276
|
-
|
|
277
|
-
const invalid = toInstall.filter(n => !availableNames.includes(n));
|
|
278
|
-
if (invalid.length > 0) {
|
|
279
|
-
printJSON({
|
|
280
|
-
error: true,
|
|
281
|
-
message: `Unknown skills: ${invalid.join(', ')}`,
|
|
282
|
-
available: availableNames,
|
|
283
|
-
});
|
|
284
|
-
process.exit(1);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 安装到各目标目录
|
|
288
|
-
const results = {};
|
|
289
|
-
for (const target of resolved.targets) {
|
|
290
|
-
const targetDir = target.targetDir;
|
|
291
|
-
|
|
292
|
-
// 确保目标目录存在
|
|
293
|
-
if (!existsSync(targetDir)) {
|
|
294
|
-
mkdirSync(targetDir, { recursive: true });
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// 安装 skills
|
|
298
|
-
const installed = [];
|
|
299
|
-
for (const skillName of toInstall) {
|
|
300
|
-
const skillSourcePath = path.join(sourceDir, skillName);
|
|
301
|
-
const skillTargetPath = path.join(targetDir, skillName);
|
|
302
|
-
|
|
303
|
-
// 复制 skill 目录
|
|
304
|
-
cpSync(skillSourcePath, skillTargetPath, { recursive: true });
|
|
305
|
-
installed.push(skillName);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
results[target.platform] = {
|
|
309
|
-
targetDir: targetDir,
|
|
310
|
-
installed: installed,
|
|
311
|
-
count: installed.length,
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
|
|
239
|
+
const skills = getAvailableSkills();
|
|
315
240
|
printJSON({
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
totalPlatforms: resolved.targets.length,
|
|
319
|
-
totalSkills: toInstall.length,
|
|
241
|
+
skills: skills.map(s => ({ name: s.name, description: s.description })),
|
|
242
|
+
total: skills.length,
|
|
320
243
|
});
|
|
321
244
|
} catch (error) {
|
|
322
245
|
printError(error);
|
|
323
246
|
}
|
|
324
247
|
});
|
|
325
248
|
|
|
326
|
-
// skills
|
|
249
|
+
// skills installed 子命令
|
|
327
250
|
skillsCmd
|
|
328
|
-
.command('
|
|
329
|
-
.description('
|
|
330
|
-
.
|
|
251
|
+
.command('installed')
|
|
252
|
+
.description('List installed BytePlan skills')
|
|
253
|
+
.addHelpText('after', `
|
|
254
|
+
Options (from parent):
|
|
255
|
+
-p, --platform Target platform(s): claude-code, codex, openclaw
|
|
256
|
+
-d, --dir Custom target directory
|
|
257
|
+
|
|
258
|
+
Examples:
|
|
259
|
+
byteplan skills installed -p claude-code
|
|
260
|
+
byteplan skills installed -d ~/.claude/commands
|
|
261
|
+
`)
|
|
262
|
+
.action(() => {
|
|
331
263
|
try {
|
|
332
264
|
const parentOptions = skillsCmd.opts();
|
|
333
|
-
const
|
|
334
|
-
const dirArg = parentOptions.dir;
|
|
265
|
+
const resolved = resolveTargetDirs(parentOptions.platform, parentOptions.dir);
|
|
335
266
|
|
|
336
|
-
const resolved = resolveTargetDirs(platformArg, dirArg);
|
|
337
267
|
if (resolved.error) {
|
|
338
268
|
printJSON(resolved);
|
|
339
269
|
process.exit(1);
|
|
340
270
|
}
|
|
341
271
|
|
|
342
|
-
const sourceDir = getSkillsSourceDir();
|
|
343
|
-
|
|
344
|
-
// 获取所有 skills
|
|
345
|
-
const availableSkills = getAvailableSkills();
|
|
346
|
-
const toInstall = availableSkills.map(s => s.name);
|
|
347
|
-
|
|
348
|
-
// 安装到各目标目录
|
|
349
272
|
const results = {};
|
|
350
273
|
for (const target of resolved.targets) {
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
// 确保目标目录存在
|
|
354
|
-
if (!existsSync(targetDir)) {
|
|
355
|
-
mkdirSync(targetDir, { recursive: true });
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// 安装所有 skills
|
|
359
|
-
const installed = [];
|
|
360
|
-
for (const skillName of toInstall) {
|
|
361
|
-
const skillSourcePath = path.join(sourceDir, skillName);
|
|
362
|
-
const skillTargetPath = path.join(targetDir, skillName);
|
|
363
|
-
|
|
364
|
-
// 复制 skill 目录
|
|
365
|
-
cpSync(skillSourcePath, skillTargetPath, { recursive: true });
|
|
366
|
-
installed.push(skillName);
|
|
367
|
-
}
|
|
368
|
-
|
|
274
|
+
const skills = getInstalledSkills(target.targetDir);
|
|
369
275
|
results[target.platform] = {
|
|
370
|
-
targetDir: targetDir,
|
|
371
|
-
|
|
372
|
-
count:
|
|
276
|
+
targetDir: target.targetDir,
|
|
277
|
+
skills: skills.map(s => ({ name: s.name, description: s.description })),
|
|
278
|
+
count: skills.length,
|
|
373
279
|
};
|
|
374
280
|
}
|
|
375
281
|
|
|
376
282
|
printJSON({
|
|
377
|
-
success: true,
|
|
378
283
|
platforms: results,
|
|
379
284
|
totalPlatforms: resolved.targets.length,
|
|
380
|
-
totalSkills: toInstall.length,
|
|
381
285
|
});
|
|
382
286
|
} catch (error) {
|
|
383
287
|
printError(error);
|