skillbin 0.1.0 → 0.1.2

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.
Files changed (2) hide show
  1. package/bin/skillbin.js +59 -18
  2. package/package.json +1 -1
package/bin/skillbin.js CHANGED
@@ -88,20 +88,24 @@ async function install(ref, opts) {
88
88
  throw new Error('Skill has no files');
89
89
  }
90
90
 
91
- let scope = opts.scope;
92
- if (getFormatConfig(format).hasScope && !scope) {
93
- if (process.stdin.isTTY) {
94
- console.error(`\nInstall "${skillName}" (${format}) to:`);
95
- console.error(' 1) Global (~/.claude/skills/)');
96
- console.error(' 2) Project (.claude/skills/ in current directory)\n');
97
- const choice = await promptUser('Choice (1 or 2, default 1): ');
98
- scope = choice === '2' ? 'project' : 'global';
99
- } else {
100
- scope = 'global';
91
+ let destDir;
92
+ if (opts.dir) {
93
+ destDir = path.resolve(opts.dir);
94
+ } else {
95
+ let scope = opts.scope;
96
+ if (getFormatConfig(format).hasScope && !scope) {
97
+ if (process.stdin.isTTY) {
98
+ console.error(`\nInstall "${skillName}" (${format}) to:`);
99
+ console.error(' 1) Global (~/.claude/skills/)');
100
+ console.error(' 2) Project (.claude/skills/ in current directory)\n');
101
+ const choice = await promptUser('Choice (1 or 2, default 1): ');
102
+ scope = choice === '2' ? 'project' : 'global';
103
+ } else {
104
+ scope = 'global';
105
+ }
101
106
  }
107
+ destDir = resolveInstallDir(skillName, scope, format);
102
108
  }
103
-
104
- const destDir = resolveInstallDir(skillName, scope, format);
105
109
  fs.mkdirSync(destDir, { recursive: true });
106
110
 
107
111
  for (const file of files) {
@@ -188,10 +192,44 @@ async function upload(filePath, opts) {
188
192
  if (fs.existsSync(cwdSkill)) {
189
193
  skills.push({ name: path.basename(process.cwd()), dir: process.cwd(), format: 'claude' });
190
194
  } else {
191
- console.error('Usage: skillbin upload [path] [--all] [--token=xxx]');
192
- console.error(' path: a skill file or directory containing one');
193
- console.error(' --all: upload all discovered skills (all formats)');
194
- process.exit(1);
195
+ const discovered = discoverSkills();
196
+ if (discovered.length === 0) {
197
+ console.error('No skills found. Supported locations:');
198
+ console.error(' Claude: .claude/skills/*/SKILL.md');
199
+ console.error(' Cursor: .cursor/rules/*.mdc');
200
+ console.error(' Windsurf: .windsurf/rules/*.md');
201
+ console.error(' Copilot: .github/instructions/*.instructions.md');
202
+ console.error(' Codex: AGENTS.md');
203
+ console.error(' Cline: .clinerules/*.md');
204
+ process.exit(1);
205
+ }
206
+
207
+ console.error(`\nFound ${discovered.length} skill(s):\n`);
208
+ discovered.forEach((s, i) => {
209
+ console.error(` ${i + 1}) ${s.name} [${s.format}]`);
210
+ });
211
+ console.error('');
212
+
213
+ if (!process.stdin.isTTY) {
214
+ console.error('Error: no terminal available. Pass a path or use --all.');
215
+ process.exit(1);
216
+ }
217
+
218
+ const selection = await promptUser(`Upload which? (1-${discovered.length}, space-separated, or "all"): `);
219
+ let indices;
220
+ if (selection.trim() === 'all') {
221
+ indices = discovered.map((_, i) => i);
222
+ } else {
223
+ indices = selection.trim().split(/\s+/).map(n => parseInt(n, 10) - 1)
224
+ .filter(i => i >= 0 && i < discovered.length);
225
+ }
226
+
227
+ if (indices.length === 0) {
228
+ console.error('No skills selected.');
229
+ process.exit(0);
230
+ }
231
+
232
+ skills = indices.map(i => discovered[i]);
195
233
  }
196
234
  }
197
235
 
@@ -367,6 +405,7 @@ Usage:
367
405
  Options:
368
406
  --global Install to ~/.claude/skills/ (default for claude format)
369
407
  --project Install to .claude/skills/ in current directory
408
+ --dir=<path> Install to a custom directory (overrides --global/--project)
370
409
  --token=<token> API token for auth
371
410
  --all Upload all local skills (all formats)
372
411
  --help, -h Show this help
@@ -382,6 +421,7 @@ Supported formats:
382
421
  Examples:
383
422
  npx skillbin https://skillb.in/s/abc123
384
423
  npx skillbin install abc123 --project
424
+ npx skillbin install abc123 --dir=./my-skills/
385
425
  npx skillbin upload ./my-skill/
386
426
  npx skillbin upload .cursor/rules/my-rule.mdc
387
427
  npx skillbin upload --all --token=abc123
@@ -401,6 +441,7 @@ async function main() {
401
441
 
402
442
  const token = flags.token || process.env.SKILLBIN_TOKEN || '';
403
443
  const scope = flags.project ? 'project' : flags.global ? 'global' : '';
444
+ const dir = flags.dir || '';
404
445
 
405
446
  const command = args[0];
406
447
 
@@ -415,7 +456,7 @@ async function main() {
415
456
  console.error('Usage: skillbin install <url-or-id>');
416
457
  process.exit(1);
417
458
  }
418
- await install(args[1], { token, scope });
459
+ await install(args[1], { token, scope, dir });
419
460
  } else if (command === 'upload') {
420
461
  await upload(args[1], { token, all: flags.all });
421
462
  } else if (command === 'info') {
@@ -428,7 +469,7 @@ async function main() {
428
469
  } else if (command === 'help') {
429
470
  printHelp();
430
471
  } else {
431
- await install(command, { token, scope });
472
+ await install(command, { token, scope, dir });
432
473
  }
433
474
  } catch (err) {
434
475
  console.error(`Error: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillbin",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Fetch, install, and upload agent skills from skillb.in",
5
5
  "author": "Wallfacer Technologies",
6
6
  "bin": {