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 CHANGED
@@ -45,55 +45,44 @@ byteplan data query <modelCode>
45
45
 
46
46
  ### Skills Commands
47
47
 
48
- BytePlan CLI 提供了一系列 Claude Code Skills,可以安装到多个 AI CLI 平台。
48
+ BytePlan CLI 提供了一系列 Skills,可以安装到多个 AI CLI 平台。
49
49
 
50
- ### 查看可用 Skills
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-all -p claude-code
69
- ```
57
+ byteplan skills install -p claude-code
70
58
 
71
- ### 支持的平台
59
+ # 查看已安装的 Skills
60
+ byteplan skills installed -p claude-code
61
+ ```
72
62
 
73
- | Platform | 目录 | 说明 |
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
- 也可以使用 `-d` 参数指定自定义目录:
71
+ **安装示例**:
82
72
 
83
73
  ```bash
84
- # 向后兼容:指定自定义目录
85
- byteplan skills install byteplan-api -d ~/.claude/commands
74
+ # 安装到单个平台
75
+ byteplan skills install -p claude-code
86
76
 
87
- # 组合使用:覆盖平台默认目录
88
- byteplan skills install byteplan-api -p claude-code -d /custom/path
89
- ```
90
-
91
- ### 查看已安装 Skills
77
+ # 安装到多个平台
78
+ byteplan skills install -p claude-code,codex,openclaw
92
79
 
93
- ```bash
94
- byteplan skills installed -p claude-code
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "byteplan-cli",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "BytePlan CLI - Command line tool for BytePlan API",
5
5
  "keywords": [
6
6
  "byteplan",
@@ -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: dirArg || defaultDir, // 如果指定了 -d,覆盖所有平台的默认目录
65
+ targetDir: expandedDirArg || defaultDir,
54
66
  });
55
67
  }
56
- } else if (dirArg) {
57
- // 向后兼容:只指定 -d 的情况
68
+ } else if (expandedDirArg) {
58
69
  targets.push({
59
70
  platform: 'custom',
60
- targetDir: dirArg,
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 skillPath = path.join(sourceDir, dir.name);
121
- const info = getSkillInfo(skillPath);
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 skillPath = path.join(targetDir, dir.name);
143
- const info = getSkillInfo(skillPath);
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 Claude Code skills')
156
- .option('-d, --dir <directory>', 'Target installation directory (overrides platform default)')
157
- .option('-p, --platform <platforms>', 'Target platform(s): claude-code, codex, openclaw (comma-separated for multiple)')
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 list List all available skills
161
- skills installed List installed skills (requires -p or -d)
162
- skills install [names] Install skills (requires -p or -d)
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
- Supported Platforms:
166
- claude-code (alias: claude) ~/.claude/commands
167
- codex~/.codex
168
- openclaw~/.openclaw/skills
185
+ Platforms:
186
+ claude-code (claude) ~/.claude/commands
187
+ codex ~/.codex
188
+ openclaw ~/.openclaw/skills
169
189
 
170
190
  Examples:
171
- $ byteplan skills list
172
- $ byteplan skills installed -p claude-code
173
- $ byteplan skills install byteplan-api -p claude-code
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 list 子命令
196
+ // skills install 子命令 - 安装全部
181
197
  skillsCmd
182
- .command('list')
183
- .description('List all available BytePlan skills')
184
- .action(() => {
185
- try {
186
- const skills = getAvailableSkills();
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
- // skills installed 子命令
202
- skillsCmd
203
- .command('installed')
204
- .description('List installed BytePlan skills in target directory')
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 platformArg = parentOptions.platform;
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 install 子命令
233
+ // skills list 子命令
240
234
  skillsCmd
241
- .command('install [names...]')
242
- .description('Install specified skills to target directory')
243
- .option('-a, --all', 'Install all BytePlan skills', false)
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 parentOptions = skillsCmd.opts();
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
- success: true,
317
- platforms: results,
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 install-all 子命令(快捷方式)
249
+ // skills installed 子命令
327
250
  skillsCmd
328
- .command('install-all')
329
- .description('Install all BytePlan skills to target directory')
330
- .action(async () => {
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 platformArg = parentOptions.platform;
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 targetDir = target.targetDir;
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
- installed: installed,
372
- count: installed.length,
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);