agentskillsdk 0.1.2 → 0.1.3

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.2",
3
+ "version": "0.1.3",
4
4
  "description": "Install agent skills from agentskills.dk",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,6 +13,7 @@
13
13
  "dependencies": {
14
14
  "commander": "^13.0.0",
15
15
  "chalk": "^5.4.0",
16
+ "ora": "^8.1.0",
16
17
  "tar-stream": "^3.1.0"
17
18
  },
18
19
  "engines": {
@@ -1,19 +1,30 @@
1
1
  import chalk from 'chalk';
2
- import { join } from 'node:path';
2
+ import ora from 'ora';
3
3
  import { fetchSkill, fetchSkills } from '../lib/api.js';
4
4
  import { detectAgents } from '../lib/detect-agent.js';
5
5
  import { downloadSkill } from '../lib/download.js';
6
6
  import { parseGithubSource } from '../lib/parse-source.js';
7
+ import { selectPrompt } from '../lib/prompt.js';
8
+ import { printBanner, printCompletionSummary, step, error } from '../lib/ui.js';
7
9
 
8
10
  export async function addCommand(skillName, options) {
11
+ printBanner();
12
+
9
13
  const cwd = process.cwd();
10
14
  const githubSource = parseGithubSource(skillName);
15
+ const isGithub = !!githubSource;
16
+
17
+ // --- resolve scope flags ---
18
+ if (options.global && options.project) {
19
+ error('Cannot use both --global and --project. Pick one.');
20
+ process.exit(1);
21
+ }
11
22
 
23
+ // --- resolve skill ---
12
24
  let skill;
13
25
  let installName;
14
26
 
15
- if (githubSource) {
16
- // GitHub owner/repo source
27
+ if (isGithub) {
17
28
  const skillPath = options.skill || '';
18
29
  installName = skillPath ? skillPath.split('/').pop() : githubSource.repo;
19
30
  skill = {
@@ -23,19 +34,22 @@ export async function addCommand(skillName, options) {
23
34
  githubPath: skillPath,
24
35
  defaultBranch: 'main',
25
36
  };
26
- console.log(chalk.green(' \u2713') + ` GitHub source: ${chalk.bold(`${githubSource.owner}/${githubSource.repo}`)}` +
37
+ step(`GitHub source: ${chalk.bold(`${githubSource.owner}/${githubSource.repo}`)}` +
27
38
  (options.skill ? ` (skill: ${chalk.bold(options.skill)})` : ''));
28
39
  } else {
29
- // Registry source
30
40
  installName = skillName;
41
+ const spinner = ora({ text: `Looking up ${chalk.bold(skillName)}...`, indent: 2 }).start();
31
42
  try {
32
43
  skill = await fetchSkill(skillName);
33
44
  } catch (err) {
34
- console.error(chalk.red(`\n ${err.message}\n`));
45
+ spinner.fail('Registry lookup failed');
46
+ error(err.message);
35
47
  process.exit(1);
36
48
  }
37
49
 
38
50
  if (!skill) {
51
+ spinner.fail(`Skill "${skillName}" not found`);
52
+
39
53
  let suggestions = [];
40
54
  try {
41
55
  const allSkills = await fetchSkills();
@@ -43,44 +57,58 @@ export async function addCommand(skillName, options) {
43
57
  .filter(s => s.name.includes(skillName) || skillName.includes(s.name))
44
58
  .slice(0, 3);
45
59
  } catch {
46
- // If we can't fetch suggestions, just show the not-found message
60
+ // ignore
47
61
  }
48
62
 
49
- console.error(chalk.red(`\n Skill "${skillName}" not found.\n`));
50
63
  if (suggestions.length > 0) {
51
- console.error(' Did you mean:');
64
+ console.error('\n Did you mean:');
52
65
  suggestions.forEach(s => console.error(` - ${chalk.bold(s.name)}`));
53
- console.error('');
54
66
  }
55
- console.error(` Browse all skills: ${chalk.underline('https://agentskills.dk/skills')}\n`);
67
+ console.error(`\n Browse all skills: ${chalk.underline('https://agentskills.dk/skills')}\n`);
56
68
  process.exit(1);
57
69
  }
58
70
 
59
- console.log(chalk.green(' \u2713') + ` Found skill: ${chalk.bold(skill.name)}`);
71
+ spinner.stop();
72
+ step(`Found skill: ${chalk.bold(skill.name)}`);
60
73
  }
61
74
 
62
- // 2. Detect agent
75
+ // --- detect agent ---
63
76
  const agents = await detectAgents(cwd);
64
- console.log(chalk.green(' \u2713') + ` Detected agent: ${chalk.bold(agents[0].name)}`);
77
+ const agent = agents[0];
78
+ step(`Detected agent: ${chalk.bold(agent.name)}`);
65
79
 
66
- // 3. Download + install for each selected agent
67
- for (const agent of agents) {
68
- const destDir = join(cwd, agent.path(installName));
69
- try {
70
- await downloadSkill(skill, destDir);
71
- } catch (err) {
72
- console.error(chalk.red(`\n Failed to download skill files. Try again or report at agentskills.dk/support`));
73
- console.error(chalk.red(` ${err.message}\n`));
74
- process.exit(1);
75
- }
76
- console.log(chalk.green(' \u2713') + ` Installed to ${agent.path(installName)}`);
80
+ // --- resolve scope ---
81
+ let scope;
82
+ if (options.global) {
83
+ scope = 'global';
84
+ } else if (options.project) {
85
+ scope = 'project';
86
+ } else {
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' },
90
+ ]);
77
91
  }
78
92
 
79
- // 4. Done
80
- console.log(`\n Skill ready. Start a new ${agents[0].name} session to use it.`);
81
- if (!githubSource) {
82
- console.log(` Learn more: ${chalk.underline(`https://agentskills.dk/skills/${skill.namespace}`)}\n`);
83
- } else {
84
- console.log('');
93
+ // --- download ---
94
+ const destDir = agent.path(installName, { cwd, scope });
95
+ const spinner = ora({ text: 'Downloading skill files...', indent: 2 }).start();
96
+ try {
97
+ await downloadSkill(skill, destDir);
98
+ } catch (err) {
99
+ spinner.fail('Download failed');
100
+ error(err.message);
101
+ process.exit(1);
85
102
  }
103
+ spinner.succeed('Downloaded skill files');
104
+
105
+ // --- completion summary ---
106
+ printCompletionSummary({
107
+ skillName: installName,
108
+ scope,
109
+ installPath: agent.displayPath(installName, scope),
110
+ agentName: agent.name,
111
+ isGithub,
112
+ namespace: skill.namespace,
113
+ });
86
114
  }
@@ -1,7 +1,9 @@
1
1
  import chalk from 'chalk';
2
2
  import { fetchSkills } from '../lib/api.js';
3
+ import { printBanner } from '../lib/ui.js';
3
4
 
4
5
  export async function listCommand() {
6
+ printBanner();
5
7
  let skills;
6
8
  try {
7
9
  skills = await fetchSkills();
package/src/index.js CHANGED
@@ -1,19 +1,25 @@
1
1
  import { Command } from 'commander';
2
+ import { createRequire } from 'node:module';
2
3
  import { addCommand } from './commands/add.js';
3
4
  import { listCommand } from './commands/list.js';
4
5
 
6
+ const require = createRequire(import.meta.url);
7
+ const pkg = require('../package.json');
8
+
5
9
  const program = new Command();
6
10
 
7
11
  program
8
12
  .name('agentskillsdk')
9
13
  .description('Install agent skills from agentskills.dk')
10
- .version('0.1.0');
14
+ .version(pkg.version);
11
15
 
12
16
  program
13
17
  .command('add')
14
18
  .description('Install a skill into your project')
15
19
  .argument('<skill-name>', 'name of the skill to install (or owner/repo for GitHub)')
16
20
  .option('--skill <path>', 'path within repo, e.g. skills/twitter')
21
+ .option('-g, --global', 'install globally to ~/.claude/skills/')
22
+ .option('-p, --project', 'install to project .claude/skills/ (default)')
17
23
  .action(addCommand);
18
24
 
19
25
  program
@@ -1,34 +1,35 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { createInterface } from 'node:readline';
3
+ import { homedir } from 'node:os';
4
+ import { selectPrompt } from './prompt.js';
4
5
 
5
6
  const AGENTS = [
6
- { name: 'Claude Code', folder: '.claude', path: (skill) => `.claude/skills/${skill}/` },
7
+ {
8
+ name: 'Claude Code',
9
+ folder: '.claude',
10
+ path: (skill, { cwd, scope }) => {
11
+ if (scope === 'global') return join(homedir(), '.claude', 'skills', skill);
12
+ return join(cwd, '.claude', 'skills', skill);
13
+ },
14
+ displayPath: (skill, scope) => {
15
+ if (scope === 'global') return `~/.claude/skills/${skill}/`;
16
+ return `.claude/skills/${skill}/`;
17
+ },
18
+ },
7
19
  ];
8
20
 
9
21
  export async function detectAgents(cwd) {
10
22
  const found = AGENTS.filter(agent => existsSync(join(cwd, agent.folder)));
11
23
 
12
24
  if (found.length === 1) return [found[0]];
13
- if (found.length > 1) return await promptUserChoice(found);
14
- return await promptUserChoice(AGENTS);
15
- }
16
-
17
- async function promptUserChoice(agents) {
18
- const rl = createInterface({ input: process.stdin, output: process.stdout });
19
-
20
- console.log('\nWhich agent do you use?\n');
21
- agents.forEach((a, i) => console.log(` ${i + 1}) ${a.name}`));
25
+ if (found.length > 1) {
26
+ const choices = found.map(a => ({ label: a.name, value: a }));
27
+ const selected = await selectPrompt('Which agent do you use?', choices);
28
+ return [selected];
29
+ }
22
30
 
23
- return new Promise((resolve) => {
24
- rl.question('\nChoice: ', (answer) => {
25
- rl.close();
26
- const idx = parseInt(answer, 10) - 1;
27
- if (idx >= 0 && idx < agents.length) {
28
- resolve([agents[idx]]);
29
- } else {
30
- resolve([agents[0]]);
31
- }
32
- });
33
- });
31
+ // None found ask user to pick
32
+ const choices = AGENTS.map(a => ({ label: a.name, value: a }));
33
+ const selected = await selectPrompt('Which agent do you use?', choices);
34
+ return [selected];
34
35
  }
@@ -0,0 +1,42 @@
1
+ import chalk from 'chalk';
2
+ import { createInterface } from 'node:readline';
3
+
4
+ /**
5
+ * Enhanced select prompt with arrow indicator and hints.
6
+ *
7
+ * @param {string} question
8
+ * @param {{ label: string, hint?: string, value: any }[]} choices
9
+ * @param {{ defaultIndex?: number }} opts
10
+ * @returns {Promise<any>} selected value
11
+ */
12
+ 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
+ });
23
+
24
+ const defaultDisplay = defaultIndex + 1;
25
+
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);
32
+ return;
33
+ }
34
+ const idx = parseInt(trimmed, 10) - 1;
35
+ if (idx >= 0 && idx < choices.length) {
36
+ resolve(choices[idx].value);
37
+ } else {
38
+ resolve(choices[defaultIndex].value);
39
+ }
40
+ });
41
+ });
42
+ }
package/src/lib/ui.js ADDED
@@ -0,0 +1,80 @@
1
+ import chalk from 'chalk';
2
+ import { createRequire } from 'node:module';
3
+
4
+ const require = createRequire(import.meta.url);
5
+ const pkg = require('../../package.json');
6
+
7
+ // --- strip ANSI for width calculation ---
8
+
9
+ const ANSI_RE = /\x1b\[[0-9;]*m/g;
10
+ function stripAnsi(str) {
11
+ return str.replace(ANSI_RE, '');
12
+ }
13
+
14
+ // --- box drawing ---
15
+
16
+ export function box(lines, { borderColor, padding = 2 } = {}) {
17
+ const maxWidth = Math.max(...lines.map(l => stripAnsi(l).length));
18
+ const pad = ' '.repeat(padding);
19
+ const inner = maxWidth + padding * 2;
20
+
21
+ const colorize = borderColor ? chalk[borderColor].bind(chalk) : (s) => s;
22
+
23
+ const top = colorize(` \u250c${'─'.repeat(inner)}\u2510`);
24
+ const bottom = colorize(` \u2514${'─'.repeat(inner)}\u2518`);
25
+ const rowLines = lines.map(l => {
26
+ const visible = stripAnsi(l).length;
27
+ const rightPad = ' '.repeat(maxWidth - visible);
28
+ return ` ${colorize('\u2502')}${pad}${l}${rightPad}${pad}${colorize('\u2502')}`;
29
+ });
30
+
31
+ return [top, ...rowLines, bottom].join('\n');
32
+ }
33
+
34
+ // --- banner ---
35
+
36
+ export function printBanner() {
37
+ const lines = [
38
+ `${chalk.bold('agentskillsdk')} ${chalk.dim(`v${pkg.version}`)}`,
39
+ chalk.dim('Install skills for AI agents'),
40
+ ];
41
+ console.log('');
42
+ console.log(box(lines));
43
+ console.log('');
44
+ }
45
+
46
+ // --- completion summary ---
47
+
48
+ export function printCompletionSummary({ skillName, scope, installPath, agentName, isGithub, namespace }) {
49
+ const scopeLabel = scope === 'global'
50
+ ? 'Global (all projects)'
51
+ : 'Project (local)';
52
+
53
+ const lines = [
54
+ chalk.bold.green('Skill installed successfully!'),
55
+ '',
56
+ ` ${chalk.dim('Skill:')} ${skillName}`,
57
+ ` ${chalk.dim('Scope:')} ${scopeLabel}`,
58
+ ` ${chalk.dim('Path:')} ${installPath}`,
59
+ '',
60
+ `Start a new ${agentName} session to use it.`,
61
+ ];
62
+
63
+ if (!isGithub && namespace) {
64
+ lines.push(`Learn more: ${chalk.underline(`https://agentskills.dk/skills/${namespace}`)}`);
65
+ }
66
+
67
+ console.log('');
68
+ console.log(box(lines, { borderColor: 'green' }));
69
+ console.log('');
70
+ }
71
+
72
+ // --- step / error output ---
73
+
74
+ export function step(text) {
75
+ console.log(chalk.green(' \u2713') + ` ${text}`);
76
+ }
77
+
78
+ export function error(text) {
79
+ console.error(chalk.red(` ${text}`));
80
+ }