@sstar/skill-install 1.1.1 → 1.2.1
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/dist/cli.js +254 -589
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +17 -0
- package/dist/ui/prompts.d.ts +78 -0
- package/dist/ui/prompts.js +283 -0
- package/dist/wiki/skill-selector.d.ts +3 -7
- package/dist/wiki/skill-selector.js +39 -83
- package/dist/wiki/wiki-searcher.d.ts +1 -0
- package/dist/wiki/wiki-searcher.js +2 -1
- package/package.json +4 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './prompts';
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./prompts"), exports);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { AiTool } from '../skills/skills-manager';
|
|
2
|
+
import { SkillInPackage } from '../installer/install-service';
|
|
3
|
+
import { PluginInfo } from '../plugins/types';
|
|
4
|
+
/**
|
|
5
|
+
* Display intro message
|
|
6
|
+
*/
|
|
7
|
+
export declare function intro(title?: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Display outro message
|
|
10
|
+
*/
|
|
11
|
+
export declare function outro(message?: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* Create a spinner
|
|
14
|
+
*/
|
|
15
|
+
export declare function spinner(): {
|
|
16
|
+
start(msg: string): void;
|
|
17
|
+
stop(msg?: string): void;
|
|
18
|
+
message(msg: string): void;
|
|
19
|
+
error(msg: string): void;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Check if value is a cancel symbol (inquirer uses Ctrl+C which throws)
|
|
23
|
+
*/
|
|
24
|
+
export declare function isCancel(value: unknown): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Prompt for AI tool selection
|
|
27
|
+
*/
|
|
28
|
+
export declare function promptAiTool(): Promise<AiTool>;
|
|
29
|
+
/**
|
|
30
|
+
* Prompt for skills directory (global vs local)
|
|
31
|
+
*/
|
|
32
|
+
export declare function promptSkillsDir(aiTool: AiTool, globalDir: string, localDir: string): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Prompt for password (masked input)
|
|
35
|
+
*/
|
|
36
|
+
export declare function promptPassword(message?: string): Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* Prompt for yes/no confirmation
|
|
39
|
+
*/
|
|
40
|
+
export declare function promptYesNo(message: string, initialValue?: boolean): Promise<boolean>;
|
|
41
|
+
/**
|
|
42
|
+
* Prompt for text input
|
|
43
|
+
*/
|
|
44
|
+
export declare function promptText(message: string, placeholder?: string, initialValue?: string): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Select skills from a multi-skill package
|
|
47
|
+
*/
|
|
48
|
+
export declare function selectSkillsFromPackage(skills: SkillInPackage[]): Promise<SkillInPackage[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Select skills to uninstall
|
|
51
|
+
*/
|
|
52
|
+
export declare function promptSkillSelection(skills: Array<{
|
|
53
|
+
name: string;
|
|
54
|
+
description: string;
|
|
55
|
+
}>): Promise<number[]>;
|
|
56
|
+
/**
|
|
57
|
+
* Select plugins from marketplace
|
|
58
|
+
*/
|
|
59
|
+
export declare function selectPluginsFromMarketplace(plugins: PluginInfo[]): Promise<Array<{
|
|
60
|
+
name: string;
|
|
61
|
+
version?: string;
|
|
62
|
+
}>>;
|
|
63
|
+
/**
|
|
64
|
+
* Display success message
|
|
65
|
+
*/
|
|
66
|
+
export declare function success(message: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* Display error message
|
|
69
|
+
*/
|
|
70
|
+
export declare function error(message: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* Display info message
|
|
73
|
+
*/
|
|
74
|
+
export declare function info(message: string): void;
|
|
75
|
+
/**
|
|
76
|
+
* Display warning message
|
|
77
|
+
*/
|
|
78
|
+
export declare function warn(message: string): void;
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.intro = intro;
|
|
7
|
+
exports.outro = outro;
|
|
8
|
+
exports.spinner = spinner;
|
|
9
|
+
exports.isCancel = isCancel;
|
|
10
|
+
exports.promptAiTool = promptAiTool;
|
|
11
|
+
exports.promptSkillsDir = promptSkillsDir;
|
|
12
|
+
exports.promptPassword = promptPassword;
|
|
13
|
+
exports.promptYesNo = promptYesNo;
|
|
14
|
+
exports.promptText = promptText;
|
|
15
|
+
exports.selectSkillsFromPackage = selectSkillsFromPackage;
|
|
16
|
+
exports.promptSkillSelection = promptSkillSelection;
|
|
17
|
+
exports.selectPluginsFromMarketplace = selectPluginsFromMarketplace;
|
|
18
|
+
exports.success = success;
|
|
19
|
+
exports.error = error;
|
|
20
|
+
exports.info = info;
|
|
21
|
+
exports.warn = warn;
|
|
22
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
23
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
24
|
+
/**
|
|
25
|
+
* Common theme for prompts - clack-like style
|
|
26
|
+
*/
|
|
27
|
+
const commonTheme = {
|
|
28
|
+
icon: {
|
|
29
|
+
cursor: picocolors_1.default.cyan('❯'),
|
|
30
|
+
},
|
|
31
|
+
style: {
|
|
32
|
+
description: (text) => picocolors_1.default.green(text),
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Display intro message
|
|
37
|
+
*/
|
|
38
|
+
function intro(title) {
|
|
39
|
+
console.log();
|
|
40
|
+
console.log(picocolors_1.default.cyan('◆') + (title ? ` ${title}` : ''));
|
|
41
|
+
console.log(picocolors_1.default.gray('│'));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Display outro message
|
|
45
|
+
*/
|
|
46
|
+
function outro(message) {
|
|
47
|
+
console.log(picocolors_1.default.gray('│'));
|
|
48
|
+
console.log(picocolors_1.default.cyan('└') + (message ? ` ${message}` : ''));
|
|
49
|
+
console.log();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a spinner
|
|
53
|
+
*/
|
|
54
|
+
function spinner() {
|
|
55
|
+
let intervalId = null;
|
|
56
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
57
|
+
let frameIndex = 0;
|
|
58
|
+
let currentMsg = '';
|
|
59
|
+
const clearLine = () => {
|
|
60
|
+
process.stdout.write('\r\x1b[K');
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
start(msg) {
|
|
64
|
+
currentMsg = msg;
|
|
65
|
+
intervalId = setInterval(() => {
|
|
66
|
+
const frame = picocolors_1.default.cyan(frames[frameIndex]);
|
|
67
|
+
clearLine();
|
|
68
|
+
process.stdout.write(`${frame} ${currentMsg}`);
|
|
69
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
70
|
+
}, 80);
|
|
71
|
+
},
|
|
72
|
+
stop(msg) {
|
|
73
|
+
if (intervalId) {
|
|
74
|
+
clearInterval(intervalId);
|
|
75
|
+
intervalId = null;
|
|
76
|
+
}
|
|
77
|
+
clearLine();
|
|
78
|
+
if (msg) {
|
|
79
|
+
console.log(picocolors_1.default.green('✓') + ` ${msg}`);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
message(msg) {
|
|
83
|
+
currentMsg = msg;
|
|
84
|
+
},
|
|
85
|
+
error(msg) {
|
|
86
|
+
if (intervalId) {
|
|
87
|
+
clearInterval(intervalId);
|
|
88
|
+
intervalId = null;
|
|
89
|
+
}
|
|
90
|
+
clearLine();
|
|
91
|
+
console.log(picocolors_1.default.red('✗') + ` ${msg}`);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check if value is a cancel symbol (inquirer uses Ctrl+C which throws)
|
|
97
|
+
*/
|
|
98
|
+
function isCancel(value) {
|
|
99
|
+
return value === null || value === undefined;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Prompt for AI tool selection
|
|
103
|
+
*/
|
|
104
|
+
async function promptAiTool() {
|
|
105
|
+
const choice = await (0, prompts_1.select)({
|
|
106
|
+
message: 'Select AI tool',
|
|
107
|
+
choices: [
|
|
108
|
+
{ value: 'claude', name: 'Claude Code', description: '~/.claude/skills/' },
|
|
109
|
+
{ value: 'codex', name: 'Codex', description: '~/.codex/skills/' },
|
|
110
|
+
],
|
|
111
|
+
theme: commonTheme,
|
|
112
|
+
});
|
|
113
|
+
return choice;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Prompt for skills directory (global vs local)
|
|
117
|
+
*/
|
|
118
|
+
async function promptSkillsDir(aiTool, globalDir, localDir) {
|
|
119
|
+
const choice = await (0, prompts_1.select)({
|
|
120
|
+
message: 'Select installation directory',
|
|
121
|
+
choices: [
|
|
122
|
+
{ value: globalDir, name: 'Global', description: globalDir },
|
|
123
|
+
{ value: localDir, name: 'Local', description: localDir },
|
|
124
|
+
],
|
|
125
|
+
theme: commonTheme,
|
|
126
|
+
});
|
|
127
|
+
return choice;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Prompt for password (masked input)
|
|
131
|
+
*/
|
|
132
|
+
async function promptPassword(message = 'Password') {
|
|
133
|
+
const value = await (0, prompts_1.password)({
|
|
134
|
+
message,
|
|
135
|
+
mask: '*',
|
|
136
|
+
});
|
|
137
|
+
return value;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Prompt for yes/no confirmation
|
|
141
|
+
*/
|
|
142
|
+
async function promptYesNo(message, initialValue = true) {
|
|
143
|
+
const value = await (0, prompts_1.confirm)({
|
|
144
|
+
message,
|
|
145
|
+
default: initialValue,
|
|
146
|
+
});
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Prompt for text input
|
|
151
|
+
*/
|
|
152
|
+
async function promptText(message, placeholder, initialValue) {
|
|
153
|
+
const value = await (0, prompts_1.input)({
|
|
154
|
+
message,
|
|
155
|
+
default: initialValue,
|
|
156
|
+
});
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Select skills from a multi-skill package
|
|
161
|
+
*/
|
|
162
|
+
async function selectSkillsFromPackage(skills) {
|
|
163
|
+
if (skills.length === 0)
|
|
164
|
+
return [];
|
|
165
|
+
if (skills.length === 1) {
|
|
166
|
+
const install = await promptYesNo(`Install skill "${skills[0].name}"?`);
|
|
167
|
+
return install ? skills : [];
|
|
168
|
+
}
|
|
169
|
+
const indices = await (0, prompts_1.checkbox)({
|
|
170
|
+
message: `Select skills to install (${skills.length} found)`,
|
|
171
|
+
choices: skills.map((skill, index) => ({
|
|
172
|
+
value: index,
|
|
173
|
+
name: skill.name,
|
|
174
|
+
description: skill.description.length > 50
|
|
175
|
+
? skill.description.slice(0, 50) + '...'
|
|
176
|
+
: skill.description,
|
|
177
|
+
})),
|
|
178
|
+
required: false,
|
|
179
|
+
theme: {
|
|
180
|
+
icon: {
|
|
181
|
+
checked: picocolors_1.default.green('◉'),
|
|
182
|
+
unchecked: '○',
|
|
183
|
+
cursor: picocolors_1.default.cyan('❯'),
|
|
184
|
+
},
|
|
185
|
+
style: {
|
|
186
|
+
description: (text) => picocolors_1.default.green(text),
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
if (indices.length === 0) {
|
|
191
|
+
const installAll = await promptYesNo('No skills selected. Install all?', false);
|
|
192
|
+
return installAll ? skills : [];
|
|
193
|
+
}
|
|
194
|
+
return indices.map(i => skills[i]);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Select skills to uninstall
|
|
198
|
+
*/
|
|
199
|
+
async function promptSkillSelection(skills) {
|
|
200
|
+
if (skills.length === 0)
|
|
201
|
+
return [];
|
|
202
|
+
const indices = await (0, prompts_1.checkbox)({
|
|
203
|
+
message: 'Select skills to uninstall',
|
|
204
|
+
choices: skills.map((skill, index) => ({
|
|
205
|
+
value: index,
|
|
206
|
+
name: skill.name,
|
|
207
|
+
description: skill.description.length > 50
|
|
208
|
+
? skill.description.slice(0, 50) + '...'
|
|
209
|
+
: skill.description,
|
|
210
|
+
})),
|
|
211
|
+
required: false,
|
|
212
|
+
theme: {
|
|
213
|
+
icon: {
|
|
214
|
+
checked: picocolors_1.default.green('◉'),
|
|
215
|
+
unchecked: '○',
|
|
216
|
+
cursor: picocolors_1.default.cyan('❯'),
|
|
217
|
+
},
|
|
218
|
+
style: {
|
|
219
|
+
description: (text) => picocolors_1.default.green(text),
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
return indices;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Select plugins from marketplace
|
|
227
|
+
*/
|
|
228
|
+
async function selectPluginsFromMarketplace(plugins) {
|
|
229
|
+
if (plugins.length === 0)
|
|
230
|
+
return [];
|
|
231
|
+
if (plugins.length === 1) {
|
|
232
|
+
const install = await promptYesNo(`Install plugin "${plugins[0].name}"?`);
|
|
233
|
+
return install ? [{ name: plugins[0].name, version: plugins[0].version }] : [];
|
|
234
|
+
}
|
|
235
|
+
const indices = await (0, prompts_1.checkbox)({
|
|
236
|
+
message: `Select plugins to install (${plugins.length} found)`,
|
|
237
|
+
choices: plugins.map((plugin, index) => ({
|
|
238
|
+
value: index,
|
|
239
|
+
name: `${plugin.name} v${plugin.version || 'unknown'}`,
|
|
240
|
+
description: plugin.metadata.description?.slice(0, 50) || '',
|
|
241
|
+
})),
|
|
242
|
+
required: false,
|
|
243
|
+
theme: {
|
|
244
|
+
icon: {
|
|
245
|
+
checked: picocolors_1.default.green('◉'),
|
|
246
|
+
unchecked: '○',
|
|
247
|
+
cursor: picocolors_1.default.cyan('❯'),
|
|
248
|
+
},
|
|
249
|
+
style: {
|
|
250
|
+
description: (text) => picocolors_1.default.green(text),
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
if (indices.length === 0) {
|
|
255
|
+
const installAll = await promptYesNo('No plugins selected. Install all?', false);
|
|
256
|
+
return installAll ? plugins.map(p => ({ name: p.name, version: p.version })) : [];
|
|
257
|
+
}
|
|
258
|
+
return indices.map(i => ({ name: plugins[i].name, version: plugins[i].version }));
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Display success message
|
|
262
|
+
*/
|
|
263
|
+
function success(message) {
|
|
264
|
+
console.log(picocolors_1.default.green(`✓ ${message}`));
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Display error message
|
|
268
|
+
*/
|
|
269
|
+
function error(message) {
|
|
270
|
+
console.log(picocolors_1.default.red(`✗ ${message}`));
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Display info message
|
|
274
|
+
*/
|
|
275
|
+
function info(message) {
|
|
276
|
+
console.log(picocolors_1.default.cyan(`ℹ ${message}`));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Display warning message
|
|
280
|
+
*/
|
|
281
|
+
function warn(message) {
|
|
282
|
+
console.log(picocolors_1.default.yellow(`⚠ ${message}`));
|
|
283
|
+
}
|
|
@@ -2,12 +2,8 @@ import { ParsedSkillItem } from './wiki-parser';
|
|
|
2
2
|
export declare class SkillSelector {
|
|
3
3
|
private readonly logger;
|
|
4
4
|
/**
|
|
5
|
-
* Display skill list and wait for user selection
|
|
6
|
-
* Returns the download
|
|
5
|
+
* Display skill list and wait for user selection (multi-select)
|
|
6
|
+
* Returns the download URLs of selected skills, or empty array if cancelled
|
|
7
7
|
*/
|
|
8
|
-
displayAndSelect(items: ParsedSkillItem[]): Promise<string
|
|
9
|
-
/**
|
|
10
|
-
* Prompt user to select a skill from the list
|
|
11
|
-
*/
|
|
12
|
-
private promptChoice;
|
|
8
|
+
displayAndSelect(items: ParsedSkillItem[]): Promise<string[]>;
|
|
13
9
|
}
|
|
@@ -4,101 +4,57 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.SkillSelector = void 0;
|
|
7
|
-
const
|
|
7
|
+
const checkbox_1 = __importDefault(require("@inquirer/checkbox"));
|
|
8
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
8
9
|
const logger_1 = require("../core/logger");
|
|
9
|
-
// Color codes for terminal output
|
|
10
|
-
const colors = {
|
|
11
|
-
reset: '\x1b[0m',
|
|
12
|
-
bright: '\x1b[1m',
|
|
13
|
-
dim: '\x1b[2m',
|
|
14
|
-
red: '\x1b[31m',
|
|
15
|
-
green: '\x1b[32m',
|
|
16
|
-
yellow: '\x1b[33m',
|
|
17
|
-
blue: '\x1b[34m',
|
|
18
|
-
magenta: '\x1b[35m',
|
|
19
|
-
cyan: '\x1b[36m',
|
|
20
|
-
white: '\x1b[37m',
|
|
21
|
-
gray: '\x1b[90m'
|
|
22
|
-
};
|
|
23
10
|
class SkillSelector {
|
|
24
11
|
constructor() {
|
|
25
12
|
this.logger = new logger_1.Logger('SkillSelector');
|
|
26
13
|
}
|
|
27
14
|
/**
|
|
28
|
-
* Display skill list and wait for user selection
|
|
29
|
-
* Returns the download
|
|
15
|
+
* Display skill list and wait for user selection (multi-select)
|
|
16
|
+
* Returns the download URLs of selected skills, or empty array if cancelled
|
|
30
17
|
*/
|
|
31
18
|
async displayAndSelect(items) {
|
|
32
19
|
if (items.length === 0) {
|
|
33
20
|
this.logger.info('No skills found to display.');
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
console.log('');
|
|
37
|
-
console.log(`${colors.cyan}Found ${items.length} skill(s):${colors.reset}`);
|
|
38
|
-
console.log('');
|
|
39
|
-
// Display all skills
|
|
40
|
-
for (let i = 0; i < items.length; i++) {
|
|
41
|
-
const item = items[i];
|
|
42
|
-
// Add separator line before each skill
|
|
43
|
-
console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
|
|
44
|
-
// Index with color
|
|
45
|
-
console.log(`${colors.bright}${colors.green}[${i + 1}]${colors.reset} ${colors.bright}${colors.yellow}${item.name}${colors.reset} ${colors.dim}${colors.gray}${item.pageUrl}${colors.reset}`);
|
|
46
|
-
// Display comment (may be multiline)
|
|
47
|
-
if (item.description) {
|
|
48
|
-
const lines = item.description.split('\n');
|
|
49
|
-
for (const line of lines) {
|
|
50
|
-
console.log(`${colors.dim} ${line}${colors.reset}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
21
|
+
return [];
|
|
53
22
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const num = parseInt(trimmed, 10);
|
|
84
|
-
if (isNaN(num)) {
|
|
85
|
-
console.log('Invalid input. Please enter a number.');
|
|
86
|
-
resolve(null);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (num < 0 || num > max) {
|
|
90
|
-
console.log(`Invalid selection. Please enter a number between 0 and ${max}.`);
|
|
91
|
-
resolve(null);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (num === 0) {
|
|
95
|
-
console.log('Cancelled.');
|
|
96
|
-
resolve(null);
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
resolve(num - 1); // Convert to 0-based index
|
|
100
|
-
});
|
|
23
|
+
const selectedIndices = await (0, checkbox_1.default)({
|
|
24
|
+
message: `Found ${items.length} skill(s)`,
|
|
25
|
+
pageSize: 30, // Show more items since each takes 3 lines
|
|
26
|
+
choices: items.map((item, index) => {
|
|
27
|
+
// Build multi-line name with colors
|
|
28
|
+
const num = picocolors_1.default.bold(picocolors_1.default.white(`[${index + 1}/${items.length}]`));
|
|
29
|
+
const name = picocolors_1.default.bold(picocolors_1.default.yellow(item.name));
|
|
30
|
+
const uploader = item.uploader ? picocolors_1.default.cyan(` (${item.uploader})`) : '';
|
|
31
|
+
const desc = picocolors_1.default.green(item.description);
|
|
32
|
+
const url = picocolors_1.default.magenta(item.pageUrl);
|
|
33
|
+
return {
|
|
34
|
+
value: index,
|
|
35
|
+
name: `${num} ${name}${uploader}\n ${desc}\n ${url}`,
|
|
36
|
+
description: '', // Disable default description
|
|
37
|
+
};
|
|
38
|
+
}),
|
|
39
|
+
required: false,
|
|
40
|
+
theme: {
|
|
41
|
+
icon: {
|
|
42
|
+
checked: picocolors_1.default.green('◉'),
|
|
43
|
+
unchecked: '○',
|
|
44
|
+
cursor: picocolors_1.default.cyan('❯'),
|
|
45
|
+
},
|
|
46
|
+
style: {
|
|
47
|
+
description: (text) => text, // No extra styling
|
|
48
|
+
},
|
|
49
|
+
},
|
|
101
50
|
});
|
|
51
|
+
if (selectedIndices.length === 0) {
|
|
52
|
+
this.logger.info('No skills selected.');
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const selectedItems = selectedIndices.map(index => items[index]);
|
|
56
|
+
this.logger.info(`Selected: ${selectedItems.map(s => s.name).join(', ')}`);
|
|
57
|
+
return selectedItems.map(s => s.downloadUrl);
|
|
102
58
|
}
|
|
103
59
|
}
|
|
104
60
|
exports.SkillSelector = SkillSelector;
|
|
@@ -70,7 +70,8 @@ class WikiSearcher {
|
|
|
70
70
|
name: filename,
|
|
71
71
|
downloadUrl: this.buildDownloadUrl(baseUrl, item),
|
|
72
72
|
containerId: item.container?.id || '',
|
|
73
|
-
pageUrl: item._links?.webui ? `${baseUrl}${item._links.webui}` : ''
|
|
73
|
+
pageUrl: item._links?.webui ? `${baseUrl}${item._links.webui}` : '',
|
|
74
|
+
uploader: item.version?.by?.displayName
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
77
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sstar/skill-install",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Agent Skill installation tool - download, extract, validate, and install skills for Claude Code and Codex",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -37,11 +37,14 @@
|
|
|
37
37
|
"node": ">=18.0.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
+
"@inquirer/prompts": "^8.3.0",
|
|
41
|
+
"@inquirer/type": "^4.0.3",
|
|
40
42
|
"adm-zip": "^0.5.16",
|
|
41
43
|
"axios": "^1.6.0",
|
|
42
44
|
"cheerio": "^1.0.0-rc.12",
|
|
43
45
|
"commander": "^12.0.0",
|
|
44
46
|
"js-yaml": "^4.1.1",
|
|
47
|
+
"picocolors": "^1.1.1",
|
|
45
48
|
"tar": "^7.5.6",
|
|
46
49
|
"tough-cookie": "^4.1.3"
|
|
47
50
|
},
|