agentskillsdk 0.1.4 → 0.1.6
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 +49 -27
- 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,5 +1,12 @@
|
|
|
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
|
+
}
|
|
9
|
+
|
|
3
10
|
/**
|
|
4
11
|
* Interactive select prompt with arrow-key navigation.
|
|
5
12
|
* Up/Down to move, Enter to confirm.
|
|
@@ -13,57 +20,70 @@ export function selectPrompt(question, choices, { defaultIndex = 0 } = {}) {
|
|
|
13
20
|
return new Promise((resolve) => {
|
|
14
21
|
let selected = defaultIndex;
|
|
15
22
|
const { stdin, stdout } = process;
|
|
23
|
+
const cols = stdout.columns || 80;
|
|
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
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Total physical lines used by the rendered choices. */
|
|
32
|
+
function totalPhysicalLines(lines) {
|
|
33
|
+
return lines.reduce((sum, l) => sum + physicalLines(l), 0);
|
|
34
|
+
}
|
|
16
35
|
|
|
17
36
|
function render() {
|
|
18
|
-
|
|
19
|
-
// On first render we just printed the question, so we only clear choice lines after that
|
|
20
|
-
const lines = choices.map((c, i) => {
|
|
37
|
+
return choices.map((c, i) => {
|
|
21
38
|
const marker = i === selected ? chalk.cyan('\u276f') : ' ';
|
|
22
39
|
const label = i === selected ? chalk.cyan(c.label) : c.label;
|
|
23
40
|
const hint = c.hint ? chalk.dim(` ${c.hint}`) : '';
|
|
24
|
-
return `
|
|
41
|
+
return ` ${marker} ${label}${hint}`;
|
|
25
42
|
});
|
|
26
|
-
return lines.join('\n');
|
|
27
43
|
}
|
|
28
44
|
|
|
29
|
-
//
|
|
30
|
-
stdout.write(
|
|
45
|
+
// Hide cursor
|
|
46
|
+
stdout.write('\x1b[?25l');
|
|
47
|
+
|
|
48
|
+
// Print question (4-space indent)
|
|
49
|
+
stdout.write(`\n ${question}\n\n`);
|
|
31
50
|
|
|
32
51
|
// Initial render
|
|
33
|
-
|
|
52
|
+
let prevLines = render();
|
|
53
|
+
stdout.write(prevLines.join('\n'));
|
|
34
54
|
|
|
35
55
|
// Enter raw mode for keypress detection
|
|
36
56
|
const wasRaw = stdin.isRaw;
|
|
37
57
|
stdin.setRawMode(true);
|
|
38
58
|
stdin.resume();
|
|
39
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
|
+
|
|
40
68
|
function onData(buf) {
|
|
41
69
|
const key = buf.toString();
|
|
42
70
|
|
|
43
71
|
// Ctrl+C
|
|
44
72
|
if (key === '\x03') {
|
|
45
|
-
|
|
46
|
-
stdin.removeListener('data', onData);
|
|
47
|
-
stdin.pause();
|
|
73
|
+
cleanup();
|
|
48
74
|
stdout.write('\n');
|
|
49
75
|
process.exit(0);
|
|
50
76
|
}
|
|
51
77
|
|
|
52
78
|
// Enter
|
|
53
79
|
if (key === '\r' || key === '\n') {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
stdout.write('\x1b[J'); // clear to end
|
|
60
|
-
const final = choices.map((c, i) => {
|
|
61
|
-
if (i === selected) {
|
|
62
|
-
return ` ${chalk.cyan('\u276f')} ${chalk.cyan(c.label)}`;
|
|
63
|
-
}
|
|
64
|
-
return '';
|
|
65
|
-
}).filter(Boolean).join('\n');
|
|
80
|
+
// Move cursor to start of first choice line, then clear
|
|
81
|
+
const upCount = totalPhysicalLines(prevLines) - 1;
|
|
82
|
+
if (upCount > 0) stdout.write(`\x1b[${upCount}A`);
|
|
83
|
+
stdout.write('\r\x1b[J'); // col 0, clear to end
|
|
84
|
+
const final = ` ${chalk.cyan('\u276f')} ${chalk.cyan(choices[selected].label)}`;
|
|
66
85
|
stdout.write(final + '\n');
|
|
86
|
+
cleanup();
|
|
67
87
|
resolve(choices[selected].value);
|
|
68
88
|
return;
|
|
69
89
|
}
|
|
@@ -79,10 +99,12 @@ export function selectPrompt(question, choices, { defaultIndex = 0 } = {}) {
|
|
|
79
99
|
return; // ignore other keys
|
|
80
100
|
}
|
|
81
101
|
|
|
82
|
-
// Redraw: move cursor
|
|
83
|
-
|
|
84
|
-
stdout.write(
|
|
85
|
-
stdout.write(
|
|
102
|
+
// Redraw: move cursor to start of first choice line, clear, rewrite
|
|
103
|
+
const upCount = totalPhysicalLines(prevLines) - 1;
|
|
104
|
+
if (upCount > 0) stdout.write(`\x1b[${upCount}A`);
|
|
105
|
+
stdout.write('\r\x1b[J');
|
|
106
|
+
prevLines = render();
|
|
107
|
+
stdout.write(prevLines.join('\n'));
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
stdin.on('data', onData);
|
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
|
}
|