@botskill/cli 1.0.4 → 1.0.5
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/package.json +1 -1
- package/src/commands/get.js +3 -2
- package/src/commands/info.js +3 -2
- package/src/commands/init.js +34 -15
- package/src/commands/list.js +8 -4
- package/src/commands/login.js +2 -2
- package/src/commands/logout.js +2 -2
- package/src/commands/publish.js +3 -2
- package/src/commands/push.js +3 -2
- package/src/commands/search.js +8 -4
- package/src/index.js +2 -1
package/package.json
CHANGED
package/src/commands/get.js
CHANGED
|
@@ -25,7 +25,8 @@ getCommand
|
|
|
25
25
|
.option('-o, --output <dir>', 'Output directory (default: current directory)')
|
|
26
26
|
.option('--dry-run', 'Show what would be downloaded without actually downloading')
|
|
27
27
|
.option('--api-url <url>', 'API base URL (overrides config for this command)')
|
|
28
|
-
.action(async (specifier, options) => {
|
|
28
|
+
.action(async (specifier, options, command) => {
|
|
29
|
+
const apiUrl = command.optsWithGlobals().apiUrl;
|
|
29
30
|
const { name, version } = parseSpecifier(specifier);
|
|
30
31
|
const outputDir = path.resolve(options.output || process.cwd());
|
|
31
32
|
|
|
@@ -42,7 +43,7 @@ getCommand
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
try {
|
|
45
|
-
const api = createApiClient(
|
|
46
|
+
const api = createApiClient(apiUrl);
|
|
46
47
|
|
|
47
48
|
const fullSpec = version ? `${name}@${version}` : name;
|
|
48
49
|
console.log(`Downloading skill: ${fullSpec}`);
|
package/src/commands/info.js
CHANGED
|
@@ -17,7 +17,7 @@ infoCommand
|
|
|
17
17
|
.description('Show skill details from BotSkill')
|
|
18
18
|
.argument('<specifier>', 'Skill name or name@version')
|
|
19
19
|
.option('--api-url <url>', 'API base URL (overrides config for this command)')
|
|
20
|
-
.action(async (specifier, options
|
|
20
|
+
.action(async (specifier, options, command) => {
|
|
21
21
|
const { name, version } = parseSpecifier(specifier);
|
|
22
22
|
|
|
23
23
|
if (!name) {
|
|
@@ -25,8 +25,9 @@ infoCommand
|
|
|
25
25
|
process.exit(1);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
const apiUrl = command.optsWithGlobals().apiUrl;
|
|
28
29
|
try {
|
|
29
|
-
const api = createApiClient(
|
|
30
|
+
const api = createApiClient(apiUrl);
|
|
30
31
|
const fullSpec = version ? `${name}@${version}` : name;
|
|
31
32
|
|
|
32
33
|
const resolveRes = await api.get(`/skills/by-name/${encodeURIComponent(fullSpec)}`);
|
package/src/commands/init.js
CHANGED
|
@@ -3,22 +3,25 @@ import path from 'path';
|
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
4
|
import inquirer from 'inquirer';
|
|
5
5
|
|
|
6
|
+
function toSkillName(raw) {
|
|
7
|
+
const s = String(raw || 'my-skill').trim();
|
|
8
|
+
return s.toLowerCase()
|
|
9
|
+
.replace(/\s+/g, '-')
|
|
10
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
11
|
+
.replace(/-+/g, '-')
|
|
12
|
+
.replace(/^-|-$/g, '') || 'my-skill';
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
const initCommand = new Command('init');
|
|
7
16
|
initCommand
|
|
8
17
|
.description('Initialize a new skill project')
|
|
18
|
+
.argument('[path]', 'Target directory: "." or path for current/specified dir; omit to create skill-named directory')
|
|
9
19
|
.option('-n, --name <name>', 'Project/skill name')
|
|
10
20
|
.option('-d, --description <description>', 'Skill description')
|
|
11
21
|
.option('-c, --category <category>', 'Category: ai, data, web, devops, security, tools')
|
|
12
22
|
.option('-y, --yes', 'Use defaults without prompting')
|
|
13
|
-
.action(async (options) => {
|
|
23
|
+
.action(async (pathArg, options) => {
|
|
14
24
|
const cwd = process.cwd();
|
|
15
|
-
const configPath = path.join(cwd, 'skill.config.json');
|
|
16
|
-
|
|
17
|
-
if (await fs.pathExists(configPath)) {
|
|
18
|
-
console.error('skill.config.json already exists in this directory.');
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
25
|
const validCategories = ['ai', 'data', 'web', 'devops', 'security', 'tools'];
|
|
23
26
|
let answers = {};
|
|
24
27
|
|
|
@@ -68,6 +71,25 @@ initCommand
|
|
|
68
71
|
]);
|
|
69
72
|
}
|
|
70
73
|
|
|
74
|
+
const skillName = toSkillName(answers.name);
|
|
75
|
+
let targetDir;
|
|
76
|
+
|
|
77
|
+
if (pathArg && pathArg !== '.') {
|
|
78
|
+
targetDir = path.resolve(cwd, pathArg);
|
|
79
|
+
await fs.ensureDir(targetDir);
|
|
80
|
+
} else if (pathArg === '.') {
|
|
81
|
+
targetDir = cwd;
|
|
82
|
+
} else {
|
|
83
|
+
targetDir = path.join(cwd, skillName);
|
|
84
|
+
await fs.ensureDir(targetDir);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const configPath = path.join(targetDir, 'skill.config.json');
|
|
88
|
+
if (await fs.pathExists(configPath)) {
|
|
89
|
+
console.error(`skill.config.json already exists in ${targetDir}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
71
93
|
const config = {
|
|
72
94
|
name: answers.name,
|
|
73
95
|
description: answers.description,
|
|
@@ -82,12 +104,6 @@ initCommand
|
|
|
82
104
|
|
|
83
105
|
await fs.writeJson(configPath, config, { spaces: 2 });
|
|
84
106
|
|
|
85
|
-
// Agent Skills spec: https://agentskills.io/specification
|
|
86
|
-
// name: required, 1-64 chars, lowercase + hyphens
|
|
87
|
-
// description: required, max 1024 chars
|
|
88
|
-
// metadata.version, metadata.author: optional
|
|
89
|
-
const rawName = String(answers.name || 'my-skill').trim();
|
|
90
|
-
const skillName = rawName.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'my-skill';
|
|
91
107
|
const skillMd = `---
|
|
92
108
|
name: ${skillName}
|
|
93
109
|
description: ${answers.description}
|
|
@@ -115,10 +131,13 @@ Add your usage documentation here. The Markdown body contains skill instructions
|
|
|
115
131
|
\`\`\`
|
|
116
132
|
`;
|
|
117
133
|
|
|
118
|
-
const skillMdPath = path.join(
|
|
134
|
+
const skillMdPath = path.join(targetDir, 'SKILL.md');
|
|
119
135
|
await fs.writeFile(skillMdPath, skillMd, 'utf-8');
|
|
120
136
|
|
|
121
137
|
console.log('Created skill.config.json and SKILL.md');
|
|
138
|
+
if (targetDir !== cwd) {
|
|
139
|
+
console.log(`Location: ${targetDir}`);
|
|
140
|
+
}
|
|
122
141
|
console.log('\nNext steps:');
|
|
123
142
|
console.log('1. Edit SKILL.md to add documentation (the content below the frontmatter)');
|
|
124
143
|
console.log('2. Run "skm login" to authenticate');
|
package/src/commands/list.js
CHANGED
|
@@ -8,7 +8,9 @@ function formatSkillDisplay(skill) {
|
|
|
8
8
|
const downloads = skill.downloads ?? 0;
|
|
9
9
|
const category = skill.category || '—';
|
|
10
10
|
const status = skill.status || '—';
|
|
11
|
-
|
|
11
|
+
const desc = (skill.description || '').trim();
|
|
12
|
+
const description = desc.length > 80 ? desc.slice(0, 77) + '...' : desc || '—';
|
|
13
|
+
return { name, version, downloads, category, status, description };
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
const listCommand = new Command('list');
|
|
@@ -21,8 +23,9 @@ listCommand
|
|
|
21
23
|
.option('-l, --limit <number>', 'Maximum number of results (default: 20)', '20')
|
|
22
24
|
.option('-p, --page <number>', 'Page number for pagination (default: 1)', '1')
|
|
23
25
|
.option('--api-url <url>', 'API base URL (overrides config for this command)')
|
|
24
|
-
.action(async (options) => {
|
|
25
|
-
const
|
|
26
|
+
.action(async (options, command) => {
|
|
27
|
+
const apiUrl = command.optsWithGlobals().apiUrl;
|
|
28
|
+
const api = createApiClient(apiUrl);
|
|
26
29
|
const limit = parseInt(options.limit, 10) || 20;
|
|
27
30
|
const page = parseInt(options.page, 10) || 1;
|
|
28
31
|
|
|
@@ -59,9 +62,10 @@ listCommand
|
|
|
59
62
|
console.log(`\nFound ${pagination.totalSkills ?? skills.length} skill(s):`);
|
|
60
63
|
console.log('─'.repeat(60));
|
|
61
64
|
skills.forEach((skill) => {
|
|
62
|
-
const { name, version, downloads, category, status } = formatSkillDisplay(skill);
|
|
65
|
+
const { name, version, downloads, category, status, description } = formatSkillDisplay(skill);
|
|
63
66
|
const statusStr = options.mine ? ` | ${status}` : '';
|
|
64
67
|
console.log(` ${name}`);
|
|
68
|
+
console.log(` ${description}`);
|
|
65
69
|
console.log(` Version: ${version} | Downloads: ${downloads} | Category: ${category}${statusStr}`);
|
|
66
70
|
});
|
|
67
71
|
if (pagination.totalPages > 1) {
|
package/src/commands/login.js
CHANGED
|
@@ -12,8 +12,8 @@ loginCommand
|
|
|
12
12
|
.option('-p, --password <password>', 'Password')
|
|
13
13
|
.option('-t, --token <token>', 'Use access token directly (from web)')
|
|
14
14
|
.option('--api-url <url>', 'API base URL (overrides config for this command)')
|
|
15
|
-
.action(async (options) => {
|
|
16
|
-
const apiUrl = normalizeApiUrl(
|
|
15
|
+
.action(async (options, command) => {
|
|
16
|
+
const apiUrl = normalizeApiUrl(command.optsWithGlobals().apiUrl || getApiUrl());
|
|
17
17
|
|
|
18
18
|
if (options.token) {
|
|
19
19
|
setAuth({ token: options.token });
|
package/src/commands/logout.js
CHANGED
|
@@ -6,8 +6,8 @@ const logoutCommand = new Command('logout');
|
|
|
6
6
|
logoutCommand
|
|
7
7
|
.description('Logout from BotSkill')
|
|
8
8
|
.option('--api-url <url>', 'API base URL (overrides config for this command)')
|
|
9
|
-
.action(async (options) => {
|
|
10
|
-
const apiUrl = normalizeApiUrl(
|
|
9
|
+
.action(async (options, command) => {
|
|
10
|
+
const apiUrl = normalizeApiUrl(command.optsWithGlobals().apiUrl || getApiUrl());
|
|
11
11
|
const refreshToken = getRefreshToken();
|
|
12
12
|
if (refreshToken) {
|
|
13
13
|
try {
|
package/src/commands/publish.js
CHANGED
|
@@ -11,7 +11,8 @@ publishCommand
|
|
|
11
11
|
.option('-f, --file <path>', 'Path to SKILL.md, .zip, or .tar.gz')
|
|
12
12
|
.option('--dry-run', 'Validate without uploading')
|
|
13
13
|
.option('--api-url <url>', 'API base URL (overrides config for this command)')
|
|
14
|
-
.action(async (options) => {
|
|
14
|
+
.action(async (options, command) => {
|
|
15
|
+
const apiUrl = command.optsWithGlobals().apiUrl;
|
|
15
16
|
if (!getToken()) {
|
|
16
17
|
console.error('Not logged in. Run: skm login');
|
|
17
18
|
process.exit(1);
|
|
@@ -38,7 +39,7 @@ publishCommand
|
|
|
38
39
|
|
|
39
40
|
console.log(`Publishing skill from ${path.basename(filePath)}...`);
|
|
40
41
|
try {
|
|
41
|
-
const skill = await uploadSkillFile(filePath, { apiUrl
|
|
42
|
+
const skill = await uploadSkillFile(filePath, { apiUrl });
|
|
42
43
|
console.log('Skill published successfully!');
|
|
43
44
|
console.log(`Name: ${skill?.name}`);
|
|
44
45
|
console.log(`Version: ${skill?.version || (skill?.versions?.[0]?.version)}`);
|
package/src/commands/push.js
CHANGED
|
@@ -11,7 +11,8 @@ pushCommand
|
|
|
11
11
|
.option('-f, --file <path>', 'Path to SKILL.md, .zip, or .tar.gz')
|
|
12
12
|
.option('--dry-run', 'Validate without uploading')
|
|
13
13
|
.option('--api-url <url>', 'API base URL')
|
|
14
|
-
.action(async (options) => {
|
|
14
|
+
.action(async (options, command) => {
|
|
15
|
+
const apiUrl = command.optsWithGlobals().apiUrl;
|
|
15
16
|
if (!getToken()) {
|
|
16
17
|
console.error('Not logged in. Run: skm login');
|
|
17
18
|
process.exit(1);
|
|
@@ -38,7 +39,7 @@ pushCommand
|
|
|
38
39
|
|
|
39
40
|
console.log(`Pushing skill from ${path.basename(filePath)}...`);
|
|
40
41
|
try {
|
|
41
|
-
const skill = await uploadSkillFile(filePath, { apiUrl
|
|
42
|
+
const skill = await uploadSkillFile(filePath, { apiUrl });
|
|
42
43
|
console.log('Skill uploaded successfully!');
|
|
43
44
|
console.log(`Name: ${skill?.name}`);
|
|
44
45
|
console.log(`Version: ${skill?.version || (skill?.versions?.[0]?.version)}`);
|
package/src/commands/search.js
CHANGED
|
@@ -9,7 +9,9 @@ function formatSkillDisplay(skill) {
|
|
|
9
9
|
const version = skill.version || (skill.versions?.[0]?.version) || '—';
|
|
10
10
|
const downloads = skill.downloads ?? 0;
|
|
11
11
|
const category = skill.category || '—';
|
|
12
|
-
|
|
12
|
+
const desc = (skill.description || '').trim();
|
|
13
|
+
const description = desc.length > 80 ? desc.slice(0, 77) + '...' : desc || '—';
|
|
14
|
+
return { displayName, version, downloads, category, description };
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
const searchCommand = new Command('search');
|
|
@@ -20,8 +22,9 @@ searchCommand
|
|
|
20
22
|
.option('-l, --limit <number>', 'Maximum number of results (default: 20)', '20')
|
|
21
23
|
.option('-p, --page <number>', 'Page number for pagination (default: 1)', '1')
|
|
22
24
|
.option('--api-url <url>', 'API base URL (overrides config for this command)')
|
|
23
|
-
.action(async (query, options) => {
|
|
24
|
-
const
|
|
25
|
+
.action(async (query, options, command) => {
|
|
26
|
+
const apiUrl = command.optsWithGlobals().apiUrl;
|
|
27
|
+
const api = createApiClient(apiUrl);
|
|
25
28
|
const limit = parseInt(options.limit, 10) || 20;
|
|
26
29
|
const page = parseInt(options.page, 10) || 1;
|
|
27
30
|
|
|
@@ -41,8 +44,9 @@ searchCommand
|
|
|
41
44
|
console.log(`\nFound ${pagination.totalSkills ?? skills.length} skill(s) for "${query}":`);
|
|
42
45
|
console.log('─'.repeat(60));
|
|
43
46
|
skills.forEach((skill) => {
|
|
44
|
-
const { displayName, version, downloads, category } = formatSkillDisplay(skill);
|
|
47
|
+
const { displayName, version, downloads, category, description } = formatSkillDisplay(skill);
|
|
45
48
|
console.log(` ${displayName}`);
|
|
49
|
+
console.log(` ${description}`);
|
|
46
50
|
console.log(` Version: ${version} | Downloads: ${downloads} | Category: ${category}`);
|
|
47
51
|
});
|
|
48
52
|
if (pagination.totalPages > 1) {
|
package/src/index.js
CHANGED
|
@@ -28,7 +28,8 @@ const program = new Command();
|
|
|
28
28
|
program
|
|
29
29
|
.name('skm')
|
|
30
30
|
.description('CLI tool for managing BotSkill - a platform for AI agent skills')
|
|
31
|
-
.version(version)
|
|
31
|
+
.version(version)
|
|
32
|
+
.option('--api-url <url>', 'API base URL (overrides config for this command)');
|
|
32
33
|
|
|
33
34
|
program.addCommand(initCommand);
|
|
34
35
|
program.addCommand(loginCommand);
|