@skillkit/cli 1.9.0 → 1.11.0
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/dist/index.d.ts +39 -1
- package/dist/index.js +834 -22
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -3391,7 +3391,10 @@ var RecommendCommand = class extends Command15 {
|
|
|
3391
3391
|
["Show detailed reasons", "$0 recommend --verbose"],
|
|
3392
3392
|
["Update skill index", "$0 recommend --update"],
|
|
3393
3393
|
["Search for skills by task", '$0 recommend --task "authentication"'],
|
|
3394
|
-
["Search for skills (alias)", '$0 recommend --search "testing"']
|
|
3394
|
+
["Search for skills (alias)", '$0 recommend --search "testing"'],
|
|
3395
|
+
["Hybrid search (vector + keyword)", '$0 recommend --search "auth" --hybrid'],
|
|
3396
|
+
["Hybrid search with query expansion", '$0 recommend --search "auth" --hybrid --expand'],
|
|
3397
|
+
["Build hybrid search index", "$0 recommend --build-index"]
|
|
3395
3398
|
]
|
|
3396
3399
|
});
|
|
3397
3400
|
// Limit number of results
|
|
@@ -3450,11 +3453,33 @@ var RecommendCommand = class extends Command15 {
|
|
|
3450
3453
|
showPath = Option14.Boolean("--show-path", false, {
|
|
3451
3454
|
description: "Show category path for each recommendation"
|
|
3452
3455
|
});
|
|
3456
|
+
// Hybrid search mode
|
|
3457
|
+
hybrid = Option14.Boolean("--hybrid,-H", false, {
|
|
3458
|
+
description: "Use hybrid search (vector + keyword)"
|
|
3459
|
+
});
|
|
3460
|
+
// Query expansion
|
|
3461
|
+
expand = Option14.Boolean("--expand,-x", false, {
|
|
3462
|
+
description: "Enable query expansion (requires --hybrid)"
|
|
3463
|
+
});
|
|
3464
|
+
// Reranking
|
|
3465
|
+
rerank = Option14.Boolean("--rerank", false, {
|
|
3466
|
+
description: "Enable LLM reranking (requires --hybrid)"
|
|
3467
|
+
});
|
|
3468
|
+
// Build index
|
|
3469
|
+
buildIndex = Option14.Boolean("--build-index", false, {
|
|
3470
|
+
description: "Build/rebuild the hybrid search embedding index"
|
|
3471
|
+
});
|
|
3453
3472
|
async execute() {
|
|
3454
3473
|
const targetPath = resolve4(this.projectPath || process.cwd());
|
|
3455
3474
|
if (this.update) {
|
|
3456
3475
|
return await this.updateIndex();
|
|
3457
3476
|
}
|
|
3477
|
+
if (this.buildIndex) {
|
|
3478
|
+
return await this.buildHybridIndex();
|
|
3479
|
+
}
|
|
3480
|
+
if ((this.expand || this.rerank) && !this.hybrid) {
|
|
3481
|
+
warn("--expand and --rerank require --hybrid flag. These options will be ignored.");
|
|
3482
|
+
}
|
|
3458
3483
|
if (!this.quiet && !this.json) {
|
|
3459
3484
|
header("Skill Recommendations");
|
|
3460
3485
|
}
|
|
@@ -3478,6 +3503,9 @@ var RecommendCommand = class extends Command15 {
|
|
|
3478
3503
|
engine.loadIndex(index);
|
|
3479
3504
|
const searchQuery = this.search || this.task;
|
|
3480
3505
|
if (searchQuery) {
|
|
3506
|
+
if (this.hybrid) {
|
|
3507
|
+
return await this.handleHybridSearch(engine, searchQuery);
|
|
3508
|
+
}
|
|
3481
3509
|
return this.handleSearch(engine, searchQuery);
|
|
3482
3510
|
}
|
|
3483
3511
|
const result = engine.recommend(profile, {
|
|
@@ -3691,6 +3719,110 @@ var RecommendCommand = class extends Command15 {
|
|
|
3691
3719
|
console.log(colors.muted("Install with: skillkit install <source>"));
|
|
3692
3720
|
console.log(colors.muted("More details: skillkit recommend --explain --verbose"));
|
|
3693
3721
|
}
|
|
3722
|
+
async handleHybridSearch(engine, query) {
|
|
3723
|
+
if (!this.quiet && !this.json) {
|
|
3724
|
+
header(`Hybrid Search: "${query}"`);
|
|
3725
|
+
}
|
|
3726
|
+
const s = !this.quiet && !this.json ? spinner2() : null;
|
|
3727
|
+
s?.start("Initializing hybrid search...");
|
|
3728
|
+
try {
|
|
3729
|
+
await engine.initHybridSearch();
|
|
3730
|
+
s?.message("Searching...");
|
|
3731
|
+
const results = await engine.hybridSearch({
|
|
3732
|
+
query,
|
|
3733
|
+
limit: this.limit ? parseInt(this.limit, 10) : 10,
|
|
3734
|
+
hybrid: true,
|
|
3735
|
+
enableExpansion: this.expand,
|
|
3736
|
+
enableReranking: this.rerank,
|
|
3737
|
+
filters: {
|
|
3738
|
+
minScore: this.minScore ? parseInt(this.minScore, 10) : void 0
|
|
3739
|
+
}
|
|
3740
|
+
});
|
|
3741
|
+
s?.stop(`Found ${results.length} results`);
|
|
3742
|
+
if (this.json) {
|
|
3743
|
+
console.log(JSON.stringify(results, null, 2));
|
|
3744
|
+
return 0;
|
|
3745
|
+
}
|
|
3746
|
+
if (results.length === 0) {
|
|
3747
|
+
warn(`No skills found matching "${query}"`);
|
|
3748
|
+
return 0;
|
|
3749
|
+
}
|
|
3750
|
+
console.log("");
|
|
3751
|
+
console.log(colors.bold(`Hybrid search results for "${query}" (${results.length} found):`));
|
|
3752
|
+
if (this.expand && results[0]?.expandedTerms?.length) {
|
|
3753
|
+
console.log(colors.muted(` Expanded: ${results[0].expandedTerms.join(", ")}`));
|
|
3754
|
+
}
|
|
3755
|
+
console.log("");
|
|
3756
|
+
for (const result of results) {
|
|
3757
|
+
let relevanceColor;
|
|
3758
|
+
if (result.relevance >= 70) {
|
|
3759
|
+
relevanceColor = colors.success;
|
|
3760
|
+
} else if (result.relevance >= 50) {
|
|
3761
|
+
relevanceColor = colors.warning;
|
|
3762
|
+
} else {
|
|
3763
|
+
relevanceColor = colors.muted;
|
|
3764
|
+
}
|
|
3765
|
+
const relevanceBar = progressBar(result.relevance, 100, 10);
|
|
3766
|
+
console.log(` ${relevanceColor(`${result.relevance}%`)} ${colors.dim(relevanceBar)} ${colors.bold(result.skill.name)}`);
|
|
3767
|
+
if (result.skill.description) {
|
|
3768
|
+
console.log(` ${colors.muted(truncate3(result.skill.description, 70))}`);
|
|
3769
|
+
}
|
|
3770
|
+
if (this.verbose) {
|
|
3771
|
+
const scores = [];
|
|
3772
|
+
if (typeof result.vectorSimilarity === "number") {
|
|
3773
|
+
scores.push(`vector: ${(result.vectorSimilarity * 100).toFixed(0)}%`);
|
|
3774
|
+
}
|
|
3775
|
+
if (typeof result.keywordScore === "number") {
|
|
3776
|
+
scores.push(`keyword: ${result.keywordScore.toFixed(0)}%`);
|
|
3777
|
+
}
|
|
3778
|
+
if (typeof result.rrfScore === "number") {
|
|
3779
|
+
scores.push(`rrf: ${result.rrfScore.toFixed(3)}`);
|
|
3780
|
+
}
|
|
3781
|
+
if (scores.length > 0) {
|
|
3782
|
+
console.log(` ${colors.dim("Scores:")} ${scores.join(" | ")}`);
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
if (result.matchedTerms.length > 0) {
|
|
3786
|
+
console.log(` ${colors.dim("Matched:")} ${result.matchedTerms.join(", ")}`);
|
|
3787
|
+
}
|
|
3788
|
+
console.log("");
|
|
3789
|
+
}
|
|
3790
|
+
return 0;
|
|
3791
|
+
} catch (err) {
|
|
3792
|
+
s?.stop(colors.error("Hybrid search failed"));
|
|
3793
|
+
console.log(colors.muted(err instanceof Error ? err.message : String(err)));
|
|
3794
|
+
console.log(colors.muted("Falling back to standard search..."));
|
|
3795
|
+
return this.handleSearch(engine, query);
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
async buildHybridIndex() {
|
|
3799
|
+
if (!this.quiet) {
|
|
3800
|
+
header("Build Hybrid Search Index");
|
|
3801
|
+
}
|
|
3802
|
+
const index = this.loadIndex();
|
|
3803
|
+
if (!index || index.skills.length === 0) {
|
|
3804
|
+
warn("No skill index found. Run --update first.");
|
|
3805
|
+
return 1;
|
|
3806
|
+
}
|
|
3807
|
+
const s = spinner2();
|
|
3808
|
+
s.start("Initializing...");
|
|
3809
|
+
try {
|
|
3810
|
+
const engine = new RecommendationEngine();
|
|
3811
|
+
engine.loadIndex(index);
|
|
3812
|
+
await engine.buildHybridIndex((progress) => {
|
|
3813
|
+
const percentage = Math.round(progress.current / progress.total * 100);
|
|
3814
|
+
s.message(`${progress.phase}: ${progress.message || ""} (${percentage}%)`);
|
|
3815
|
+
});
|
|
3816
|
+
s.stop(colors.success(`${symbols.success} Built hybrid index for ${index.skills.length} skills`));
|
|
3817
|
+
console.log(colors.muted(" Index stored in: ~/.skillkit/search.db"));
|
|
3818
|
+
console.log(colors.muted(" Use --hybrid flag for vector+keyword search\n"));
|
|
3819
|
+
return 0;
|
|
3820
|
+
} catch (err) {
|
|
3821
|
+
s.stop(colors.error("Failed to build hybrid index"));
|
|
3822
|
+
console.log(colors.muted(err instanceof Error ? err.message : String(err)));
|
|
3823
|
+
return 1;
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3694
3826
|
handleSearch(engine, query) {
|
|
3695
3827
|
if (!this.quiet && !this.json) {
|
|
3696
3828
|
header(`Search: "${query}"`);
|
|
@@ -5658,12 +5790,12 @@ ${learning.title}
|
|
|
5658
5790
|
}
|
|
5659
5791
|
const outputPath = this.output || `.skillkit/exports/${skillName}/SKILL.md`;
|
|
5660
5792
|
const { dirname: dirname7 } = await import("path");
|
|
5661
|
-
const { existsSync:
|
|
5793
|
+
const { existsSync: existsSync23, mkdirSync: mkdirSync11, writeFileSync: writeFileSync12 } = await import("fs");
|
|
5662
5794
|
const outputDir = dirname7(outputPath);
|
|
5663
|
-
if (!
|
|
5664
|
-
|
|
5795
|
+
if (!existsSync23(outputDir)) {
|
|
5796
|
+
mkdirSync11(outputDir, { recursive: true });
|
|
5665
5797
|
}
|
|
5666
|
-
|
|
5798
|
+
writeFileSync12(outputPath, skillContent, "utf-8");
|
|
5667
5799
|
console.log(chalk18.green(`\u2713 Exported learning as skill: ${skillName}`));
|
|
5668
5800
|
console.log(chalk18.gray(` Path: ${outputPath}`));
|
|
5669
5801
|
return 0;
|
|
@@ -5678,10 +5810,10 @@ ${learning.title}
|
|
|
5678
5810
|
console.log(chalk18.gray("Usage: skillkit memory import --input <path>"));
|
|
5679
5811
|
return 1;
|
|
5680
5812
|
}
|
|
5681
|
-
const { existsSync:
|
|
5682
|
-
const { resolve:
|
|
5683
|
-
const fullPath =
|
|
5684
|
-
if (!
|
|
5813
|
+
const { existsSync: existsSync23, readFileSync: readFileSync12 } = await import("fs");
|
|
5814
|
+
const { resolve: resolve23 } = await import("path");
|
|
5815
|
+
const fullPath = resolve23(inputPath);
|
|
5816
|
+
if (!existsSync23(fullPath)) {
|
|
5685
5817
|
console.error(chalk18.red(`File not found: ${fullPath}`));
|
|
5686
5818
|
return 1;
|
|
5687
5819
|
}
|
|
@@ -5691,7 +5823,7 @@ ${learning.title}
|
|
|
5691
5823
|
this.global ? void 0 : projectPath
|
|
5692
5824
|
);
|
|
5693
5825
|
try {
|
|
5694
|
-
const content =
|
|
5826
|
+
const content = readFileSync12(fullPath, "utf-8");
|
|
5695
5827
|
const { parse: parseYaml2 } = await import("yaml");
|
|
5696
5828
|
const data = parseYaml2(content);
|
|
5697
5829
|
if (!data.learnings || !Array.isArray(data.learnings)) {
|
|
@@ -6760,14 +6892,14 @@ var TeamCommand = class extends Command29 {
|
|
|
6760
6892
|
}
|
|
6761
6893
|
const projectPath = process.cwd();
|
|
6762
6894
|
const bundlePath = join12(projectPath, ".skillkit", "bundles", `${this.name}.json`);
|
|
6763
|
-
const { existsSync:
|
|
6764
|
-
if (!
|
|
6895
|
+
const { existsSync: existsSync23, readFileSync: readFileSync12, writeFileSync: writeFileSync12 } = await import("fs");
|
|
6896
|
+
if (!existsSync23(bundlePath)) {
|
|
6765
6897
|
this.context.stderr.write(chalk21.red(`Bundle "${this.name}" not found. Create it first with bundle-create.
|
|
6766
6898
|
`));
|
|
6767
6899
|
return 1;
|
|
6768
6900
|
}
|
|
6769
|
-
const content =
|
|
6770
|
-
|
|
6901
|
+
const content = readFileSync12(bundlePath, "utf-8");
|
|
6902
|
+
writeFileSync12(this.output, content, "utf-8");
|
|
6771
6903
|
this.context.stdout.write(chalk21.green(`\u2713 Bundle exported to: ${this.output}
|
|
6772
6904
|
`));
|
|
6773
6905
|
return 0;
|
|
@@ -6777,8 +6909,8 @@ var TeamCommand = class extends Command29 {
|
|
|
6777
6909
|
this.context.stderr.write(chalk21.red("--source <path> is required for bundle-import\n"));
|
|
6778
6910
|
return 1;
|
|
6779
6911
|
}
|
|
6780
|
-
const { existsSync:
|
|
6781
|
-
if (!
|
|
6912
|
+
const { existsSync: existsSync23 } = await import("fs");
|
|
6913
|
+
if (!existsSync23(this.source)) {
|
|
6782
6914
|
this.context.stderr.write(chalk21.red(`Bundle file not found: ${this.source}
|
|
6783
6915
|
`));
|
|
6784
6916
|
return 1;
|
|
@@ -6946,8 +7078,8 @@ var PluginCommand = class extends Command30 {
|
|
|
6946
7078
|
}
|
|
6947
7079
|
const projectPath = this.global ? join13(homedir2(), ".skillkit") : process.cwd();
|
|
6948
7080
|
const pluginsDir = this.global ? join13(projectPath, "plugins") : join13(projectPath, ".skillkit", "plugins");
|
|
6949
|
-
const
|
|
6950
|
-
if (
|
|
7081
|
+
const isLocalPath5 = this.source.startsWith("./") || this.source.startsWith("../") || this.source.startsWith("/") || this.source.startsWith("~") || this.source.includes("\\") || isAbsolute(this.source);
|
|
7082
|
+
if (isLocalPath5 && existsSync15(resolvedSource)) {
|
|
6951
7083
|
const targetDir = join13(pluginsDir, pluginName);
|
|
6952
7084
|
const resolvedTarget = resolve13(targetDir);
|
|
6953
7085
|
const resolvedPluginsDir = resolve13(pluginsDir);
|
|
@@ -10683,13 +10815,14 @@ var CheckCommand = class extends Command39 {
|
|
|
10683
10815
|
// src/commands/find.ts
|
|
10684
10816
|
init_onboarding();
|
|
10685
10817
|
import { Command as Command40, Option as Option39 } from "clipanion";
|
|
10818
|
+
import { FederatedSearch, GitHubSkillRegistry, RateLimitError } from "@skillkit/core";
|
|
10686
10819
|
|
|
10687
10820
|
// ../../marketplace/skills.json
|
|
10688
10821
|
var skills_default = {
|
|
10689
10822
|
$schema: "./schema.json",
|
|
10690
10823
|
version: 1,
|
|
10691
|
-
updatedAt: "2026-
|
|
10692
|
-
totalSkills:
|
|
10824
|
+
updatedAt: "2026-02-02",
|
|
10825
|
+
totalSkills: 15065,
|
|
10693
10826
|
curatedCollections: 31,
|
|
10694
10827
|
skills: [
|
|
10695
10828
|
{
|
|
@@ -43799,6 +43932,23 @@ var skills_default = {
|
|
|
43799
43932
|
"k8s"
|
|
43800
43933
|
]
|
|
43801
43934
|
},
|
|
43935
|
+
{
|
|
43936
|
+
id: "rohitg00/beautiful-mermaid/beautiful-mermaid",
|
|
43937
|
+
name: "Beautiful Mermaid",
|
|
43938
|
+
description: "Render Mermaid diagrams as beautiful SVGs or ASCII art. 5 diagram types, 15 themes, Shiki integration, zero DOM dependencies.",
|
|
43939
|
+
source: "rohitg00/beautiful-mermaid",
|
|
43940
|
+
tags: [
|
|
43941
|
+
"mermaid",
|
|
43942
|
+
"diagrams",
|
|
43943
|
+
"visualization",
|
|
43944
|
+
"flowchart",
|
|
43945
|
+
"sequence",
|
|
43946
|
+
"uml",
|
|
43947
|
+
"ascii",
|
|
43948
|
+
"svg",
|
|
43949
|
+
"themes"
|
|
43950
|
+
]
|
|
43951
|
+
},
|
|
43802
43952
|
{
|
|
43803
43953
|
id: "itsmostafa/aws-agent-skills/cognito",
|
|
43804
43954
|
name: "Cognito",
|
|
@@ -136344,6 +136494,9 @@ var FindCommand = class extends Command40 {
|
|
|
136344
136494
|
quiet = Option39.Boolean("--quiet,-q", false, {
|
|
136345
136495
|
description: "Minimal output (just list skills)"
|
|
136346
136496
|
});
|
|
136497
|
+
federated = Option39.Boolean("--federated,-f", false, {
|
|
136498
|
+
description: "Search external registries (GitHub SKILL.md files)"
|
|
136499
|
+
});
|
|
136347
136500
|
async execute() {
|
|
136348
136501
|
const s = spinner2();
|
|
136349
136502
|
const limit = parseInt(this.limit, 10) || 10;
|
|
@@ -136393,12 +136546,42 @@ var FindCommand = class extends Command40 {
|
|
|
136393
136546
|
results = allSkills.slice(0, limit);
|
|
136394
136547
|
}
|
|
136395
136548
|
}
|
|
136549
|
+
if (this.federated && this.query) {
|
|
136550
|
+
s.start("Searching external registries...");
|
|
136551
|
+
const fedSearch = new FederatedSearch();
|
|
136552
|
+
fedSearch.addRegistry(new GitHubSkillRegistry());
|
|
136553
|
+
try {
|
|
136554
|
+
const fedResult = await fedSearch.search(this.query, { limit: parseInt(this.limit, 10) || 10 });
|
|
136555
|
+
s.stop(`Found ${fedResult.total} external skill(s) from ${fedResult.registries.join(", ") || "none"}`);
|
|
136556
|
+
if (fedResult.skills.length > 0) {
|
|
136557
|
+
console.log("");
|
|
136558
|
+
console.log(colors.bold("External Skills (SKILL.md):"));
|
|
136559
|
+
for (const skill of fedResult.skills) {
|
|
136560
|
+
const stars = typeof skill.stars === "number" ? colors.muted(` \u2605${skill.stars}`) : "";
|
|
136561
|
+
const desc = skill.description ? colors.muted(` - ${skill.description.slice(0, 50)}${skill.description.length > 50 ? "..." : ""}`) : "";
|
|
136562
|
+
console.log(` ${colors.cyan(symbols.bullet)} ${colors.primary(skill.name)}${stars}${desc}`);
|
|
136563
|
+
if (!this.quiet) {
|
|
136564
|
+
console.log(` ${colors.muted(skill.source)}`);
|
|
136565
|
+
}
|
|
136566
|
+
}
|
|
136567
|
+
console.log("");
|
|
136568
|
+
}
|
|
136569
|
+
} catch (err) {
|
|
136570
|
+
if (err instanceof RateLimitError) {
|
|
136571
|
+
s.stop(colors.warning("Rate limited by GitHub API"));
|
|
136572
|
+
console.log(colors.muted("Set GITHUB_TOKEN env var or wait before retrying."));
|
|
136573
|
+
} else {
|
|
136574
|
+
s.stop(colors.warning("Federated search failed"));
|
|
136575
|
+
}
|
|
136576
|
+
}
|
|
136577
|
+
}
|
|
136396
136578
|
if (results.length === 0) {
|
|
136397
136579
|
console.log(colors.muted("No skills found matching your search"));
|
|
136398
136580
|
console.log("");
|
|
136399
136581
|
console.log(colors.muted("Try:"));
|
|
136400
|
-
console.log(colors.muted(" skillkit find --top
|
|
136401
|
-
console.log(colors.muted(" skillkit
|
|
136582
|
+
console.log(colors.muted(" skillkit find --top # Show featured skills"));
|
|
136583
|
+
console.log(colors.muted(" skillkit find -f <query> # Search external registries"));
|
|
136584
|
+
console.log(colors.muted(" skillkit ui # Browse in TUI"));
|
|
136402
136585
|
return 0;
|
|
136403
136586
|
}
|
|
136404
136587
|
console.log("");
|
|
@@ -139260,6 +139443,631 @@ var TreeCommand = class extends Command49 {
|
|
|
139260
139443
|
}
|
|
139261
139444
|
};
|
|
139262
139445
|
|
|
139446
|
+
// src/commands/quick.ts
|
|
139447
|
+
init_helpers();
|
|
139448
|
+
init_onboarding();
|
|
139449
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, cpSync as cpSync4, rmSync as rmSync5, symlinkSync as symlinkSync2 } from "fs";
|
|
139450
|
+
import { join as join20, resolve as resolve21, normalize } from "path";
|
|
139451
|
+
import { execFile as execFile2 } from "child_process";
|
|
139452
|
+
import { promisify as promisify2 } from "util";
|
|
139453
|
+
import { Command as Command50 } from "clipanion";
|
|
139454
|
+
import {
|
|
139455
|
+
ContextManager as ContextManager3,
|
|
139456
|
+
RecommendationEngine as RecommendationEngine2,
|
|
139457
|
+
loadIndex as loadIndexFromCache4,
|
|
139458
|
+
isIndexStale as isIndexStale2,
|
|
139459
|
+
buildSkillIndex as buildSkillIndex2,
|
|
139460
|
+
saveIndex as saveIndex2,
|
|
139461
|
+
KNOWN_SKILL_REPOS as KNOWN_SKILL_REPOS2,
|
|
139462
|
+
isPathInside as isPathInside2,
|
|
139463
|
+
isLocalPath as isLocalPath4,
|
|
139464
|
+
detectProvider as detectProvider4
|
|
139465
|
+
} from "@skillkit/core";
|
|
139466
|
+
import { getAdapter as getAdapter6, detectAgent as detectAgent5, getAllAdapters as getAllAdapters6 } from "@skillkit/agents";
|
|
139467
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
139468
|
+
function truncate6(str, maxLen) {
|
|
139469
|
+
if (str.length <= maxLen) return str;
|
|
139470
|
+
return str.slice(0, maxLen - 3) + "...";
|
|
139471
|
+
}
|
|
139472
|
+
var QuickCommand = class extends Command50 {
|
|
139473
|
+
static paths = [["quick"]];
|
|
139474
|
+
static usage = Command50.Usage({
|
|
139475
|
+
description: "Zero-friction skill setup: detect agents, analyze project, recommend and install",
|
|
139476
|
+
examples: [
|
|
139477
|
+
["Quick setup", "$0 quick"]
|
|
139478
|
+
]
|
|
139479
|
+
});
|
|
139480
|
+
async execute() {
|
|
139481
|
+
const s = spinner2();
|
|
139482
|
+
try {
|
|
139483
|
+
welcome();
|
|
139484
|
+
step(`${colors.bold("Quick Setup")} - zero-friction skill installation`);
|
|
139485
|
+
console.log("");
|
|
139486
|
+
s.start("Detecting installed AI agents...");
|
|
139487
|
+
const allAdapters = getAllAdapters6();
|
|
139488
|
+
const detectedAgents = [];
|
|
139489
|
+
for (const adapter of allAdapters) {
|
|
139490
|
+
try {
|
|
139491
|
+
const installDir = getInstallDir(false, adapter.type);
|
|
139492
|
+
const globalDir = getInstallDir(true, adapter.type);
|
|
139493
|
+
if (existsSync21(installDir) || existsSync21(globalDir) || existsSync21(adapter.configFile)) {
|
|
139494
|
+
detectedAgents.push(adapter.type);
|
|
139495
|
+
}
|
|
139496
|
+
} catch {
|
|
139497
|
+
}
|
|
139498
|
+
}
|
|
139499
|
+
if (detectedAgents.length === 0) {
|
|
139500
|
+
const fallback = await detectAgent5();
|
|
139501
|
+
detectedAgents.push(fallback);
|
|
139502
|
+
}
|
|
139503
|
+
s.stop(`Detected ${detectedAgents.length} agent${detectedAgents.length !== 1 ? "s" : ""}: ${detectedAgents.map(formatAgent).join(", ")}`);
|
|
139504
|
+
s.start("Analyzing project...");
|
|
139505
|
+
const targetPath = resolve21(process.cwd());
|
|
139506
|
+
const manager = new ContextManager3(targetPath);
|
|
139507
|
+
let context = manager.get();
|
|
139508
|
+
if (!context) {
|
|
139509
|
+
context = manager.init();
|
|
139510
|
+
}
|
|
139511
|
+
s.stop("Project analyzed");
|
|
139512
|
+
if (!context) {
|
|
139513
|
+
error("Failed to analyze project");
|
|
139514
|
+
return 1;
|
|
139515
|
+
}
|
|
139516
|
+
const profile = {
|
|
139517
|
+
name: context.project.name,
|
|
139518
|
+
type: context.project.type,
|
|
139519
|
+
stack: context.stack,
|
|
139520
|
+
patterns: context.patterns,
|
|
139521
|
+
installedSkills: context.skills?.installed || [],
|
|
139522
|
+
excludedSkills: context.skills?.excluded || []
|
|
139523
|
+
};
|
|
139524
|
+
const languages = profile.stack.languages.map((l) => `${l.name}${l.version ? ` ${l.version}` : ""}`);
|
|
139525
|
+
const frameworks = profile.stack.frameworks.map((f) => `${f.name}${f.version ? ` ${f.version}` : ""}`);
|
|
139526
|
+
showProjectSummary({
|
|
139527
|
+
name: profile.name,
|
|
139528
|
+
type: profile.type,
|
|
139529
|
+
languages,
|
|
139530
|
+
frameworks
|
|
139531
|
+
});
|
|
139532
|
+
let index = loadIndexFromCache4();
|
|
139533
|
+
if (!index || index.skills.length === 0) {
|
|
139534
|
+
s.start("Building skill index from known sources...");
|
|
139535
|
+
try {
|
|
139536
|
+
const result2 = await buildSkillIndex2(KNOWN_SKILL_REPOS2, (message) => {
|
|
139537
|
+
s.message(message);
|
|
139538
|
+
});
|
|
139539
|
+
index = result2.index;
|
|
139540
|
+
saveIndex2(index);
|
|
139541
|
+
s.stop(`Indexed ${index.skills.length} skills`);
|
|
139542
|
+
} catch {
|
|
139543
|
+
s.stop(colors.error("Failed to build index"));
|
|
139544
|
+
warn("Could not fetch skill index. Try running: skillkit recommend --update");
|
|
139545
|
+
return 1;
|
|
139546
|
+
}
|
|
139547
|
+
} else if (isIndexStale2(index)) {
|
|
139548
|
+
step(colors.muted('Skill index is stale. Run "skillkit recommend --update" to refresh.'));
|
|
139549
|
+
}
|
|
139550
|
+
const engine = new RecommendationEngine2();
|
|
139551
|
+
engine.loadIndex(index);
|
|
139552
|
+
const result = engine.recommend(profile, {
|
|
139553
|
+
limit: 5,
|
|
139554
|
+
minScore: 20,
|
|
139555
|
+
excludeInstalled: true,
|
|
139556
|
+
includeReasons: true
|
|
139557
|
+
});
|
|
139558
|
+
const recommendations = result.recommendations;
|
|
139559
|
+
if (recommendations.length === 0) {
|
|
139560
|
+
warn("No matching skills found for your project.");
|
|
139561
|
+
console.log(colors.muted("Try running: skillkit recommend --update"));
|
|
139562
|
+
return 0;
|
|
139563
|
+
}
|
|
139564
|
+
console.log("");
|
|
139565
|
+
console.log(colors.bold(`Top ${recommendations.length} Recommended Skills:`));
|
|
139566
|
+
console.log("");
|
|
139567
|
+
const options = [];
|
|
139568
|
+
for (let i = 0; i < recommendations.length; i++) {
|
|
139569
|
+
const rec = recommendations[i];
|
|
139570
|
+
let scoreColor;
|
|
139571
|
+
if (rec.score >= 70) {
|
|
139572
|
+
scoreColor = colors.success;
|
|
139573
|
+
} else if (rec.score >= 50) {
|
|
139574
|
+
scoreColor = colors.warning;
|
|
139575
|
+
} else {
|
|
139576
|
+
scoreColor = colors.muted;
|
|
139577
|
+
}
|
|
139578
|
+
const scoreBar = progressBar(rec.score, 100, 10);
|
|
139579
|
+
const qualityScore = rec.skill.quality ?? null;
|
|
139580
|
+
const qualityDisplay = typeof qualityScore === "number" && Number.isFinite(qualityScore) ? ` ${formatQualityBadge(qualityScore)}` : "";
|
|
139581
|
+
console.log(` ${colors.bold(`${i + 1}.`)} ${scoreColor(`${rec.score}%`)} ${colors.dim(scoreBar)} ${colors.bold(rec.skill.name)}${qualityDisplay}`);
|
|
139582
|
+
if (rec.skill.description) {
|
|
139583
|
+
console.log(` ${colors.muted(truncate6(rec.skill.description, 70))}`);
|
|
139584
|
+
}
|
|
139585
|
+
options.push({
|
|
139586
|
+
value: `${i}`,
|
|
139587
|
+
label: `${rec.skill.name} (${rec.score}% match)`,
|
|
139588
|
+
hint: rec.skill.source || void 0
|
|
139589
|
+
});
|
|
139590
|
+
}
|
|
139591
|
+
console.log("");
|
|
139592
|
+
const skipOption = { value: "skip", label: "Skip installation" };
|
|
139593
|
+
const selectResult = await select2({
|
|
139594
|
+
message: "Select a skill to install",
|
|
139595
|
+
options: [...options, skipOption]
|
|
139596
|
+
});
|
|
139597
|
+
if (isCancel2(selectResult) || selectResult === "skip") {
|
|
139598
|
+
cancel2("Quick setup complete (no installation)");
|
|
139599
|
+
return 0;
|
|
139600
|
+
}
|
|
139601
|
+
const selectedIndex = parseInt(selectResult, 10);
|
|
139602
|
+
const selectedSkill = recommendations[selectedIndex];
|
|
139603
|
+
if (!selectedSkill || !selectedSkill.skill.source) {
|
|
139604
|
+
error("Selected skill has no installable source");
|
|
139605
|
+
return 1;
|
|
139606
|
+
}
|
|
139607
|
+
const agentDisplay = detectedAgents.length <= 3 ? detectedAgents.map(formatAgent).join(", ") : `${detectedAgents.slice(0, 2).map(formatAgent).join(", ")} +${detectedAgents.length - 2} more`;
|
|
139608
|
+
const confirmResult = await confirm2({
|
|
139609
|
+
message: `Install ${colors.bold(selectedSkill.skill.name)} to ${agentDisplay}?`,
|
|
139610
|
+
initialValue: true
|
|
139611
|
+
});
|
|
139612
|
+
if (isCancel2(confirmResult) || !confirmResult) {
|
|
139613
|
+
cancel2("Installation cancelled");
|
|
139614
|
+
return 0;
|
|
139615
|
+
}
|
|
139616
|
+
saveLastAgents(detectedAgents);
|
|
139617
|
+
const providerAdapter = detectProvider4(selectedSkill.skill.source);
|
|
139618
|
+
if (!providerAdapter) {
|
|
139619
|
+
error(`Could not detect provider for: ${selectedSkill.skill.source}`);
|
|
139620
|
+
return 1;
|
|
139621
|
+
}
|
|
139622
|
+
s.start(`Fetching ${selectedSkill.skill.name}...`);
|
|
139623
|
+
const cloneResult = await providerAdapter.clone(selectedSkill.skill.source, "", { depth: 1 });
|
|
139624
|
+
if (!cloneResult.success || !cloneResult.path) {
|
|
139625
|
+
s.stop(colors.error(cloneResult.error || "Failed to fetch source"));
|
|
139626
|
+
return 1;
|
|
139627
|
+
}
|
|
139628
|
+
const discoveredSkills = cloneResult.discoveredSkills || [];
|
|
139629
|
+
const matchedSkill = discoveredSkills.find((ds) => ds.name === selectedSkill.skill.name) || discoveredSkills[0];
|
|
139630
|
+
if (!matchedSkill) {
|
|
139631
|
+
s.stop(colors.error("Skill not found in repository"));
|
|
139632
|
+
return 1;
|
|
139633
|
+
}
|
|
139634
|
+
s.stop(`Found ${selectedSkill.skill.name}`);
|
|
139635
|
+
let primaryPath = null;
|
|
139636
|
+
const installedAgents = [];
|
|
139637
|
+
for (const agentType of detectedAgents) {
|
|
139638
|
+
const adapter = getAdapter6(agentType);
|
|
139639
|
+
const installDir = getInstallDir(false, agentType);
|
|
139640
|
+
if (!existsSync21(installDir)) {
|
|
139641
|
+
mkdirSync10(installDir, { recursive: true });
|
|
139642
|
+
}
|
|
139643
|
+
const sanitizedName = matchedSkill.name.split(/[/\\]/).pop() || matchedSkill.name;
|
|
139644
|
+
const targetInstallPath = normalize(join20(installDir, sanitizedName));
|
|
139645
|
+
if (!isPathInside2(targetInstallPath, installDir)) {
|
|
139646
|
+
error(`Skipping ${matchedSkill.name} for ${adapter.name} (install path escapes target directory)`);
|
|
139647
|
+
continue;
|
|
139648
|
+
}
|
|
139649
|
+
const securityRoot = cloneResult.tempRoot || cloneResult.path;
|
|
139650
|
+
if (!isPathInside2(matchedSkill.path, securityRoot)) {
|
|
139651
|
+
error(`Skipping ${matchedSkill.name} for ${adapter.name} (path traversal detected)`);
|
|
139652
|
+
continue;
|
|
139653
|
+
}
|
|
139654
|
+
const useSymlink = detectedAgents.length > 1 && primaryPath !== null;
|
|
139655
|
+
s.start(`Installing to ${adapter.name}${useSymlink ? " (symlink)" : ""}...`);
|
|
139656
|
+
try {
|
|
139657
|
+
if (existsSync21(targetInstallPath)) {
|
|
139658
|
+
rmSync5(targetInstallPath, { recursive: true, force: true });
|
|
139659
|
+
}
|
|
139660
|
+
if (useSymlink && primaryPath) {
|
|
139661
|
+
symlinkSync2(primaryPath, targetInstallPath, "dir");
|
|
139662
|
+
} else {
|
|
139663
|
+
cpSync4(matchedSkill.path, targetInstallPath, { recursive: true, dereference: true });
|
|
139664
|
+
if (detectedAgents.length > 1 && primaryPath === null) {
|
|
139665
|
+
primaryPath = targetInstallPath;
|
|
139666
|
+
}
|
|
139667
|
+
const packageJsonPath = join20(targetInstallPath, "package.json");
|
|
139668
|
+
if (existsSync21(packageJsonPath)) {
|
|
139669
|
+
s.stop(`Installed to ${adapter.name}`);
|
|
139670
|
+
s.start("Installing npm dependencies...");
|
|
139671
|
+
try {
|
|
139672
|
+
await execFileAsync2("npm", ["install", "--production"], { cwd: targetInstallPath });
|
|
139673
|
+
s.stop("Dependencies installed");
|
|
139674
|
+
} catch {
|
|
139675
|
+
s.stop(colors.warning("Dependencies failed"));
|
|
139676
|
+
console.log(colors.muted("Run manually: npm install in " + targetInstallPath));
|
|
139677
|
+
}
|
|
139678
|
+
s.start("Finishing installation...");
|
|
139679
|
+
}
|
|
139680
|
+
}
|
|
139681
|
+
const metadata = {
|
|
139682
|
+
name: matchedSkill.name,
|
|
139683
|
+
description: selectedSkill.skill.description || "",
|
|
139684
|
+
source: selectedSkill.skill.source,
|
|
139685
|
+
sourceType: providerAdapter.type,
|
|
139686
|
+
subpath: matchedSkill.name,
|
|
139687
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
139688
|
+
enabled: true
|
|
139689
|
+
};
|
|
139690
|
+
saveSkillMetadata(targetInstallPath, metadata);
|
|
139691
|
+
installedAgents.push(agentType);
|
|
139692
|
+
s.stop(`Installed to ${adapter.name}${useSymlink ? " (symlink)" : ""}`);
|
|
139693
|
+
} catch (err) {
|
|
139694
|
+
s.stop(colors.error(`Failed to install to ${adapter.name}`));
|
|
139695
|
+
console.log(colors.muted(err instanceof Error ? err.message : String(err)));
|
|
139696
|
+
}
|
|
139697
|
+
}
|
|
139698
|
+
const cleanupPath = cloneResult.tempRoot || cloneResult.path;
|
|
139699
|
+
if (!isLocalPath4(selectedSkill.skill.source) && cleanupPath && existsSync21(cleanupPath)) {
|
|
139700
|
+
rmSync5(cleanupPath, { recursive: true, force: true });
|
|
139701
|
+
}
|
|
139702
|
+
if (installedAgents.length > 0) {
|
|
139703
|
+
console.log("");
|
|
139704
|
+
success(`Installed ${colors.bold(matchedSkill.name)} to ${installedAgents.length} agent${installedAgents.length !== 1 ? "s" : ""}`);
|
|
139705
|
+
for (const a of installedAgents) {
|
|
139706
|
+
console.log(colors.muted(` ${symbols.success} ${getAgentIcon(a)} ${formatAgent(a)}`));
|
|
139707
|
+
}
|
|
139708
|
+
console.log("");
|
|
139709
|
+
const badgeUrl = `https://img.shields.io/badge/SkillKit-${encodeURIComponent(matchedSkill.name)}-black?style=flat-square`;
|
|
139710
|
+
console.log(colors.dim("Add to your README:"));
|
|
139711
|
+
console.log(colors.muted(` [](https://agenstskills.com)`));
|
|
139712
|
+
outro2("Quick setup complete!");
|
|
139713
|
+
console.log("");
|
|
139714
|
+
console.log(colors.muted("Next steps:"));
|
|
139715
|
+
console.log(colors.muted(` ${symbols.arrowRight} Run ${colors.cyan("skillkit sync")} to update agent configs`));
|
|
139716
|
+
console.log(colors.muted(` ${symbols.arrowRight} Run ${colors.cyan("skillkit recommend")} for more suggestions`));
|
|
139717
|
+
console.log(colors.muted(` ${symbols.arrowRight} Run ${colors.cyan("skillkit list")} to see installed skills`));
|
|
139718
|
+
console.log("");
|
|
139719
|
+
} else {
|
|
139720
|
+
warn("No agents were installed");
|
|
139721
|
+
}
|
|
139722
|
+
return 0;
|
|
139723
|
+
} catch (err) {
|
|
139724
|
+
s.stop(colors.error("Quick setup failed"));
|
|
139725
|
+
console.log(colors.muted(err instanceof Error ? err.message : String(err)));
|
|
139726
|
+
return 1;
|
|
139727
|
+
}
|
|
139728
|
+
}
|
|
139729
|
+
};
|
|
139730
|
+
|
|
139731
|
+
// src/commands/skillmd.ts
|
|
139732
|
+
init_onboarding();
|
|
139733
|
+
import { existsSync as existsSync22, readFileSync as readFileSync11, writeFileSync as writeFileSync11, readdirSync as readdirSync4 } from "fs";
|
|
139734
|
+
import { join as join21, resolve as resolve22, basename as basename6, relative } from "path";
|
|
139735
|
+
import { Command as Command51, Option as Option49 } from "clipanion";
|
|
139736
|
+
function validateSkillMd(filePath) {
|
|
139737
|
+
const result = {
|
|
139738
|
+
path: filePath,
|
|
139739
|
+
exists: false,
|
|
139740
|
+
hasName: false,
|
|
139741
|
+
hasDescription: false,
|
|
139742
|
+
hasTriggers: false,
|
|
139743
|
+
hasCapabilities: false,
|
|
139744
|
+
triggerCount: 0,
|
|
139745
|
+
capabilityCount: 0,
|
|
139746
|
+
score: 0,
|
|
139747
|
+
issues: []
|
|
139748
|
+
};
|
|
139749
|
+
if (!existsSync22(filePath)) {
|
|
139750
|
+
result.issues.push("File does not exist");
|
|
139751
|
+
return result;
|
|
139752
|
+
}
|
|
139753
|
+
result.exists = true;
|
|
139754
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
139755
|
+
if (!content.trim()) {
|
|
139756
|
+
result.issues.push("File is empty");
|
|
139757
|
+
return result;
|
|
139758
|
+
}
|
|
139759
|
+
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
139760
|
+
const bodyContent = frontmatterMatch ? content.slice(frontmatterMatch[0].length) : content;
|
|
139761
|
+
if (frontmatterMatch) {
|
|
139762
|
+
const frontmatter = frontmatterMatch[1];
|
|
139763
|
+
if (/^name:\s*.+$/m.test(frontmatter)) result.hasName = true;
|
|
139764
|
+
if (/^description:\s*.+$/m.test(frontmatter)) result.hasDescription = true;
|
|
139765
|
+
}
|
|
139766
|
+
if (!result.hasName) {
|
|
139767
|
+
if (/^#\s+.+$/m.test(bodyContent)) result.hasName = true;
|
|
139768
|
+
}
|
|
139769
|
+
if (!result.hasDescription) {
|
|
139770
|
+
if (/^##?\s*(description|about|overview)\b/im.test(bodyContent)) {
|
|
139771
|
+
result.hasDescription = true;
|
|
139772
|
+
} else {
|
|
139773
|
+
const lines = bodyContent.split(/\r?\n/).filter((l) => l.trim());
|
|
139774
|
+
const nonHeaderLines = lines.filter((l) => !l.startsWith("#"));
|
|
139775
|
+
if (nonHeaderLines.length >= 2) result.hasDescription = true;
|
|
139776
|
+
}
|
|
139777
|
+
}
|
|
139778
|
+
const triggerPatterns = [
|
|
139779
|
+
/triggers?\s*when/i,
|
|
139780
|
+
/when\s*to\s*use/i,
|
|
139781
|
+
/use\s*this\s*when/i,
|
|
139782
|
+
/\*\*triggers?\b/i,
|
|
139783
|
+
/^[-*]\s+.+(when|if|pattern|trigger)/im,
|
|
139784
|
+
/glob:/i
|
|
139785
|
+
];
|
|
139786
|
+
for (const pattern of triggerPatterns) {
|
|
139787
|
+
if (pattern.test(content)) {
|
|
139788
|
+
result.hasTriggers = true;
|
|
139789
|
+
break;
|
|
139790
|
+
}
|
|
139791
|
+
}
|
|
139792
|
+
if (result.hasTriggers) {
|
|
139793
|
+
const triggerSection = content.match(/triggers?\s*when[:\s]*\n((?:[-*]\s+.+\n?)+)/i);
|
|
139794
|
+
result.triggerCount = triggerSection ? (triggerSection[1].match(/^[-*]\s+/gm) || []).length : 1;
|
|
139795
|
+
}
|
|
139796
|
+
const capabilityPatterns = [
|
|
139797
|
+
/capabilities?[:\s]/i,
|
|
139798
|
+
/features?[:\s]/i,
|
|
139799
|
+
/what\s*(it|this)\s*(does|can)/i,
|
|
139800
|
+
/^##?\s*capabilities/im,
|
|
139801
|
+
/^##?\s*features/im
|
|
139802
|
+
];
|
|
139803
|
+
for (const pattern of capabilityPatterns) {
|
|
139804
|
+
if (pattern.test(content)) {
|
|
139805
|
+
result.hasCapabilities = true;
|
|
139806
|
+
break;
|
|
139807
|
+
}
|
|
139808
|
+
}
|
|
139809
|
+
if (result.hasCapabilities) {
|
|
139810
|
+
const capSection = content.match(/(?:capabilities?|features?)[:\s]*\n((?:[-*]\s+.+\n?)+)/i);
|
|
139811
|
+
result.capabilityCount = capSection ? (capSection[1].match(/^[-*]\s+/gm) || []).length : 1;
|
|
139812
|
+
}
|
|
139813
|
+
if (!result.hasName) result.issues.push("Missing name field");
|
|
139814
|
+
if (!result.hasDescription) result.issues.push("Missing description section");
|
|
139815
|
+
if (!result.hasTriggers) result.issues.push("No trigger patterns found");
|
|
139816
|
+
if (!result.hasCapabilities) result.issues.push("No capabilities list found");
|
|
139817
|
+
let score = 0;
|
|
139818
|
+
if (result.exists) score += 10;
|
|
139819
|
+
if (result.hasName) score += 20;
|
|
139820
|
+
if (result.hasDescription) score += 25;
|
|
139821
|
+
if (result.hasTriggers) score += 20;
|
|
139822
|
+
if (result.hasCapabilities) score += 15;
|
|
139823
|
+
if (result.triggerCount >= 3) score += 5;
|
|
139824
|
+
if (result.capabilityCount >= 3) score += 5;
|
|
139825
|
+
if (frontmatterMatch) {
|
|
139826
|
+
const fm = frontmatterMatch[1];
|
|
139827
|
+
if (/^version:/m.test(fm)) score += 3;
|
|
139828
|
+
if (/^license:/m.test(fm)) score += 2;
|
|
139829
|
+
if (/^tags:/m.test(fm)) score += 3;
|
|
139830
|
+
if (/^metadata:/m.test(fm)) score += 2;
|
|
139831
|
+
}
|
|
139832
|
+
result.score = Math.min(100, score);
|
|
139833
|
+
return result;
|
|
139834
|
+
}
|
|
139835
|
+
function printValidation(validation, verbose = false) {
|
|
139836
|
+
const scoreColor = validation.score >= 80 ? colors.success : validation.score >= 60 ? colors.warning : colors.error;
|
|
139837
|
+
console.log(` ${colors.muted("Score:")} ${scoreColor(`${validation.score}`)}/100`);
|
|
139838
|
+
console.log(` ${validation.hasName ? colors.success(symbols.success) : colors.error(symbols.error)} Name field`);
|
|
139839
|
+
console.log(` ${validation.hasDescription ? colors.success(symbols.success) : colors.error(symbols.error)} Description section`);
|
|
139840
|
+
console.log(` ${validation.hasTriggers ? colors.success(symbols.success) : colors.error(symbols.error)} Trigger patterns${validation.triggerCount > 0 ? ` (${validation.triggerCount})` : ""}`);
|
|
139841
|
+
console.log(` ${validation.hasCapabilities ? colors.success(symbols.success) : colors.error(symbols.error)} Capabilities list${validation.capabilityCount > 0 ? ` (${validation.capabilityCount})` : ""}`);
|
|
139842
|
+
if (verbose && validation.issues.length > 0) {
|
|
139843
|
+
console.log("");
|
|
139844
|
+
console.log(` ${colors.warning("Issues:")}`);
|
|
139845
|
+
for (const issue of validation.issues) {
|
|
139846
|
+
console.log(` ${colors.warning(symbols.warning)} ${issue}`);
|
|
139847
|
+
}
|
|
139848
|
+
}
|
|
139849
|
+
}
|
|
139850
|
+
var SkillMdValidateCommand = class extends Command51 {
|
|
139851
|
+
static paths = [["skillmd", "validate"], ["skill-md", "validate"]];
|
|
139852
|
+
static usage = Command51.Usage({
|
|
139853
|
+
description: "Validate a SKILL.md file against the standard",
|
|
139854
|
+
examples: [
|
|
139855
|
+
["Validate SKILL.md in current directory", "$0 skillmd validate"],
|
|
139856
|
+
["Validate specific file", "$0 skillmd validate ./path/to/SKILL.md"]
|
|
139857
|
+
]
|
|
139858
|
+
});
|
|
139859
|
+
targetPath = Option49.String({ required: false, name: "path" });
|
|
139860
|
+
verbose = Option49.Boolean("--verbose,-v", false, {
|
|
139861
|
+
description: "Show detailed validation results"
|
|
139862
|
+
});
|
|
139863
|
+
json = Option49.Boolean("--json", false, {
|
|
139864
|
+
description: "Output as JSON"
|
|
139865
|
+
});
|
|
139866
|
+
async execute() {
|
|
139867
|
+
const targetPath = this.targetPath || join21(process.cwd(), "SKILL.md");
|
|
139868
|
+
const resolvedPath = resolve22(targetPath);
|
|
139869
|
+
const filePath = resolvedPath.endsWith("SKILL.md") ? resolvedPath : join21(resolvedPath, "SKILL.md");
|
|
139870
|
+
if (!this.json) {
|
|
139871
|
+
header("SKILL.md Validation");
|
|
139872
|
+
step(`Validating: ${colors.cyan(filePath)}`);
|
|
139873
|
+
console.log("");
|
|
139874
|
+
}
|
|
139875
|
+
const validation = validateSkillMd(filePath);
|
|
139876
|
+
if (this.json) {
|
|
139877
|
+
console.log(JSON.stringify(validation, null, 2));
|
|
139878
|
+
return validation.score >= 60 ? 0 : 1;
|
|
139879
|
+
}
|
|
139880
|
+
console.log(colors.primary(basename6(filePath)));
|
|
139881
|
+
printValidation(validation, this.verbose);
|
|
139882
|
+
console.log("");
|
|
139883
|
+
if (validation.score >= 80) {
|
|
139884
|
+
success(`SKILL.md is compliant (score: ${validation.score}/100)`);
|
|
139885
|
+
} else if (validation.score >= 60) {
|
|
139886
|
+
warn(`SKILL.md needs improvement (score: ${validation.score}/100)`);
|
|
139887
|
+
} else {
|
|
139888
|
+
error(`SKILL.md does not meet minimum standard (score: ${validation.score}/100)`);
|
|
139889
|
+
}
|
|
139890
|
+
return validation.score >= 60 ? 0 : 1;
|
|
139891
|
+
}
|
|
139892
|
+
};
|
|
139893
|
+
var SkillMdInitCommand = class extends Command51 {
|
|
139894
|
+
static paths = [["skillmd", "init"], ["skill-md", "init"]];
|
|
139895
|
+
static usage = Command51.Usage({
|
|
139896
|
+
description: "Create a template SKILL.md in the current directory",
|
|
139897
|
+
examples: [
|
|
139898
|
+
["Create SKILL.md template", "$0 skillmd init"],
|
|
139899
|
+
["Create with custom name", "$0 skillmd init --name my-skill"]
|
|
139900
|
+
]
|
|
139901
|
+
});
|
|
139902
|
+
name = Option49.String("--name,-n", {
|
|
139903
|
+
description: "Skill name"
|
|
139904
|
+
});
|
|
139905
|
+
force = Option49.Boolean("--force,-f", false, {
|
|
139906
|
+
description: "Overwrite existing SKILL.md"
|
|
139907
|
+
});
|
|
139908
|
+
async execute() {
|
|
139909
|
+
const outputPath = join21(process.cwd(), "SKILL.md");
|
|
139910
|
+
if (existsSync22(outputPath) && !this.force) {
|
|
139911
|
+
warn("SKILL.md already exists");
|
|
139912
|
+
console.log(colors.muted("Use --force to overwrite"));
|
|
139913
|
+
return 1;
|
|
139914
|
+
}
|
|
139915
|
+
const skillName = this.name || basename6(process.cwd());
|
|
139916
|
+
const formattedName = skillName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
139917
|
+
const template = [
|
|
139918
|
+
"---",
|
|
139919
|
+
`name: ${skillName}`,
|
|
139920
|
+
"description: What this skill does and when to use it",
|
|
139921
|
+
'version: "1.0"',
|
|
139922
|
+
"license: MIT",
|
|
139923
|
+
"tags: [general]",
|
|
139924
|
+
"metadata:",
|
|
139925
|
+
" author: your-name",
|
|
139926
|
+
"---",
|
|
139927
|
+
"",
|
|
139928
|
+
`# ${formattedName}`,
|
|
139929
|
+
"",
|
|
139930
|
+
"Brief description of what this skill provides to AI coding agents.",
|
|
139931
|
+
"",
|
|
139932
|
+
"## Triggers when:",
|
|
139933
|
+
`- User asks about ${skillName} related tasks`,
|
|
139934
|
+
`- Project contains ${skillName} configuration files`,
|
|
139935
|
+
`- Code changes involve ${skillName} patterns`,
|
|
139936
|
+
"",
|
|
139937
|
+
"## Capabilities:",
|
|
139938
|
+
`- Provides best practices for ${skillName}`,
|
|
139939
|
+
"- Generates boilerplate code and configurations",
|
|
139940
|
+
"- Validates existing implementations",
|
|
139941
|
+
"- Suggests improvements and optimizations",
|
|
139942
|
+
"",
|
|
139943
|
+
"## Steps",
|
|
139944
|
+
"1. First step the AI agent should take",
|
|
139945
|
+
"2. Second step with specific instructions",
|
|
139946
|
+
"3. Third step with expected outcome",
|
|
139947
|
+
"",
|
|
139948
|
+
"## Examples",
|
|
139949
|
+
"",
|
|
139950
|
+
"```",
|
|
139951
|
+
"Example code or configuration here",
|
|
139952
|
+
"```",
|
|
139953
|
+
""
|
|
139954
|
+
].join("\n");
|
|
139955
|
+
writeFileSync11(outputPath, template);
|
|
139956
|
+
success(`Created SKILL.md for "${skillName}"`);
|
|
139957
|
+
console.log("");
|
|
139958
|
+
console.log(colors.muted("Next steps:"));
|
|
139959
|
+
console.log(` ${colors.cyan("1.")} Edit SKILL.md with your skill instructions`);
|
|
139960
|
+
console.log(` ${colors.cyan("2.")} Run ${colors.cyan("skillkit skillmd validate")} to check compliance`);
|
|
139961
|
+
console.log(` ${colors.cyan("3.")} Run ${colors.cyan("skillkit publish submit")} to share with the marketplace`);
|
|
139962
|
+
return 0;
|
|
139963
|
+
}
|
|
139964
|
+
};
|
|
139965
|
+
var SkillMdCheckCommand = class extends Command51 {
|
|
139966
|
+
static paths = [["skillmd", "check"], ["skill-md", "check"]];
|
|
139967
|
+
static usage = Command51.Usage({
|
|
139968
|
+
description: "Check all SKILL.md files in the project for compliance",
|
|
139969
|
+
examples: [
|
|
139970
|
+
["Check current directory", "$0 skillmd check"],
|
|
139971
|
+
["Check specific directory", "$0 skillmd check ./skills"]
|
|
139972
|
+
]
|
|
139973
|
+
});
|
|
139974
|
+
targetPath = Option49.String({ required: false, name: "path" });
|
|
139975
|
+
verbose = Option49.Boolean("--verbose,-v", false, {
|
|
139976
|
+
description: "Show detailed validation results"
|
|
139977
|
+
});
|
|
139978
|
+
async execute() {
|
|
139979
|
+
const targetPath = resolve22(this.targetPath || process.cwd());
|
|
139980
|
+
header("SKILL.md Compliance Check");
|
|
139981
|
+
const s = spinner2();
|
|
139982
|
+
s.start("Scanning for SKILL.md files...");
|
|
139983
|
+
const skillMdFiles = this.findSkillMdFiles(targetPath);
|
|
139984
|
+
s.stop(`Found ${skillMdFiles.length} SKILL.md file(s)`);
|
|
139985
|
+
if (skillMdFiles.length === 0) {
|
|
139986
|
+
warn("No SKILL.md files found");
|
|
139987
|
+
console.log(colors.muted(`Searched: ${targetPath}`));
|
|
139988
|
+
console.log("");
|
|
139989
|
+
console.log(colors.muted("Create one with:"));
|
|
139990
|
+
console.log(` ${colors.cyan("skillkit skillmd init")}`);
|
|
139991
|
+
return 0;
|
|
139992
|
+
}
|
|
139993
|
+
const validations = skillMdFiles.map((f) => validateSkillMd(f));
|
|
139994
|
+
validations.sort((a, b) => b.score - a.score);
|
|
139995
|
+
console.log("");
|
|
139996
|
+
for (const validation of validations) {
|
|
139997
|
+
const relativePath = relative(targetPath, validation.path);
|
|
139998
|
+
const scoreColor = validation.score >= 80 ? colors.success : validation.score >= 60 ? colors.warning : colors.error;
|
|
139999
|
+
console.log(`${scoreColor(symbols.bullet)} ${colors.primary(relativePath)} ${colors.muted(`(${validation.score}/100)`)}`);
|
|
140000
|
+
if (this.verbose) {
|
|
140001
|
+
printValidation(validation, true);
|
|
140002
|
+
console.log("");
|
|
140003
|
+
}
|
|
140004
|
+
}
|
|
140005
|
+
console.log("");
|
|
140006
|
+
const passing = validations.filter((v) => v.score >= 60).length;
|
|
140007
|
+
const failing = validations.filter((v) => v.score < 60).length;
|
|
140008
|
+
const avgScore = Math.round(validations.reduce((sum, v) => sum + v.score, 0) / validations.length);
|
|
140009
|
+
console.log(colors.muted("Summary:"));
|
|
140010
|
+
console.log(` ${colors.muted("Total:")} ${validations.length}`);
|
|
140011
|
+
console.log(` ${colors.success(symbols.success)} Passing (60+): ${passing}`);
|
|
140012
|
+
if (failing > 0) {
|
|
140013
|
+
console.log(` ${colors.error(symbols.error)} Failing: ${failing}`);
|
|
140014
|
+
}
|
|
140015
|
+
console.log(` ${colors.muted("Average score:")} ${avgScore}/100`);
|
|
140016
|
+
console.log("");
|
|
140017
|
+
if (failing > 0) {
|
|
140018
|
+
error(`${failing} SKILL.md file(s) below minimum standard`);
|
|
140019
|
+
return 1;
|
|
140020
|
+
}
|
|
140021
|
+
success("All SKILL.md files are compliant");
|
|
140022
|
+
return 0;
|
|
140023
|
+
}
|
|
140024
|
+
findSkillMdFiles(dir) {
|
|
140025
|
+
const results = [];
|
|
140026
|
+
const directFile = join21(dir, "SKILL.md");
|
|
140027
|
+
if (existsSync22(directFile)) results.push(directFile);
|
|
140028
|
+
const searchDirs = [
|
|
140029
|
+
join21(dir, "skills"),
|
|
140030
|
+
join21(dir, ".claude", "skills"),
|
|
140031
|
+
join21(dir, ".cursor", "skills"),
|
|
140032
|
+
join21(dir, ".codex", "skills"),
|
|
140033
|
+
join21(dir, ".github", "skills")
|
|
140034
|
+
];
|
|
140035
|
+
for (const searchDir of searchDirs) {
|
|
140036
|
+
if (!existsSync22(searchDir)) continue;
|
|
140037
|
+
this.scanDir(searchDir, results, 0);
|
|
140038
|
+
}
|
|
140039
|
+
try {
|
|
140040
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
140041
|
+
for (const entry of entries) {
|
|
140042
|
+
if (!entry.isDirectory()) continue;
|
|
140043
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
140044
|
+
const entrySkillMd = join21(dir, entry.name, "SKILL.md");
|
|
140045
|
+
if (existsSync22(entrySkillMd) && !results.includes(entrySkillMd)) {
|
|
140046
|
+
results.push(entrySkillMd);
|
|
140047
|
+
}
|
|
140048
|
+
}
|
|
140049
|
+
} catch {
|
|
140050
|
+
}
|
|
140051
|
+
return results;
|
|
140052
|
+
}
|
|
140053
|
+
scanDir(dir, results, depth) {
|
|
140054
|
+
if (depth > 3) return;
|
|
140055
|
+
try {
|
|
140056
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
140057
|
+
for (const entry of entries) {
|
|
140058
|
+
const entryPath = join21(dir, entry.name);
|
|
140059
|
+
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
140060
|
+
if (!results.includes(entryPath)) results.push(entryPath);
|
|
140061
|
+
}
|
|
140062
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
140063
|
+
this.scanDir(entryPath, results, depth + 1);
|
|
140064
|
+
}
|
|
140065
|
+
}
|
|
140066
|
+
} catch {
|
|
140067
|
+
}
|
|
140068
|
+
}
|
|
140069
|
+
};
|
|
140070
|
+
|
|
139263
140071
|
// src/index.ts
|
|
139264
140072
|
init_helpers();
|
|
139265
140073
|
export {
|
|
@@ -139329,6 +140137,7 @@ export {
|
|
|
139329
140137
|
ProfileRemoveCommand,
|
|
139330
140138
|
PublishCommand,
|
|
139331
140139
|
PublishSubmitCommand,
|
|
140140
|
+
QuickCommand,
|
|
139332
140141
|
ReadCommand,
|
|
139333
140142
|
RecommendCommand,
|
|
139334
140143
|
RemoveCommand,
|
|
@@ -139343,6 +140152,9 @@ export {
|
|
|
139343
140152
|
SessionStartCommand,
|
|
139344
140153
|
SessionStatusCommand,
|
|
139345
140154
|
SettingsCommand,
|
|
140155
|
+
SkillMdCheckCommand,
|
|
140156
|
+
SkillMdInitCommand,
|
|
140157
|
+
SkillMdValidateCommand,
|
|
139346
140158
|
StatusCommand,
|
|
139347
140159
|
SyncCommand,
|
|
139348
140160
|
TeamCommand,
|