skill-guide 0.2.0 → 0.2.1

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/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to skill-guide will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.1] - 2026-05-17
9
+
10
+ ### Fixed
11
+ - `--skill <name>` now resolves shorthand skill names such as `tdd` to the best matching installed skill, preferring name prefixes like `tdd-workflow`.
12
+ - Missing skill lookups in HTML mode now print a terminal error and exit without generating an empty HTML guide.
13
+ - Missing skill JSON output now preserves scan counts, source counts, and close-match suggestions.
14
+
8
15
  ## [0.2.0] - 2026-05-15
9
16
 
10
17
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skill-guide",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Scan 6+ skill directories across Claude Code, Codex, and cc-switch, then generate beautiful HTML slide presentations.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
package/scan-skills.js CHANGED
@@ -492,6 +492,47 @@ function cleanSkill(skill, includeFull) {
492
492
  return base;
493
493
  }
494
494
 
495
+ function normalizeSkillName(name) {
496
+ return String(name || '').replace(/^[^:]+:/, '').toLowerCase();
497
+ }
498
+
499
+ function findSkill(skills, requestedName) {
500
+ const requested = String(requestedName || '').toLowerCase();
501
+ const bareRequested = normalizeSkillName(requestedName);
502
+
503
+ const exact = skills.find((skill) => {
504
+ const name = skill.name.toLowerCase();
505
+ const bareName = normalizeSkillName(skill.name);
506
+ return name === requested || bareName === bareRequested;
507
+ });
508
+ if (exact) return exact;
509
+
510
+ const prefixMatches = skills
511
+ .filter((skill) => normalizeSkillName(skill.name).startsWith(bareRequested))
512
+ .sort((a, b) => normalizeSkillName(a.name).length - normalizeSkillName(b.name).length
513
+ || a.name.localeCompare(b.name));
514
+ if (prefixMatches.length > 0) return prefixMatches[0];
515
+
516
+ const containsMatches = skills
517
+ .filter((skill) => normalizeSkillName(skill.name).includes(bareRequested))
518
+ .sort((a, b) => normalizeSkillName(a.name).length - normalizeSkillName(b.name).length
519
+ || a.name.localeCompare(b.name));
520
+ return containsMatches[0] || null;
521
+ }
522
+
523
+ function suggestSkills(skills, requestedName) {
524
+ const requested = normalizeSkillName(requestedName);
525
+ return skills
526
+ .filter((skill) => {
527
+ const name = normalizeSkillName(skill.name);
528
+ const description = String(skill.description || '').toLowerCase();
529
+ const triggers = (skill.triggers || []).join(' ').toLowerCase();
530
+ return name.includes(requested) || description.includes(requested) || triggers.includes(requested);
531
+ })
532
+ .slice(0, 8)
533
+ .map((skill) => cleanSkill(skill, false));
534
+ }
535
+
495
536
  // ---------------------------------------------------------------------------
496
537
  // Main
497
538
  // ---------------------------------------------------------------------------
@@ -536,12 +577,16 @@ function main() {
536
577
  skills: skills.map(s => cleanSkill(s, false)),
537
578
  };
538
579
  } else if (mode === 'skill') {
539
- // Strip plugin prefix (e.g. "everything-claude-code:tdd-workflow" → "tdd-workflow")
540
- const bareName = skillName.replace(/^[^:]+:/, '');
541
- const found = skills.find(s => s.name.toLowerCase() === bareName.toLowerCase()
542
- || s.name.toLowerCase() === skillName.toLowerCase());
580
+ const found = findSkill(skills, skillName);
543
581
  if (!found) {
544
- output = { error: `Skill "${skillName}" not found`, skills: [] };
582
+ output = {
583
+ error: `Skill "${skillName}" not found`,
584
+ scanDate: scanResult.scanDate,
585
+ totalCount: skills.length,
586
+ sources: sourceCounts,
587
+ skills: [],
588
+ suggestions: suggestSkills(skills, skillName),
589
+ };
545
590
  } else {
546
591
  output = {
547
592
  scanDate: scanResult.scanDate,
package/skill-guide.js CHANGED
@@ -738,6 +738,23 @@ function printDoctor(data) {
738
738
  return lines.join('\n');
739
739
  }
740
740
 
741
+ function formatScannerError(data) {
742
+ const lines = [
743
+ data.error || 'Skill not found',
744
+ `Scanned ${data.totalCount || 0} skills.`,
745
+ ];
746
+ if ((data.suggestions || []).length > 0) {
747
+ lines.push('Possible matches:');
748
+ for (const skill of data.suggestions) {
749
+ const sources = (skill.sources || []).join(', ');
750
+ lines.push(` - ${skill.name}${sources ? ` (${sources})` : ''}`);
751
+ }
752
+ } else {
753
+ lines.push('No close matches found. Try --search <query> to search descriptions and triggers.');
754
+ }
755
+ return lines.join('\n');
756
+ }
757
+
741
758
  function main() {
742
759
  const mode = parseMode();
743
760
  if (mode.mode === 'help') {
@@ -771,6 +788,11 @@ function main() {
771
788
  return;
772
789
  }
773
790
 
791
+ if (data.error) {
792
+ process.stderr.write(`${formatScannerError(data)}\n`);
793
+ process.exit(1);
794
+ }
795
+
774
796
  const output = path.resolve(getArgValue('--output') || defaultOutputPath(mode));
775
797
  fs.mkdirSync(path.dirname(output), { recursive: true });
776
798
  fs.writeFileSync(output, renderHtml(data, mode), 'utf8');