@sstar/skill-install 1.1.1 → 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 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
- // Color codes for terminal output
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
- let aiTool;
59
- if (globalOpts.tool) {
60
- aiTool = globalOpts.tool;
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
- skillsDir = await promptSkillsDir(aiTool);
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,285 +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 (!source) {
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 selectedUrl = await searchAndSelectSkill(username, password, options.wikiUrl, options.allowSelfSigned);
91
- if (!selectedUrl) {
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
- source = selectedUrl;
83
+ sources = selectedUrls;
96
84
  }
97
- // Check if source appears to be a wiki URL
98
- const needsAuth = source.includes('/download/attachments/') ||
99
- source.includes('/wiki/download/') ||
100
- source.includes('/confluence/download/');
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
- console.log('');
96
+ // Install all sources
113
97
  const installer = new install_service_1.InstallService();
114
- const result = await installer.install({
115
- source,
116
- skillsDir,
117
- username,
118
- password,
119
- allowSelfSigned: options.allowSelfSigned,
120
- force: options.force
121
- });
122
- // Handle multi-skill package
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}`);
132
104
  }
133
- // Install selected skills
134
- const installResults = await installer.installSelectedSkills(selectedSkills, '', // extractDir is already handled in install()
135
- {
136
- source,
105
+ else {
106
+ console.log(`Installing skill from: ${currentSource}`);
107
+ }
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
- // Clean up temp directory after installation
144
- if (result.tempDir) {
145
- await installer.cleanup(result.tempDir);
146
- }
147
- // Display results
148
- console.log('');
149
- let successCount = 0;
150
- let failCount = 0;
151
- for (const res of installResults) {
152
- if (res.success) {
153
- console.log(`✓ Skill "${res.skillName}" installed successfully!`);
154
- console.log(` Path: ${res.skillPath}`);
155
- successCount++;
156
- }
157
- else {
158
- console.error(`✗ Failed to install skill: ${res.error}`);
159
- failCount++;
160
- }
161
- }
162
- console.log('');
163
- console.log(`Installation complete: ${successCount} succeeded, ${failCount} failed.`);
164
- process.exit(failCount > 0 ? 1 : 0);
165
- }
166
- // Handle Marketplace installation
167
- if (result.packageType === types_1.PackageType.MARKETPLACE && result.singleSkillResult?.marketplaceResult) {
168
- // Plugins only support Claude Code, not Codex
169
- // For Codex, fallback to skill installation
170
- if (aiTool === 'codex') {
171
- console.log('');
172
- console.log(`${colors.yellow}⚠ Note: Plugins and Marketplaces are only supported for Claude Code.${colors.reset}`);
173
- console.log(` Selected AI Tool: ${colors.cyan}Codex${colors.reset}`);
174
- console.log(` Scanning for Skills in this package...`);
175
- console.log('');
176
- // Re-scan for skills in the package
177
- const skills = await installer.scanForSkills(result.tempDir || '');
178
- if (skills.length > 0) {
179
- const selectedSkills = await selectSkillsFromPackage(skills);
180
- if (selectedSkills.length > 0) {
181
- const installResults = await installer.installSelectedSkills(selectedSkills, result.tempDir || '', {
182
- source,
183
- skillsDir,
184
- username,
185
- password,
186
- allowSelfSigned: options.allowSelfSigned,
187
- force: options.force
188
- });
189
- // Display results
190
- console.log('');
191
- let successCount = 0;
192
- let failCount = 0;
193
- for (const res of installResults) {
194
- if (res.success) {
195
- console.log(`✓ Skill "${res.skillName}" installed successfully!`);
196
- console.log(` Path: ${res.skillPath}`);
197
- successCount++;
198
- }
199
- else {
200
- console.error(`✗ Failed to install skill: ${res.error}`);
201
- failCount++;
202
- }
203
- }
204
- console.log('');
205
- console.log(`Installation complete: ${successCount} succeeded, ${failCount} failed.`);
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);
206
128
  }
129
+ continue;
207
130
  }
208
- else {
209
- console.log('No Skills found in this package.');
210
- }
211
- // Clean up temp directory
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
212
142
  if (result.tempDir) {
213
143
  await installer.cleanup(result.tempDir);
214
144
  }
215
- process.exit(0);
216
- }
217
- const marketplaceResult = result.singleSkillResult.marketplaceResult;
218
- const pluginManager = new plugin_manager_1.PluginManager();
219
- console.log('');
220
- console.log(`${colors.cyan}📦 Marketplace installed successfully!${colors.reset}`);
221
- console.log(` Name: ${marketplaceResult.marketplace}`);
222
- console.log(` Path: ${marketplaceResult.path}`);
223
- console.log('');
224
- // Ask if user wants to install plugins from this marketplace
225
- if (result.packageStructure && result.packageStructure.plugins.length > 0) {
226
- const plugins = result.packageStructure.plugins;
227
- console.log(`This marketplace contains ${plugins.length} plugin(s):`);
228
- console.log('');
229
- for (const plugin of plugins) {
230
- console.log(` - ${colors.green}${plugin.name}${colors.reset} v${plugin.version || 'unknown'}`);
231
- if (plugin.metadata.description) {
232
- console.log(` ${plugin.metadata.description}`);
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++;
233
155
  }
234
156
  }
235
- console.log('');
236
- const installPlugins = await promptYesNo('Would you like to install plugins from this marketplace?');
237
- if (installPlugins) {
238
- // Select plugins to install
239
- const selectedPlugins = await selectPluginsFromMarketplace(plugins);
240
- if (selectedPlugins.length > 0) {
241
- console.log('');
242
- const results = await installer.installPluginsFromMarketplace(marketplaceResult.marketplace, selectedPlugins, (current, total, plugin) => {
243
- console.log(`[${current}/${total}] Installing ${plugin}...`);
244
- });
245
- // Display plugin installation results
246
- console.log('');
247
- for (const res of results) {
248
- if (res.success) {
249
- console.log(`${colors.green}✓${colors.reset} Plugin "${res.plugin}" installed successfully!`);
250
- console.log(` Path: ${res.path}`);
251
- }
252
- else {
253
- console.error(`${colors.red}✗${colors.reset} Failed to install plugin: ${res.error || 'Unknown error'}`);
157
+ }
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
+ }
254
192
  }
255
193
  }
256
194
  }
257
- }
258
- }
259
- // Also check if marketplace can be installed as a plugin
260
- if (result.packageStructure?.marketplaceAsPlugin) {
261
- console.log('');
262
- console.log(`${colors.yellow}💡 Note: This marketplace can also be installed as a plugin.${colors.reset}`);
263
- console.log(` As a plugin, it provides: ${result.packageStructure.marketplaceAsPlugin.description || ''}`);
264
- const installAsPlugin = await promptYesNo('Install as plugin as well?');
265
- if (installAsPlugin) {
266
- try {
267
- const pluginResult = await pluginManager.installPluginFromMarketplace(marketplaceResult.marketplace, result.packageStructure.marketplaceAsPlugin.name, result.packageStructure.marketplaceAsPlugin.version);
268
- console.log(`${colors.green}✓${colors.reset} Plugin "${pluginResult.plugin}" installed as well!`);
269
- console.log(` Path: ${pluginResult.path}`);
195
+ else {
196
+ console.log('No Skills found in this package.');
270
197
  }
271
- catch (error) {
272
- console.error(`${colors.red}✗${colors.reset} Failed to install as plugin: ${error}`);
198
+ // Clean up temp directory
199
+ if (result.tempDir) {
200
+ await installer.cleanup(result.tempDir);
273
201
  }
202
+ continue;
274
203
  }
275
- }
276
- // Clean up temp directory
277
- if (result.tempDir) {
278
- await installer.cleanup(result.tempDir);
279
- }
280
- console.log('');
281
- console.log('Installation complete!');
282
- process.exit(0);
283
- }
284
- // Handle Plugin installation
285
- if (result.packageType === types_1.PackageType.PLUGIN && result.singleSkillResult?.pluginResult) {
286
- // Plugins only support Claude Code, not Codex
287
- // For Codex, fallback to skill installation
288
- if (aiTool === 'codex') {
204
+ const marketplaceResult = result.singleSkillResult.marketplaceResult;
205
+ const pluginManager = new plugin_manager_1.PluginManager();
289
206
  console.log('');
290
- console.log(`${colors.yellow}⚠ Note: Plugins are only supported for Claude Code.${colors.reset}`);
291
- console.log(` Selected AI Tool: ${colors.cyan}Codex${colors.reset}`);
292
- console.log(` Scanning for Skills in this package...`);
207
+ console.log(picocolors_1.default.cyan('📦 Marketplace installed successfully!'));
208
+ console.log(` Name: ${marketplaceResult.marketplace}`);
209
+ console.log(` Path: ${marketplaceResult.path}`);
293
210
  console.log('');
294
- // Re-scan for skills in the package
295
- const skills = await installer.scanForSkills(result.tempDir || '');
296
- if (skills.length > 0) {
297
- const selectedSkills = await selectSkillsFromPackage(skills);
298
- if (selectedSkills.length > 0) {
299
- const installResults = await installer.installSelectedSkills(selectedSkills, result.tempDir || '', {
300
- source,
301
- skillsDir,
302
- username,
303
- password,
304
- allowSelfSigned: options.allowSelfSigned,
305
- force: options.force
306
- });
307
- // Display results
308
- console.log('');
309
- let successCount = 0;
310
- let failCount = 0;
311
- for (const res of installResults) {
312
- if (res.success) {
313
- console.log(`✓ Skill "${res.skillName}" installed successfully!`);
314
- console.log(` Path: ${res.skillPath}`);
315
- successCount++;
316
- }
317
- else {
318
- console.error(`✗ Failed to install skill: ${res.error}`);
319
- failCount++;
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
+ }
320
242
  }
321
243
  }
322
- console.log('');
323
- console.log(`Installation complete: ${successCount} succeeded, ${failCount} failed.`);
324
244
  }
325
245
  }
326
- else {
327
- console.log('No Skills found in this package.');
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
+ }
261
+ }
328
262
  }
329
263
  // Clean up temp directory
330
264
  if (result.tempDir) {
331
265
  await installer.cleanup(result.tempDir);
332
266
  }
333
- process.exit(0);
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
+ }
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}`);
322
+ }
323
+ console.log(` Path: ${pluginResult.path}`);
324
+ console.log('');
325
+ totalSuccessCount++;
326
+ continue;
334
327
  }
335
- const pluginResult = result.singleSkillResult.pluginResult;
336
- console.log('');
337
- console.log(`${colors.cyan}🔌 Plugin installed successfully!${colors.reset}`);
338
- console.log(` Name: ${pluginResult.plugin}`);
339
- if (pluginResult.version) {
340
- console.log(` Version: ${pluginResult.version}`);
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++;
341
333
  }
342
- console.log(` Path: ${pluginResult.path}`);
343
- console.log('');
344
- console.log('Installation complete!');
345
- process.exit(0);
346
- }
347
- // Handle single skill result (legacy behavior)
348
- const singleResult = result.singleSkillResult;
349
- if (!singleResult) {
350
- console.error('✗ Installation failed: No result returned');
351
- process.exit(1);
352
- }
334
+ else if (singleResult.success) {
335
+ console.log('');
336
+ console.log(`✓ Skill "${singleResult.skillName}" installed successfully!`);
337
+ console.log(` Path: ${singleResult.skillPath}`);
338
+ totalSuccessCount++;
339
+ }
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
353
347
  console.log('');
354
- if (singleResult.success) {
355
- console.log(`✓ Skill installed successfully!`);
356
- console.log(` Name: ${singleResult.skillName}`);
357
- console.log(` Path: ${singleResult.skillPath}`);
358
- process.exit(0);
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!');
359
354
  }
360
355
  else {
361
- console.error(`✗ Installation failed!`);
362
- console.error(` Error: ${singleResult.error || 'Unknown error'}`);
356
+ console.log('Installation failed.');
363
357
  process.exit(1);
364
358
  }
365
359
  }
@@ -389,7 +383,7 @@ program
389
383
  aiTool = globalOpts.tool;
390
384
  }
391
385
  else {
392
- aiTool = await promptAiTool();
386
+ aiTool = await (0, ui_1.promptAiTool)();
393
387
  }
394
388
  // If no specific directory and --all flag, show both directories
395
389
  if (!globalOpts.skillsDir && options.all) {
@@ -441,7 +435,7 @@ program
441
435
  aiTool = globalOpts.tool;
442
436
  }
443
437
  else {
444
- aiTool = await promptAiTool();
438
+ aiTool = await (0, ui_1.promptAiTool)();
445
439
  }
446
440
  const skills = await skillsManager.listInstalled(globalOpts.skillsDir, aiTool);
447
441
  if (skills.length === 0) {
@@ -450,7 +444,7 @@ program
450
444
  }
451
445
  // If no name provided, prompt to select from list
452
446
  if (!name) {
453
- const selectedIndices = await promptSkillSelection(skills);
447
+ const selectedIndices = await (0, ui_1.promptSkillSelection)(skills);
454
448
  if (selectedIndices.length === 0) {
455
449
  console.log('No skills selected.');
456
450
  process.exit(0);
@@ -463,7 +457,7 @@ program
463
457
  try {
464
458
  // Confirm uninstall unless --yes flag is provided
465
459
  if (!options.yes) {
466
- const confirmed = await confirm(`Uninstall skill "${skill.name}" from ${aiTool}?`);
460
+ const confirmed = await (0, ui_1.promptYesNo)(`Uninstall skill "${skill.name}" from ${aiTool}?`);
467
461
  if (!confirmed) {
468
462
  console.log(`Skipped: ${skill.name}`);
469
463
  continue;
@@ -494,7 +488,7 @@ program
494
488
  }
495
489
  // Confirm uninstall unless --yes flag is provided
496
490
  if (!options.yes) {
497
- const confirmed = await confirm(`Uninstall skill "${name}" from ${aiTool}?`);
491
+ const confirmed = await (0, ui_1.promptYesNo)(`Uninstall skill "${name}" from ${aiTool}?`);
498
492
  if (!confirmed) {
499
493
  console.log('Uninstall cancelled.');
500
494
  process.exit(0);
@@ -513,245 +507,6 @@ program
513
507
  process.exit(1);
514
508
  }
515
509
  });
516
- function promptPassword() {
517
- return new Promise((resolve, reject) => {
518
- // Check if setRawMode is available (TTY required)
519
- if (typeof process.stdin.setRawMode !== 'function') {
520
- console.error('\nError: Interactive password input requires a TTY.');
521
- console.error('Please use -p option or WIKI_PASSWORD environment variable.');
522
- reject(new Error('TTY not available for interactive password input'));
523
- return;
524
- }
525
- const rl = readline_1.default.createInterface({
526
- input: process.stdin,
527
- output: process.stdout
528
- });
529
- // Turn off echo
530
- process.stdout.write('Password: ');
531
- process.stdin.setRawMode(true);
532
- let password = '';
533
- const onData = (char) => {
534
- const str = char.toString();
535
- if (str === '\n' || str === '\r' || str === '\u0004') {
536
- // Enter or Ctrl-D
537
- process.stdin.setRawMode(false);
538
- process.stdin.removeListener('data', onData);
539
- process.stdout.write('\n');
540
- rl.close();
541
- resolve(password);
542
- }
543
- else if (str === '\u0003') {
544
- // Ctrl-C
545
- process.stdout.write('\n');
546
- process.exit(1);
547
- }
548
- else if (str === '\u007f') {
549
- // Backspace
550
- if (password.length > 0) {
551
- password = password.slice(0, -1);
552
- }
553
- }
554
- else {
555
- // Regular character
556
- password += str;
557
- }
558
- };
559
- process.stdin.on('data', onData);
560
- process.stdin.setRawMode(true);
561
- });
562
- }
563
- function confirm(message) {
564
- return new Promise((resolve) => {
565
- const rl = readline_1.default.createInterface({
566
- input: process.stdin,
567
- output: process.stdout
568
- });
569
- rl.question(`${message} (y/N): `, (answer) => {
570
- rl.close();
571
- resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
572
- });
573
- });
574
- }
575
- function promptSkillsDir(aiTool) {
576
- return new Promise((resolve) => {
577
- const rl = readline_1.default.createInterface({
578
- input: process.stdin,
579
- output: process.stdout
580
- });
581
- const globalDir = skillsManager.getGlobalSkillsDir(aiTool);
582
- const localDir = skillsManager.getLocalSkillsDir(aiTool);
583
- console.log('');
584
- console.log('Select installation directory:');
585
- console.log(` 1. Global: ${globalDir}`);
586
- console.log(` 2. Local: ${localDir}`);
587
- console.log('');
588
- rl.question('Choose [1/2] (default: 1): ', (answer) => {
589
- rl.close();
590
- const choice = answer.trim() || '1';
591
- if (choice === '1') {
592
- resolve(globalDir);
593
- }
594
- else if (choice === '2') {
595
- resolve(localDir);
596
- }
597
- else {
598
- console.log('Invalid choice, using global directory.');
599
- resolve(globalDir);
600
- }
601
- });
602
- });
603
- }
604
- function promptAiTool() {
605
- return new Promise((resolve) => {
606
- const rl = readline_1.default.createInterface({
607
- input: process.stdin,
608
- output: process.stdout
609
- });
610
- console.log('');
611
- console.log('Select AI tool:');
612
- console.log(' 1. Claude Code (~/.claude/skills/)');
613
- console.log(' 2. Codex (~/.codex/skills/)');
614
- console.log('');
615
- rl.question('Choose [1/2] (default: 1): ', (answer) => {
616
- rl.close();
617
- const choice = answer.trim() || '1';
618
- if (choice === '1') {
619
- resolve('claude');
620
- }
621
- else if (choice === '2') {
622
- resolve('codex');
623
- }
624
- else {
625
- console.log('Invalid choice, using claude.');
626
- resolve('claude');
627
- }
628
- });
629
- });
630
- }
631
- function selectSkillsFromPackage(skills) {
632
- return new Promise((resolve) => {
633
- const rl = readline_1.default.createInterface({
634
- input: process.stdin,
635
- output: process.stdout
636
- });
637
- console.log('');
638
- console.log(`${colors.cyan}Multi-skill package detected!${colors.reset}`);
639
- console.log(`${colors.cyan}Found ${skills.length} skill(s) in this package:${colors.reset}`);
640
- console.log('');
641
- // Display all skills with colors and separators
642
- for (let i = 0; i < skills.length; i++) {
643
- const skill = skills[i];
644
- // Separator line before each skill
645
- console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
646
- // Index and skill name
647
- console.log(`${colors.bright}${colors.green}[${i + 1}]${colors.reset} ${colors.bright}${colors.yellow}${skill.name}${colors.reset}`);
648
- // Description
649
- const lines = skill.description.split('\n');
650
- for (const line of lines) {
651
- console.log(`${colors.dim} ${line}${colors.reset}`);
652
- }
653
- // Relative path in package
654
- console.log(`${colors.dim} Package path: ${skill.relativePath}${colors.reset}`);
655
- }
656
- // Separator line after last skill
657
- console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
658
- console.log('');
659
- console.log('Enter the numbers of the skills to install (separated by spaces, e.g., "1 3 5"):');
660
- console.log('Press Enter to install all skills:');
661
- rl.question('Your choice: ', (answer) => {
662
- rl.close();
663
- const input = answer.trim();
664
- if (!input) {
665
- // Install all skills
666
- console.log(`Installing all ${skills.length} skills...`);
667
- resolve(skills);
668
- return;
669
- }
670
- // Parse the input and extract valid indices
671
- const selectedNumbers = input.split(/\s+/).map(s => parseInt(s, 10));
672
- const selectedSkills = [];
673
- for (const num of selectedNumbers) {
674
- if (isNaN(num)) {
675
- console.log(`Skipping invalid number: ${input}`);
676
- continue;
677
- }
678
- if (num < 1 || num > skills.length) {
679
- console.log(`Skipping out of range number: ${num}`);
680
- continue;
681
- }
682
- selectedSkills.push(skills[num - 1]);
683
- }
684
- if (selectedSkills.length === 0) {
685
- console.log('No valid skills selected.');
686
- resolve([]);
687
- return;
688
- }
689
- console.log('');
690
- console.log(`Selected skills: ${selectedSkills.map(s => s.name).join(', ')}`);
691
- resolve(selectedSkills);
692
- });
693
- });
694
- }
695
- function promptSkillSelection(skills) {
696
- return new Promise((resolve) => {
697
- const rl = readline_1.default.createInterface({
698
- input: process.stdin,
699
- output: process.stdout
700
- });
701
- console.log('');
702
- console.log(`${colors.cyan}Installed skills:${colors.reset}`);
703
- console.log('');
704
- // Display all skills with colors and separators
705
- for (let i = 0; i < skills.length; i++) {
706
- const skill = skills[i];
707
- // Separator line before each skill
708
- console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
709
- // Index and skill name
710
- console.log(`${colors.bright}${colors.green}[${i + 1}]${colors.reset} ${colors.bright}${colors.yellow}${skill.name}${colors.reset}`);
711
- // Description
712
- console.log(`${colors.dim} ${skill.description}${colors.reset}`);
713
- // Path
714
- console.log(`${colors.dim} ${skill.path}${colors.reset}`);
715
- }
716
- // Separator line after last skill
717
- console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
718
- console.log('');
719
- console.log('Enter the numbers of the skills to uninstall (separated by spaces, e.g., "1 3 5"):');
720
- console.log('Press Enter to cancel:');
721
- rl.question('Your choice: ', (answer) => {
722
- rl.close();
723
- const input = answer.trim();
724
- if (!input) {
725
- resolve([]);
726
- return;
727
- }
728
- // Parse the input and extract valid indices
729
- const selectedNumbers = input.split(/\s+/).map(s => parseInt(s, 10));
730
- const validIndices = [];
731
- for (const num of selectedNumbers) {
732
- if (isNaN(num)) {
733
- console.log(`Skipping invalid number: ${input}`);
734
- continue;
735
- }
736
- if (num < 1 || num > skills.length) {
737
- console.log(`Skipping out of range number: ${num}`);
738
- continue;
739
- }
740
- validIndices.push(num - 1); // Convert to 0-based index
741
- }
742
- // Remove duplicates
743
- const uniqueIndices = [...new Set(validIndices)];
744
- if (uniqueIndices.length === 0) {
745
- console.log('No valid skills selected.');
746
- resolve([]);
747
- return;
748
- }
749
- console.log('');
750
- console.log(`Selected skills: ${uniqueIndices.map(i => skills[i].name).join(', ')}`);
751
- resolve(uniqueIndices);
752
- });
753
- });
754
- }
755
510
  async function listSkillsFromBothDirectories(aiTool) {
756
511
  const globalDir = skillsManager.getGlobalSkillsDir(aiTool);
757
512
  const localDir = skillsManager.getLocalSkillsDir(aiTool);
@@ -793,18 +548,6 @@ async function listSkillsFromBothDirectories(aiTool) {
793
548
  console.log('No skills installed in either location.');
794
549
  }
795
550
  }
796
- function promptUsername() {
797
- return new Promise((resolve) => {
798
- const rl = readline_1.default.createInterface({
799
- input: process.stdin,
800
- output: process.stdout
801
- });
802
- rl.question('Wiki username: ', (answer) => {
803
- rl.close();
804
- resolve(answer.trim());
805
- });
806
- });
807
- }
808
551
  async function searchAndSelectSkill(username, password, wikiUrl, allowSelfSigned) {
809
552
  const searcher = new wiki_1.WikiSearcher();
810
553
  const selector = new wiki_1.SkillSelector();
@@ -821,96 +564,18 @@ async function searchAndSelectSkill(username, password, wikiUrl, allowSelfSigned
821
564
  });
822
565
  if (items.length === 0) {
823
566
  console.log('No skills found.');
824
- return null;
567
+ return [];
825
568
  }
826
569
  // Parse items (fetch comments)
827
570
  const parser = new wiki_1.WikiParser(searcher.httpClientInstance);
828
571
  const parsed = await parser.parseSkillItems(items);
829
572
  // Display and select
830
- const selectedUrl = await selector.displayAndSelect(parsed);
831
- return selectedUrl;
573
+ const selectedUrls = await selector.displayAndSelect(parsed);
574
+ return selectedUrls;
832
575
  }
833
576
  catch (error) {
834
577
  console.error(`Search failed: ${error.message}`);
835
- return null;
578
+ return [];
836
579
  }
837
580
  }
838
- /**
839
- * Prompt user for yes/no confirmation
840
- */
841
- function promptYesNo(message) {
842
- return new Promise((resolve) => {
843
- const rl = readline_1.default.createInterface({
844
- input: process.stdin,
845
- output: process.stdout
846
- });
847
- rl.question(`${message} [Y/n]: `, (answer) => {
848
- rl.close();
849
- const trimmed = answer.trim().toLowerCase();
850
- resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes');
851
- });
852
- });
853
- }
854
- /**
855
- * Select plugins from a marketplace
856
- */
857
- function selectPluginsFromMarketplace(plugins) {
858
- return new Promise((resolve) => {
859
- const rl = readline_1.default.createInterface({
860
- input: process.stdin,
861
- output: process.stdout
862
- });
863
- console.log('');
864
- console.log(`${colors.cyan}Available plugins:${colors.reset}`);
865
- console.log('');
866
- for (let i = 0; i < plugins.length; i++) {
867
- const plugin = plugins[i];
868
- console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
869
- console.log(`${colors.bright}${colors.green}[${i + 1}]${colors.reset} ${colors.bright}${colors.yellow}${plugin.name}${colors.reset} v${plugin.version || 'unknown'}`);
870
- if (plugin.metadata.description) {
871
- const lines = plugin.metadata.description.split('\n');
872
- for (const line of lines) {
873
- console.log(`${colors.dim} ${line}${colors.reset}`);
874
- }
875
- }
876
- }
877
- console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
878
- console.log('');
879
- console.log('Enter the numbers of the plugins to install (separated by spaces, e.g., "1 3 5"):');
880
- console.log('Press Enter to install all plugins:');
881
- rl.question('Your choice: ', (answer) => {
882
- rl.close();
883
- const input = answer.trim();
884
- if (!input) {
885
- // Install all
886
- console.log(`Installing all ${plugins.length} plugins...`);
887
- resolve(plugins.map(p => ({ name: p.name, version: p.version })));
888
- return;
889
- }
890
- // Parse selection
891
- const selectedNumbers = input.split(/\s+/).map(s => parseInt(s, 10));
892
- const selected = [];
893
- for (const num of selectedNumbers) {
894
- if (isNaN(num)) {
895
- console.log(`Skipping invalid number: ${input}`);
896
- continue;
897
- }
898
- if (num < 1 || num > plugins.length) {
899
- console.log(`Skipping out of range number: ${num}`);
900
- continue;
901
- }
902
- const plugin = plugins[num - 1];
903
- selected.push({ name: plugin.name, version: plugin.version });
904
- }
905
- if (selected.length === 0) {
906
- console.log('No valid plugins selected.');
907
- resolve([]);
908
- return;
909
- }
910
- console.log('');
911
- console.log(`Selected plugins: ${selected.map(p => p.name).join(', ')}`);
912
- resolve(selected);
913
- });
914
- });
915
- }
916
581
  program.parse();