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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentskillsdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Install agent skills from agentskills.dk",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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: 2 }).start();
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/ in current directory)', value: 'project' },
89
- { label: 'Global', hint: '(~/.claude/skills/ for all projects)', value: 'global' },
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: 2 }).start();
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
- import { createInterface } from 'node:readline';
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
- * Enhanced select prompt with arrow indicator and hints.
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
- const rl = createInterface({ input: process.stdin, output: process.stdout });
14
-
15
- console.log(`\n ${question}\n`);
16
- choices.forEach((c, i) => {
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
- const defaultDisplay = defaultIndex + 1;
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
- return new Promise((resolve) => {
27
- rl.question(`\n Choice ${chalk.dim(`[${defaultDisplay}]`)}: `, (answer) => {
28
- rl.close();
29
- const trimmed = answer.trim();
30
- if (trimmed === '') {
31
- resolve(choices[defaultIndex].value);
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
- const idx = parseInt(trimmed, 10) - 1;
35
- if (idx >= 0 && idx < choices.length) {
36
- resolve(choices[idx].value);
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
- resolve(choices[defaultIndex].value);
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(` \u250c${'─'.repeat(inner)}\u2510`);
24
- const bottom = colorize(` \u2514${'─'.repeat(inner)}\u2518`);
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 ` ${colorize('\u2502')}${pad}${l}${rightPad}${pad}${colorize('\u2502')}`;
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(' \u2713') + ` ${text}`);
75
+ console.log(chalk.green(' \u2713') + ` ${text}`);
76
76
  }
77
77
 
78
78
  export function error(text) {
79
- console.error(chalk.red(` ${text}`));
79
+ console.error(chalk.red(` ${text}`));
80
80
  }