@skillkit/cli 1.8.1 → 1.10.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 +54 -3
- package/dist/index.js +934 -106
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __esm = (fn, res) => function __init() {
|
|
4
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
11
|
};
|
|
@@ -1215,7 +1221,7 @@ init_onboarding();
|
|
|
1215
1221
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1216
1222
|
import { dirname } from "path";
|
|
1217
1223
|
import { Command as Command3, Option as Option3 } from "clipanion";
|
|
1218
|
-
import {
|
|
1224
|
+
import { findAllSkills as findAllSkills2 } from "@skillkit/core";
|
|
1219
1225
|
import { getAdapter as getAdapter2, detectAgent as detectAgent2, getAllAdapters } from "@skillkit/agents";
|
|
1220
1226
|
var SyncCommand = class extends Command3 {
|
|
1221
1227
|
static paths = [["sync"], ["s"]];
|
|
@@ -1255,8 +1261,7 @@ var SyncCommand = class extends Command3 {
|
|
|
1255
1261
|
agentType = this.agent;
|
|
1256
1262
|
} else if (isInteractive) {
|
|
1257
1263
|
s.start("Detecting agent...");
|
|
1258
|
-
const
|
|
1259
|
-
const detected = config2.agent || await detectAgent2();
|
|
1264
|
+
const detected = await detectAgent2();
|
|
1260
1265
|
s.stop(`Detected: ${formatAgent(detected)}`);
|
|
1261
1266
|
const allAgents = getAllAdapters().map((a) => a.type);
|
|
1262
1267
|
const agentResult = await select2({
|
|
@@ -1274,8 +1279,7 @@ var SyncCommand = class extends Command3 {
|
|
|
1274
1279
|
}
|
|
1275
1280
|
agentType = agentResult;
|
|
1276
1281
|
} else {
|
|
1277
|
-
|
|
1278
|
-
agentType = config2.agent || await detectAgent2();
|
|
1282
|
+
agentType = await detectAgent2();
|
|
1279
1283
|
}
|
|
1280
1284
|
const adapter = getAdapter2(agentType);
|
|
1281
1285
|
const outputPath = this.output || getAgentConfigPath(agentType);
|
|
@@ -1650,10 +1654,13 @@ init_helpers();
|
|
|
1650
1654
|
init_onboarding();
|
|
1651
1655
|
import { existsSync as existsSync5, mkdirSync as mkdirSync3, cpSync, rmSync as rmSync2, symlinkSync } from "fs";
|
|
1652
1656
|
import { join as join3 } from "path";
|
|
1657
|
+
import { execFile } from "child_process";
|
|
1658
|
+
import { promisify } from "util";
|
|
1653
1659
|
import { Command as Command7, Option as Option7 } from "clipanion";
|
|
1654
1660
|
import { detectProvider, isLocalPath, getProvider, evaluateSkillDirectory as evaluateSkillDirectory2 } from "@skillkit/core";
|
|
1655
1661
|
import { isPathInside } from "@skillkit/core";
|
|
1656
1662
|
import { getAdapter as getAdapter4, detectAgent as detectAgent4, getAllAdapters as getAllAdapters3 } from "@skillkit/agents";
|
|
1663
|
+
var execFileAsync = promisify(execFile);
|
|
1657
1664
|
var InstallCommand = class extends Command7 {
|
|
1658
1665
|
static paths = [["install"], ["i"], ["add"]];
|
|
1659
1666
|
static usage = Command7.Usage({
|
|
@@ -1662,7 +1669,8 @@ var InstallCommand = class extends Command7 {
|
|
|
1662
1669
|
["Install from GitHub", "$0 install owner/repo"],
|
|
1663
1670
|
["Install from GitLab", "$0 install gitlab:owner/repo"],
|
|
1664
1671
|
["Install from Bitbucket", "$0 install bitbucket:owner/repo"],
|
|
1665
|
-
["Install specific
|
|
1672
|
+
["Install specific skill", "$0 install owner/repo --skill=pdf"],
|
|
1673
|
+
["Install multiple skills (CI/CD)", "$0 install owner/repo --skills=pdf,xlsx"],
|
|
1666
1674
|
["Install all skills non-interactively", "$0 install owner/repo --all"],
|
|
1667
1675
|
["Install from local path", "$0 install ./my-skills"],
|
|
1668
1676
|
["Install globally", "$0 install owner/repo --global"],
|
|
@@ -1671,7 +1679,7 @@ var InstallCommand = class extends Command7 {
|
|
|
1671
1679
|
]
|
|
1672
1680
|
});
|
|
1673
1681
|
source = Option7.String({ required: true });
|
|
1674
|
-
skills = Option7.String("--skills,-s", {
|
|
1682
|
+
skills = Option7.String("--skills,--skill,-s", {
|
|
1675
1683
|
description: "Comma-separated list of skills to install (non-interactive)"
|
|
1676
1684
|
});
|
|
1677
1685
|
all = Option7.Boolean("--all,-a", false, {
|
|
@@ -1740,7 +1748,7 @@ var InstallCommand = class extends Command7 {
|
|
|
1740
1748
|
}
|
|
1741
1749
|
console.log("");
|
|
1742
1750
|
console.log(colors.muted(`Total: ${discoveredSkills.length} skill(s)`));
|
|
1743
|
-
console.log(colors.muted("To install: skillkit install <source> --
|
|
1751
|
+
console.log(colors.muted("To install: skillkit install <source> --skill=name"));
|
|
1744
1752
|
}
|
|
1745
1753
|
const cleanupPath2 = result.tempRoot || result.path;
|
|
1746
1754
|
if (!isLocalPath(this.source) && cleanupPath2 && existsSync5(cleanupPath2)) {
|
|
@@ -1880,6 +1888,19 @@ var InstallCommand = class extends Command7 {
|
|
|
1880
1888
|
if (isSymlinkMode && primaryPath === null) {
|
|
1881
1889
|
primaryPath = targetPath;
|
|
1882
1890
|
}
|
|
1891
|
+
const packageJsonPath = join3(targetPath, "package.json");
|
|
1892
|
+
if (existsSync5(packageJsonPath)) {
|
|
1893
|
+
s.stop(`Installed ${skillName} to ${adapter.name}`);
|
|
1894
|
+
s.start(`Installing npm dependencies for ${skillName}...`);
|
|
1895
|
+
try {
|
|
1896
|
+
await execFileAsync("npm", ["install", "--production"], { cwd: targetPath });
|
|
1897
|
+
s.stop(`Installed dependencies for ${skillName}`);
|
|
1898
|
+
} catch (npmErr) {
|
|
1899
|
+
s.stop(colors.warning(`Dependencies failed for ${skillName}`));
|
|
1900
|
+
console.log(colors.muted("Run manually: npm install in " + targetPath));
|
|
1901
|
+
}
|
|
1902
|
+
s.start(`Finishing ${skillName} installation...`);
|
|
1903
|
+
}
|
|
1883
1904
|
}
|
|
1884
1905
|
const metadata = {
|
|
1885
1906
|
name: skillName,
|
|
@@ -3338,6 +3359,7 @@ import { resolve as resolve4 } from "path";
|
|
|
3338
3359
|
import {
|
|
3339
3360
|
ContextManager as ContextManager2,
|
|
3340
3361
|
RecommendationEngine,
|
|
3362
|
+
ReasoningRecommendationEngine,
|
|
3341
3363
|
buildSkillIndex,
|
|
3342
3364
|
saveIndex,
|
|
3343
3365
|
loadIndex as loadIndexFromCache,
|
|
@@ -3369,7 +3391,10 @@ var RecommendCommand = class extends Command15 {
|
|
|
3369
3391
|
["Show detailed reasons", "$0 recommend --verbose"],
|
|
3370
3392
|
["Update skill index", "$0 recommend --update"],
|
|
3371
3393
|
["Search for skills by task", '$0 recommend --task "authentication"'],
|
|
3372
|
-
["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"]
|
|
3373
3398
|
]
|
|
3374
3399
|
});
|
|
3375
3400
|
// Limit number of results
|
|
@@ -3416,11 +3441,45 @@ var RecommendCommand = class extends Command15 {
|
|
|
3416
3441
|
quiet = Option14.Boolean("--quiet,-q", false, {
|
|
3417
3442
|
description: "Minimal output"
|
|
3418
3443
|
});
|
|
3444
|
+
// Explain mode (reasoning-based recommendations)
|
|
3445
|
+
explain = Option14.Boolean("--explain,-e", false, {
|
|
3446
|
+
description: "Show detailed explanations for recommendations (uses reasoning engine)"
|
|
3447
|
+
});
|
|
3448
|
+
// Reasoning mode
|
|
3449
|
+
reasoning = Option14.Boolean("--reasoning,-r", false, {
|
|
3450
|
+
description: "Use LLM-based reasoning for recommendations"
|
|
3451
|
+
});
|
|
3452
|
+
// Show tree path
|
|
3453
|
+
showPath = Option14.Boolean("--show-path", false, {
|
|
3454
|
+
description: "Show category path for each recommendation"
|
|
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
|
+
});
|
|
3419
3472
|
async execute() {
|
|
3420
3473
|
const targetPath = resolve4(this.projectPath || process.cwd());
|
|
3421
3474
|
if (this.update) {
|
|
3422
3475
|
return await this.updateIndex();
|
|
3423
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
|
+
}
|
|
3424
3483
|
if (!this.quiet && !this.json) {
|
|
3425
3484
|
header("Skill Recommendations");
|
|
3426
3485
|
}
|
|
@@ -3437,10 +3496,16 @@ var RecommendCommand = class extends Command15 {
|
|
|
3437
3496
|
this.showProjectProfile(profile);
|
|
3438
3497
|
return 0;
|
|
3439
3498
|
}
|
|
3499
|
+
if (this.explain || this.reasoning || this.showPath) {
|
|
3500
|
+
return await this.handleReasoningRecommendations(profile, index);
|
|
3501
|
+
}
|
|
3440
3502
|
const engine = new RecommendationEngine();
|
|
3441
3503
|
engine.loadIndex(index);
|
|
3442
3504
|
const searchQuery = this.search || this.task;
|
|
3443
3505
|
if (searchQuery) {
|
|
3506
|
+
if (this.hybrid) {
|
|
3507
|
+
return await this.handleHybridSearch(engine, searchQuery);
|
|
3508
|
+
}
|
|
3444
3509
|
return this.handleSearch(engine, searchQuery);
|
|
3445
3510
|
}
|
|
3446
3511
|
const result = engine.recommend(profile, {
|
|
@@ -3457,6 +3522,53 @@ var RecommendCommand = class extends Command15 {
|
|
|
3457
3522
|
this.displayRecommendations(result.recommendations, profile, result.totalSkillsScanned);
|
|
3458
3523
|
return 0;
|
|
3459
3524
|
}
|
|
3525
|
+
async handleReasoningRecommendations(profile, index) {
|
|
3526
|
+
const s = !this.quiet && !this.json ? spinner2() : null;
|
|
3527
|
+
s?.start("Analyzing with reasoning engine...");
|
|
3528
|
+
try {
|
|
3529
|
+
const engine = new ReasoningRecommendationEngine();
|
|
3530
|
+
engine.loadIndex(index);
|
|
3531
|
+
await engine.initReasoning();
|
|
3532
|
+
const result = await engine.recommendWithReasoning(profile, {
|
|
3533
|
+
limit: this.limit ? parseInt(this.limit, 10) : 10,
|
|
3534
|
+
minScore: this.minScore ? parseInt(this.minScore, 10) : 30,
|
|
3535
|
+
categories: this.category,
|
|
3536
|
+
excludeInstalled: !this.includeInstalled,
|
|
3537
|
+
includeReasons: this.verbose,
|
|
3538
|
+
reasoning: this.reasoning,
|
|
3539
|
+
explainResults: this.explain,
|
|
3540
|
+
useTree: true
|
|
3541
|
+
});
|
|
3542
|
+
s?.stop("Analysis complete");
|
|
3543
|
+
if (this.json) {
|
|
3544
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3545
|
+
return 0;
|
|
3546
|
+
}
|
|
3547
|
+
this.displayExplainedRecommendations(
|
|
3548
|
+
result.recommendations,
|
|
3549
|
+
profile,
|
|
3550
|
+
result.totalSkillsScanned,
|
|
3551
|
+
result.reasoningSummary
|
|
3552
|
+
);
|
|
3553
|
+
return 0;
|
|
3554
|
+
} catch (err) {
|
|
3555
|
+
s?.stop(colors.error("Reasoning analysis failed"));
|
|
3556
|
+
console.log(colors.muted(err instanceof Error ? err.message : String(err)));
|
|
3557
|
+
console.log(colors.muted("Falling back to standard recommendations..."));
|
|
3558
|
+
console.log("");
|
|
3559
|
+
const engine = new RecommendationEngine();
|
|
3560
|
+
engine.loadIndex(index);
|
|
3561
|
+
const result = engine.recommend(profile, {
|
|
3562
|
+
limit: this.limit ? parseInt(this.limit, 10) : 10,
|
|
3563
|
+
minScore: this.minScore ? parseInt(this.minScore, 10) : 30,
|
|
3564
|
+
categories: this.category,
|
|
3565
|
+
excludeInstalled: !this.includeInstalled,
|
|
3566
|
+
includeReasons: this.verbose
|
|
3567
|
+
});
|
|
3568
|
+
this.displayRecommendations(result.recommendations, profile, result.totalSkillsScanned);
|
|
3569
|
+
return 0;
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3460
3572
|
async getProjectProfile(projectPath) {
|
|
3461
3573
|
const manager = new ContextManager2(projectPath);
|
|
3462
3574
|
let context = manager.get();
|
|
@@ -3539,6 +3651,178 @@ var RecommendCommand = class extends Command15 {
|
|
|
3539
3651
|
}
|
|
3540
3652
|
console.log(colors.muted("Install with: skillkit install <source>"));
|
|
3541
3653
|
}
|
|
3654
|
+
displayExplainedRecommendations(recommendations, profile, totalScanned, reasoningSummary) {
|
|
3655
|
+
this.showProjectProfile(profile);
|
|
3656
|
+
if (reasoningSummary && !this.quiet) {
|
|
3657
|
+
console.log(colors.dim("Reasoning: ") + colors.muted(reasoningSummary));
|
|
3658
|
+
console.log("");
|
|
3659
|
+
}
|
|
3660
|
+
if (recommendations.length === 0) {
|
|
3661
|
+
warn("No matching skills found.");
|
|
3662
|
+
console.log(colors.muted("Try lowering the minimum score with --min-score"));
|
|
3663
|
+
return;
|
|
3664
|
+
}
|
|
3665
|
+
console.log(colors.bold(`Explained Recommendations (${recommendations.length} of ${totalScanned} scanned):`));
|
|
3666
|
+
console.log("");
|
|
3667
|
+
for (const rec of recommendations) {
|
|
3668
|
+
let scoreColor;
|
|
3669
|
+
if (rec.score >= 70) {
|
|
3670
|
+
scoreColor = colors.success;
|
|
3671
|
+
} else if (rec.score >= 50) {
|
|
3672
|
+
scoreColor = colors.warning;
|
|
3673
|
+
} else {
|
|
3674
|
+
scoreColor = colors.muted;
|
|
3675
|
+
}
|
|
3676
|
+
const scoreBar = progressBar(rec.score, 100, 10);
|
|
3677
|
+
const qualityScore = rec.skill.quality ?? null;
|
|
3678
|
+
const qualityDisplay = qualityScore !== null && qualityScore !== void 0 ? ` ${formatQualityBadge(qualityScore)}` : "";
|
|
3679
|
+
console.log(` ${scoreColor(`${rec.score}%`)} ${colors.dim(scoreBar)} ${colors.bold(rec.skill.name)}${qualityDisplay}`);
|
|
3680
|
+
if (this.showPath && rec.treePath && rec.treePath.length > 0) {
|
|
3681
|
+
console.log(` ${colors.accent("Path:")} ${rec.treePath.join(" > ")}`);
|
|
3682
|
+
}
|
|
3683
|
+
if (rec.skill.description) {
|
|
3684
|
+
console.log(` ${colors.muted(truncate3(rec.skill.description, 70))}`);
|
|
3685
|
+
}
|
|
3686
|
+
if (rec.skill.source) {
|
|
3687
|
+
console.log(` ${colors.dim("Source:")} ${rec.skill.source}`);
|
|
3688
|
+
}
|
|
3689
|
+
if (this.explain && rec.explanation) {
|
|
3690
|
+
console.log(colors.dim(" Why this skill:"));
|
|
3691
|
+
if (rec.explanation.matchedBecause.length > 0) {
|
|
3692
|
+
console.log(` ${colors.success("\u251C\u2500")} Matched: ${rec.explanation.matchedBecause.join(", ")}`);
|
|
3693
|
+
}
|
|
3694
|
+
if (rec.explanation.relevantFor.length > 0) {
|
|
3695
|
+
console.log(` ${colors.accent("\u251C\u2500")} Relevant for: ${rec.explanation.relevantFor.join(", ")}`);
|
|
3696
|
+
}
|
|
3697
|
+
if (rec.explanation.confidence) {
|
|
3698
|
+
const confidenceColor = rec.explanation.confidence === "high" ? colors.success : rec.explanation.confidence === "medium" ? colors.warning : colors.muted;
|
|
3699
|
+
console.log(` ${colors.dim("\u2514\u2500")} Confidence: ${confidenceColor(rec.explanation.confidence)}`);
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
if (this.verbose && rec.reasons.length > 0) {
|
|
3703
|
+
console.log(colors.dim(" Score breakdown:"));
|
|
3704
|
+
for (const reason of rec.reasons.filter((r) => r.weight > 0)) {
|
|
3705
|
+
console.log(` ${colors.muted(symbols.stepActive)} ${reason.description} (+${reason.weight})`);
|
|
3706
|
+
}
|
|
3707
|
+
if (qualityScore !== null && qualityScore !== void 0) {
|
|
3708
|
+
const grade = getQualityGradeFromScore(qualityScore);
|
|
3709
|
+
console.log(` ${colors.muted(symbols.stepActive)} Quality: ${qualityScore}/100 (${grade})`);
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
if (rec.warnings.length > 0) {
|
|
3713
|
+
for (const warning of rec.warnings) {
|
|
3714
|
+
console.log(` ${colors.warning(symbols.warning)} ${warning}`);
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
console.log("");
|
|
3718
|
+
}
|
|
3719
|
+
console.log(colors.muted("Install with: skillkit install <source>"));
|
|
3720
|
+
console.log(colors.muted("More details: skillkit recommend --explain --verbose"));
|
|
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
|
+
}
|
|
3542
3826
|
handleSearch(engine, query) {
|
|
3543
3827
|
if (!this.quiet && !this.json) {
|
|
3544
3828
|
header(`Search: "${query}"`);
|
|
@@ -5506,12 +5790,12 @@ ${learning.title}
|
|
|
5506
5790
|
}
|
|
5507
5791
|
const outputPath = this.output || `.skillkit/exports/${skillName}/SKILL.md`;
|
|
5508
5792
|
const { dirname: dirname7 } = await import("path");
|
|
5509
|
-
const { existsSync: existsSync21, mkdirSync:
|
|
5793
|
+
const { existsSync: existsSync21, mkdirSync: mkdirSync10, writeFileSync: writeFileSync11 } = await import("fs");
|
|
5510
5794
|
const outputDir = dirname7(outputPath);
|
|
5511
5795
|
if (!existsSync21(outputDir)) {
|
|
5512
|
-
|
|
5796
|
+
mkdirSync10(outputDir, { recursive: true });
|
|
5513
5797
|
}
|
|
5514
|
-
|
|
5798
|
+
writeFileSync11(outputPath, skillContent, "utf-8");
|
|
5515
5799
|
console.log(chalk18.green(`\u2713 Exported learning as skill: ${skillName}`));
|
|
5516
5800
|
console.log(chalk18.gray(` Path: ${outputPath}`));
|
|
5517
5801
|
return 0;
|
|
@@ -5527,8 +5811,8 @@ ${learning.title}
|
|
|
5527
5811
|
return 1;
|
|
5528
5812
|
}
|
|
5529
5813
|
const { existsSync: existsSync21, readFileSync: readFileSync11 } = await import("fs");
|
|
5530
|
-
const { resolve:
|
|
5531
|
-
const fullPath =
|
|
5814
|
+
const { resolve: resolve21 } = await import("path");
|
|
5815
|
+
const fullPath = resolve21(inputPath);
|
|
5532
5816
|
if (!existsSync21(fullPath)) {
|
|
5533
5817
|
console.error(chalk18.red(`File not found: ${fullPath}`));
|
|
5534
5818
|
return 1;
|
|
@@ -5786,7 +6070,7 @@ ${learning.title}
|
|
|
5786
6070
|
// src/commands/settings.ts
|
|
5787
6071
|
import { Command as Command27, Option as Option26 } from "clipanion";
|
|
5788
6072
|
import chalk19 from "chalk";
|
|
5789
|
-
import { loadConfig as
|
|
6073
|
+
import { loadConfig as loadConfig2, saveConfig } from "@skillkit/core";
|
|
5790
6074
|
var VALID_AGENTS = [
|
|
5791
6075
|
"claude-code",
|
|
5792
6076
|
"cursor",
|
|
@@ -5846,7 +6130,7 @@ var SettingsCommand = class extends Command27 {
|
|
|
5846
6130
|
description: "Reset all settings to defaults"
|
|
5847
6131
|
});
|
|
5848
6132
|
async execute() {
|
|
5849
|
-
const config =
|
|
6133
|
+
const config = loadConfig2(this.global);
|
|
5850
6134
|
if (this.reset) {
|
|
5851
6135
|
const defaultConfig = {
|
|
5852
6136
|
version: 1,
|
|
@@ -6608,14 +6892,14 @@ var TeamCommand = class extends Command29 {
|
|
|
6608
6892
|
}
|
|
6609
6893
|
const projectPath = process.cwd();
|
|
6610
6894
|
const bundlePath = join12(projectPath, ".skillkit", "bundles", `${this.name}.json`);
|
|
6611
|
-
const { existsSync: existsSync21, readFileSync: readFileSync11, writeFileSync:
|
|
6895
|
+
const { existsSync: existsSync21, readFileSync: readFileSync11, writeFileSync: writeFileSync11 } = await import("fs");
|
|
6612
6896
|
if (!existsSync21(bundlePath)) {
|
|
6613
6897
|
this.context.stderr.write(chalk21.red(`Bundle "${this.name}" not found. Create it first with bundle-create.
|
|
6614
6898
|
`));
|
|
6615
6899
|
return 1;
|
|
6616
6900
|
}
|
|
6617
6901
|
const content = readFileSync11(bundlePath, "utf-8");
|
|
6618
|
-
|
|
6902
|
+
writeFileSync11(this.output, content, "utf-8");
|
|
6619
6903
|
this.context.stdout.write(chalk21.green(`\u2713 Bundle exported to: ${this.output}
|
|
6620
6904
|
`));
|
|
6621
6905
|
return 0;
|
|
@@ -6806,8 +7090,8 @@ var PluginCommand = class extends Command30 {
|
|
|
6806
7090
|
if (!existsSync15(pluginsDir)) {
|
|
6807
7091
|
mkdirSync7(pluginsDir, { recursive: true });
|
|
6808
7092
|
}
|
|
6809
|
-
const { statSync:
|
|
6810
|
-
const sourceStat =
|
|
7093
|
+
const { statSync: statSync4 } = await import("fs");
|
|
7094
|
+
const sourceStat = statSync4(resolvedSource);
|
|
6811
7095
|
if (sourceStat.isDirectory()) {
|
|
6812
7096
|
cpSync3(resolvedSource, targetDir, { recursive: true });
|
|
6813
7097
|
} else {
|
|
@@ -9148,27 +9432,245 @@ Recent Errors (${stats.recentErrors.length}):`));
|
|
|
9148
9432
|
};
|
|
9149
9433
|
|
|
9150
9434
|
// src/commands/publish.ts
|
|
9151
|
-
import { existsSync as existsSync17, readFileSync as readFileSync9 } from "fs";
|
|
9152
|
-
import { join as join16, basename as basename4, dirname as dirname6 } from "path";
|
|
9153
|
-
import { execSync } from "child_process";
|
|
9435
|
+
import { existsSync as existsSync17, readFileSync as readFileSync9, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
9436
|
+
import { join as join16, basename as basename4, dirname as dirname6, resolve as resolve18 } from "path";
|
|
9154
9437
|
import chalk27 from "chalk";
|
|
9155
9438
|
import { Command as Command37, Option as Option36 } from "clipanion";
|
|
9439
|
+
import { generateWellKnownIndex } from "@skillkit/core";
|
|
9440
|
+
function sanitizeSkillName(name) {
|
|
9441
|
+
if (!name || typeof name !== "string") return null;
|
|
9442
|
+
const base = basename4(name);
|
|
9443
|
+
if (base !== name || name.includes("..") || name.includes("/") || name.includes("\\")) {
|
|
9444
|
+
return null;
|
|
9445
|
+
}
|
|
9446
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(name)) {
|
|
9447
|
+
return null;
|
|
9448
|
+
}
|
|
9449
|
+
return name;
|
|
9450
|
+
}
|
|
9156
9451
|
var PublishCommand = class extends Command37 {
|
|
9157
9452
|
static paths = [["publish"]];
|
|
9158
9453
|
static usage = Command37.Usage({
|
|
9159
|
-
description: "
|
|
9454
|
+
description: "Generate well-known skills structure for hosting",
|
|
9455
|
+
details: `
|
|
9456
|
+
This command generates the RFC 8615 well-known URI structure for hosting skills.
|
|
9457
|
+
|
|
9458
|
+
The output includes:
|
|
9459
|
+
- .well-known/skills/index.json - Skill manifest for auto-discovery
|
|
9460
|
+
- .well-known/skills/{skill-name}/SKILL.md - Individual skill files
|
|
9461
|
+
|
|
9462
|
+
Users can then install skills via: skillkit add https://your-domain.com
|
|
9463
|
+
`,
|
|
9464
|
+
examples: [
|
|
9465
|
+
["Generate from current directory", "$0 publish"],
|
|
9466
|
+
["Generate from specific path", "$0 publish ./my-skills"],
|
|
9467
|
+
["Generate to custom output directory", "$0 publish --output ./public"],
|
|
9468
|
+
["Preview without writing", "$0 publish --dry-run"]
|
|
9469
|
+
]
|
|
9470
|
+
});
|
|
9471
|
+
skillPath = Option36.String({ required: false, name: "path" });
|
|
9472
|
+
output = Option36.String("--output,-o", {
|
|
9473
|
+
description: "Output directory for well-known structure (default: current directory)"
|
|
9474
|
+
});
|
|
9475
|
+
dryRun = Option36.Boolean("--dry-run,-n", false, {
|
|
9476
|
+
description: "Show what would be generated without writing files"
|
|
9477
|
+
});
|
|
9478
|
+
async execute() {
|
|
9479
|
+
const basePath = this.skillPath || process.cwd();
|
|
9480
|
+
const outputDir = this.output || basePath;
|
|
9481
|
+
console.log(chalk27.cyan("Generating well-known skills structure...\n"));
|
|
9482
|
+
const discoveredSkills = this.discoverSkills(basePath);
|
|
9483
|
+
if (discoveredSkills.length === 0) {
|
|
9484
|
+
console.error(chalk27.red("No skills found"));
|
|
9485
|
+
console.error(chalk27.dim("Skills must contain a SKILL.md file with frontmatter"));
|
|
9486
|
+
return 1;
|
|
9487
|
+
}
|
|
9488
|
+
console.log(chalk27.white(`Found ${discoveredSkills.length} skill(s):
|
|
9489
|
+
`));
|
|
9490
|
+
const wellKnownSkills = [];
|
|
9491
|
+
const validSkills = [];
|
|
9492
|
+
for (const skill of discoveredSkills) {
|
|
9493
|
+
const safeName = sanitizeSkillName(skill.name);
|
|
9494
|
+
if (!safeName) {
|
|
9495
|
+
console.log(chalk27.yellow(` ${chalk27.yellow("\u26A0")} Skipping "${skill.name}" (invalid name - must be alphanumeric with hyphens/underscores)`));
|
|
9496
|
+
continue;
|
|
9497
|
+
}
|
|
9498
|
+
const files = this.getSkillFiles(skill.path);
|
|
9499
|
+
console.log(chalk27.dim(` ${chalk27.green("\u25CF")} ${safeName}`));
|
|
9500
|
+
console.log(chalk27.dim(` Description: ${skill.description || "No description"}`));
|
|
9501
|
+
console.log(chalk27.dim(` Files: ${files.join(", ")}`));
|
|
9502
|
+
validSkills.push({ name: skill.name, safeName, description: skill.description, path: skill.path });
|
|
9503
|
+
wellKnownSkills.push({
|
|
9504
|
+
name: safeName,
|
|
9505
|
+
description: skill.description,
|
|
9506
|
+
files
|
|
9507
|
+
});
|
|
9508
|
+
}
|
|
9509
|
+
if (validSkills.length === 0) {
|
|
9510
|
+
console.error(chalk27.red("\nNo valid skills to publish"));
|
|
9511
|
+
return 1;
|
|
9512
|
+
}
|
|
9513
|
+
console.log("");
|
|
9514
|
+
if (this.dryRun) {
|
|
9515
|
+
console.log(chalk27.yellow("Dry run - not writing files\n"));
|
|
9516
|
+
console.log(chalk27.white("Would generate:"));
|
|
9517
|
+
console.log(chalk27.dim(` ${outputDir}/.well-known/skills/index.json`));
|
|
9518
|
+
for (const skill of wellKnownSkills) {
|
|
9519
|
+
for (const file of skill.files) {
|
|
9520
|
+
console.log(chalk27.dim(` ${outputDir}/.well-known/skills/${skill.name}/${file}`));
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
console.log("");
|
|
9524
|
+
console.log(chalk27.white("index.json preview:"));
|
|
9525
|
+
console.log(JSON.stringify(generateWellKnownIndex(wellKnownSkills), null, 2));
|
|
9526
|
+
return 0;
|
|
9527
|
+
}
|
|
9528
|
+
const wellKnownDir = join16(outputDir, ".well-known", "skills");
|
|
9529
|
+
mkdirSync8(wellKnownDir, { recursive: true });
|
|
9530
|
+
for (const skill of validSkills) {
|
|
9531
|
+
const skillDir = join16(wellKnownDir, skill.safeName);
|
|
9532
|
+
const resolvedSkillDir = resolve18(skillDir);
|
|
9533
|
+
const resolvedWellKnownDir = resolve18(wellKnownDir);
|
|
9534
|
+
if (!resolvedSkillDir.startsWith(resolvedWellKnownDir)) {
|
|
9535
|
+
console.log(chalk27.yellow(` Skipping "${skill.name}" (path traversal detected)`));
|
|
9536
|
+
continue;
|
|
9537
|
+
}
|
|
9538
|
+
mkdirSync8(skillDir, { recursive: true });
|
|
9539
|
+
const files = this.getSkillFiles(skill.path);
|
|
9540
|
+
for (const file of files) {
|
|
9541
|
+
const safeFile = basename4(file);
|
|
9542
|
+
const sourcePath = join16(skill.path, file);
|
|
9543
|
+
const destPath = join16(skillDir, safeFile);
|
|
9544
|
+
const content = readFileSync9(sourcePath, "utf-8");
|
|
9545
|
+
writeFileSync8(destPath, content);
|
|
9546
|
+
}
|
|
9547
|
+
}
|
|
9548
|
+
const index = generateWellKnownIndex(wellKnownSkills);
|
|
9549
|
+
writeFileSync8(join16(wellKnownDir, "index.json"), JSON.stringify(index, null, 2));
|
|
9550
|
+
console.log(chalk27.green("Generated well-known structure:\n"));
|
|
9551
|
+
console.log(chalk27.dim(` ${wellKnownDir}/index.json`));
|
|
9552
|
+
for (const skill of wellKnownSkills) {
|
|
9553
|
+
console.log(chalk27.dim(` ${wellKnownDir}/${skill.name}/`));
|
|
9554
|
+
}
|
|
9555
|
+
console.log("");
|
|
9556
|
+
console.log(chalk27.cyan("Next steps:"));
|
|
9557
|
+
console.log(chalk27.dim(" 1. Deploy the .well-known directory to your web server"));
|
|
9558
|
+
console.log(chalk27.dim(" 2. Users can install via: skillkit add https://your-domain.com"));
|
|
9559
|
+
console.log(chalk27.dim(" 3. Skills auto-discovered from /.well-known/skills/index.json"));
|
|
9560
|
+
return 0;
|
|
9561
|
+
}
|
|
9562
|
+
discoverSkills(basePath) {
|
|
9563
|
+
const skills = [];
|
|
9564
|
+
const skillMdPath = join16(basePath, "SKILL.md");
|
|
9565
|
+
if (existsSync17(skillMdPath)) {
|
|
9566
|
+
const content = readFileSync9(skillMdPath, "utf-8");
|
|
9567
|
+
const frontmatter = this.parseFrontmatter(content);
|
|
9568
|
+
skills.push({
|
|
9569
|
+
name: frontmatter.name || basename4(basePath),
|
|
9570
|
+
description: frontmatter.description,
|
|
9571
|
+
path: basePath
|
|
9572
|
+
});
|
|
9573
|
+
return skills;
|
|
9574
|
+
}
|
|
9575
|
+
const searchDirs = [
|
|
9576
|
+
basePath,
|
|
9577
|
+
join16(basePath, "skills"),
|
|
9578
|
+
join16(basePath, ".claude", "skills")
|
|
9579
|
+
];
|
|
9580
|
+
for (const searchDir of searchDirs) {
|
|
9581
|
+
if (!existsSync17(searchDir)) continue;
|
|
9582
|
+
const entries = readdirSync3(searchDir);
|
|
9583
|
+
for (const entry of entries) {
|
|
9584
|
+
const entryPath = join16(searchDir, entry);
|
|
9585
|
+
if (!statSync3(entryPath).isDirectory()) continue;
|
|
9586
|
+
const entrySkillMd = join16(entryPath, "SKILL.md");
|
|
9587
|
+
if (existsSync17(entrySkillMd)) {
|
|
9588
|
+
const content = readFileSync9(entrySkillMd, "utf-8");
|
|
9589
|
+
const frontmatter = this.parseFrontmatter(content);
|
|
9590
|
+
skills.push({
|
|
9591
|
+
name: frontmatter.name || entry,
|
|
9592
|
+
description: frontmatter.description,
|
|
9593
|
+
path: entryPath
|
|
9594
|
+
});
|
|
9595
|
+
}
|
|
9596
|
+
}
|
|
9597
|
+
}
|
|
9598
|
+
return skills;
|
|
9599
|
+
}
|
|
9600
|
+
getSkillFiles(skillPath) {
|
|
9601
|
+
const files = [];
|
|
9602
|
+
const entries = readdirSync3(skillPath);
|
|
9603
|
+
for (const entry of entries) {
|
|
9604
|
+
const entryPath = join16(skillPath, entry);
|
|
9605
|
+
if (statSync3(entryPath).isFile()) {
|
|
9606
|
+
if (entry.startsWith(".") || entry === ".skillkit-metadata.json") continue;
|
|
9607
|
+
files.push(entry);
|
|
9608
|
+
}
|
|
9609
|
+
}
|
|
9610
|
+
if (!files.includes("SKILL.md")) {
|
|
9611
|
+
files.unshift("SKILL.md");
|
|
9612
|
+
}
|
|
9613
|
+
return files;
|
|
9614
|
+
}
|
|
9615
|
+
parseFrontmatter(content) {
|
|
9616
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
9617
|
+
if (!match) return {};
|
|
9618
|
+
const frontmatter = {};
|
|
9619
|
+
const lines = match[1].split(/\r?\n/);
|
|
9620
|
+
let inTagsList = false;
|
|
9621
|
+
for (const line of lines) {
|
|
9622
|
+
if (inTagsList) {
|
|
9623
|
+
const tagMatch = line.match(/^\s*-\s*(.+)$/);
|
|
9624
|
+
if (tagMatch) {
|
|
9625
|
+
frontmatter.tags ??= [];
|
|
9626
|
+
frontmatter.tags.push(tagMatch[1].trim().replace(/^["']|["']$/g, ""));
|
|
9627
|
+
continue;
|
|
9628
|
+
}
|
|
9629
|
+
if (line.trim() === "") continue;
|
|
9630
|
+
inTagsList = false;
|
|
9631
|
+
}
|
|
9632
|
+
const colonIdx = line.indexOf(":");
|
|
9633
|
+
if (colonIdx === -1) continue;
|
|
9634
|
+
const key = line.slice(0, colonIdx).trim();
|
|
9635
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
9636
|
+
switch (key) {
|
|
9637
|
+
case "name":
|
|
9638
|
+
frontmatter.name = value.replace(/^["']|["']$/g, "");
|
|
9639
|
+
break;
|
|
9640
|
+
case "description":
|
|
9641
|
+
frontmatter.description = value.replace(/^["']|["']$/g, "");
|
|
9642
|
+
break;
|
|
9643
|
+
case "version":
|
|
9644
|
+
frontmatter.version = value.replace(/^["']|["']$/g, "");
|
|
9645
|
+
break;
|
|
9646
|
+
case "tags":
|
|
9647
|
+
if (value.startsWith("[")) {
|
|
9648
|
+
frontmatter.tags = value.slice(1, -1).split(",").map((t) => t.trim().replace(/^["']|["']$/g, "")).filter((t) => t.length > 0);
|
|
9649
|
+
} else if (value === "") {
|
|
9650
|
+
inTagsList = true;
|
|
9651
|
+
frontmatter.tags = [];
|
|
9652
|
+
}
|
|
9653
|
+
break;
|
|
9654
|
+
}
|
|
9655
|
+
}
|
|
9656
|
+
return frontmatter;
|
|
9657
|
+
}
|
|
9658
|
+
};
|
|
9659
|
+
var PublishSubmitCommand = class extends Command37 {
|
|
9660
|
+
static paths = [["publish", "submit"]];
|
|
9661
|
+
static usage = Command37.Usage({
|
|
9662
|
+
description: "Submit skill to SkillKit marketplace (requires review)",
|
|
9160
9663
|
examples: [
|
|
9161
|
-
["
|
|
9162
|
-
["
|
|
9163
|
-
["Publish with custom name", "$0 publish --name my-awesome-skill"]
|
|
9664
|
+
["Submit skill from current directory", "$0 publish submit"],
|
|
9665
|
+
["Submit with custom name", "$0 publish submit --name my-skill"]
|
|
9164
9666
|
]
|
|
9165
9667
|
});
|
|
9166
9668
|
skillPath = Option36.String({ required: false, name: "path" });
|
|
9167
9669
|
name = Option36.String("--name,-n", {
|
|
9168
|
-
description: "Custom skill name
|
|
9670
|
+
description: "Custom skill name"
|
|
9169
9671
|
});
|
|
9170
9672
|
dryRun = Option36.Boolean("--dry-run", false, {
|
|
9171
|
-
description: "Show what would be
|
|
9673
|
+
description: "Show what would be submitted"
|
|
9172
9674
|
});
|
|
9173
9675
|
async execute() {
|
|
9174
9676
|
const skillPath = this.skillPath || process.cwd();
|
|
@@ -9176,10 +9678,9 @@ var PublishCommand = class extends Command37 {
|
|
|
9176
9678
|
if (!skillMdPath) {
|
|
9177
9679
|
console.error(chalk27.red("No SKILL.md found"));
|
|
9178
9680
|
console.error(chalk27.dim("Run this command from a directory containing SKILL.md"));
|
|
9179
|
-
console.error(chalk27.dim("Or specify the path: skillkit publish ./path/to/skill"));
|
|
9180
9681
|
return 1;
|
|
9181
9682
|
}
|
|
9182
|
-
console.log(chalk27.cyan("
|
|
9683
|
+
console.log(chalk27.cyan("Submitting skill to SkillKit marketplace...\n"));
|
|
9183
9684
|
const content = readFileSync9(skillMdPath, "utf-8");
|
|
9184
9685
|
const frontmatter = this.parseFrontmatter(content);
|
|
9185
9686
|
const skillName = this.name || frontmatter.name || basename4(dirname6(skillMdPath));
|
|
@@ -9198,9 +9699,9 @@ var PublishCommand = class extends Command37 {
|
|
|
9198
9699
|
const skillEntry = {
|
|
9199
9700
|
id: `${repoInfo.owner}/${repoInfo.repo}/${skillSlug}`,
|
|
9200
9701
|
name: this.formatName(skillName),
|
|
9201
|
-
description: frontmatter.description || `Best practices
|
|
9702
|
+
description: frontmatter.description || `Best practices for ${this.formatName(skillName)}`,
|
|
9202
9703
|
source: `${repoInfo.owner}/${repoInfo.repo}`,
|
|
9203
|
-
tags: frontmatter.tags ||
|
|
9704
|
+
tags: frontmatter.tags || ["general"]
|
|
9204
9705
|
};
|
|
9205
9706
|
console.log(chalk27.white("Skill details:"));
|
|
9206
9707
|
console.log(chalk27.dim(` ID: ${skillEntry.id}`));
|
|
@@ -9210,8 +9711,7 @@ var PublishCommand = class extends Command37 {
|
|
|
9210
9711
|
console.log(chalk27.dim(` Tags: ${skillEntry.tags.join(", ")}`));
|
|
9211
9712
|
console.log();
|
|
9212
9713
|
if (this.dryRun) {
|
|
9213
|
-
console.log(chalk27.yellow("Dry run - not
|
|
9214
|
-
console.log(chalk27.dim("JSON entry that would be added:"));
|
|
9714
|
+
console.log(chalk27.yellow("Dry run - not submitting"));
|
|
9215
9715
|
console.log(JSON.stringify(skillEntry, null, 2));
|
|
9216
9716
|
return 0;
|
|
9217
9717
|
}
|
|
@@ -9221,20 +9721,16 @@ var PublishCommand = class extends Command37 {
|
|
|
9221
9721
|
const issueUrl = `https://github.com/rohitg00/skillkit/issues/new?title=${issueTitle}&body=${issueBodyEncoded}&labels=skill-submission,publish`;
|
|
9222
9722
|
console.log(chalk27.green("Opening GitHub to submit your skill...\n"));
|
|
9223
9723
|
try {
|
|
9724
|
+
const { execSync } = await import("child_process");
|
|
9224
9725
|
const openCmd = process.platform === "darwin" ? `open "${issueUrl}"` : process.platform === "win32" ? `cmd /c start "" "${issueUrl}"` : `xdg-open "${issueUrl}"`;
|
|
9225
9726
|
execSync(openCmd, { stdio: "ignore" });
|
|
9226
9727
|
console.log(chalk27.green("GitHub issue page opened!"));
|
|
9227
|
-
console.log(chalk27.dim("Review and submit the issue
|
|
9728
|
+
console.log(chalk27.dim("Review and submit the issue."));
|
|
9228
9729
|
} catch {
|
|
9229
9730
|
console.log(chalk27.yellow("Could not open browser automatically."));
|
|
9230
9731
|
console.log(chalk27.dim("Please open this URL manually:\n"));
|
|
9231
9732
|
console.log(chalk27.cyan(issueUrl));
|
|
9232
9733
|
}
|
|
9233
|
-
console.log();
|
|
9234
|
-
console.log(chalk27.cyan("Next steps:"));
|
|
9235
|
-
console.log(chalk27.dim(" 1. Review the skill details in the GitHub issue"));
|
|
9236
|
-
console.log(chalk27.dim(" 2. Submit the issue"));
|
|
9237
|
-
console.log(chalk27.dim(" 3. A maintainer will review and add your skill"));
|
|
9238
9734
|
return 0;
|
|
9239
9735
|
}
|
|
9240
9736
|
findSkillMd(basePath) {
|
|
@@ -9247,8 +9743,7 @@ var PublishCommand = class extends Command37 {
|
|
|
9247
9743
|
}
|
|
9248
9744
|
const locations = [
|
|
9249
9745
|
join16(basePath, "skills", "SKILL.md"),
|
|
9250
|
-
join16(basePath, ".claude", "skills", "SKILL.md")
|
|
9251
|
-
join16(basePath, ".cursor", "skills", "SKILL.md")
|
|
9746
|
+
join16(basePath, ".claude", "skills", "SKILL.md")
|
|
9252
9747
|
];
|
|
9253
9748
|
for (const loc of locations) {
|
|
9254
9749
|
if (existsSync17(loc)) {
|
|
@@ -9262,18 +9757,7 @@ var PublishCommand = class extends Command37 {
|
|
|
9262
9757
|
if (!match) return {};
|
|
9263
9758
|
const frontmatter = {};
|
|
9264
9759
|
const lines = match[1].split(/\r?\n/);
|
|
9265
|
-
let inTagsList = false;
|
|
9266
9760
|
for (const line of lines) {
|
|
9267
|
-
if (inTagsList) {
|
|
9268
|
-
const tagMatch = line.match(/^\s*-\s*(.+)$/);
|
|
9269
|
-
if (tagMatch) {
|
|
9270
|
-
frontmatter.tags ??= [];
|
|
9271
|
-
frontmatter.tags.push(tagMatch[1].trim().replace(/^["']|["']$/g, ""));
|
|
9272
|
-
continue;
|
|
9273
|
-
}
|
|
9274
|
-
if (line.trim() === "") continue;
|
|
9275
|
-
inTagsList = false;
|
|
9276
|
-
}
|
|
9277
9761
|
const colonIdx = line.indexOf(":");
|
|
9278
9762
|
if (colonIdx === -1) continue;
|
|
9279
9763
|
const key = line.slice(0, colonIdx).trim();
|
|
@@ -9291,9 +9775,6 @@ var PublishCommand = class extends Command37 {
|
|
|
9291
9775
|
case "tags":
|
|
9292
9776
|
if (value.startsWith("[")) {
|
|
9293
9777
|
frontmatter.tags = value.slice(1, -1).split(",").map((t) => t.trim().replace(/^["']|["']$/g, "")).filter((t) => t.length > 0);
|
|
9294
|
-
} else if (value === "") {
|
|
9295
|
-
inTagsList = true;
|
|
9296
|
-
frontmatter.tags = [];
|
|
9297
9778
|
}
|
|
9298
9779
|
break;
|
|
9299
9780
|
}
|
|
@@ -9305,6 +9786,7 @@ var PublishCommand = class extends Command37 {
|
|
|
9305
9786
|
}
|
|
9306
9787
|
getRepoInfo(dir) {
|
|
9307
9788
|
try {
|
|
9789
|
+
const { execSync } = __require("child_process");
|
|
9308
9790
|
const remote = execSync("git remote get-url origin", {
|
|
9309
9791
|
cwd: dir,
|
|
9310
9792
|
encoding: "utf-8",
|
|
@@ -9321,27 +9803,6 @@ var PublishCommand = class extends Command37 {
|
|
|
9321
9803
|
formatName(name) {
|
|
9322
9804
|
return name.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
9323
9805
|
}
|
|
9324
|
-
inferTags(name, description) {
|
|
9325
|
-
const tags = [];
|
|
9326
|
-
const text3 = `${name} ${description}`.toLowerCase();
|
|
9327
|
-
const tagMap = {
|
|
9328
|
-
react: ["react", "jsx", "tsx"],
|
|
9329
|
-
typescript: ["typescript", "ts"],
|
|
9330
|
-
nextjs: ["next", "nextjs"],
|
|
9331
|
-
testing: ["test", "jest", "vitest"],
|
|
9332
|
-
mobile: ["mobile", "react-native", "expo"],
|
|
9333
|
-
backend: ["backend", "api", "server"],
|
|
9334
|
-
database: ["database", "postgres", "mysql", "supabase"],
|
|
9335
|
-
frontend: ["frontend", "ui", "design"],
|
|
9336
|
-
devops: ["devops", "ci", "cd", "docker"]
|
|
9337
|
-
};
|
|
9338
|
-
for (const [tag, keywords] of Object.entries(tagMap)) {
|
|
9339
|
-
if (keywords.some((k) => text3.includes(k))) {
|
|
9340
|
-
tags.push(tag);
|
|
9341
|
-
}
|
|
9342
|
-
}
|
|
9343
|
-
return tags.length > 0 ? tags : ["general"];
|
|
9344
|
-
}
|
|
9345
9806
|
createIssueBody(skill) {
|
|
9346
9807
|
return `## Publish Skill Request
|
|
9347
9808
|
|
|
@@ -9364,15 +9825,15 @@ ${JSON.stringify(skill, null, 2)}
|
|
|
9364
9825
|
- [ ] Tags are appropriate
|
|
9365
9826
|
|
|
9366
9827
|
---
|
|
9367
|
-
Submitted via \`skillkit publish\``;
|
|
9828
|
+
Submitted via \`skillkit publish submit\``;
|
|
9368
9829
|
}
|
|
9369
9830
|
};
|
|
9370
9831
|
|
|
9371
9832
|
// src/commands/agent.ts
|
|
9372
9833
|
import chalk28 from "chalk";
|
|
9373
9834
|
import { Command as Command38, Option as Option37 } from "clipanion";
|
|
9374
|
-
import { existsSync as existsSync18, mkdirSync as
|
|
9375
|
-
import { join as join17 } from "path";
|
|
9835
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
9836
|
+
import { join as join17, basename as basename5 } from "path";
|
|
9376
9837
|
import { homedir as homedir3 } from "os";
|
|
9377
9838
|
import {
|
|
9378
9839
|
findAllAgents,
|
|
@@ -9381,7 +9842,10 @@ import {
|
|
|
9381
9842
|
discoverAgentsFromPath,
|
|
9382
9843
|
validateAgent,
|
|
9383
9844
|
translateAgent,
|
|
9384
|
-
getAgentTargetDirectory
|
|
9845
|
+
getAgentTargetDirectory,
|
|
9846
|
+
discoverSkills as discoverSkills2,
|
|
9847
|
+
readSkillContent as readSkillContent2,
|
|
9848
|
+
generateSubagentFromSkill
|
|
9385
9849
|
} from "@skillkit/core";
|
|
9386
9850
|
import {
|
|
9387
9851
|
getBundledAgents,
|
|
@@ -9402,17 +9866,19 @@ var AgentCommand = class extends Command38 {
|
|
|
9402
9866
|
that can be invoked with @mentions or the --agent flag.
|
|
9403
9867
|
|
|
9404
9868
|
Sub-commands:
|
|
9405
|
-
agent list
|
|
9406
|
-
agent show
|
|
9407
|
-
agent create
|
|
9408
|
-
agent
|
|
9409
|
-
agent
|
|
9410
|
-
agent
|
|
9869
|
+
agent list - List all installed agents
|
|
9870
|
+
agent show - Show agent details
|
|
9871
|
+
agent create - Create a new agent
|
|
9872
|
+
agent from-skill - Convert a skill to a subagent
|
|
9873
|
+
agent translate - Translate agents between formats
|
|
9874
|
+
agent sync - Sync agents to target AI agent
|
|
9875
|
+
agent validate - Validate agent definitions
|
|
9411
9876
|
`,
|
|
9412
9877
|
examples: [
|
|
9413
9878
|
["List all agents", "$0 agent list"],
|
|
9414
9879
|
["Show agent details", "$0 agent show architect"],
|
|
9415
9880
|
["Create new agent", "$0 agent create security-reviewer"],
|
|
9881
|
+
["Convert skill to subagent", "$0 agent from-skill code-simplifier"],
|
|
9416
9882
|
["Translate to Cursor format", "$0 agent translate --to cursor"],
|
|
9417
9883
|
["Sync agents", "$0 agent sync --agent claude-code"]
|
|
9418
9884
|
]
|
|
@@ -9422,6 +9888,7 @@ var AgentCommand = class extends Command38 {
|
|
|
9422
9888
|
console.log(" agent list List all installed agents");
|
|
9423
9889
|
console.log(" agent show <name> Show agent details");
|
|
9424
9890
|
console.log(" agent create <name> Create a new agent");
|
|
9891
|
+
console.log(" agent from-skill <name> Convert a skill to a subagent");
|
|
9425
9892
|
console.log(" agent translate Translate agents between formats");
|
|
9426
9893
|
console.log(" agent sync Sync agents to target AI agent");
|
|
9427
9894
|
console.log(" agent validate [path] Validate agent definitions");
|
|
@@ -9595,7 +10062,7 @@ var AgentCreateCommand = class extends Command38 {
|
|
|
9595
10062
|
targetDir = join17(process.cwd(), ".claude", "agents");
|
|
9596
10063
|
}
|
|
9597
10064
|
if (!existsSync18(targetDir)) {
|
|
9598
|
-
|
|
10065
|
+
mkdirSync9(targetDir, { recursive: true });
|
|
9599
10066
|
}
|
|
9600
10067
|
const agentPath = join17(targetDir, `${this.name}.md`);
|
|
9601
10068
|
if (existsSync18(agentPath)) {
|
|
@@ -9604,7 +10071,7 @@ var AgentCreateCommand = class extends Command38 {
|
|
|
9604
10071
|
}
|
|
9605
10072
|
const description = this.description || `${this.name} agent`;
|
|
9606
10073
|
const content = generateAgentTemplate(this.name, description, this.model);
|
|
9607
|
-
|
|
10074
|
+
writeFileSync9(agentPath, content);
|
|
9608
10075
|
console.log(chalk28.green(`Created agent: ${agentPath}`));
|
|
9609
10076
|
console.log();
|
|
9610
10077
|
console.log(chalk28.dim("Edit the file to customize the agent system prompt."));
|
|
@@ -9713,9 +10180,9 @@ var AgentTranslateCommand = class extends Command38 {
|
|
|
9713
10180
|
}
|
|
9714
10181
|
} else {
|
|
9715
10182
|
if (!existsSync18(outputDir)) {
|
|
9716
|
-
|
|
10183
|
+
mkdirSync9(outputDir, { recursive: true });
|
|
9717
10184
|
}
|
|
9718
|
-
|
|
10185
|
+
writeFileSync9(outputPath, result.content);
|
|
9719
10186
|
console.log(chalk28.green(`\u2713 ${agent.name} \u2192 ${outputPath}`));
|
|
9720
10187
|
}
|
|
9721
10188
|
successCount++;
|
|
@@ -9759,13 +10226,13 @@ var AgentSyncCommand = class extends Command38 {
|
|
|
9759
10226
|
const outputDir = getAgentTargetDirectory(process.cwd(), targetAgent);
|
|
9760
10227
|
console.log(chalk28.blue(`\u2192 ${targetAgent} (${outputDir})`));
|
|
9761
10228
|
if (!existsSync18(outputDir)) {
|
|
9762
|
-
|
|
10229
|
+
mkdirSync9(outputDir, { recursive: true });
|
|
9763
10230
|
}
|
|
9764
10231
|
for (const agent of agents) {
|
|
9765
10232
|
const result = translateAgent(agent, targetAgent);
|
|
9766
10233
|
if (result.success) {
|
|
9767
10234
|
const outputPath = join17(outputDir, result.filename);
|
|
9768
|
-
|
|
10235
|
+
writeFileSync9(outputPath, result.content);
|
|
9769
10236
|
console.log(chalk28.green(` \u2713 ${agent.name}`));
|
|
9770
10237
|
} else {
|
|
9771
10238
|
console.log(chalk28.red(` \u2717 ${agent.name}`));
|
|
@@ -10039,9 +10506,148 @@ var AgentAvailableCommand = class extends Command38 {
|
|
|
10039
10506
|
return 0;
|
|
10040
10507
|
}
|
|
10041
10508
|
};
|
|
10509
|
+
var AgentFromSkillCommand = class extends Command38 {
|
|
10510
|
+
static paths = [["agent", "from-skill"]];
|
|
10511
|
+
static usage = Command38.Usage({
|
|
10512
|
+
description: "Convert a skill into a Claude Code subagent",
|
|
10513
|
+
details: `
|
|
10514
|
+
Converts a SkillKit skill into a Claude Code native subagent format.
|
|
10515
|
+
The generated .md file can be used with @mentions in Claude Code.
|
|
10516
|
+
|
|
10517
|
+
By default, the subagent references the skill (skills: [skill-name]).
|
|
10518
|
+
Use --inline to embed the full skill content in the system prompt.
|
|
10519
|
+
`,
|
|
10520
|
+
examples: [
|
|
10521
|
+
["Convert skill to subagent", "$0 agent from-skill code-simplifier"],
|
|
10522
|
+
["Create global subagent", "$0 agent from-skill code-simplifier --global"],
|
|
10523
|
+
["Embed skill content inline", "$0 agent from-skill code-simplifier --inline"],
|
|
10524
|
+
["Set model for subagent", "$0 agent from-skill code-simplifier --model opus"],
|
|
10525
|
+
["Preview without writing", "$0 agent from-skill code-simplifier --dry-run"]
|
|
10526
|
+
]
|
|
10527
|
+
});
|
|
10528
|
+
skillName = Option37.String({ required: true });
|
|
10529
|
+
inline = Option37.Boolean("--inline,-i", false, {
|
|
10530
|
+
description: "Embed full skill content in system prompt"
|
|
10531
|
+
});
|
|
10532
|
+
model = Option37.String("--model,-m", {
|
|
10533
|
+
description: "Model to use (sonnet, opus, haiku, inherit)"
|
|
10534
|
+
});
|
|
10535
|
+
permission = Option37.String("--permission,-p", {
|
|
10536
|
+
description: "Permission mode (default, plan, auto-edit, full-auto, bypassPermissions)"
|
|
10537
|
+
});
|
|
10538
|
+
global = Option37.Boolean("--global,-g", false, {
|
|
10539
|
+
description: "Create in ~/.claude/agents/ instead of .claude/agents/"
|
|
10540
|
+
});
|
|
10541
|
+
output = Option37.String("--output,-o", {
|
|
10542
|
+
description: "Custom output filename (without .md)"
|
|
10543
|
+
});
|
|
10544
|
+
dryRun = Option37.Boolean("--dry-run,-n", false, {
|
|
10545
|
+
description: "Preview without writing files"
|
|
10546
|
+
});
|
|
10547
|
+
async execute() {
|
|
10548
|
+
const skills = discoverSkills2(process.cwd());
|
|
10549
|
+
const skill = skills.find((s) => s.name === this.skillName);
|
|
10550
|
+
if (!skill) {
|
|
10551
|
+
console.log(chalk28.red(`Skill not found: ${this.skillName}`));
|
|
10552
|
+
console.log(chalk28.dim("Available skills:"));
|
|
10553
|
+
for (const s of skills.slice(0, 10)) {
|
|
10554
|
+
console.log(chalk28.dim(` - ${s.name}`));
|
|
10555
|
+
}
|
|
10556
|
+
if (skills.length > 10) {
|
|
10557
|
+
console.log(chalk28.dim(` ... and ${skills.length - 10} more`));
|
|
10558
|
+
}
|
|
10559
|
+
return 1;
|
|
10560
|
+
}
|
|
10561
|
+
const skillContent = readSkillContent2(skill.path);
|
|
10562
|
+
if (!skillContent) {
|
|
10563
|
+
console.log(chalk28.red(`Could not read skill content: ${skill.path}`));
|
|
10564
|
+
return 1;
|
|
10565
|
+
}
|
|
10566
|
+
const options = {
|
|
10567
|
+
inline: this.inline
|
|
10568
|
+
};
|
|
10569
|
+
if (this.model) {
|
|
10570
|
+
const validModels = ["sonnet", "opus", "haiku", "inherit"];
|
|
10571
|
+
if (!validModels.includes(this.model)) {
|
|
10572
|
+
console.log(chalk28.red(`Invalid model: ${this.model}`));
|
|
10573
|
+
console.log(chalk28.dim(`Valid options: ${validModels.join(", ")}`));
|
|
10574
|
+
return 1;
|
|
10575
|
+
}
|
|
10576
|
+
options.model = this.model;
|
|
10577
|
+
}
|
|
10578
|
+
if (this.permission) {
|
|
10579
|
+
const validModes = ["default", "plan", "auto-edit", "full-auto", "bypassPermissions"];
|
|
10580
|
+
if (!validModes.includes(this.permission)) {
|
|
10581
|
+
console.log(chalk28.red(`Invalid permission mode: ${this.permission}`));
|
|
10582
|
+
console.log(chalk28.dim(`Valid options: ${validModes.join(", ")}`));
|
|
10583
|
+
return 1;
|
|
10584
|
+
}
|
|
10585
|
+
options.permissionMode = this.permission;
|
|
10586
|
+
}
|
|
10587
|
+
const content = generateSubagentFromSkill(skill, skillContent, options);
|
|
10588
|
+
const targetDir = this.global ? join17(homedir3(), ".claude", "agents") : join17(process.cwd(), ".claude", "agents");
|
|
10589
|
+
let filename;
|
|
10590
|
+
if (this.output) {
|
|
10591
|
+
const sanitized = sanitizeFilename(this.output);
|
|
10592
|
+
if (!sanitized) {
|
|
10593
|
+
console.log(chalk28.red(`Invalid output filename: ${this.output}`));
|
|
10594
|
+
console.log(chalk28.dim("Filename must contain only alphanumeric characters, hyphens, and underscores"));
|
|
10595
|
+
return 1;
|
|
10596
|
+
}
|
|
10597
|
+
filename = `${sanitized}.md`;
|
|
10598
|
+
} else {
|
|
10599
|
+
const sanitized = sanitizeFilename(skill.name);
|
|
10600
|
+
if (!sanitized) {
|
|
10601
|
+
console.log(chalk28.red(`Invalid skill name for filename: ${skill.name}`));
|
|
10602
|
+
console.log(chalk28.dim("Skill name must contain only alphanumeric characters, hyphens, and underscores"));
|
|
10603
|
+
return 1;
|
|
10604
|
+
}
|
|
10605
|
+
filename = `${sanitized}.md`;
|
|
10606
|
+
}
|
|
10607
|
+
const outputPath = join17(targetDir, filename);
|
|
10608
|
+
if (this.dryRun) {
|
|
10609
|
+
console.log(chalk28.cyan("Preview (dry run):\n"));
|
|
10610
|
+
console.log(chalk28.dim(`Would write to: ${outputPath}`));
|
|
10611
|
+
console.log(chalk28.dim("\u2500".repeat(50)));
|
|
10612
|
+
console.log(content);
|
|
10613
|
+
console.log(chalk28.dim("\u2500".repeat(50)));
|
|
10614
|
+
return 0;
|
|
10615
|
+
}
|
|
10616
|
+
if (!existsSync18(targetDir)) {
|
|
10617
|
+
mkdirSync9(targetDir, { recursive: true });
|
|
10618
|
+
}
|
|
10619
|
+
if (existsSync18(outputPath)) {
|
|
10620
|
+
console.log(chalk28.yellow(`Overwriting existing file: ${outputPath}`));
|
|
10621
|
+
}
|
|
10622
|
+
writeFileSync9(outputPath, content);
|
|
10623
|
+
console.log(chalk28.green(`Created subagent: ${outputPath}`));
|
|
10624
|
+
console.log();
|
|
10625
|
+
console.log(chalk28.dim(`Invoke with: @${skill.name}`));
|
|
10626
|
+
if (!this.inline) {
|
|
10627
|
+
console.log(chalk28.dim(`Skills referenced: ${skill.name}`));
|
|
10628
|
+
} else {
|
|
10629
|
+
console.log(chalk28.dim("Skill content embedded inline"));
|
|
10630
|
+
}
|
|
10631
|
+
return 0;
|
|
10632
|
+
}
|
|
10633
|
+
};
|
|
10042
10634
|
function formatCategoryName(category) {
|
|
10043
10635
|
return category.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
10044
10636
|
}
|
|
10637
|
+
function sanitizeFilename(input) {
|
|
10638
|
+
const base = basename5(input);
|
|
10639
|
+
const stem = base.replace(/\.md$/i, "");
|
|
10640
|
+
if (!stem || stem.startsWith(".") || stem.startsWith("-")) {
|
|
10641
|
+
return null;
|
|
10642
|
+
}
|
|
10643
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(stem)) {
|
|
10644
|
+
return null;
|
|
10645
|
+
}
|
|
10646
|
+
if (stem.length > 64) {
|
|
10647
|
+
return null;
|
|
10648
|
+
}
|
|
10649
|
+
return stem;
|
|
10650
|
+
}
|
|
10045
10651
|
|
|
10046
10652
|
// src/commands/check.ts
|
|
10047
10653
|
init_helpers();
|
|
@@ -10129,9 +10735,9 @@ var CheckCommand = class extends Command39 {
|
|
|
10129
10735
|
const sourceSkillMd = join18(localPath, "SKILL.md");
|
|
10130
10736
|
const installedSkillMd = join18(skill.path, "SKILL.md");
|
|
10131
10737
|
if (existsSync19(sourceSkillMd) && existsSync19(installedSkillMd)) {
|
|
10132
|
-
const { statSync:
|
|
10133
|
-
const sourceTime =
|
|
10134
|
-
const installedTime =
|
|
10738
|
+
const { statSync: statSync4 } = await import("fs");
|
|
10739
|
+
const sourceTime = statSync4(sourceSkillMd).mtime;
|
|
10740
|
+
const installedTime = statSync4(installedSkillMd).mtime;
|
|
10135
10741
|
if (sourceTime > installedTime) {
|
|
10136
10742
|
results.push({ name: skill.name, hasUpdate: true });
|
|
10137
10743
|
updatesAvailable++;
|
|
@@ -10214,8 +10820,8 @@ import { Command as Command40, Option as Option39 } from "clipanion";
|
|
|
10214
10820
|
var skills_default = {
|
|
10215
10821
|
$schema: "./schema.json",
|
|
10216
10822
|
version: 1,
|
|
10217
|
-
updatedAt: "2026-
|
|
10218
|
-
totalSkills:
|
|
10823
|
+
updatedAt: "2026-02-02",
|
|
10824
|
+
totalSkills: 15065,
|
|
10219
10825
|
curatedCollections: 31,
|
|
10220
10826
|
skills: [
|
|
10221
10827
|
{
|
|
@@ -43325,6 +43931,23 @@ var skills_default = {
|
|
|
43325
43931
|
"k8s"
|
|
43326
43932
|
]
|
|
43327
43933
|
},
|
|
43934
|
+
{
|
|
43935
|
+
id: "rohitg00/beautiful-mermaid/beautiful-mermaid",
|
|
43936
|
+
name: "Beautiful Mermaid",
|
|
43937
|
+
description: "Render Mermaid diagrams as beautiful SVGs or ASCII art. 5 diagram types, 15 themes, Shiki integration, zero DOM dependencies.",
|
|
43938
|
+
source: "rohitg00/beautiful-mermaid",
|
|
43939
|
+
tags: [
|
|
43940
|
+
"mermaid",
|
|
43941
|
+
"diagrams",
|
|
43942
|
+
"visualization",
|
|
43943
|
+
"flowchart",
|
|
43944
|
+
"sequence",
|
|
43945
|
+
"uml",
|
|
43946
|
+
"ascii",
|
|
43947
|
+
"svg",
|
|
43948
|
+
"themes"
|
|
43949
|
+
]
|
|
43950
|
+
},
|
|
43328
43951
|
{
|
|
43329
43952
|
id: "itsmostafa/aws-agent-skills/cognito",
|
|
43330
43953
|
name: "Cognito",
|
|
@@ -136210,7 +136833,7 @@ var ManifestGenerateCommand = class extends Command41 {
|
|
|
136210
136833
|
|
|
136211
136834
|
// src/commands/primer.ts
|
|
136212
136835
|
import { Command as Command42, Option as Option41 } from "clipanion";
|
|
136213
|
-
import { resolve as
|
|
136836
|
+
import { resolve as resolve19 } from "path";
|
|
136214
136837
|
import chalk29 from "chalk";
|
|
136215
136838
|
import {
|
|
136216
136839
|
AgentType as AgentTypeSchema,
|
|
@@ -136278,7 +136901,7 @@ var PrimerCommand = class extends Command42 {
|
|
|
136278
136901
|
description: "Number of commits to analyze for learning (default: 100)"
|
|
136279
136902
|
});
|
|
136280
136903
|
async execute() {
|
|
136281
|
-
const projectPath =
|
|
136904
|
+
const projectPath = resolve19(this.directory || process.cwd());
|
|
136282
136905
|
if (this.analyzeOnly) {
|
|
136283
136906
|
return this.runAnalysis(projectPath);
|
|
136284
136907
|
}
|
|
@@ -136347,7 +136970,7 @@ var PrimerCommand = class extends Command42 {
|
|
|
136347
136970
|
const result = generatePrimer(projectPath, {
|
|
136348
136971
|
agents,
|
|
136349
136972
|
allAgents: this.allAgents,
|
|
136350
|
-
outputDir: this.output ?
|
|
136973
|
+
outputDir: this.output ? resolve19(this.output) : void 0,
|
|
136351
136974
|
dryRun: this.dryRun,
|
|
136352
136975
|
verbose: this.verbose,
|
|
136353
136976
|
includeExamples: this.includeExamples
|
|
@@ -137558,7 +138181,7 @@ Sent Messages (${messages.length})
|
|
|
137558
138181
|
|
|
137559
138182
|
// src/commands/learn.ts
|
|
137560
138183
|
import { Command as Command45, Option as Option44 } from "clipanion";
|
|
137561
|
-
import { resolve as
|
|
138184
|
+
import { resolve as resolve20 } from "path";
|
|
137562
138185
|
import chalk32 from "chalk";
|
|
137563
138186
|
import {
|
|
137564
138187
|
analyzeGitHistory as analyzeGitHistory2,
|
|
@@ -137578,7 +138201,7 @@ import {
|
|
|
137578
138201
|
getPatternStats,
|
|
137579
138202
|
clusterPatterns
|
|
137580
138203
|
} from "@skillkit/core";
|
|
137581
|
-
import { writeFileSync as
|
|
138204
|
+
import { writeFileSync as writeFileSync10, readFileSync as readFileSync10, existsSync as existsSync20 } from "fs";
|
|
137582
138205
|
var LearnCommand = class extends Command45 {
|
|
137583
138206
|
static paths = [["learn"]];
|
|
137584
138207
|
static usage = Command45.Usage({
|
|
@@ -137615,7 +138238,7 @@ var LearnCommand = class extends Command45 {
|
|
|
137615
138238
|
description: "Project directory to analyze (default: current directory)"
|
|
137616
138239
|
});
|
|
137617
138240
|
async execute() {
|
|
137618
|
-
const projectPath =
|
|
138241
|
+
const projectPath = resolve20(this.directory || process.cwd());
|
|
137619
138242
|
if (this.show) {
|
|
137620
138243
|
return this.showPatterns();
|
|
137621
138244
|
}
|
|
@@ -137862,7 +138485,7 @@ var PatternExportCommand = class extends Command45 {
|
|
|
137862
138485
|
content = exportPatternsAsJson(patterns);
|
|
137863
138486
|
}
|
|
137864
138487
|
if (this.output) {
|
|
137865
|
-
|
|
138488
|
+
writeFileSync10(this.output, content);
|
|
137866
138489
|
console.log(chalk32.green(`\u2713 Exported ${patterns.length} patterns to ${this.output}`));
|
|
137867
138490
|
} else {
|
|
137868
138491
|
console.log(content);
|
|
@@ -138584,6 +139207,208 @@ function formatCategoryName2(category) {
|
|
|
138584
139207
|
return category.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
138585
139208
|
}
|
|
138586
139209
|
|
|
139210
|
+
// src/commands/tree.ts
|
|
139211
|
+
init_onboarding();
|
|
139212
|
+
import { Command as Command49, Option as Option48 } from "clipanion";
|
|
139213
|
+
import { join as join19 } from "path";
|
|
139214
|
+
import { homedir as homedir4 } from "os";
|
|
139215
|
+
import {
|
|
139216
|
+
loadIndex as loadIndexFromCache3,
|
|
139217
|
+
generateSkillTree,
|
|
139218
|
+
saveTree,
|
|
139219
|
+
loadTree
|
|
139220
|
+
} from "@skillkit/core";
|
|
139221
|
+
var TREE_PATH = join19(homedir4(), ".skillkit", "skill-tree.json");
|
|
139222
|
+
var TreeCommand = class extends Command49 {
|
|
139223
|
+
static paths = [["tree"]];
|
|
139224
|
+
static usage = Command49.Usage({
|
|
139225
|
+
description: "Browse skills in a hierarchical tree structure",
|
|
139226
|
+
details: `
|
|
139227
|
+
The tree command displays skills organized in a hierarchical taxonomy.
|
|
139228
|
+
Navigate through categories like Development, Testing, DevOps, AI/ML, etc.
|
|
139229
|
+
|
|
139230
|
+
Features:
|
|
139231
|
+
- Visual tree structure of all skills
|
|
139232
|
+
- Filter by category path (e.g., "Frontend > React")
|
|
139233
|
+
- Generate tree from skill index
|
|
139234
|
+
- Export to markdown format
|
|
139235
|
+
`,
|
|
139236
|
+
examples: [
|
|
139237
|
+
["Show full tree", "$0 tree"],
|
|
139238
|
+
["Show specific category", "$0 tree Frontend"],
|
|
139239
|
+
["Show subcategory", '$0 tree "Frontend > React"'],
|
|
139240
|
+
["Limit depth", "$0 tree --depth 2"],
|
|
139241
|
+
["Generate/update tree", "$0 tree --generate"],
|
|
139242
|
+
["Export to markdown", "$0 tree --markdown"],
|
|
139243
|
+
["Show tree stats", "$0 tree --stats"]
|
|
139244
|
+
]
|
|
139245
|
+
});
|
|
139246
|
+
treePath = Option48.String({ required: false });
|
|
139247
|
+
depth = Option48.String("--depth,-d", {
|
|
139248
|
+
description: "Maximum depth to display"
|
|
139249
|
+
});
|
|
139250
|
+
generate = Option48.Boolean("--generate,-g", false, {
|
|
139251
|
+
description: "Generate/update tree from skill index"
|
|
139252
|
+
});
|
|
139253
|
+
markdown = Option48.Boolean("--markdown,-m", false, {
|
|
139254
|
+
description: "Output in markdown format"
|
|
139255
|
+
});
|
|
139256
|
+
stats = Option48.Boolean("--stats,-s", false, {
|
|
139257
|
+
description: "Show tree statistics"
|
|
139258
|
+
});
|
|
139259
|
+
json = Option48.Boolean("--json,-j", false, {
|
|
139260
|
+
description: "Output in JSON format"
|
|
139261
|
+
});
|
|
139262
|
+
quiet = Option48.Boolean("--quiet,-q", false, {
|
|
139263
|
+
description: "Minimal output"
|
|
139264
|
+
});
|
|
139265
|
+
async execute() {
|
|
139266
|
+
if (this.generate) {
|
|
139267
|
+
return await this.generateTree();
|
|
139268
|
+
}
|
|
139269
|
+
const tree = this.loadOrGenerateTree();
|
|
139270
|
+
if (!tree) {
|
|
139271
|
+
warn('No skill tree found. Run "skillkit tree --generate" first.');
|
|
139272
|
+
return 1;
|
|
139273
|
+
}
|
|
139274
|
+
if (this.stats) {
|
|
139275
|
+
return this.showStats(tree);
|
|
139276
|
+
}
|
|
139277
|
+
if (this.json) {
|
|
139278
|
+
console.log(JSON.stringify(tree, null, 2));
|
|
139279
|
+
return 0;
|
|
139280
|
+
}
|
|
139281
|
+
if (this.markdown) {
|
|
139282
|
+
const { treeToMarkdown } = await import("@skillkit/core");
|
|
139283
|
+
console.log(treeToMarkdown(tree));
|
|
139284
|
+
return 0;
|
|
139285
|
+
}
|
|
139286
|
+
return this.displayTree(tree);
|
|
139287
|
+
}
|
|
139288
|
+
async generateTree() {
|
|
139289
|
+
if (!this.quiet) {
|
|
139290
|
+
header("Generate Skill Tree");
|
|
139291
|
+
}
|
|
139292
|
+
const index = loadIndexFromCache3();
|
|
139293
|
+
if (!index || index.skills.length === 0) {
|
|
139294
|
+
warn('No skill index found. Run "skillkit recommend --update" first.');
|
|
139295
|
+
return 1;
|
|
139296
|
+
}
|
|
139297
|
+
const s = spinner2();
|
|
139298
|
+
s.start("Generating skill tree...");
|
|
139299
|
+
try {
|
|
139300
|
+
const tree = generateSkillTree(index.skills);
|
|
139301
|
+
saveTree(tree, TREE_PATH);
|
|
139302
|
+
s.stop(`Generated tree with ${tree.totalCategories} categories`);
|
|
139303
|
+
console.log("");
|
|
139304
|
+
console.log(colors.success(`${symbols.success} Tree generated successfully`));
|
|
139305
|
+
console.log(colors.muted(` Total skills: ${tree.totalSkills}`));
|
|
139306
|
+
console.log(colors.muted(` Categories: ${tree.totalCategories}`));
|
|
139307
|
+
console.log(colors.muted(` Max depth: ${tree.maxDepth}`));
|
|
139308
|
+
console.log(colors.muted(` Saved to: ${TREE_PATH}`));
|
|
139309
|
+
console.log("");
|
|
139310
|
+
return 0;
|
|
139311
|
+
} catch (err) {
|
|
139312
|
+
s.stop(colors.error("Failed to generate tree"));
|
|
139313
|
+
console.log(colors.muted(err instanceof Error ? err.message : String(err)));
|
|
139314
|
+
return 1;
|
|
139315
|
+
}
|
|
139316
|
+
}
|
|
139317
|
+
loadOrGenerateTree() {
|
|
139318
|
+
let tree = loadTree(TREE_PATH);
|
|
139319
|
+
if (!tree) {
|
|
139320
|
+
const index = loadIndexFromCache3();
|
|
139321
|
+
if (index && index.skills.length > 0) {
|
|
139322
|
+
tree = generateSkillTree(index.skills);
|
|
139323
|
+
saveTree(tree, TREE_PATH);
|
|
139324
|
+
}
|
|
139325
|
+
}
|
|
139326
|
+
return tree;
|
|
139327
|
+
}
|
|
139328
|
+
showStats(tree) {
|
|
139329
|
+
if (!this.quiet) {
|
|
139330
|
+
header("Skill Tree Statistics");
|
|
139331
|
+
}
|
|
139332
|
+
console.log("");
|
|
139333
|
+
console.log(colors.bold("Overview:"));
|
|
139334
|
+
console.log(` Total Skills: ${colors.accent(String(tree.totalSkills))}`);
|
|
139335
|
+
console.log(` Categories: ${colors.accent(String(tree.totalCategories))}`);
|
|
139336
|
+
console.log(` Max Depth: ${colors.accent(String(tree.maxDepth))}`);
|
|
139337
|
+
console.log(` Generated: ${colors.muted(tree.generatedAt)}`);
|
|
139338
|
+
console.log("");
|
|
139339
|
+
console.log(colors.bold("Top-Level Categories:"));
|
|
139340
|
+
for (const child of tree.rootNode.children) {
|
|
139341
|
+
const percentage = tree.totalSkills > 0 ? (child.skillCount / tree.totalSkills * 100).toFixed(1) : "0.0";
|
|
139342
|
+
console.log(
|
|
139343
|
+
` ${colors.accent(child.name.padEnd(15))} ${String(child.skillCount).padStart(6)} skills (${percentage}%)`
|
|
139344
|
+
);
|
|
139345
|
+
if (child.children.length > 0) {
|
|
139346
|
+
const subcats = child.children.sort((a, b) => b.skillCount - a.skillCount).slice(0, 3).map((c) => `${c.name} (${c.skillCount})`).join(", ");
|
|
139347
|
+
console.log(` ${colors.muted(subcats)}`);
|
|
139348
|
+
}
|
|
139349
|
+
}
|
|
139350
|
+
console.log("");
|
|
139351
|
+
return 0;
|
|
139352
|
+
}
|
|
139353
|
+
displayTree(tree) {
|
|
139354
|
+
if (!this.quiet) {
|
|
139355
|
+
header("Skill Tree");
|
|
139356
|
+
console.log(colors.muted(`${tree.totalSkills} skills in ${tree.totalCategories} categories`));
|
|
139357
|
+
console.log("");
|
|
139358
|
+
}
|
|
139359
|
+
let targetNode = tree.rootNode;
|
|
139360
|
+
if (this.treePath) {
|
|
139361
|
+
const segments = this.treePath.split(">").map((s) => s.trim());
|
|
139362
|
+
let current = tree.rootNode;
|
|
139363
|
+
for (const segment of segments) {
|
|
139364
|
+
const child = current.children.find(
|
|
139365
|
+
(c) => c.name.toLowerCase() === segment.toLowerCase()
|
|
139366
|
+
);
|
|
139367
|
+
if (!child) {
|
|
139368
|
+
warn(`Category not found: ${segment}`);
|
|
139369
|
+
console.log(colors.muted(`Available categories: ${current.children.map((c) => c.name).join(", ")}`));
|
|
139370
|
+
return 1;
|
|
139371
|
+
}
|
|
139372
|
+
current = child;
|
|
139373
|
+
}
|
|
139374
|
+
targetNode = current;
|
|
139375
|
+
}
|
|
139376
|
+
let maxDepth = this.depth ? parseInt(this.depth, 10) : 3;
|
|
139377
|
+
if (Number.isNaN(maxDepth) || maxDepth < 0) {
|
|
139378
|
+
warn("Invalid depth value. Using default depth of 3.");
|
|
139379
|
+
maxDepth = 3;
|
|
139380
|
+
}
|
|
139381
|
+
this.renderNode(targetNode, "", true, 0, maxDepth);
|
|
139382
|
+
console.log("");
|
|
139383
|
+
console.log(colors.muted('Navigate: skillkit tree "Category > Subcategory"'));
|
|
139384
|
+
console.log(colors.muted("Generate: skillkit tree --generate"));
|
|
139385
|
+
return 0;
|
|
139386
|
+
}
|
|
139387
|
+
renderNode(node, prefix, isLast, depth, maxDepth) {
|
|
139388
|
+
if (depth > maxDepth) return;
|
|
139389
|
+
const connector = depth === 0 ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
139390
|
+
const icon = node.children.length > 0 ? "\u{1F4C1} " : "\u{1F4C4} ";
|
|
139391
|
+
const skillInfo = node.skillCount > 0 ? colors.muted(` (${node.skillCount})`) : "";
|
|
139392
|
+
const nameColor = depth === 0 ? colors.bold : depth === 1 ? colors.accent : colors.dim;
|
|
139393
|
+
console.log(`${prefix}${connector}${icon}${nameColor(node.name)}${skillInfo}`);
|
|
139394
|
+
if (depth === maxDepth && node.skills.length > 0 && node.skills.length <= 5) {
|
|
139395
|
+
const childPrefix = prefix + (isLast ? " " : "\u2502 ");
|
|
139396
|
+
for (const skill of node.skills) {
|
|
139397
|
+
console.log(`${childPrefix} \u2022 ${colors.muted(skill)}`);
|
|
139398
|
+
}
|
|
139399
|
+
}
|
|
139400
|
+
const newPrefix = prefix + (depth === 0 ? "" : isLast ? " " : "\u2502 ");
|
|
139401
|
+
if (depth < maxDepth) {
|
|
139402
|
+
node.children.forEach((child, index) => {
|
|
139403
|
+
const childIsLast = index === node.children.length - 1;
|
|
139404
|
+
this.renderNode(child, newPrefix, childIsLast, depth + 1, maxDepth);
|
|
139405
|
+
});
|
|
139406
|
+
} else if (node.children.length > 0) {
|
|
139407
|
+
console.log(`${newPrefix} ${colors.muted(`... ${node.children.length} more subcategories`)}`);
|
|
139408
|
+
}
|
|
139409
|
+
}
|
|
139410
|
+
};
|
|
139411
|
+
|
|
138587
139412
|
// src/index.ts
|
|
138588
139413
|
init_helpers();
|
|
138589
139414
|
export {
|
|
@@ -138591,6 +139416,7 @@ export {
|
|
|
138591
139416
|
AgentAvailableCommand,
|
|
138592
139417
|
AgentCommand,
|
|
138593
139418
|
AgentCreateCommand,
|
|
139419
|
+
AgentFromSkillCommand,
|
|
138594
139420
|
AgentInstallCommand,
|
|
138595
139421
|
AgentListCommand,
|
|
138596
139422
|
AgentShowCommand,
|
|
@@ -138651,6 +139477,7 @@ export {
|
|
|
138651
139477
|
ProfileListCommand,
|
|
138652
139478
|
ProfileRemoveCommand,
|
|
138653
139479
|
PublishCommand,
|
|
139480
|
+
PublishSubmitCommand,
|
|
138654
139481
|
ReadCommand,
|
|
138655
139482
|
RecommendCommand,
|
|
138656
139483
|
RemoveCommand,
|
|
@@ -138670,6 +139497,7 @@ export {
|
|
|
138670
139497
|
TeamCommand,
|
|
138671
139498
|
TestCommand,
|
|
138672
139499
|
TranslateCommand,
|
|
139500
|
+
TreeCommand,
|
|
138673
139501
|
UICommand,
|
|
138674
139502
|
UpdateCommand,
|
|
138675
139503
|
ValidateCommand,
|