@sstar/skill-install 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,27 @@
1
- import { intro, outro, spinner, isCancel } from '@clack/prompts';
2
1
  import { AiTool } from '../skills/skills-manager';
3
2
  import { SkillInPackage } from '../installer/install-service';
4
3
  import { PluginInfo } from '../plugins/types';
5
- export { intro, outro, spinner, isCancel };
4
+ /**
5
+ * Display intro message
6
+ */
7
+ export declare function intro(title?: string): void;
8
+ /**
9
+ * Display outro message
10
+ */
11
+ export declare function outro(message?: string): void;
12
+ /**
13
+ * Create a spinner
14
+ */
15
+ export declare function spinner(): {
16
+ start(msg: string): void;
17
+ stop(msg?: string): void;
18
+ message(msg: string): void;
19
+ error(msg: string): void;
20
+ };
21
+ /**
22
+ * Check if value is a cancel symbol (inquirer uses Ctrl+C which throws)
23
+ */
24
+ export declare function isCancel(value: unknown): boolean;
6
25
  /**
7
26
  * Prompt for AI tool selection
8
27
  */
@@ -3,7 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.isCancel = exports.spinner = exports.outro = exports.intro = void 0;
6
+ exports.intro = intro;
7
+ exports.outro = outro;
8
+ exports.spinner = spinner;
9
+ exports.isCancel = isCancel;
7
10
  exports.promptAiTool = promptAiTool;
8
11
  exports.promptSkillsDir = promptSkillsDir;
9
12
  exports.promptPassword = promptPassword;
@@ -16,21 +19,84 @@ exports.success = success;
16
19
  exports.error = error;
17
20
  exports.info = info;
18
21
  exports.warn = warn;
19
- const prompts_1 = require("@clack/prompts");
20
- Object.defineProperty(exports, "intro", { enumerable: true, get: function () { return prompts_1.intro; } });
21
- Object.defineProperty(exports, "outro", { enumerable: true, get: function () { return prompts_1.outro; } });
22
- Object.defineProperty(exports, "spinner", { enumerable: true, get: function () { return prompts_1.spinner; } });
23
- Object.defineProperty(exports, "isCancel", { enumerable: true, get: function () { return prompts_1.isCancel; } });
22
+ const prompts_1 = require("@inquirer/prompts");
24
23
  const picocolors_1 = __importDefault(require("picocolors"));
25
24
  /**
26
- * Wrapper to handle cancellation consistently
25
+ * Common theme for prompts - clack-like style
27
26
  */
28
- function handleCancel(value) {
29
- if ((0, prompts_1.isCancel)(value)) {
30
- (0, prompts_1.outro)(picocolors_1.default.yellow('Cancelled'));
31
- process.exit(0);
32
- }
33
- return value;
27
+ const commonTheme = {
28
+ icon: {
29
+ cursor: picocolors_1.default.cyan(''),
30
+ },
31
+ style: {
32
+ description: (text) => picocolors_1.default.green(text),
33
+ },
34
+ };
35
+ /**
36
+ * Display intro message
37
+ */
38
+ function intro(title) {
39
+ console.log();
40
+ console.log(picocolors_1.default.cyan('◆') + (title ? ` ${title}` : ''));
41
+ console.log(picocolors_1.default.gray('│'));
42
+ }
43
+ /**
44
+ * Display outro message
45
+ */
46
+ function outro(message) {
47
+ console.log(picocolors_1.default.gray('│'));
48
+ console.log(picocolors_1.default.cyan('└') + (message ? ` ${message}` : ''));
49
+ console.log();
50
+ }
51
+ /**
52
+ * Create a spinner
53
+ */
54
+ function spinner() {
55
+ let intervalId = null;
56
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
57
+ let frameIndex = 0;
58
+ let currentMsg = '';
59
+ const clearLine = () => {
60
+ process.stdout.write('\r\x1b[K');
61
+ };
62
+ return {
63
+ start(msg) {
64
+ currentMsg = msg;
65
+ intervalId = setInterval(() => {
66
+ const frame = picocolors_1.default.cyan(frames[frameIndex]);
67
+ clearLine();
68
+ process.stdout.write(`${frame} ${currentMsg}`);
69
+ frameIndex = (frameIndex + 1) % frames.length;
70
+ }, 80);
71
+ },
72
+ stop(msg) {
73
+ if (intervalId) {
74
+ clearInterval(intervalId);
75
+ intervalId = null;
76
+ }
77
+ clearLine();
78
+ if (msg) {
79
+ console.log(picocolors_1.default.green('✓') + ` ${msg}`);
80
+ }
81
+ },
82
+ message(msg) {
83
+ currentMsg = msg;
84
+ },
85
+ error(msg) {
86
+ if (intervalId) {
87
+ clearInterval(intervalId);
88
+ intervalId = null;
89
+ }
90
+ clearLine();
91
+ console.log(picocolors_1.default.red('✗') + ` ${msg}`);
92
+ },
93
+ };
94
+ }
95
+ /**
96
+ * Check if value is a cancel symbol (inquirer uses Ctrl+C which throws)
97
+ */
98
+ function isCancel(value) {
99
+ return value === null || value === undefined;
34
100
  }
35
101
  /**
36
102
  * Prompt for AI tool selection
@@ -38,12 +104,13 @@ function handleCancel(value) {
38
104
  async function promptAiTool() {
39
105
  const choice = await (0, prompts_1.select)({
40
106
  message: 'Select AI tool',
41
- options: [
42
- { value: 'claude', label: 'Claude Code', hint: '~/.claude/skills/' },
43
- { value: 'codex', label: 'Codex', hint: '~/.codex/skills/' },
107
+ choices: [
108
+ { value: 'claude', name: 'Claude Code', description: '~/.claude/skills/' },
109
+ { value: 'codex', name: 'Codex', description: '~/.codex/skills/' },
44
110
  ],
111
+ theme: commonTheme,
45
112
  });
46
- return handleCancel(choice);
113
+ return choice;
47
114
  }
48
115
  /**
49
116
  * Prompt for skills directory (global vs local)
@@ -51,12 +118,13 @@ async function promptAiTool() {
51
118
  async function promptSkillsDir(aiTool, globalDir, localDir) {
52
119
  const choice = await (0, prompts_1.select)({
53
120
  message: 'Select installation directory',
54
- options: [
55
- { value: globalDir, label: 'Global', hint: globalDir },
56
- { value: localDir, label: 'Local', hint: localDir },
121
+ choices: [
122
+ { value: globalDir, name: 'Global', description: globalDir },
123
+ { value: localDir, name: 'Local', description: localDir },
57
124
  ],
125
+ theme: commonTheme,
58
126
  });
59
- return handleCancel(choice);
127
+ return choice;
60
128
  }
61
129
  /**
62
130
  * Prompt for password (masked input)
@@ -66,7 +134,7 @@ async function promptPassword(message = 'Password') {
66
134
  message,
67
135
  mask: '*',
68
136
  });
69
- return handleCancel(value);
137
+ return value;
70
138
  }
71
139
  /**
72
140
  * Prompt for yes/no confirmation
@@ -74,20 +142,19 @@ async function promptPassword(message = 'Password') {
74
142
  async function promptYesNo(message, initialValue = true) {
75
143
  const value = await (0, prompts_1.confirm)({
76
144
  message,
77
- initialValue,
145
+ default: initialValue,
78
146
  });
79
- return handleCancel(value);
147
+ return value;
80
148
  }
81
149
  /**
82
150
  * Prompt for text input
83
151
  */
84
152
  async function promptText(message, placeholder, initialValue) {
85
- const value = await (0, prompts_1.text)({
153
+ const value = await (0, prompts_1.input)({
86
154
  message,
87
- placeholder,
88
- initialValue,
155
+ default: initialValue,
89
156
  });
90
- return handleCancel(value);
157
+ return value;
91
158
  }
92
159
  /**
93
160
  * Select skills from a multi-skill package
@@ -99,21 +166,28 @@ async function selectSkillsFromPackage(skills) {
99
166
  const install = await promptYesNo(`Install skill "${skills[0].name}"?`);
100
167
  return install ? skills : [];
101
168
  }
102
- const options = skills.map((skill, index) => ({
103
- value: index,
104
- label: skill.name,
105
- hint: skill.description.length > 50
106
- ? skill.description.slice(0, 50) + '...'
107
- : skill.description,
108
- }));
109
- const selected = await (0, prompts_1.multiselect)({
169
+ const indices = await (0, prompts_1.checkbox)({
110
170
  message: `Select skills to install (${skills.length} found)`,
111
- options,
171
+ choices: skills.map((skill, index) => ({
172
+ value: index,
173
+ name: skill.name,
174
+ description: skill.description.length > 50
175
+ ? skill.description.slice(0, 50) + '...'
176
+ : skill.description,
177
+ })),
112
178
  required: false,
179
+ theme: {
180
+ icon: {
181
+ checked: picocolors_1.default.green('◉'),
182
+ unchecked: '○',
183
+ cursor: picocolors_1.default.cyan('❯'),
184
+ },
185
+ style: {
186
+ description: (text) => picocolors_1.default.green(text),
187
+ },
188
+ },
113
189
  });
114
- const indices = handleCancel(selected);
115
190
  if (indices.length === 0) {
116
- // If nothing selected, ask if they want all
117
191
  const installAll = await promptYesNo('No skills selected. Install all?', false);
118
192
  return installAll ? skills : [];
119
193
  }
@@ -125,19 +199,27 @@ async function selectSkillsFromPackage(skills) {
125
199
  async function promptSkillSelection(skills) {
126
200
  if (skills.length === 0)
127
201
  return [];
128
- const options = skills.map((skill, index) => ({
129
- value: index,
130
- label: skill.name,
131
- hint: skill.description.length > 50
132
- ? skill.description.slice(0, 50) + '...'
133
- : skill.description,
134
- }));
135
- const selected = await (0, prompts_1.multiselect)({
202
+ const indices = await (0, prompts_1.checkbox)({
136
203
  message: 'Select skills to uninstall',
137
- options,
204
+ choices: skills.map((skill, index) => ({
205
+ value: index,
206
+ name: skill.name,
207
+ description: skill.description.length > 50
208
+ ? skill.description.slice(0, 50) + '...'
209
+ : skill.description,
210
+ })),
138
211
  required: false,
212
+ theme: {
213
+ icon: {
214
+ checked: picocolors_1.default.green('◉'),
215
+ unchecked: '○',
216
+ cursor: picocolors_1.default.cyan('❯'),
217
+ },
218
+ style: {
219
+ description: (text) => picocolors_1.default.green(text),
220
+ },
221
+ },
139
222
  });
140
- const indices = handleCancel(selected);
141
223
  return indices;
142
224
  }
143
225
  /**
@@ -150,17 +232,25 @@ async function selectPluginsFromMarketplace(plugins) {
150
232
  const install = await promptYesNo(`Install plugin "${plugins[0].name}"?`);
151
233
  return install ? [{ name: plugins[0].name, version: plugins[0].version }] : [];
152
234
  }
153
- const options = plugins.map((plugin, index) => ({
154
- value: index,
155
- label: `${plugin.name} v${plugin.version || 'unknown'}`,
156
- hint: plugin.metadata.description?.slice(0, 50) || '',
157
- }));
158
- const selected = await (0, prompts_1.multiselect)({
235
+ const indices = await (0, prompts_1.checkbox)({
159
236
  message: `Select plugins to install (${plugins.length} found)`,
160
- options,
237
+ choices: plugins.map((plugin, index) => ({
238
+ value: index,
239
+ name: `${plugin.name} v${plugin.version || 'unknown'}`,
240
+ description: plugin.metadata.description?.slice(0, 50) || '',
241
+ })),
161
242
  required: false,
243
+ theme: {
244
+ icon: {
245
+ checked: picocolors_1.default.green('◉'),
246
+ unchecked: '○',
247
+ cursor: picocolors_1.default.cyan('❯'),
248
+ },
249
+ style: {
250
+ description: (text) => picocolors_1.default.green(text),
251
+ },
252
+ },
162
253
  });
163
- const indices = handleCancel(selected);
164
254
  if (indices.length === 0) {
165
255
  const installAll = await promptYesNo('No plugins selected. Install all?', false);
166
256
  return installAll ? plugins.map(p => ({ name: p.name, version: p.version })) : [];
@@ -183,7 +273,7 @@ function error(message) {
183
273
  * Display info message
184
274
  */
185
275
  function info(message) {
186
- console.log(picocolors_1.default.blue(`ℹ ${message}`));
276
+ console.log(picocolors_1.default.cyan(`ℹ ${message}`));
187
277
  }
188
278
  /**
189
279
  * Display warning message
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SkillSelector = void 0;
7
- const prompts_1 = require("@clack/prompts");
7
+ const checkbox_1 = __importDefault(require("@inquirer/checkbox"));
8
8
  const picocolors_1 = __importDefault(require("picocolors"));
9
9
  const logger_1 = require("../core/logger");
10
10
  class SkillSelector {
@@ -20,26 +20,39 @@ class SkillSelector {
20
20
  this.logger.info('No skills found to display.');
21
21
  return [];
22
22
  }
23
- const options = items.map((item, index) => ({
24
- value: index,
25
- label: item.description
26
- ? `${item.name}: ${item.description.slice(0, 50)}${item.description.length > 50 ? '...' : ''}`
27
- : item.name,
28
- hint: item.pageUrl,
29
- }));
30
- const choice = await (0, prompts_1.multiselect)({
23
+ const selectedIndices = await (0, checkbox_1.default)({
31
24
  message: `Found ${items.length} skill(s)`,
32
- options: options,
25
+ pageSize: 30, // Show more items since each takes 3 lines
26
+ choices: items.map((item, index) => {
27
+ // Build multi-line name with colors
28
+ const num = picocolors_1.default.bold(picocolors_1.default.white(`[${index + 1}/${items.length}]`));
29
+ const name = picocolors_1.default.bold(picocolors_1.default.yellow(item.name));
30
+ const uploader = item.uploader ? picocolors_1.default.cyan(` (${item.uploader})`) : '';
31
+ const desc = picocolors_1.default.green(item.description);
32
+ const url = picocolors_1.default.magenta(item.pageUrl);
33
+ return {
34
+ value: index,
35
+ name: `${num} ${name}${uploader}\n ${desc}\n ${url}`,
36
+ description: '', // Disable default description
37
+ };
38
+ }),
39
+ required: false,
40
+ theme: {
41
+ icon: {
42
+ checked: picocolors_1.default.green('◉'),
43
+ unchecked: '○',
44
+ cursor: picocolors_1.default.cyan('❯'),
45
+ },
46
+ style: {
47
+ description: (text) => text, // No extra styling
48
+ },
49
+ },
33
50
  });
34
- if ((0, prompts_1.isCancel)(choice)) {
35
- (0, prompts_1.outro)(picocolors_1.default.yellow('Cancelled'));
36
- return [];
37
- }
38
- if (choice.length === 0) {
51
+ if (selectedIndices.length === 0) {
39
52
  this.logger.info('No skills selected.');
40
53
  return [];
41
54
  }
42
- const selectedItems = choice.map((index) => items[index]);
55
+ const selectedItems = selectedIndices.map(index => items[index]);
43
56
  this.logger.info(`Selected: ${selectedItems.map(s => s.name).join(', ')}`);
44
57
  return selectedItems.map(s => s.downloadUrl);
45
58
  }
@@ -5,6 +5,7 @@ export interface WikiSkillItem {
5
5
  downloadUrl: string;
6
6
  containerId: string;
7
7
  pageUrl: string;
8
+ uploader?: string;
8
9
  }
9
10
  export interface SearchOptions {
10
11
  username: string;
@@ -70,7 +70,8 @@ class WikiSearcher {
70
70
  name: filename,
71
71
  downloadUrl: this.buildDownloadUrl(baseUrl, item),
72
72
  containerId: item.container?.id || '',
73
- pageUrl: item._links?.webui ? `${baseUrl}${item._links.webui}` : ''
73
+ pageUrl: item._links?.webui ? `${baseUrl}${item._links.webui}` : '',
74
+ uploader: item.version?.by?.displayName
74
75
  });
75
76
  }
76
77
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sstar/skill-install",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Agent Skill installation tool - download, extract, validate, and install skills for Claude Code and Codex",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -37,12 +37,12 @@
37
37
  "node": ">=18.0.0"
38
38
  },
39
39
  "dependencies": {
40
- "@clack/prompts": "^1.0.0",
40
+ "@inquirer/prompts": "^8.3.0",
41
+ "@inquirer/type": "^4.0.3",
41
42
  "adm-zip": "^0.5.16",
42
43
  "axios": "^1.6.0",
43
44
  "cheerio": "^1.0.0-rc.12",
44
45
  "commander": "^12.0.0",
45
- "inquirer": "^13.2.2",
46
46
  "js-yaml": "^4.1.1",
47
47
  "picocolors": "^1.1.1",
48
48
  "tar": "^7.5.6",
@@ -50,7 +50,6 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/adm-zip": "^0.5.7",
53
- "@types/inquirer": "^9.0.9",
54
53
  "@types/js-yaml": "^4.0.9",
55
54
  "@types/node": "^20.0.0",
56
55
  "@types/tar": "^6.1.13",