@botskill/cli 1.0.3 → 1.0.4

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.
@@ -1,62 +1,60 @@
1
- import { Command } from 'commander';
2
- import path from 'path';
3
- import fs from 'fs-extra';
4
- import { getToken } from '../lib/auth.js';
5
- import { uploadSkillFile, findUploadFile } from '../lib/uploadSkill.js';
6
-
7
- const publishCommand = new Command('publish');
8
- publishCommand
9
- .description('Publish a skill to BotSkill (alias for push)')
10
- .option('-f, --file <path>', 'Path to SKILL.md, .zip, or .tar.gz')
11
- .option('--dry-run', 'Validate without uploading')
12
- .option('--api-url <url>', 'API base URL')
13
- .action(async (options) => {
14
- if (!getToken()) {
15
- console.error('Not logged in. Run: skm login');
16
- process.exit(1);
17
- }
18
-
19
- let filePath = options.file;
20
- if (!filePath) {
21
- filePath = await findUploadFile();
22
- if (!filePath) {
23
- console.error('No skill file found. Create SKILL.md or a .zip/.tar.gz package, or use --file <path>');
24
- process.exit(1);
25
- }
26
- } else {
27
- if (!await fs.pathExists(filePath)) {
28
- console.error('File not found:', filePath);
29
- process.exit(1);
30
- }
31
- }
32
-
33
- if (options.dryRun) {
34
- console.log('[DRY RUN] Would publish:', path.resolve(filePath));
35
- return;
36
- }
37
-
38
- console.log(`Publishing skill from ${path.basename(filePath)}...`);
39
- try {
40
- const skill = await uploadSkillFile(filePath);
41
- console.log('Skill published successfully!');
42
- console.log(`Name: ${skill?.name}`);
43
- console.log(`Version: ${skill?.version || (skill?.versions?.[0]?.version)}`);
44
- console.log(`Status: ${skill?.status || 'pending_review'}`);
45
- } catch (err) {
46
- if (err.message === 'NOT_LOGGED_IN') {
47
- console.error('Not logged in. Run: skm login');
48
- } else if (err.message === 'FILE_NOT_FOUND') {
49
- console.error('File not found');
50
- } else {
51
- const msg = err.response?.data?.error || err.response?.data?.details?.[0] || err.message || 'Publish failed';
52
- if (err.response?.status === 401) {
53
- console.error('Token expired or invalid. Run: skm login');
54
- } else {
55
- console.error('Publish failed:', msg);
56
- }
57
- }
58
- process.exit(1);
59
- }
60
- });
61
-
62
- export { publishCommand };
1
+ import { Command } from 'commander';
2
+ import path from 'path';
3
+ import fs from 'fs-extra';
4
+ import { getToken } from '../lib/auth.js';
5
+ import { printApiError, printSimpleError } from '../lib/formatError.js';
6
+ import { uploadSkillFile, findUploadFile } from '../lib/uploadSkill.js';
7
+
8
+ const publishCommand = new Command('publish');
9
+ publishCommand
10
+ .description('Publish a skill to BotSkill (alias for push)')
11
+ .option('-f, --file <path>', 'Path to SKILL.md, .zip, or .tar.gz')
12
+ .option('--dry-run', 'Validate without uploading')
13
+ .option('--api-url <url>', 'API base URL (overrides config for this command)')
14
+ .action(async (options) => {
15
+ if (!getToken()) {
16
+ console.error('Not logged in. Run: skm login');
17
+ process.exit(1);
18
+ }
19
+
20
+ let filePath = options.file;
21
+ if (!filePath) {
22
+ filePath = await findUploadFile();
23
+ if (!filePath) {
24
+ console.error('No skill file found. Create SKILL.md or a .zip/.tar.gz package, or use --file <path>');
25
+ process.exit(1);
26
+ }
27
+ } else {
28
+ if (!await fs.pathExists(filePath)) {
29
+ console.error('File not found:', filePath);
30
+ process.exit(1);
31
+ }
32
+ }
33
+
34
+ if (options.dryRun) {
35
+ console.log('[DRY RUN] Would publish:', path.resolve(filePath));
36
+ return;
37
+ }
38
+
39
+ console.log(`Publishing skill from ${path.basename(filePath)}...`);
40
+ try {
41
+ const skill = await uploadSkillFile(filePath, { apiUrl: options.apiUrl });
42
+ console.log('Skill published successfully!');
43
+ console.log(`Name: ${skill?.name}`);
44
+ console.log(`Version: ${skill?.version || (skill?.versions?.[0]?.version)}`);
45
+ console.log(`Status: ${skill?.status || 'pending_review'}`);
46
+ } catch (err) {
47
+ if (err.message === 'NOT_LOGGED_IN') {
48
+ printSimpleError('Not logged in', 'Run "skm login" first');
49
+ } else if (err.message === 'FILE_NOT_FOUND') {
50
+ printSimpleError('File not found', 'Check the path with --file <path>');
51
+ } else {
52
+ const msg = err.response?.data?.error || err.response?.data?.details?.[0];
53
+ if (msg) err._overrideMsg = msg;
54
+ if (err.response?.status === 401) err._overrideMsg = 'Token expired or invalid. Run "skm login" first.';
55
+ printApiError(err, { prefix: 'Publish failed' });
56
+ }
57
+ }
58
+ });
59
+
60
+ export { publishCommand };
@@ -1,62 +1,60 @@
1
- import { Command } from 'commander';
2
- import path from 'path';
3
- import fs from 'fs-extra';
4
- import { getToken } from '../lib/auth.js';
5
- import { uploadSkillFile, findUploadFile, validCategories } from '../lib/uploadSkill.js';
6
-
7
- const pushCommand = new Command('push');
8
- pushCommand
9
- .description('Upload/push a skill to BotSkill (SKILL.md, .zip, or .tar.gz)')
10
- .option('-f, --file <path>', 'Path to SKILL.md, .zip, or .tar.gz')
11
- .option('--dry-run', 'Validate without uploading')
12
- .option('--api-url <url>', 'API base URL')
13
- .action(async (options) => {
14
- if (!getToken()) {
15
- console.error('Not logged in. Run: skm login');
16
- process.exit(1);
17
- }
18
-
19
- let filePath = options.file;
20
- if (!filePath) {
21
- filePath = await findUploadFile();
22
- if (!filePath) {
23
- console.error('No skill file found. Create SKILL.md or a .zip/.tar.gz package, or use --file <path>');
24
- process.exit(1);
25
- }
26
- } else {
27
- if (!await fs.pathExists(filePath)) {
28
- console.error('File not found:', filePath);
29
- process.exit(1);
30
- }
31
- }
32
-
33
- if (options.dryRun) {
34
- console.log('[DRY RUN] Would upload:', path.resolve(filePath));
35
- return;
36
- }
37
-
38
- console.log(`Pushing skill from ${path.basename(filePath)}...`);
39
- try {
40
- const skill = await uploadSkillFile(filePath);
41
- console.log('Skill uploaded successfully!');
42
- console.log(`Name: ${skill?.name}`);
43
- console.log(`Version: ${skill?.version || (skill?.versions?.[0]?.version)}`);
44
- console.log(`Status: ${skill?.status || 'pending_review'}`);
45
- } catch (err) {
46
- if (err.message === 'NOT_LOGGED_IN') {
47
- console.error('Not logged in. Run: skm login');
48
- } else if (err.message === 'FILE_NOT_FOUND') {
49
- console.error('File not found');
50
- } else {
51
- const msg = err.response?.data?.error || err.response?.data?.details?.[0] || err.message || 'Upload failed';
52
- if (err.response?.status === 401) {
53
- console.error('Token expired or invalid. Run: skm login');
54
- } else {
55
- console.error('Upload failed:', msg);
56
- }
57
- }
58
- process.exit(1);
59
- }
60
- });
61
-
62
- export { pushCommand };
1
+ import { Command } from 'commander';
2
+ import path from 'path';
3
+ import fs from 'fs-extra';
4
+ import { getToken } from '../lib/auth.js';
5
+ import { printApiError, printSimpleError } from '../lib/formatError.js';
6
+ import { uploadSkillFile, findUploadFile, validCategories } from '../lib/uploadSkill.js';
7
+
8
+ const pushCommand = new Command('push');
9
+ pushCommand
10
+ .description('Upload/push a skill to BotSkill (SKILL.md, .zip, or .tar.gz)')
11
+ .option('-f, --file <path>', 'Path to SKILL.md, .zip, or .tar.gz')
12
+ .option('--dry-run', 'Validate without uploading')
13
+ .option('--api-url <url>', 'API base URL')
14
+ .action(async (options) => {
15
+ if (!getToken()) {
16
+ console.error('Not logged in. Run: skm login');
17
+ process.exit(1);
18
+ }
19
+
20
+ let filePath = options.file;
21
+ if (!filePath) {
22
+ filePath = await findUploadFile();
23
+ if (!filePath) {
24
+ console.error('No skill file found. Create SKILL.md or a .zip/.tar.gz package, or use --file <path>');
25
+ process.exit(1);
26
+ }
27
+ } else {
28
+ if (!await fs.pathExists(filePath)) {
29
+ console.error('File not found:', filePath);
30
+ process.exit(1);
31
+ }
32
+ }
33
+
34
+ if (options.dryRun) {
35
+ console.log('[DRY RUN] Would upload:', path.resolve(filePath));
36
+ return;
37
+ }
38
+
39
+ console.log(`Pushing skill from ${path.basename(filePath)}...`);
40
+ try {
41
+ const skill = await uploadSkillFile(filePath, { apiUrl: options.apiUrl });
42
+ console.log('Skill uploaded successfully!');
43
+ console.log(`Name: ${skill?.name}`);
44
+ console.log(`Version: ${skill?.version || (skill?.versions?.[0]?.version)}`);
45
+ console.log(`Status: ${skill?.status || 'pending_review'}`);
46
+ } catch (err) {
47
+ if (err.message === 'NOT_LOGGED_IN') {
48
+ printSimpleError('Not logged in', 'Run "skm login" first');
49
+ } else if (err.message === 'FILE_NOT_FOUND') {
50
+ printSimpleError('File not found', 'Check the path with --file <path>');
51
+ } else {
52
+ const msg = err.response?.data?.error || err.response?.data?.details?.[0];
53
+ if (msg) err._overrideMsg = msg;
54
+ if (err.response?.status === 401) err._overrideMsg = 'Token expired or invalid. Run "skm login" first.';
55
+ printApiError(err, { prefix: 'Push failed' });
56
+ }
57
+ }
58
+ });
59
+
60
+ export { pushCommand };
@@ -1,61 +1,57 @@
1
- import { Command } from 'commander';
2
- import { createApiClient } from '../lib/auth.js';
3
-
4
- function formatSkillDisplay(skill) {
5
- const author = skill.author?.username || skill.author?.fullName || '?';
6
- const name = skill.name || '?';
7
- const displayName = `@${author}/${name}`;
8
- const version = skill.version || (skill.versions?.[0]?.version) || '—';
9
- const downloads = skill.downloads ?? 0;
10
- const category = skill.category || '—';
11
- return { displayName, version, downloads, category };
12
- }
13
-
14
- const searchCommand = new Command('search');
15
- searchCommand
16
- .description('Search skills from BotSkill')
17
- .argument('<query>', 'Search query (name or description)')
18
- .option('-c, --category <category>', 'Filter by category (ai, data, web, devops, security, tools)')
19
- .option('-l, --limit <number>', 'Maximum number of results (default: 20)', '20')
20
- .option('-p, --page <number>', 'Page number for pagination (default: 1)', '1')
21
- .action(async (query, options) => {
22
- const api = createApiClient();
23
- const limit = parseInt(options.limit, 10) || 20;
24
- const page = parseInt(options.page, 10) || 1;
25
-
26
- try {
27
- const params = { q: query, page, limit };
28
- if (options.category) params.category = options.category;
29
-
30
- const res = await api.get('/skills/search', { params });
31
- const skills = res.data?.skills ?? res.data ?? [];
32
- const pagination = res.data?.pagination ?? {};
33
-
34
- if (skills.length === 0) {
35
- console.log(`No skills found for "${query}".`);
36
- return;
37
- }
38
-
39
- console.log(`\nFound ${pagination.totalSkills ?? skills.length} skill(s) for "${query}":`);
40
- console.log('─'.repeat(60));
41
- skills.forEach((skill) => {
42
- const { displayName, version, downloads, category } = formatSkillDisplay(skill);
43
- console.log(` ${displayName}`);
44
- console.log(` Version: ${version} | Downloads: ${downloads} | Category: ${category}`);
45
- });
46
- if (pagination.totalPages > 1) {
47
- console.log(`\nPage ${pagination.currentPage}/${pagination.totalPages}`);
48
- }
49
- console.log('\nUse "skm get @author/name" or "skm get @author/name@version" to download.');
50
- } catch (err) {
51
- let msg = err.message;
52
- if (err.response?.data) {
53
- const d = err.response.data;
54
- msg = d.error || d.message || msg;
55
- }
56
- console.error('Error:', msg);
57
- process.exit(1);
58
- }
59
- });
60
-
61
- export { searchCommand };
1
+ import { Command } from 'commander';
2
+ import { createApiClient } from '../lib/auth.js';
3
+ import { printApiError } from '../lib/formatError.js';
4
+
5
+ function formatSkillDisplay(skill) {
6
+ const author = skill.author?.username || skill.author?.fullName || '?';
7
+ const name = skill.name || '?';
8
+ const displayName = `@${author}/${name}`;
9
+ const version = skill.version || (skill.versions?.[0]?.version) || '—';
10
+ const downloads = skill.downloads ?? 0;
11
+ const category = skill.category || '—';
12
+ return { displayName, version, downloads, category };
13
+ }
14
+
15
+ const searchCommand = new Command('search');
16
+ searchCommand
17
+ .description('Search skills from BotSkill')
18
+ .argument('<query>', 'Search query (name or description)')
19
+ .option('-c, --category <category>', 'Filter by category (ai, data, web, devops, security, tools)')
20
+ .option('-l, --limit <number>', 'Maximum number of results (default: 20)', '20')
21
+ .option('-p, --page <number>', 'Page number for pagination (default: 1)', '1')
22
+ .option('--api-url <url>', 'API base URL (overrides config for this command)')
23
+ .action(async (query, options) => {
24
+ const api = createApiClient(options.apiUrl);
25
+ const limit = parseInt(options.limit, 10) || 20;
26
+ const page = parseInt(options.page, 10) || 1;
27
+
28
+ try {
29
+ const params = { q: query, page, limit };
30
+ if (options.category) params.category = options.category;
31
+
32
+ const res = await api.get('/skills/search', { params });
33
+ const skills = res.data?.skills ?? res.data ?? [];
34
+ const pagination = res.data?.pagination ?? {};
35
+
36
+ if (skills.length === 0) {
37
+ console.log(`No skills found for "${query}".`);
38
+ return;
39
+ }
40
+
41
+ console.log(`\nFound ${pagination.totalSkills ?? skills.length} skill(s) for "${query}":`);
42
+ console.log('─'.repeat(60));
43
+ skills.forEach((skill) => {
44
+ const { displayName, version, downloads, category } = formatSkillDisplay(skill);
45
+ console.log(` ${displayName}`);
46
+ console.log(` Version: ${version} | Downloads: ${downloads} | Category: ${category}`);
47
+ });
48
+ if (pagination.totalPages > 1) {
49
+ console.log(`\nPage ${pagination.currentPage}/${pagination.totalPages}`);
50
+ }
51
+ console.log('\nUse "skm get @author/name" or "skm get @author/name@version" to download.');
52
+ } catch (err) {
53
+ printApiError(err, { prefix: 'Search failed' });
54
+ }
55
+ });
56
+
57
+ export { searchCommand };
package/src/index.js CHANGED
@@ -1,45 +1,45 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from 'commander';
4
- import { readFileSync } from 'fs';
5
- import { fileURLToPath } from 'url';
6
- import { dirname, join } from 'path';
7
- import { initCommand } from './commands/init.js';
8
- import { loginCommand } from './commands/login.js';
9
- import { logoutCommand } from './commands/logout.js';
10
- import { configCommand } from './commands/config.js';
11
- import { getCommand } from './commands/get.js';
12
- import { pushCommand } from './commands/push.js';
13
- import { publishCommand } from './commands/publish.js';
14
- import { listCommand } from './commands/list.js';
15
- import { searchCommand } from './commands/search.js';
16
- import { infoCommand } from './commands/info.js';
17
- import { helpCommand } from './commands/help.js';
18
-
19
- // 读取 package.json 中的版本号
20
- const __filename = fileURLToPath(import.meta.url);
21
- const __dirname = dirname(__filename);
22
- const packageJsonPath = join(__dirname, '../package.json');
23
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
24
- const version = packageJson.version || '0.0.0';
25
-
26
- const program = new Command();
27
-
28
- program
29
- .name('skm')
30
- .description('CLI tool for managing BotSkill - a platform for AI agent skills')
31
- .version(version);
32
-
33
- program.addCommand(initCommand);
34
- program.addCommand(loginCommand);
35
- program.addCommand(logoutCommand);
36
- program.addCommand(configCommand);
37
- program.addCommand(getCommand);
38
- program.addCommand(pushCommand);
39
- program.addCommand(publishCommand);
40
- program.addCommand(listCommand);
41
- program.addCommand(searchCommand);
42
- program.addCommand(infoCommand);
43
- program.addCommand(helpCommand);
44
-
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { readFileSync } from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+ import { initCommand } from './commands/init.js';
8
+ import { loginCommand } from './commands/login.js';
9
+ import { logoutCommand } from './commands/logout.js';
10
+ import { configCommand } from './commands/config.js';
11
+ import { getCommand } from './commands/get.js';
12
+ import { pushCommand } from './commands/push.js';
13
+ import { publishCommand } from './commands/publish.js';
14
+ import { listCommand } from './commands/list.js';
15
+ import { searchCommand } from './commands/search.js';
16
+ import { infoCommand } from './commands/info.js';
17
+ import { helpCommand } from './commands/help.js';
18
+
19
+ // 读取 package.json 中的版本号
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = dirname(__filename);
22
+ const packageJsonPath = join(__dirname, '../package.json');
23
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
24
+ const version = packageJson.version || '0.0.0';
25
+
26
+ const program = new Command();
27
+
28
+ program
29
+ .name('skm')
30
+ .description('CLI tool for managing BotSkill - a platform for AI agent skills')
31
+ .version(version);
32
+
33
+ program.addCommand(initCommand);
34
+ program.addCommand(loginCommand);
35
+ program.addCommand(logoutCommand);
36
+ program.addCommand(configCommand);
37
+ program.addCommand(getCommand);
38
+ program.addCommand(pushCommand);
39
+ program.addCommand(publishCommand);
40
+ program.addCommand(listCommand);
41
+ program.addCommand(searchCommand);
42
+ program.addCommand(infoCommand);
43
+ program.addCommand(helpCommand);
44
+
45
45
  program.parse();