@sstar/skill-install 1.1.0 → 1.2.0
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 +264 -501
- package/dist/installer/install-service.d.ts +5 -0
- package/dist/installer/install-service.js +28 -2
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +17 -0
- package/dist/ui/prompts.d.ts +59 -0
- package/dist/ui/prompts.js +193 -0
- package/dist/wiki/skill-selector.d.ts +3 -7
- package/dist/wiki/skill-selector.js +25 -82
- package/package.json +5 -1
package/dist/cli.js
CHANGED
|
@@ -5,28 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
|
-
const readline_1 = __importDefault(require("readline"));
|
|
9
8
|
const os_1 = __importDefault(require("os"));
|
|
9
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
10
10
|
const install_service_1 = require("./installer/install-service");
|
|
11
11
|
const skills_manager_1 = require("./skills/skills-manager");
|
|
12
12
|
const plugin_manager_1 = require("./plugins/plugin-manager");
|
|
13
13
|
const logger_1 = require("./core/logger");
|
|
14
14
|
const wiki_1 = require("./wiki");
|
|
15
15
|
const types_1 = require("./plugins/types");
|
|
16
|
-
|
|
17
|
-
const colors = {
|
|
18
|
-
reset: '\x1b[0m',
|
|
19
|
-
bright: '\x1b[1m',
|
|
20
|
-
dim: '\x1b[2m',
|
|
21
|
-
red: '\x1b[31m',
|
|
22
|
-
green: '\x1b[32m',
|
|
23
|
-
yellow: '\x1b[33m',
|
|
24
|
-
blue: '\x1b[34m',
|
|
25
|
-
magenta: '\x1b[35m',
|
|
26
|
-
cyan: '\x1b[36m',
|
|
27
|
-
white: '\x1b[37m',
|
|
28
|
-
gray: '\x1b[90m'
|
|
29
|
-
};
|
|
16
|
+
const ui_1 = require("./ui");
|
|
30
17
|
const program = new commander_1.Command();
|
|
31
18
|
const skillsManager = new skills_manager_1.SkillsManager();
|
|
32
19
|
program
|
|
@@ -54,19 +41,18 @@ program
|
|
|
54
41
|
logger_1.Logger.setLevel('debug');
|
|
55
42
|
}
|
|
56
43
|
try {
|
|
44
|
+
(0, ui_1.intro)(picocolors_1.default.cyan('Agent Skill Installer'));
|
|
57
45
|
// Determine AI tool - prompt if not specified
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
aiTool = await promptAiTool();
|
|
64
|
-
}
|
|
46
|
+
const aiTool = globalOpts.tool
|
|
47
|
+
? globalOpts.tool
|
|
48
|
+
: await (0, ui_1.promptAiTool)();
|
|
65
49
|
// Determine skills directory
|
|
66
50
|
let skillsDir = globalOpts.skillsDir;
|
|
67
51
|
// If neither --global nor --local nor explicit --skills-dir, prompt interactively
|
|
68
52
|
if (!skillsDir && !options.global && !options.local) {
|
|
69
|
-
|
|
53
|
+
const globalDir = skillsManager.getGlobalSkillsDir(aiTool);
|
|
54
|
+
const localDir = skillsManager.getLocalSkillsDir(aiTool);
|
|
55
|
+
skillsDir = await (0, ui_1.promptSkillsDir)(aiTool, globalDir, localDir);
|
|
70
56
|
}
|
|
71
57
|
else if (options.global) {
|
|
72
58
|
skillsDir = skillsManager.getGlobalSkillsDir(aiTool);
|
|
@@ -81,187 +67,293 @@ program
|
|
|
81
67
|
// Auto-detect username from system if not provided
|
|
82
68
|
let username = options.username || process.env.WIKI_USERNAME || os_1.default.userInfo().username;
|
|
83
69
|
let password = options.password || process.env.WIKI_PASSWORD || '';
|
|
70
|
+
// Collect sources to install
|
|
71
|
+
let sources = source ? [source] : [];
|
|
84
72
|
// If no source provided, trigger search flow
|
|
85
|
-
if (
|
|
73
|
+
if (sources.length === 0) {
|
|
86
74
|
// Only require password, username is auto-detected
|
|
87
75
|
if (!password) {
|
|
88
|
-
password = await promptPassword();
|
|
76
|
+
password = await (0, ui_1.promptPassword)('Enter your Wiki password');
|
|
89
77
|
}
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
78
|
+
const selectedUrls = await searchAndSelectSkill(username, password, options.wikiUrl, options.allowSelfSigned);
|
|
79
|
+
if (selectedUrls.length === 0) {
|
|
92
80
|
console.log('No skill selected.');
|
|
93
81
|
process.exit(0);
|
|
94
82
|
}
|
|
95
|
-
|
|
83
|
+
sources = selectedUrls;
|
|
96
84
|
}
|
|
97
|
-
// Check if source appears to be a wiki URL
|
|
98
|
-
const needsAuth =
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
// Check if any source appears to be a wiki URL
|
|
86
|
+
const needsAuth = sources.some(s => s.includes('/download/attachments/') ||
|
|
87
|
+
s.includes('/wiki/download/') ||
|
|
88
|
+
s.includes('/confluence/download/'));
|
|
101
89
|
if (needsAuth && !username) {
|
|
102
90
|
console.error('Error: Wiki URL detected. Username is required. Use -u option or set WIKI_USERNAME environment variable.');
|
|
103
91
|
process.exit(1);
|
|
104
92
|
}
|
|
105
93
|
if (needsAuth && !password) {
|
|
106
|
-
password = await promptPassword();
|
|
107
|
-
}
|
|
108
|
-
console.log(`Installing skill from: ${source}`);
|
|
109
|
-
if (needsAuth) {
|
|
110
|
-
console.log(`Username: ${username}`);
|
|
94
|
+
password = await (0, ui_1.promptPassword)();
|
|
111
95
|
}
|
|
112
|
-
|
|
96
|
+
// Install all sources
|
|
113
97
|
const installer = new install_service_1.InstallService();
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (result.isMultiSkill && result.skills) {
|
|
124
|
-
const selectedSkills = await selectSkillsFromPackage(result.skills);
|
|
125
|
-
if (selectedSkills.length === 0) {
|
|
126
|
-
console.log('No skills selected.');
|
|
127
|
-
// Clean up temp directory before exit
|
|
128
|
-
if (result.tempDir) {
|
|
129
|
-
await installer.cleanup(result.tempDir);
|
|
130
|
-
}
|
|
131
|
-
process.exit(0);
|
|
98
|
+
let totalSuccessCount = 0;
|
|
99
|
+
let totalFailCount = 0;
|
|
100
|
+
for (let i = 0; i < sources.length; i++) {
|
|
101
|
+
const currentSource = sources[i];
|
|
102
|
+
if (sources.length > 1) {
|
|
103
|
+
console.log(`\n[${i + 1}/${sources.length}] Installing from: ${currentSource}`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
console.log(`Installing skill from: ${currentSource}`);
|
|
132
107
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
108
|
+
if (needsAuth) {
|
|
109
|
+
console.log(`Username: ${username}`);
|
|
110
|
+
}
|
|
111
|
+
console.log('');
|
|
112
|
+
const result = await installer.install({
|
|
113
|
+
source: currentSource,
|
|
137
114
|
skillsDir,
|
|
138
115
|
username,
|
|
139
116
|
password,
|
|
140
117
|
allowSelfSigned: options.allowSelfSigned,
|
|
141
118
|
force: options.force
|
|
142
119
|
});
|
|
143
|
-
//
|
|
144
|
-
if (result.
|
|
145
|
-
await
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
console.log(`✓ Skill "${res.skillName}" installed successfully!`);
|
|
154
|
-
console.log(` Path: ${res.skillPath}`);
|
|
155
|
-
successCount++;
|
|
120
|
+
// Handle multi-skill package
|
|
121
|
+
if (result.isMultiSkill && result.skills) {
|
|
122
|
+
const selectedSkills = await (0, ui_1.selectSkillsFromPackage)(result.skills);
|
|
123
|
+
if (selectedSkills.length === 0) {
|
|
124
|
+
console.log('No skills selected.');
|
|
125
|
+
// Clean up temp directory before continuing
|
|
126
|
+
if (result.tempDir) {
|
|
127
|
+
await installer.cleanup(result.tempDir);
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
156
130
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
131
|
+
// Install selected skills
|
|
132
|
+
const installResults = await installer.installSelectedSkills(selectedSkills, '', // extractDir is already handled in install()
|
|
133
|
+
{
|
|
134
|
+
source: currentSource,
|
|
135
|
+
skillsDir,
|
|
136
|
+
username,
|
|
137
|
+
password,
|
|
138
|
+
allowSelfSigned: options.allowSelfSigned,
|
|
139
|
+
force: options.force
|
|
140
|
+
});
|
|
141
|
+
// Clean up temp directory after installation
|
|
142
|
+
if (result.tempDir) {
|
|
143
|
+
await installer.cleanup(result.tempDir);
|
|
144
|
+
}
|
|
145
|
+
// Count results
|
|
146
|
+
for (const res of installResults) {
|
|
147
|
+
if (res.success) {
|
|
148
|
+
console.log(`✓ Skill "${res.skillName}" installed successfully!`);
|
|
149
|
+
console.log(` Path: ${res.skillPath}`);
|
|
150
|
+
totalSuccessCount++;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
console.error(`✗ Failed to install skill: ${res.error}`);
|
|
154
|
+
totalFailCount++;
|
|
155
|
+
}
|
|
160
156
|
}
|
|
161
157
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
158
|
+
// Handle Marketplace installation
|
|
159
|
+
if (result.packageType === types_1.PackageType.MARKETPLACE && result.singleSkillResult?.marketplaceResult) {
|
|
160
|
+
// Plugins only support Claude Code, not Codex
|
|
161
|
+
// For Codex, fallback to skill installation
|
|
162
|
+
if (aiTool === 'codex') {
|
|
163
|
+
console.log('');
|
|
164
|
+
console.log(picocolors_1.default.yellow('⚠ Note: Plugins and Marketplaces are only supported for Claude Code.'));
|
|
165
|
+
console.log(` Selected AI Tool: ${picocolors_1.default.cyan('Codex')}`);
|
|
166
|
+
console.log(` Scanning for Skills in this package...`);
|
|
167
|
+
console.log('');
|
|
168
|
+
// Re-scan for skills in the package
|
|
169
|
+
const skills = await installer.scanForSkills(result.tempDir || '');
|
|
170
|
+
if (skills.length > 0) {
|
|
171
|
+
const selectedSkills = await (0, ui_1.selectSkillsFromPackage)(skills);
|
|
172
|
+
if (selectedSkills.length > 0) {
|
|
173
|
+
const installResults = await installer.installSelectedSkills(selectedSkills, result.tempDir || '', {
|
|
174
|
+
source: currentSource,
|
|
175
|
+
skillsDir,
|
|
176
|
+
username,
|
|
177
|
+
password,
|
|
178
|
+
allowSelfSigned: options.allowSelfSigned,
|
|
179
|
+
force: options.force
|
|
180
|
+
});
|
|
181
|
+
// Count results
|
|
182
|
+
for (const res of installResults) {
|
|
183
|
+
if (res.success) {
|
|
184
|
+
console.log(`✓ Skill "${res.skillName}" installed successfully!`);
|
|
185
|
+
console.log(` Path: ${res.skillPath}`);
|
|
186
|
+
totalSuccessCount++;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
console.error(`✗ Failed to install skill: ${res.error}`);
|
|
190
|
+
totalFailCount++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
console.log('No Skills found in this package.');
|
|
184
197
|
}
|
|
198
|
+
// Clean up temp directory
|
|
199
|
+
if (result.tempDir) {
|
|
200
|
+
await installer.cleanup(result.tempDir);
|
|
201
|
+
}
|
|
202
|
+
continue;
|
|
185
203
|
}
|
|
204
|
+
const marketplaceResult = result.singleSkillResult.marketplaceResult;
|
|
205
|
+
const pluginManager = new plugin_manager_1.PluginManager();
|
|
186
206
|
console.log('');
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
console.log('');
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
207
|
+
console.log(picocolors_1.default.cyan('📦 Marketplace installed successfully!'));
|
|
208
|
+
console.log(` Name: ${marketplaceResult.marketplace}`);
|
|
209
|
+
console.log(` Path: ${marketplaceResult.path}`);
|
|
210
|
+
console.log('');
|
|
211
|
+
// Ask if user wants to install plugins from this marketplace
|
|
212
|
+
if (result.packageStructure && result.packageStructure.plugins.length > 0) {
|
|
213
|
+
const plugins = result.packageStructure.plugins;
|
|
214
|
+
console.log(`This marketplace contains ${plugins.length} plugin(s):`);
|
|
215
|
+
console.log('');
|
|
216
|
+
for (const plugin of plugins) {
|
|
217
|
+
console.log(` - ${picocolors_1.default.green(plugin.name)} v${plugin.version || 'unknown'}`);
|
|
218
|
+
if (plugin.metadata.description) {
|
|
219
|
+
console.log(` ${plugin.metadata.description}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
console.log('');
|
|
223
|
+
const installPlugins = await (0, ui_1.promptYesNo)('Would you like to install plugins from this marketplace?');
|
|
224
|
+
if (installPlugins) {
|
|
225
|
+
// Select plugins to install
|
|
226
|
+
const selectedPlugins = await (0, ui_1.selectPluginsFromMarketplace)(plugins);
|
|
227
|
+
if (selectedPlugins.length > 0) {
|
|
228
|
+
console.log('');
|
|
229
|
+
const results = await installer.installPluginsFromMarketplace(marketplaceResult.marketplace, selectedPlugins, (current, total, plugin) => {
|
|
230
|
+
console.log(`[${current}/${total}] Installing ${plugin}...`);
|
|
231
|
+
});
|
|
232
|
+
// Display plugin installation results
|
|
233
|
+
console.log('');
|
|
234
|
+
for (const res of results) {
|
|
235
|
+
if (res.success) {
|
|
236
|
+
console.log(`${picocolors_1.default.green('✓')} Plugin "${res.plugin}" installed successfully!`);
|
|
237
|
+
console.log(` Path: ${res.path}`);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
console.error(`${picocolors_1.default.red('✗')} Failed to install plugin: ${res.error || 'Unknown error'}`);
|
|
241
|
+
}
|
|
205
242
|
}
|
|
206
243
|
}
|
|
207
244
|
}
|
|
208
245
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
246
|
+
// Also check if marketplace can be installed as a plugin
|
|
247
|
+
if (result.packageStructure?.marketplaceAsPlugin) {
|
|
248
|
+
console.log('');
|
|
249
|
+
console.log(picocolors_1.default.yellow('💡 Note: This marketplace can also be installed as a plugin.'));
|
|
250
|
+
console.log(` As a plugin, it provides: ${result.packageStructure.marketplaceAsPlugin.description || ''}`);
|
|
251
|
+
const installAsPlugin = await (0, ui_1.promptYesNo)('Install as plugin as well?');
|
|
252
|
+
if (installAsPlugin) {
|
|
253
|
+
try {
|
|
254
|
+
const pluginResult = await pluginManager.installPluginFromMarketplace(marketplaceResult.marketplace, result.packageStructure.marketplaceAsPlugin.name, result.packageStructure.marketplaceAsPlugin.version);
|
|
255
|
+
console.log(`${picocolors_1.default.green('✓')} Plugin "${pluginResult.plugin}" installed as well!`);
|
|
256
|
+
console.log(` Path: ${pluginResult.path}`);
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
console.error(`${picocolors_1.default.red('✗')} Failed to install as plugin: ${error}`);
|
|
260
|
+
}
|
|
221
261
|
}
|
|
222
|
-
|
|
223
|
-
|
|
262
|
+
}
|
|
263
|
+
// Clean up temp directory
|
|
264
|
+
if (result.tempDir) {
|
|
265
|
+
await installer.cleanup(result.tempDir);
|
|
266
|
+
}
|
|
267
|
+
totalSuccessCount++;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
// Handle Plugin installation
|
|
271
|
+
if (result.packageType === types_1.PackageType.PLUGIN && result.singleSkillResult?.pluginResult) {
|
|
272
|
+
// Plugins only support Claude Code, not Codex
|
|
273
|
+
// For Codex, fallback to skill installation
|
|
274
|
+
if (aiTool === 'codex') {
|
|
275
|
+
console.log('');
|
|
276
|
+
console.log(picocolors_1.default.yellow('⚠ Note: Plugins are only supported for Claude Code.'));
|
|
277
|
+
console.log(` Selected AI Tool: ${picocolors_1.default.cyan('Codex')}`);
|
|
278
|
+
console.log(` Scanning for Skills in this package...`);
|
|
279
|
+
console.log('');
|
|
280
|
+
// Re-scan for skills in the package
|
|
281
|
+
const skills = await installer.scanForSkills(result.tempDir || '');
|
|
282
|
+
if (skills.length > 0) {
|
|
283
|
+
const selectedSkills = await (0, ui_1.selectSkillsFromPackage)(skills);
|
|
284
|
+
if (selectedSkills.length > 0) {
|
|
285
|
+
const installResults = await installer.installSelectedSkills(selectedSkills, result.tempDir || '', {
|
|
286
|
+
source: currentSource,
|
|
287
|
+
skillsDir,
|
|
288
|
+
username,
|
|
289
|
+
password,
|
|
290
|
+
allowSelfSigned: options.allowSelfSigned,
|
|
291
|
+
force: options.force
|
|
292
|
+
});
|
|
293
|
+
// Count results
|
|
294
|
+
for (const res of installResults) {
|
|
295
|
+
if (res.success) {
|
|
296
|
+
console.log(`✓ Skill "${res.skillName}" installed successfully!`);
|
|
297
|
+
console.log(` Path: ${res.skillPath}`);
|
|
298
|
+
totalSuccessCount++;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
console.error(`✗ Failed to install skill: ${res.error}`);
|
|
302
|
+
totalFailCount++;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
224
306
|
}
|
|
307
|
+
else {
|
|
308
|
+
console.log('No Skills found in this package.');
|
|
309
|
+
}
|
|
310
|
+
// Clean up temp directory
|
|
311
|
+
if (result.tempDir) {
|
|
312
|
+
await installer.cleanup(result.tempDir);
|
|
313
|
+
}
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const pluginResult = result.singleSkillResult.pluginResult;
|
|
317
|
+
console.log('');
|
|
318
|
+
console.log(picocolors_1.default.cyan('🔌 Plugin installed successfully!'));
|
|
319
|
+
console.log(` Name: ${pluginResult.plugin}`);
|
|
320
|
+
if (pluginResult.version) {
|
|
321
|
+
console.log(` Version: ${pluginResult.version}`);
|
|
225
322
|
}
|
|
323
|
+
console.log(` Path: ${pluginResult.path}`);
|
|
324
|
+
console.log('');
|
|
325
|
+
totalSuccessCount++;
|
|
326
|
+
continue;
|
|
226
327
|
}
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
|
|
328
|
+
// Handle single skill result (legacy behavior)
|
|
329
|
+
const singleResult = result.singleSkillResult;
|
|
330
|
+
if (!singleResult) {
|
|
331
|
+
console.error('✗ Installation failed: No result returned');
|
|
332
|
+
totalFailCount++;
|
|
230
333
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (result.packageType === types_1.PackageType.PLUGIN && result.singleSkillResult?.pluginResult) {
|
|
237
|
-
const pluginResult = result.singleSkillResult.pluginResult;
|
|
238
|
-
console.log('');
|
|
239
|
-
console.log(`${colors.cyan}🔌 Plugin installed successfully!${colors.reset}`);
|
|
240
|
-
console.log(` Name: ${pluginResult.plugin}`);
|
|
241
|
-
if (pluginResult.version) {
|
|
242
|
-
console.log(` Version: ${pluginResult.version}`);
|
|
334
|
+
else if (singleResult.success) {
|
|
335
|
+
console.log('');
|
|
336
|
+
console.log(`✓ Skill "${singleResult.skillName}" installed successfully!`);
|
|
337
|
+
console.log(` Path: ${singleResult.skillPath}`);
|
|
338
|
+
totalSuccessCount++;
|
|
243
339
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
if (!singleResult) {
|
|
252
|
-
console.error('✗ Installation failed: No result returned');
|
|
253
|
-
process.exit(1);
|
|
254
|
-
}
|
|
340
|
+
else {
|
|
341
|
+
console.error('✗ Installation failed!');
|
|
342
|
+
console.error(` Error: ${singleResult.error || 'Unknown error'}`);
|
|
343
|
+
totalFailCount++;
|
|
344
|
+
}
|
|
345
|
+
} // End of for loop
|
|
346
|
+
// Display final summary
|
|
255
347
|
console.log('');
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
console.log(`
|
|
259
|
-
|
|
260
|
-
|
|
348
|
+
console.log('─'.repeat(50));
|
|
349
|
+
if (sources.length > 1) {
|
|
350
|
+
console.log(`Installation complete: ${totalSuccessCount} succeeded, ${totalFailCount} failed`);
|
|
351
|
+
}
|
|
352
|
+
else if (totalSuccessCount > 0) {
|
|
353
|
+
console.log('Installation complete!');
|
|
261
354
|
}
|
|
262
355
|
else {
|
|
263
|
-
console.
|
|
264
|
-
console.error(` Error: ${singleResult.error || 'Unknown error'}`);
|
|
356
|
+
console.log('Installation failed.');
|
|
265
357
|
process.exit(1);
|
|
266
358
|
}
|
|
267
359
|
}
|
|
@@ -291,7 +383,7 @@ program
|
|
|
291
383
|
aiTool = globalOpts.tool;
|
|
292
384
|
}
|
|
293
385
|
else {
|
|
294
|
-
aiTool = await promptAiTool();
|
|
386
|
+
aiTool = await (0, ui_1.promptAiTool)();
|
|
295
387
|
}
|
|
296
388
|
// If no specific directory and --all flag, show both directories
|
|
297
389
|
if (!globalOpts.skillsDir && options.all) {
|
|
@@ -343,7 +435,7 @@ program
|
|
|
343
435
|
aiTool = globalOpts.tool;
|
|
344
436
|
}
|
|
345
437
|
else {
|
|
346
|
-
aiTool = await promptAiTool();
|
|
438
|
+
aiTool = await (0, ui_1.promptAiTool)();
|
|
347
439
|
}
|
|
348
440
|
const skills = await skillsManager.listInstalled(globalOpts.skillsDir, aiTool);
|
|
349
441
|
if (skills.length === 0) {
|
|
@@ -352,7 +444,7 @@ program
|
|
|
352
444
|
}
|
|
353
445
|
// If no name provided, prompt to select from list
|
|
354
446
|
if (!name) {
|
|
355
|
-
const selectedIndices = await promptSkillSelection(skills);
|
|
447
|
+
const selectedIndices = await (0, ui_1.promptSkillSelection)(skills);
|
|
356
448
|
if (selectedIndices.length === 0) {
|
|
357
449
|
console.log('No skills selected.');
|
|
358
450
|
process.exit(0);
|
|
@@ -365,7 +457,7 @@ program
|
|
|
365
457
|
try {
|
|
366
458
|
// Confirm uninstall unless --yes flag is provided
|
|
367
459
|
if (!options.yes) {
|
|
368
|
-
const confirmed = await
|
|
460
|
+
const confirmed = await (0, ui_1.promptYesNo)(`Uninstall skill "${skill.name}" from ${aiTool}?`);
|
|
369
461
|
if (!confirmed) {
|
|
370
462
|
console.log(`Skipped: ${skill.name}`);
|
|
371
463
|
continue;
|
|
@@ -396,7 +488,7 @@ program
|
|
|
396
488
|
}
|
|
397
489
|
// Confirm uninstall unless --yes flag is provided
|
|
398
490
|
if (!options.yes) {
|
|
399
|
-
const confirmed = await
|
|
491
|
+
const confirmed = await (0, ui_1.promptYesNo)(`Uninstall skill "${name}" from ${aiTool}?`);
|
|
400
492
|
if (!confirmed) {
|
|
401
493
|
console.log('Uninstall cancelled.');
|
|
402
494
|
process.exit(0);
|
|
@@ -415,245 +507,6 @@ program
|
|
|
415
507
|
process.exit(1);
|
|
416
508
|
}
|
|
417
509
|
});
|
|
418
|
-
function promptPassword() {
|
|
419
|
-
return new Promise((resolve, reject) => {
|
|
420
|
-
// Check if setRawMode is available (TTY required)
|
|
421
|
-
if (typeof process.stdin.setRawMode !== 'function') {
|
|
422
|
-
console.error('\nError: Interactive password input requires a TTY.');
|
|
423
|
-
console.error('Please use -p option or WIKI_PASSWORD environment variable.');
|
|
424
|
-
reject(new Error('TTY not available for interactive password input'));
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
const rl = readline_1.default.createInterface({
|
|
428
|
-
input: process.stdin,
|
|
429
|
-
output: process.stdout
|
|
430
|
-
});
|
|
431
|
-
// Turn off echo
|
|
432
|
-
process.stdout.write('Password: ');
|
|
433
|
-
process.stdin.setRawMode(true);
|
|
434
|
-
let password = '';
|
|
435
|
-
const onData = (char) => {
|
|
436
|
-
const str = char.toString();
|
|
437
|
-
if (str === '\n' || str === '\r' || str === '\u0004') {
|
|
438
|
-
// Enter or Ctrl-D
|
|
439
|
-
process.stdin.setRawMode(false);
|
|
440
|
-
process.stdin.removeListener('data', onData);
|
|
441
|
-
process.stdout.write('\n');
|
|
442
|
-
rl.close();
|
|
443
|
-
resolve(password);
|
|
444
|
-
}
|
|
445
|
-
else if (str === '\u0003') {
|
|
446
|
-
// Ctrl-C
|
|
447
|
-
process.stdout.write('\n');
|
|
448
|
-
process.exit(1);
|
|
449
|
-
}
|
|
450
|
-
else if (str === '\u007f') {
|
|
451
|
-
// Backspace
|
|
452
|
-
if (password.length > 0) {
|
|
453
|
-
password = password.slice(0, -1);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
// Regular character
|
|
458
|
-
password += str;
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
process.stdin.on('data', onData);
|
|
462
|
-
process.stdin.setRawMode(true);
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
function confirm(message) {
|
|
466
|
-
return new Promise((resolve) => {
|
|
467
|
-
const rl = readline_1.default.createInterface({
|
|
468
|
-
input: process.stdin,
|
|
469
|
-
output: process.stdout
|
|
470
|
-
});
|
|
471
|
-
rl.question(`${message} (y/N): `, (answer) => {
|
|
472
|
-
rl.close();
|
|
473
|
-
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
474
|
-
});
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
function promptSkillsDir(aiTool) {
|
|
478
|
-
return new Promise((resolve) => {
|
|
479
|
-
const rl = readline_1.default.createInterface({
|
|
480
|
-
input: process.stdin,
|
|
481
|
-
output: process.stdout
|
|
482
|
-
});
|
|
483
|
-
const globalDir = skillsManager.getGlobalSkillsDir(aiTool);
|
|
484
|
-
const localDir = skillsManager.getLocalSkillsDir(aiTool);
|
|
485
|
-
console.log('');
|
|
486
|
-
console.log('Select installation directory:');
|
|
487
|
-
console.log(` 1. Global: ${globalDir}`);
|
|
488
|
-
console.log(` 2. Local: ${localDir}`);
|
|
489
|
-
console.log('');
|
|
490
|
-
rl.question('Choose [1/2] (default: 1): ', (answer) => {
|
|
491
|
-
rl.close();
|
|
492
|
-
const choice = answer.trim() || '1';
|
|
493
|
-
if (choice === '1') {
|
|
494
|
-
resolve(globalDir);
|
|
495
|
-
}
|
|
496
|
-
else if (choice === '2') {
|
|
497
|
-
resolve(localDir);
|
|
498
|
-
}
|
|
499
|
-
else {
|
|
500
|
-
console.log('Invalid choice, using global directory.');
|
|
501
|
-
resolve(globalDir);
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
function promptAiTool() {
|
|
507
|
-
return new Promise((resolve) => {
|
|
508
|
-
const rl = readline_1.default.createInterface({
|
|
509
|
-
input: process.stdin,
|
|
510
|
-
output: process.stdout
|
|
511
|
-
});
|
|
512
|
-
console.log('');
|
|
513
|
-
console.log('Select AI tool:');
|
|
514
|
-
console.log(' 1. Claude Code (~/.claude/skills/)');
|
|
515
|
-
console.log(' 2. Codex (~/.codex/skills/)');
|
|
516
|
-
console.log('');
|
|
517
|
-
rl.question('Choose [1/2] (default: 1): ', (answer) => {
|
|
518
|
-
rl.close();
|
|
519
|
-
const choice = answer.trim() || '1';
|
|
520
|
-
if (choice === '1') {
|
|
521
|
-
resolve('claude');
|
|
522
|
-
}
|
|
523
|
-
else if (choice === '2') {
|
|
524
|
-
resolve('codex');
|
|
525
|
-
}
|
|
526
|
-
else {
|
|
527
|
-
console.log('Invalid choice, using claude.');
|
|
528
|
-
resolve('claude');
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
function selectSkillsFromPackage(skills) {
|
|
534
|
-
return new Promise((resolve) => {
|
|
535
|
-
const rl = readline_1.default.createInterface({
|
|
536
|
-
input: process.stdin,
|
|
537
|
-
output: process.stdout
|
|
538
|
-
});
|
|
539
|
-
console.log('');
|
|
540
|
-
console.log(`${colors.cyan}Multi-skill package detected!${colors.reset}`);
|
|
541
|
-
console.log(`${colors.cyan}Found ${skills.length} skill(s) in this package:${colors.reset}`);
|
|
542
|
-
console.log('');
|
|
543
|
-
// Display all skills with colors and separators
|
|
544
|
-
for (let i = 0; i < skills.length; i++) {
|
|
545
|
-
const skill = skills[i];
|
|
546
|
-
// Separator line before each skill
|
|
547
|
-
console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
|
|
548
|
-
// Index and skill name
|
|
549
|
-
console.log(`${colors.bright}${colors.green}[${i + 1}]${colors.reset} ${colors.bright}${colors.yellow}${skill.name}${colors.reset}`);
|
|
550
|
-
// Description
|
|
551
|
-
const lines = skill.description.split('\n');
|
|
552
|
-
for (const line of lines) {
|
|
553
|
-
console.log(`${colors.dim} ${line}${colors.reset}`);
|
|
554
|
-
}
|
|
555
|
-
// Relative path in package
|
|
556
|
-
console.log(`${colors.dim} Package path: ${skill.relativePath}${colors.reset}`);
|
|
557
|
-
}
|
|
558
|
-
// Separator line after last skill
|
|
559
|
-
console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
|
|
560
|
-
console.log('');
|
|
561
|
-
console.log('Enter the numbers of the skills to install (separated by spaces, e.g., "1 3 5"):');
|
|
562
|
-
console.log('Press Enter to install all skills:');
|
|
563
|
-
rl.question('Your choice: ', (answer) => {
|
|
564
|
-
rl.close();
|
|
565
|
-
const input = answer.trim();
|
|
566
|
-
if (!input) {
|
|
567
|
-
// Install all skills
|
|
568
|
-
console.log(`Installing all ${skills.length} skills...`);
|
|
569
|
-
resolve(skills);
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
// Parse the input and extract valid indices
|
|
573
|
-
const selectedNumbers = input.split(/\s+/).map(s => parseInt(s, 10));
|
|
574
|
-
const selectedSkills = [];
|
|
575
|
-
for (const num of selectedNumbers) {
|
|
576
|
-
if (isNaN(num)) {
|
|
577
|
-
console.log(`Skipping invalid number: ${input}`);
|
|
578
|
-
continue;
|
|
579
|
-
}
|
|
580
|
-
if (num < 1 || num > skills.length) {
|
|
581
|
-
console.log(`Skipping out of range number: ${num}`);
|
|
582
|
-
continue;
|
|
583
|
-
}
|
|
584
|
-
selectedSkills.push(skills[num - 1]);
|
|
585
|
-
}
|
|
586
|
-
if (selectedSkills.length === 0) {
|
|
587
|
-
console.log('No valid skills selected.');
|
|
588
|
-
resolve([]);
|
|
589
|
-
return;
|
|
590
|
-
}
|
|
591
|
-
console.log('');
|
|
592
|
-
console.log(`Selected skills: ${selectedSkills.map(s => s.name).join(', ')}`);
|
|
593
|
-
resolve(selectedSkills);
|
|
594
|
-
});
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
function promptSkillSelection(skills) {
|
|
598
|
-
return new Promise((resolve) => {
|
|
599
|
-
const rl = readline_1.default.createInterface({
|
|
600
|
-
input: process.stdin,
|
|
601
|
-
output: process.stdout
|
|
602
|
-
});
|
|
603
|
-
console.log('');
|
|
604
|
-
console.log(`${colors.cyan}Installed skills:${colors.reset}`);
|
|
605
|
-
console.log('');
|
|
606
|
-
// Display all skills with colors and separators
|
|
607
|
-
for (let i = 0; i < skills.length; i++) {
|
|
608
|
-
const skill = skills[i];
|
|
609
|
-
// Separator line before each skill
|
|
610
|
-
console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
|
|
611
|
-
// Index and skill name
|
|
612
|
-
console.log(`${colors.bright}${colors.green}[${i + 1}]${colors.reset} ${colors.bright}${colors.yellow}${skill.name}${colors.reset}`);
|
|
613
|
-
// Description
|
|
614
|
-
console.log(`${colors.dim} ${skill.description}${colors.reset}`);
|
|
615
|
-
// Path
|
|
616
|
-
console.log(`${colors.dim} ${skill.path}${colors.reset}`);
|
|
617
|
-
}
|
|
618
|
-
// Separator line after last skill
|
|
619
|
-
console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
|
|
620
|
-
console.log('');
|
|
621
|
-
console.log('Enter the numbers of the skills to uninstall (separated by spaces, e.g., "1 3 5"):');
|
|
622
|
-
console.log('Press Enter to cancel:');
|
|
623
|
-
rl.question('Your choice: ', (answer) => {
|
|
624
|
-
rl.close();
|
|
625
|
-
const input = answer.trim();
|
|
626
|
-
if (!input) {
|
|
627
|
-
resolve([]);
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
// Parse the input and extract valid indices
|
|
631
|
-
const selectedNumbers = input.split(/\s+/).map(s => parseInt(s, 10));
|
|
632
|
-
const validIndices = [];
|
|
633
|
-
for (const num of selectedNumbers) {
|
|
634
|
-
if (isNaN(num)) {
|
|
635
|
-
console.log(`Skipping invalid number: ${input}`);
|
|
636
|
-
continue;
|
|
637
|
-
}
|
|
638
|
-
if (num < 1 || num > skills.length) {
|
|
639
|
-
console.log(`Skipping out of range number: ${num}`);
|
|
640
|
-
continue;
|
|
641
|
-
}
|
|
642
|
-
validIndices.push(num - 1); // Convert to 0-based index
|
|
643
|
-
}
|
|
644
|
-
// Remove duplicates
|
|
645
|
-
const uniqueIndices = [...new Set(validIndices)];
|
|
646
|
-
if (uniqueIndices.length === 0) {
|
|
647
|
-
console.log('No valid skills selected.');
|
|
648
|
-
resolve([]);
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
console.log('');
|
|
652
|
-
console.log(`Selected skills: ${uniqueIndices.map(i => skills[i].name).join(', ')}`);
|
|
653
|
-
resolve(uniqueIndices);
|
|
654
|
-
});
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
510
|
async function listSkillsFromBothDirectories(aiTool) {
|
|
658
511
|
const globalDir = skillsManager.getGlobalSkillsDir(aiTool);
|
|
659
512
|
const localDir = skillsManager.getLocalSkillsDir(aiTool);
|
|
@@ -695,18 +548,6 @@ async function listSkillsFromBothDirectories(aiTool) {
|
|
|
695
548
|
console.log('No skills installed in either location.');
|
|
696
549
|
}
|
|
697
550
|
}
|
|
698
|
-
function promptUsername() {
|
|
699
|
-
return new Promise((resolve) => {
|
|
700
|
-
const rl = readline_1.default.createInterface({
|
|
701
|
-
input: process.stdin,
|
|
702
|
-
output: process.stdout
|
|
703
|
-
});
|
|
704
|
-
rl.question('Wiki username: ', (answer) => {
|
|
705
|
-
rl.close();
|
|
706
|
-
resolve(answer.trim());
|
|
707
|
-
});
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
551
|
async function searchAndSelectSkill(username, password, wikiUrl, allowSelfSigned) {
|
|
711
552
|
const searcher = new wiki_1.WikiSearcher();
|
|
712
553
|
const selector = new wiki_1.SkillSelector();
|
|
@@ -723,96 +564,18 @@ async function searchAndSelectSkill(username, password, wikiUrl, allowSelfSigned
|
|
|
723
564
|
});
|
|
724
565
|
if (items.length === 0) {
|
|
725
566
|
console.log('No skills found.');
|
|
726
|
-
return
|
|
567
|
+
return [];
|
|
727
568
|
}
|
|
728
569
|
// Parse items (fetch comments)
|
|
729
570
|
const parser = new wiki_1.WikiParser(searcher.httpClientInstance);
|
|
730
571
|
const parsed = await parser.parseSkillItems(items);
|
|
731
572
|
// Display and select
|
|
732
|
-
const
|
|
733
|
-
return
|
|
573
|
+
const selectedUrls = await selector.displayAndSelect(parsed);
|
|
574
|
+
return selectedUrls;
|
|
734
575
|
}
|
|
735
576
|
catch (error) {
|
|
736
577
|
console.error(`Search failed: ${error.message}`);
|
|
737
|
-
return
|
|
578
|
+
return [];
|
|
738
579
|
}
|
|
739
580
|
}
|
|
740
|
-
/**
|
|
741
|
-
* Prompt user for yes/no confirmation
|
|
742
|
-
*/
|
|
743
|
-
function promptYesNo(message) {
|
|
744
|
-
return new Promise((resolve) => {
|
|
745
|
-
const rl = readline_1.default.createInterface({
|
|
746
|
-
input: process.stdin,
|
|
747
|
-
output: process.stdout
|
|
748
|
-
});
|
|
749
|
-
rl.question(`${message} [Y/n]: `, (answer) => {
|
|
750
|
-
rl.close();
|
|
751
|
-
const trimmed = answer.trim().toLowerCase();
|
|
752
|
-
resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes');
|
|
753
|
-
});
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
/**
|
|
757
|
-
* Select plugins from a marketplace
|
|
758
|
-
*/
|
|
759
|
-
function selectPluginsFromMarketplace(plugins) {
|
|
760
|
-
return new Promise((resolve) => {
|
|
761
|
-
const rl = readline_1.default.createInterface({
|
|
762
|
-
input: process.stdin,
|
|
763
|
-
output: process.stdout
|
|
764
|
-
});
|
|
765
|
-
console.log('');
|
|
766
|
-
console.log(`${colors.cyan}Available plugins:${colors.reset}`);
|
|
767
|
-
console.log('');
|
|
768
|
-
for (let i = 0; i < plugins.length; i++) {
|
|
769
|
-
const plugin = plugins[i];
|
|
770
|
-
console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
|
|
771
|
-
console.log(`${colors.bright}${colors.green}[${i + 1}]${colors.reset} ${colors.bright}${colors.yellow}${plugin.name}${colors.reset} v${plugin.version || 'unknown'}`);
|
|
772
|
-
if (plugin.metadata.description) {
|
|
773
|
-
const lines = plugin.metadata.description.split('\n');
|
|
774
|
-
for (const line of lines) {
|
|
775
|
-
console.log(`${colors.dim} ${line}${colors.reset}`);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
|
|
780
|
-
console.log('');
|
|
781
|
-
console.log('Enter the numbers of the plugins to install (separated by spaces, e.g., "1 3 5"):');
|
|
782
|
-
console.log('Press Enter to install all plugins:');
|
|
783
|
-
rl.question('Your choice: ', (answer) => {
|
|
784
|
-
rl.close();
|
|
785
|
-
const input = answer.trim();
|
|
786
|
-
if (!input) {
|
|
787
|
-
// Install all
|
|
788
|
-
console.log(`Installing all ${plugins.length} plugins...`);
|
|
789
|
-
resolve(plugins.map(p => ({ name: p.name, version: p.version })));
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
// Parse selection
|
|
793
|
-
const selectedNumbers = input.split(/\s+/).map(s => parseInt(s, 10));
|
|
794
|
-
const selected = [];
|
|
795
|
-
for (const num of selectedNumbers) {
|
|
796
|
-
if (isNaN(num)) {
|
|
797
|
-
console.log(`Skipping invalid number: ${input}`);
|
|
798
|
-
continue;
|
|
799
|
-
}
|
|
800
|
-
if (num < 1 || num > plugins.length) {
|
|
801
|
-
console.log(`Skipping out of range number: ${num}`);
|
|
802
|
-
continue;
|
|
803
|
-
}
|
|
804
|
-
const plugin = plugins[num - 1];
|
|
805
|
-
selected.push({ name: plugin.name, version: plugin.version });
|
|
806
|
-
}
|
|
807
|
-
if (selected.length === 0) {
|
|
808
|
-
console.log('No valid plugins selected.');
|
|
809
|
-
resolve([]);
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
console.log('');
|
|
813
|
-
console.log(`Selected plugins: ${selected.map(p => p.name).join(', ')}`);
|
|
814
|
-
resolve(selected);
|
|
815
|
-
});
|
|
816
|
-
});
|
|
817
|
-
}
|
|
818
581
|
program.parse();
|