@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 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,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 (!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}`);
104
+ }
105
+ else {
106
+ console.log(`Installing skill from: ${currentSource}`);
132
107
  }
133
- // Install selected skills
134
- const installResults = await installer.installSelectedSkills(selectedSkills, '', // extractDir is already handled in install()
135
- {
136
- source,
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++;
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
- else {
158
- console.error(`✗ Failed to install skill: ${res.error}`);
159
- failCount++;
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
- 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
- const marketplaceResult = result.singleSkillResult.marketplaceResult;
169
- const pluginManager = new plugin_manager_1.PluginManager();
170
- console.log('');
171
- console.log(`${colors.cyan}📦 Marketplace installed successfully!${colors.reset}`);
172
- console.log(` Name: ${marketplaceResult.marketplace}`);
173
- console.log(` Path: ${marketplaceResult.path}`);
174
- console.log('');
175
- // Ask if user wants to install plugins from this marketplace
176
- if (result.packageStructure && result.packageStructure.plugins.length > 0) {
177
- const plugins = result.packageStructure.plugins;
178
- console.log(`This marketplace contains ${plugins.length} plugin(s):`);
179
- console.log('');
180
- for (const plugin of plugins) {
181
- console.log(` - ${colors.green}${plugin.name}${colors.reset} v${plugin.version || 'unknown'}`);
182
- if (plugin.metadata.description) {
183
- console.log(` ${plugin.metadata.description}`);
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
- const installPlugins = await promptYesNo('Would you like to install plugins from this marketplace?');
188
- if (installPlugins) {
189
- // Select plugins to install
190
- const selectedPlugins = await selectPluginsFromMarketplace(plugins);
191
- if (selectedPlugins.length > 0) {
192
- console.log('');
193
- const results = await installer.installPluginsFromMarketplace(marketplaceResult.marketplace, selectedPlugins, (current, total, plugin) => {
194
- console.log(`[${current}/${total}] Installing ${plugin}...`);
195
- });
196
- // Display plugin installation results
197
- console.log('');
198
- for (const res of results) {
199
- if (res.success) {
200
- console.log(`${colors.green}✓${colors.reset} Plugin "${res.plugin}" installed successfully!`);
201
- console.log(` Path: ${res.path}`);
202
- }
203
- else {
204
- console.error(`${colors.red}✗${colors.reset} Failed to install plugin: ${res.error || 'Unknown error'}`);
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
- // Also check if marketplace can be installed as a plugin
211
- if (result.packageStructure?.marketplaceAsPlugin) {
212
- console.log('');
213
- console.log(`${colors.yellow}💡 Note: This marketplace can also be installed as a plugin.${colors.reset}`);
214
- console.log(` As a plugin, it provides: ${result.packageStructure.marketplaceAsPlugin.description || ''}`);
215
- const installAsPlugin = await promptYesNo('Install as plugin as well?');
216
- if (installAsPlugin) {
217
- try {
218
- const pluginResult = await pluginManager.installPluginFromMarketplace(marketplaceResult.marketplace, result.packageStructure.marketplaceAsPlugin.name, result.packageStructure.marketplaceAsPlugin.version);
219
- console.log(`${colors.green}✓${colors.reset} Plugin "${pluginResult.plugin}" installed as well!`);
220
- console.log(` Path: ${pluginResult.path}`);
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
- catch (error) {
223
- console.error(`${colors.red}✗${colors.reset} Failed to install as plugin: ${error}`);
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
- // Clean up temp directory
228
- if (result.tempDir) {
229
- await installer.cleanup(result.tempDir);
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
- console.log('');
232
- console.log('Installation complete!');
233
- process.exit(0);
234
- }
235
- // Handle Plugin installation
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
- console.log(` Path: ${pluginResult.path}`);
245
- console.log('');
246
- console.log('Installation complete!');
247
- process.exit(0);
248
- }
249
- // Handle single skill result (legacy behavior)
250
- const singleResult = result.singleSkillResult;
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
- if (singleResult.success) {
257
- console.log(`✓ Skill installed successfully!`);
258
- console.log(` Name: ${singleResult.skillName}`);
259
- console.log(` Path: ${singleResult.skillPath}`);
260
- 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!');
261
354
  }
262
355
  else {
263
- console.error(`✗ Installation failed!`);
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 confirm(`Uninstall skill "${skill.name}" from ${aiTool}?`);
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 confirm(`Uninstall skill "${name}" from ${aiTool}?`);
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 null;
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 selectedUrl = await selector.displayAndSelect(parsed);
733
- return selectedUrl;
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 null;
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();