byteplan-cli 1.3.1 → 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 +35 -5
- package/package.json +1 -1
- package/src/commands/skills.js +170 -158
package/README.md
CHANGED
|
@@ -43,16 +43,46 @@ byteplan model columns <modelCode>
|
|
|
43
43
|
byteplan data query <modelCode>
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
###
|
|
46
|
+
### Skills Commands
|
|
47
|
+
|
|
48
|
+
BytePlan CLI 提供了一系列 Skills,可以安装到多个 AI CLI 平台。
|
|
49
|
+
|
|
50
|
+
**命令列表**:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# 查看可用的 Skills
|
|
54
|
+
byteplan skills list
|
|
55
|
+
|
|
56
|
+
# 安装所有 Skills
|
|
57
|
+
byteplan skills install -p claude-code
|
|
58
|
+
|
|
59
|
+
# 查看已安装的 Skills
|
|
60
|
+
byteplan skills installed -p claude-code
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**支持的平台**:
|
|
64
|
+
|
|
65
|
+
| Platform | 目录 |
|
|
66
|
+
|----------|------|
|
|
67
|
+
| `claude-code` (alias: `claude`) | `~/.claude/commands` |
|
|
68
|
+
| `codex` | `~/.codex` |
|
|
69
|
+
| `openclaw` | `~/.openclaw/skills` |
|
|
70
|
+
|
|
71
|
+
**安装示例**:
|
|
47
72
|
|
|
48
73
|
```bash
|
|
49
|
-
#
|
|
50
|
-
byteplan
|
|
74
|
+
# 安装到单个平台
|
|
75
|
+
byteplan skills install -p claude-code
|
|
76
|
+
|
|
77
|
+
# 安装到多个平台
|
|
78
|
+
byteplan skills install -p claude-code,codex,openclaw
|
|
51
79
|
|
|
52
|
-
#
|
|
53
|
-
byteplan
|
|
80
|
+
# 使用自定义目录
|
|
81
|
+
byteplan skills install -d ~/.claude/commands
|
|
54
82
|
```
|
|
55
83
|
|
|
84
|
+
**Windows 用户**:`~` 路径会自动展开,也可使用 `%USERPROFILE%` 环境变量。
|
|
85
|
+
|
|
56
86
|
## Options
|
|
57
87
|
|
|
58
88
|
- `-e, --env <environment>` - Environment (uat, prod), default: uat
|
package/package.json
CHANGED
package/src/commands/skills.js
CHANGED
|
@@ -12,18 +12,73 @@ 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
|
+
|
|
32
|
+
// 平台默认目录映射
|
|
33
|
+
const PLATFORM_DIRS = {
|
|
34
|
+
'claude-code': path.join(homedir(), '.claude', 'commands'),
|
|
35
|
+
'claude': path.join(homedir(), '.claude', 'commands'), // alias
|
|
36
|
+
'codex': path.join(homedir(), '.codex'),
|
|
37
|
+
'openclaw': path.join(homedir(), '.openclaw', 'skills'),
|
|
38
|
+
};
|
|
39
|
+
|
|
15
40
|
// Skills 源目录(在 npm 包中)
|
|
16
41
|
const getSkillsSourceDir = () => {
|
|
17
|
-
// 获取当前模块所在目录,然后向上找到项目根目录
|
|
18
42
|
const currentDir = path.dirname(new URL(import.meta.url).pathname);
|
|
19
|
-
// 从 src/commands/skills.js -> src -> 项目根目录 -> skills
|
|
20
43
|
return path.join(currentDir, '..', '..', 'skills');
|
|
21
44
|
};
|
|
22
45
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
// 解析平台和目录参数
|
|
47
|
+
function resolveTargetDirs(platformArg, dirArg) {
|
|
48
|
+
const targets = [];
|
|
49
|
+
const expandedDirArg = expandHomeDir(dirArg);
|
|
50
|
+
|
|
51
|
+
if (platformArg) {
|
|
52
|
+
const platforms = platformArg.split(',').map(p => p.trim());
|
|
53
|
+
for (const platform of platforms) {
|
|
54
|
+
const normalizedName = platform.toLowerCase();
|
|
55
|
+
const defaultDir = PLATFORM_DIRS[normalizedName];
|
|
56
|
+
if (!defaultDir) {
|
|
57
|
+
return {
|
|
58
|
+
error: true,
|
|
59
|
+
message: `Unknown platform: ${platform}`,
|
|
60
|
+
available: Object.keys(PLATFORM_DIRS).filter(k => !k.includes('-')),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
targets.push({
|
|
64
|
+
platform: normalizedName,
|
|
65
|
+
targetDir: expandedDirArg || defaultDir,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
} else if (expandedDirArg) {
|
|
69
|
+
targets.push({
|
|
70
|
+
platform: 'custom',
|
|
71
|
+
targetDir: expandedDirArg,
|
|
72
|
+
});
|
|
73
|
+
} else {
|
|
74
|
+
return {
|
|
75
|
+
error: true,
|
|
76
|
+
message: 'Must specify at least one of --platform or --dir',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { targets };
|
|
81
|
+
}
|
|
27
82
|
|
|
28
83
|
function printJSON(data) {
|
|
29
84
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -37,17 +92,11 @@ function printError(error) {
|
|
|
37
92
|
// 获取 skill 信息
|
|
38
93
|
function getSkillInfo(skillPath) {
|
|
39
94
|
const skillMdPath = path.join(skillPath, 'SKILL.md');
|
|
40
|
-
if (!existsSync(skillMdPath))
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
95
|
+
if (!existsSync(skillMdPath)) return null;
|
|
43
96
|
|
|
44
97
|
const content = readFileSync(skillMdPath, 'utf-8');
|
|
45
|
-
|
|
46
|
-
// 解析 frontmatter
|
|
47
98
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
48
|
-
if (!frontmatterMatch)
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
99
|
+
if (!frontmatterMatch) return null;
|
|
51
100
|
|
|
52
101
|
const frontmatter = frontmatterMatch[1];
|
|
53
102
|
const nameMatch = frontmatter.match(/name:\s*(.+)/);
|
|
@@ -56,220 +105,183 @@ function getSkillInfo(skillPath) {
|
|
|
56
105
|
return {
|
|
57
106
|
name: nameMatch ? nameMatch[1].trim() : path.basename(skillPath),
|
|
58
107
|
description: descMatch ? descMatch[1].trim() : '',
|
|
59
|
-
path: skillPath,
|
|
60
108
|
};
|
|
61
109
|
}
|
|
62
110
|
|
|
63
111
|
// 获取所有可用 skills
|
|
64
112
|
function getAvailableSkills() {
|
|
65
113
|
const sourceDir = getSkillsSourceDir();
|
|
66
|
-
if (!existsSync(sourceDir))
|
|
67
|
-
return [];
|
|
68
|
-
}
|
|
114
|
+
if (!existsSync(sourceDir)) return [];
|
|
69
115
|
|
|
70
116
|
const skills = [];
|
|
71
117
|
const dirs = readdirSync(sourceDir, { withFileTypes: true });
|
|
72
|
-
|
|
73
118
|
for (const dir of dirs) {
|
|
74
119
|
if (dir.isDirectory()) {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
if (info) {
|
|
78
|
-
skills.push(info);
|
|
79
|
-
}
|
|
120
|
+
const info = getSkillInfo(path.join(sourceDir, dir.name));
|
|
121
|
+
if (info) skills.push(info);
|
|
80
122
|
}
|
|
81
123
|
}
|
|
82
|
-
|
|
83
124
|
return skills;
|
|
84
125
|
}
|
|
85
126
|
|
|
86
|
-
// 获取已安装的 skills
|
|
127
|
+
// 获取已安装的 skills
|
|
87
128
|
function getInstalledSkills(targetDir) {
|
|
88
|
-
if (!existsSync(targetDir))
|
|
89
|
-
return [];
|
|
90
|
-
}
|
|
129
|
+
if (!existsSync(targetDir)) return [];
|
|
91
130
|
|
|
92
131
|
const skills = [];
|
|
93
132
|
const dirs = readdirSync(targetDir, { withFileTypes: true });
|
|
94
|
-
|
|
95
133
|
for (const dir of dirs) {
|
|
96
134
|
if (dir.isDirectory() && dir.name.startsWith('byteplan-')) {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
if (info) {
|
|
100
|
-
skills.push(info);
|
|
101
|
-
}
|
|
135
|
+
const info = getSkillInfo(path.join(targetDir, dir.name));
|
|
136
|
+
if (info) skills.push(info);
|
|
102
137
|
}
|
|
103
138
|
}
|
|
104
|
-
|
|
105
139
|
return skills;
|
|
106
140
|
}
|
|
107
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
|
+
|
|
108
174
|
// Skills 命令
|
|
109
175
|
const skillsCmd = new Command('skills')
|
|
110
|
-
.description('Manage BytePlan
|
|
111
|
-
.
|
|
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')
|
|
112
179
|
.addHelpText('after', `
|
|
113
180
|
Commands:
|
|
114
|
-
skills
|
|
115
|
-
skills
|
|
116
|
-
skills
|
|
117
|
-
|
|
181
|
+
skills install Install all BytePlan skills
|
|
182
|
+
skills list List all available skills
|
|
183
|
+
skills installed List installed skills
|
|
184
|
+
|
|
185
|
+
Platforms:
|
|
186
|
+
claude-code (claude) ~/.claude/commands
|
|
187
|
+
codex ~/.codex
|
|
188
|
+
openclaw ~/.openclaw/skills
|
|
118
189
|
|
|
119
190
|
Examples:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
$ byteplan skills install-all -d ~/.claude/skills
|
|
124
|
-
$ byteplan skills install-all -d /custom/path/skills
|
|
191
|
+
byteplan skills install -p claude-code
|
|
192
|
+
byteplan skills install -p claude-code,codex,openclaw
|
|
193
|
+
byteplan skills list
|
|
125
194
|
`);
|
|
126
195
|
|
|
127
|
-
// skills
|
|
196
|
+
// skills install 子命令 - 安装全部
|
|
128
197
|
skillsCmd
|
|
129
|
-
.command('
|
|
130
|
-
.description('
|
|
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
|
|
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
|
+
`)
|
|
131
210
|
.action(() => {
|
|
132
211
|
try {
|
|
133
|
-
const
|
|
212
|
+
const parentOptions = skillsCmd.opts();
|
|
213
|
+
const resolved = resolveTargetDirs(parentOptions.platform, parentOptions.dir);
|
|
214
|
+
|
|
215
|
+
if (resolved.error) {
|
|
216
|
+
printJSON(resolved);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const { results, totalSkills } = installAllSkills(resolved.targets);
|
|
134
221
|
|
|
135
222
|
printJSON({
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
total: skills.length,
|
|
141
|
-
sourceDir: getSkillsSourceDir(),
|
|
223
|
+
success: true,
|
|
224
|
+
platforms: results,
|
|
225
|
+
totalPlatforms: resolved.targets.length,
|
|
226
|
+
totalSkills: totalSkills,
|
|
142
227
|
});
|
|
143
228
|
} catch (error) {
|
|
144
229
|
printError(error);
|
|
145
230
|
}
|
|
146
231
|
});
|
|
147
232
|
|
|
148
|
-
// skills
|
|
233
|
+
// skills list 子命令
|
|
149
234
|
skillsCmd
|
|
150
|
-
.command('
|
|
151
|
-
.description('List
|
|
235
|
+
.command('list')
|
|
236
|
+
.description('List all available BytePlan skills')
|
|
152
237
|
.action(() => {
|
|
153
238
|
try {
|
|
154
|
-
|
|
155
|
-
const parentOptions = skillsCmd.opts();
|
|
156
|
-
const targetDir = parentOptions.dir;
|
|
157
|
-
const skills = getInstalledSkills(targetDir);
|
|
158
|
-
|
|
239
|
+
const skills = getAvailableSkills();
|
|
159
240
|
printJSON({
|
|
160
|
-
skills: skills.map(s => ({
|
|
161
|
-
name: s.name,
|
|
162
|
-
description: s.description,
|
|
163
|
-
})),
|
|
241
|
+
skills: skills.map(s => ({ name: s.name, description: s.description })),
|
|
164
242
|
total: skills.length,
|
|
165
|
-
targetDir: targetDir,
|
|
166
243
|
});
|
|
167
244
|
} catch (error) {
|
|
168
245
|
printError(error);
|
|
169
246
|
}
|
|
170
247
|
});
|
|
171
248
|
|
|
172
|
-
// skills
|
|
249
|
+
// skills installed 子命令
|
|
173
250
|
skillsCmd
|
|
174
|
-
.command('
|
|
175
|
-
.description('
|
|
176
|
-
.option('-a, --all', 'Install all BytePlan skills', false)
|
|
251
|
+
.command('installed')
|
|
252
|
+
.description('List installed BytePlan skills')
|
|
177
253
|
.addHelpText('after', `
|
|
254
|
+
Options (from parent):
|
|
255
|
+
-p, --platform Target platform(s): claude-code, codex, openclaw
|
|
256
|
+
-d, --dir Custom target directory
|
|
257
|
+
|
|
178
258
|
Examples:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
$ byteplan skills install byteplan-api -d /custom/path/skills
|
|
259
|
+
byteplan skills installed -p claude-code
|
|
260
|
+
byteplan skills installed -d ~/.claude/commands
|
|
182
261
|
`)
|
|
183
|
-
.action(
|
|
262
|
+
.action(() => {
|
|
184
263
|
try {
|
|
185
|
-
// 获取父命令的 -d 选项(requiredOption 已确保必传)
|
|
186
264
|
const parentOptions = skillsCmd.opts();
|
|
187
|
-
const
|
|
188
|
-
const sourceDir = getSkillsSourceDir();
|
|
189
|
-
|
|
190
|
-
// 确保目标目录存在
|
|
191
|
-
if (!existsSync(targetDir)) {
|
|
192
|
-
mkdirSync(targetDir, { recursive: true });
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// 获取要安装的 skills
|
|
196
|
-
let toInstall = names || [];
|
|
197
|
-
if (options.all || toInstall.length === 0) {
|
|
198
|
-
const available = getAvailableSkills();
|
|
199
|
-
toInstall = available.map(s => s.name);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// 验证 skills 存在
|
|
203
|
-
const availableSkills = getAvailableSkills();
|
|
204
|
-
const availableNames = availableSkills.map(s => s.name);
|
|
265
|
+
const resolved = resolveTargetDirs(parentOptions.platform, parentOptions.dir);
|
|
205
266
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
printJSON({
|
|
209
|
-
error: true,
|
|
210
|
-
message: `Unknown skills: ${invalid.join(', ')}`,
|
|
211
|
-
available: availableNames,
|
|
212
|
-
});
|
|
267
|
+
if (resolved.error) {
|
|
268
|
+
printJSON(resolved);
|
|
213
269
|
process.exit(1);
|
|
214
270
|
}
|
|
215
271
|
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
installed.push(skillName);
|
|
272
|
+
const results = {};
|
|
273
|
+
for (const target of resolved.targets) {
|
|
274
|
+
const skills = getInstalledSkills(target.targetDir);
|
|
275
|
+
results[target.platform] = {
|
|
276
|
+
targetDir: target.targetDir,
|
|
277
|
+
skills: skills.map(s => ({ name: s.name, description: s.description })),
|
|
278
|
+
count: skills.length,
|
|
279
|
+
};
|
|
225
280
|
}
|
|
226
281
|
|
|
227
282
|
printJSON({
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
total: installed.length,
|
|
231
|
-
targetDir: targetDir,
|
|
232
|
-
});
|
|
233
|
-
} catch (error) {
|
|
234
|
-
printError(error);
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// skills install-all 子命令(快捷方式)
|
|
239
|
-
skillsCmd
|
|
240
|
-
.command('install-all')
|
|
241
|
-
.description('Install all BytePlan skills to target directory')
|
|
242
|
-
.action(async () => {
|
|
243
|
-
try {
|
|
244
|
-
// 获取父命令的 -d 选项(requiredOption 已确保必传)
|
|
245
|
-
const parentOptions = skillsCmd.opts();
|
|
246
|
-
const targetDir = parentOptions.dir;
|
|
247
|
-
const sourceDir = getSkillsSourceDir();
|
|
248
|
-
|
|
249
|
-
// 确保目标目录存在
|
|
250
|
-
if (!existsSync(targetDir)) {
|
|
251
|
-
mkdirSync(targetDir, { recursive: true });
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// 获取所有 skills
|
|
255
|
-
const availableSkills = getAvailableSkills();
|
|
256
|
-
|
|
257
|
-
// 安装所有 skills
|
|
258
|
-
const installed = [];
|
|
259
|
-
for (const skill of availableSkills) {
|
|
260
|
-
const skillSourcePath = path.join(sourceDir, skill.name);
|
|
261
|
-
const skillTargetPath = path.join(targetDir, skill.name);
|
|
262
|
-
|
|
263
|
-
// 复制 skill 目录
|
|
264
|
-
cpSync(skillSourcePath, skillTargetPath, { recursive: true });
|
|
265
|
-
installed.push(skill.name);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
printJSON({
|
|
269
|
-
success: true,
|
|
270
|
-
installed: installed,
|
|
271
|
-
total: installed.length,
|
|
272
|
-
targetDir: targetDir,
|
|
283
|
+
platforms: results,
|
|
284
|
+
totalPlatforms: resolved.targets.length,
|
|
273
285
|
});
|
|
274
286
|
} catch (error) {
|
|
275
287
|
printError(error);
|