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 +7 -0
- package/package.json +1 -1
- package/scan-skills.js +50 -5
- package/skill-guide.js +22 -0
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
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
|
-
|
|
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 = {
|
|
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');
|