agentskillsdk 0.1.3 → 0.1.5
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/package.json +1 -1
- package/src/commands/add.js +4 -4
- package/src/lib/prompt.js +94 -24
- package/src/lib/ui.js +5 -5
package/package.json
CHANGED
package/src/commands/add.js
CHANGED
|
@@ -38,7 +38,7 @@ export async function addCommand(skillName, options) {
|
|
|
38
38
|
(options.skill ? ` (skill: ${chalk.bold(options.skill)})` : ''));
|
|
39
39
|
} else {
|
|
40
40
|
installName = skillName;
|
|
41
|
-
const spinner = ora({ text: `Looking up ${chalk.bold(skillName)}...`, indent:
|
|
41
|
+
const spinner = ora({ text: `Looking up ${chalk.bold(skillName)}...`, indent: 4 }).start();
|
|
42
42
|
try {
|
|
43
43
|
skill = await fetchSkill(skillName);
|
|
44
44
|
} catch (err) {
|
|
@@ -85,14 +85,14 @@ export async function addCommand(skillName, options) {
|
|
|
85
85
|
scope = 'project';
|
|
86
86
|
} else {
|
|
87
87
|
scope = await selectPrompt('Install scope:', [
|
|
88
|
-
{ label: 'Project', hint: '(.claude/skills/
|
|
89
|
-
{ label: 'Global', hint: '(~/.claude/skills/
|
|
88
|
+
{ label: 'Project', hint: '(local .claude/skills/)', value: 'project' },
|
|
89
|
+
{ label: 'Global', hint: '(~/.claude/skills/)', value: 'global' },
|
|
90
90
|
]);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// --- download ---
|
|
94
94
|
const destDir = agent.path(installName, { cwd, scope });
|
|
95
|
-
const spinner = ora({ text: 'Downloading skill files...', indent:
|
|
95
|
+
const spinner = ora({ text: 'Downloading skill files...', indent: 4 }).start();
|
|
96
96
|
try {
|
|
97
97
|
await downloadSkill(skill, destDir);
|
|
98
98
|
} catch (err) {
|
package/src/lib/prompt.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
// --- strip ANSI for width calculation ---
|
|
4
|
+
|
|
5
|
+
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
6
|
+
function stripAnsi(str) {
|
|
7
|
+
return str.replace(ANSI_RE, '');
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
/**
|
|
5
|
-
*
|
|
11
|
+
* Interactive select prompt with arrow-key navigation.
|
|
12
|
+
* Up/Down to move, Enter to confirm.
|
|
6
13
|
*
|
|
7
14
|
* @param {string} question
|
|
8
15
|
* @param {{ label: string, hint?: string, value: any }[]} choices
|
|
@@ -10,33 +17,96 @@ import { createInterface } from 'node:readline';
|
|
|
10
17
|
* @returns {Promise<any>} selected value
|
|
11
18
|
*/
|
|
12
19
|
export function selectPrompt(question, choices, { defaultIndex = 0 } = {}) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const marker = i === defaultIndex ? chalk.cyan('>') : ' ';
|
|
18
|
-
const num = `${i + 1})`;
|
|
19
|
-
const label = i === defaultIndex ? chalk.cyan(`${num} ${c.label}`) : `${num} ${c.label}`;
|
|
20
|
-
const hint = c.hint ? chalk.dim(` ${c.hint}`) : '';
|
|
21
|
-
console.log(` ${marker} ${label}${hint}`);
|
|
22
|
-
});
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
let selected = defaultIndex;
|
|
22
|
+
const { stdin, stdout } = process;
|
|
23
|
+
const cols = stdout.columns || 80;
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
/** Count how many physical terminal lines a string occupies. */
|
|
26
|
+
function physicalLines(str) {
|
|
27
|
+
const w = stripAnsi(str).length;
|
|
28
|
+
return Math.max(1, Math.ceil(w / cols));
|
|
29
|
+
}
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
/** Total physical lines used by the rendered choices. */
|
|
32
|
+
function totalPhysicalLines(lines) {
|
|
33
|
+
return lines.reduce((sum, l) => sum + physicalLines(l), 0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function render() {
|
|
37
|
+
return choices.map((c, i) => {
|
|
38
|
+
const marker = i === selected ? chalk.cyan('\u276f') : ' ';
|
|
39
|
+
const label = i === selected ? chalk.cyan(c.label) : c.label;
|
|
40
|
+
const hint = c.hint ? chalk.dim(` ${c.hint}`) : '';
|
|
41
|
+
return ` ${marker} ${label}${hint}`;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Hide cursor
|
|
46
|
+
stdout.write('\x1b[?25l');
|
|
47
|
+
|
|
48
|
+
// Print question (4-space indent)
|
|
49
|
+
stdout.write(`\n ${question}\n\n`);
|
|
50
|
+
|
|
51
|
+
// Initial render
|
|
52
|
+
let prevLines = render();
|
|
53
|
+
stdout.write(prevLines.join('\n'));
|
|
54
|
+
|
|
55
|
+
// Enter raw mode for keypress detection
|
|
56
|
+
const wasRaw = stdin.isRaw;
|
|
57
|
+
stdin.setRawMode(true);
|
|
58
|
+
stdin.resume();
|
|
59
|
+
|
|
60
|
+
function cleanup() {
|
|
61
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
62
|
+
stdin.removeListener('data', onData);
|
|
63
|
+
stdin.pause();
|
|
64
|
+
// Restore cursor visibility
|
|
65
|
+
stdout.write('\x1b[?25h');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function onData(buf) {
|
|
69
|
+
const key = buf.toString();
|
|
70
|
+
|
|
71
|
+
// Ctrl+C
|
|
72
|
+
if (key === '\x03') {
|
|
73
|
+
cleanup();
|
|
74
|
+
stdout.write('\n');
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Enter
|
|
79
|
+
if (key === '\r' || key === '\n') {
|
|
80
|
+
// Move up by physical lines to clear previous render
|
|
81
|
+
const upCount = totalPhysicalLines(prevLines);
|
|
82
|
+
stdout.write(`\x1b[${upCount}A`);
|
|
83
|
+
stdout.write('\x1b[J'); // clear to end
|
|
84
|
+
const final = ` ${chalk.cyan('\u276f')} ${chalk.cyan(choices[selected].label)}`;
|
|
85
|
+
stdout.write(final + '\n');
|
|
86
|
+
cleanup();
|
|
87
|
+
resolve(choices[selected].value);
|
|
32
88
|
return;
|
|
33
89
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
90
|
+
|
|
91
|
+
// Arrow keys come as escape sequences: \x1b[A (up), \x1b[B (down)
|
|
92
|
+
if (key === '\x1b[A' || key === '\x1b[D') {
|
|
93
|
+
// Up or Left
|
|
94
|
+
selected = (selected - 1 + choices.length) % choices.length;
|
|
95
|
+
} else if (key === '\x1b[B' || key === '\x1b[C') {
|
|
96
|
+
// Down or Right
|
|
97
|
+
selected = (selected + 1) % choices.length;
|
|
37
98
|
} else {
|
|
38
|
-
|
|
99
|
+
return; // ignore other keys
|
|
39
100
|
}
|
|
40
|
-
|
|
101
|
+
|
|
102
|
+
// Redraw: move cursor up by physical lines, clear, rewrite
|
|
103
|
+
const upCount = totalPhysicalLines(prevLines);
|
|
104
|
+
stdout.write(`\x1b[${upCount}A`);
|
|
105
|
+
stdout.write('\x1b[J');
|
|
106
|
+
prevLines = render();
|
|
107
|
+
stdout.write(prevLines.join('\n'));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
stdin.on('data', onData);
|
|
41
111
|
});
|
|
42
112
|
}
|
package/src/lib/ui.js
CHANGED
|
@@ -20,12 +20,12 @@ export function box(lines, { borderColor, padding = 2 } = {}) {
|
|
|
20
20
|
|
|
21
21
|
const colorize = borderColor ? chalk[borderColor].bind(chalk) : (s) => s;
|
|
22
22
|
|
|
23
|
-
const top = colorize(`
|
|
24
|
-
const bottom = colorize(`
|
|
23
|
+
const top = colorize(` \u250c${'─'.repeat(inner)}\u2510`);
|
|
24
|
+
const bottom = colorize(` \u2514${'─'.repeat(inner)}\u2518`);
|
|
25
25
|
const rowLines = lines.map(l => {
|
|
26
26
|
const visible = stripAnsi(l).length;
|
|
27
27
|
const rightPad = ' '.repeat(maxWidth - visible);
|
|
28
|
-
return `
|
|
28
|
+
return ` ${colorize('\u2502')}${pad}${l}${rightPad}${pad}${colorize('\u2502')}`;
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
return [top, ...rowLines, bottom].join('\n');
|
|
@@ -72,9 +72,9 @@ export function printCompletionSummary({ skillName, scope, installPath, agentNam
|
|
|
72
72
|
// --- step / error output ---
|
|
73
73
|
|
|
74
74
|
export function step(text) {
|
|
75
|
-
console.log(chalk.green('
|
|
75
|
+
console.log(chalk.green(' \u2713') + ` ${text}`);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
export function error(text) {
|
|
79
|
-
console.error(chalk.red(`
|
|
79
|
+
console.error(chalk.red(` ${text}`));
|
|
80
80
|
}
|