istarshine 1.0.0 → 1.0.2
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/bin/cli.js +15 -4
- package/lib/find.js +136 -0
- package/lib/format.js +33 -0
- package/lib/list.js +3 -9
- package/lib/search.js +3 -8
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -9,20 +9,21 @@ import { Command } from 'commander';
|
|
|
9
9
|
import { install_skill } from '../lib/install.js';
|
|
10
10
|
import { search_skills } from '../lib/search.js';
|
|
11
11
|
import { list_skills } from '../lib/list.js';
|
|
12
|
+
import { find_skills } from '../lib/find.js';
|
|
12
13
|
|
|
13
14
|
const program = new Command();
|
|
14
15
|
|
|
15
16
|
program
|
|
16
17
|
.name('istarshine')
|
|
17
18
|
.description('iStarshine Skills Hub CLI - 一键安装和管理 Skills')
|
|
18
|
-
.version('1.0.
|
|
19
|
+
.version('1.0.2');
|
|
19
20
|
|
|
20
21
|
// install 子命令
|
|
21
22
|
program
|
|
22
23
|
.command('install <skill-name>')
|
|
23
24
|
.description('安装指定名称的 Skill 到当前项目')
|
|
24
25
|
.option('-d, --dir <path>', '安装目标目录', '.kiro/skills')
|
|
25
|
-
.option('--server <url>', 'Skills Hub 服务器地址', 'https://
|
|
26
|
+
.option('--server <url>', 'Skills Hub 服务器地址', 'https://skills.istarshine.com')
|
|
26
27
|
.action(async (skill_name, options) => {
|
|
27
28
|
await install_skill(skill_name, options);
|
|
28
29
|
});
|
|
@@ -31,7 +32,7 @@ program
|
|
|
31
32
|
program
|
|
32
33
|
.command('search <keyword>')
|
|
33
34
|
.description('搜索 Skills Hub 中的 Skill')
|
|
34
|
-
.option('--server <url>', 'Skills Hub 服务器地址', 'https://
|
|
35
|
+
.option('--server <url>', 'Skills Hub 服务器地址', 'https://skills.istarshine.com')
|
|
35
36
|
.action(async (keyword, options) => {
|
|
36
37
|
await search_skills(keyword, options);
|
|
37
38
|
});
|
|
@@ -40,9 +41,19 @@ program
|
|
|
40
41
|
program
|
|
41
42
|
.command('list')
|
|
42
43
|
.description('列出热门 Skills')
|
|
43
|
-
.option('--server <url>', 'Skills Hub 服务器地址', 'https://
|
|
44
|
+
.option('--server <url>', 'Skills Hub 服务器地址', 'https://skills.istarshine.com')
|
|
44
45
|
.action(async (options) => {
|
|
45
46
|
await list_skills(options);
|
|
46
47
|
});
|
|
47
48
|
|
|
49
|
+
// find 子命令
|
|
50
|
+
program
|
|
51
|
+
.command('find [keyword]')
|
|
52
|
+
.description('搜索 Skills Hub 中的 Skill,不带关键词时浏览热门')
|
|
53
|
+
.option('--server <url>', 'Skills Hub 服务器地址', 'https://skills.istarshine.com')
|
|
54
|
+
.option('--install', '搜索后进入快速安装模式', false)
|
|
55
|
+
.action(async (keyword, options) => {
|
|
56
|
+
await find_skills(keyword, options);
|
|
57
|
+
});
|
|
58
|
+
|
|
48
59
|
program.parse();
|
package/lib/find.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* find 命令实现
|
|
3
|
+
* 搜索 Skills Hub 中的 Skill,支持关键词搜索和热门浏览
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createInterface } from 'readline';
|
|
7
|
+
import { fetch_json } from './api.js';
|
|
8
|
+
import { format_skill_list } from './format.js';
|
|
9
|
+
import { install_skill } from './install.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 执行 find 命令
|
|
13
|
+
* @param {string|undefined} keyword - 搜索关键词,为空时进入热门浏览模式
|
|
14
|
+
* @param {object} options - 命令行选项
|
|
15
|
+
* @param {string} options.server - Skills Hub 服务器地址
|
|
16
|
+
* @param {boolean} options.install - 是否启用快速安装模式
|
|
17
|
+
*/
|
|
18
|
+
export async function find_skills(keyword, options) {
|
|
19
|
+
const { server = 'https://skills.istarshine.com' } = options;
|
|
20
|
+
const chalk = (await import('chalk')).default;
|
|
21
|
+
const ora = (await import('ora')).default;
|
|
22
|
+
|
|
23
|
+
if (keyword) {
|
|
24
|
+
// 关键词搜索模式
|
|
25
|
+
const spinner = ora(`正在搜索: ${keyword}`).start();
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const url = `${server}/api/skills/search?keyword=${encodeURIComponent(keyword)}`;
|
|
29
|
+
const results = await fetch_json(url);
|
|
30
|
+
|
|
31
|
+
if (!results || results.length === 0) {
|
|
32
|
+
spinner.info(chalk.yellow(`未找到与 "${keyword}" 相关的 Skill,请尝试其他关键词`));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
spinner.succeed(chalk.green(`找到 ${results.length} 个结果:\n`));
|
|
37
|
+
|
|
38
|
+
// 使用共享格式化函数输出结果列表
|
|
39
|
+
await format_skill_list(results);
|
|
40
|
+
|
|
41
|
+
// --install 模式:提示用户选择安装
|
|
42
|
+
if (options.install) {
|
|
43
|
+
await prompt_install(results, options, chalk);
|
|
44
|
+
} else {
|
|
45
|
+
console.log(chalk.gray(`\n安装命令: npx istarshine install <skill-name>`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
} catch (err) {
|
|
49
|
+
spinner.fail(chalk.red(`搜索失败: ${err.message}`));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
// 热门浏览模式
|
|
54
|
+
const spinner = ora('正在获取热门 Skills...').start();
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const url = `${server}/api/skills/popular`;
|
|
58
|
+
const results = await fetch_json(url);
|
|
59
|
+
|
|
60
|
+
if (!results || results.length === 0) {
|
|
61
|
+
spinner.info(chalk.yellow('暂无热门 Skill'));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
spinner.succeed(chalk.green(`热门 Skills:\n`));
|
|
66
|
+
|
|
67
|
+
// 使用共享格式化函数输出热门列表(含排名序号)
|
|
68
|
+
await format_skill_list(results, { show_rank: true });
|
|
69
|
+
|
|
70
|
+
// --install 模式:提示用户选择安装
|
|
71
|
+
if (options.install) {
|
|
72
|
+
await prompt_install(results, options, chalk);
|
|
73
|
+
} else {
|
|
74
|
+
console.log(chalk.gray(`\n安装命令: npx istarshine install <skill-name>`));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
} catch (err) {
|
|
78
|
+
spinner.fail(chalk.red(`获取列表失败: ${err.message}`));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 交互式安装提示
|
|
86
|
+
* 提示用户输入要安装的 Skill 名称,验证后调用 install_skill 执行安装
|
|
87
|
+
* @param {Array} results - 搜索结果列表
|
|
88
|
+
* @param {object} options - 命令行选项
|
|
89
|
+
* @param {object} chalk - chalk 实例
|
|
90
|
+
*/
|
|
91
|
+
async function prompt_install(results, options, chalk) {
|
|
92
|
+
// 构建有效名称集合,用于快速验证
|
|
93
|
+
const valid_names = new Set(results.map(s => s.name));
|
|
94
|
+
|
|
95
|
+
const rl = createInterface({
|
|
96
|
+
input: process.stdin,
|
|
97
|
+
output: process.stdout,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return new Promise((resolve) => {
|
|
101
|
+
// Ctrl+C 时取消安装,正常退出
|
|
102
|
+
rl.on('close', () => {
|
|
103
|
+
console.log(chalk.gray('\n已取消安装'));
|
|
104
|
+
resolve();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const ask = () => {
|
|
108
|
+
rl.question(chalk.cyan('\n输入要安装的 Skill 名称(留空取消): '), async (answer) => {
|
|
109
|
+
const name = answer.trim();
|
|
110
|
+
|
|
111
|
+
// 输入空值时取消安装
|
|
112
|
+
if (!name) {
|
|
113
|
+
rl.close();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 验证名称是否在搜索结果中
|
|
118
|
+
if (!valid_names.has(name)) {
|
|
119
|
+
console.log(chalk.red(`"${name}" 不在搜索结果中,请输入正确的 Skill 名称`));
|
|
120
|
+
ask();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 名称有效,关闭 readline 并执行安装
|
|
125
|
+
rl.close();
|
|
126
|
+
await install_skill(name, {
|
|
127
|
+
dir: '.kiro/skills',
|
|
128
|
+
server: options.server,
|
|
129
|
+
});
|
|
130
|
+
resolve();
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
ask();
|
|
135
|
+
});
|
|
136
|
+
}
|
package/lib/format.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共享格式化模块
|
|
3
|
+
* 提供统一的 skill 列表格式化输出函数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 格式化并输出 skill 列表
|
|
8
|
+
* @param {Array} skills - skill 对象数组
|
|
9
|
+
* @param {object} options - 格式化选项
|
|
10
|
+
* @param {boolean} options.show_rank - 是否显示排名序号
|
|
11
|
+
* @returns {void} 直接输出到 console
|
|
12
|
+
*/
|
|
13
|
+
export async function format_skill_list(skills, options = {}) {
|
|
14
|
+
const chalk = (await import('chalk')).default;
|
|
15
|
+
const { show_rank = false } = options;
|
|
16
|
+
|
|
17
|
+
skills.forEach((skill, index) => {
|
|
18
|
+
// 格式化各字段,缺失时使用默认值
|
|
19
|
+
const name = chalk.cyan(skill.name);
|
|
20
|
+
const version = chalk.gray(`v${skill.version || '?'}`);
|
|
21
|
+
const downloads = chalk.gray(`↓${skill.downloads || 0}`);
|
|
22
|
+
const likes = chalk.gray(`♥${skill.likes || 0}`);
|
|
23
|
+
const display = skill.display_name ? chalk.white(` - ${skill.display_name}`) : '';
|
|
24
|
+
|
|
25
|
+
// 根据 show_rank 选项决定是否显示排名序号
|
|
26
|
+
if (show_rank) {
|
|
27
|
+
const rank = chalk.gray(`${index + 1}.`);
|
|
28
|
+
console.log(` ${rank} ${name} ${version} ${downloads} ${likes}${display}`);
|
|
29
|
+
} else {
|
|
30
|
+
console.log(` ${name} ${version} ${downloads} ${likes}${display}`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
package/lib/list.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { fetch_json } from './api.js';
|
|
7
|
+
import { format_skill_list } from './format.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* 列出热门 skills
|
|
@@ -27,15 +28,8 @@ export async function list_skills(options) {
|
|
|
27
28
|
|
|
28
29
|
spinner.succeed(chalk.green(`热门 Skills:\n`));
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const name = chalk.cyan(skill.name);
|
|
33
|
-
const version = chalk.gray(`v${skill.version || '?'}`);
|
|
34
|
-
const downloads = chalk.gray(`↓${skill.downloads || 0}`);
|
|
35
|
-
const likes = chalk.gray(`♥${skill.likes || 0}`);
|
|
36
|
-
const display = skill.display_name ? chalk.white(` - ${skill.display_name}`) : '';
|
|
37
|
-
console.log(` ${rank} ${name} ${version} ${downloads} ${likes}${display}`);
|
|
38
|
-
});
|
|
31
|
+
// 使用共享格式化函数输出热门列表(含排名序号)
|
|
32
|
+
await format_skill_list(results, { show_rank: true });
|
|
39
33
|
|
|
40
34
|
console.log(chalk.gray(`\n安装命令: npx istarshine install <skill-name>`));
|
|
41
35
|
|
package/lib/search.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { fetch_json } from './api.js';
|
|
7
|
+
import { format_skill_list } from './format.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* 搜索并展示 skill 列表
|
|
@@ -28,14 +29,8 @@ export async function search_skills(keyword, options) {
|
|
|
28
29
|
|
|
29
30
|
spinner.succeed(chalk.green(`找到 ${results.length} 个结果:\n`));
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const version = chalk.gray(`v${skill.version || '?'}`);
|
|
34
|
-
const downloads = chalk.gray(`↓${skill.downloads || 0}`);
|
|
35
|
-
const likes = chalk.gray(`♥${skill.likes || 0}`);
|
|
36
|
-
const display = skill.display_name ? chalk.white(` - ${skill.display_name}`) : '';
|
|
37
|
-
console.log(` ${name} ${version} ${downloads} ${likes}${display}`);
|
|
38
|
-
});
|
|
32
|
+
// 使用共享格式化函数输出结果列表
|
|
33
|
+
await format_skill_list(results);
|
|
39
34
|
|
|
40
35
|
console.log(chalk.gray(`\n安装命令: npx istarshine install <skill-name>`));
|
|
41
36
|
|