@skillkit/cli 1.8.1 → 1.9.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 +47 -2
- package/dist/index.js +782 -103
- 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,
|
|
@@ -3416,6 +3438,18 @@ var RecommendCommand = class extends Command15 {
|
|
|
3416
3438
|
quiet = Option14.Boolean("--quiet,-q", false, {
|
|
3417
3439
|
description: "Minimal output"
|
|
3418
3440
|
});
|
|
3441
|
+
// Explain mode (reasoning-based recommendations)
|
|
3442
|
+
explain = Option14.Boolean("--explain,-e", false, {
|
|
3443
|
+
description: "Show detailed explanations for recommendations (uses reasoning engine)"
|
|
3444
|
+
});
|
|
3445
|
+
// Reasoning mode
|
|
3446
|
+
reasoning = Option14.Boolean("--reasoning,-r", false, {
|
|
3447
|
+
description: "Use LLM-based reasoning for recommendations"
|
|
3448
|
+
});
|
|
3449
|
+
// Show tree path
|
|
3450
|
+
showPath = Option14.Boolean("--show-path", false, {
|
|
3451
|
+
description: "Show category path for each recommendation"
|
|
3452
|
+
});
|
|
3419
3453
|
async execute() {
|
|
3420
3454
|
const targetPath = resolve4(this.projectPath || process.cwd());
|
|
3421
3455
|
if (this.update) {
|
|
@@ -3437,6 +3471,9 @@ var RecommendCommand = class extends Command15 {
|
|
|
3437
3471
|
this.showProjectProfile(profile);
|
|
3438
3472
|
return 0;
|
|
3439
3473
|
}
|
|
3474
|
+
if (this.explain || this.reasoning || this.showPath) {
|
|
3475
|
+
return await this.handleReasoningRecommendations(profile, index);
|
|
3476
|
+
}
|
|
3440
3477
|
const engine = new RecommendationEngine();
|
|
3441
3478
|
engine.loadIndex(index);
|
|
3442
3479
|
const searchQuery = this.search || this.task;
|
|
@@ -3457,6 +3494,53 @@ var RecommendCommand = class extends Command15 {
|
|
|
3457
3494
|
this.displayRecommendations(result.recommendations, profile, result.totalSkillsScanned);
|
|
3458
3495
|
return 0;
|
|
3459
3496
|
}
|
|
3497
|
+
async handleReasoningRecommendations(profile, index) {
|
|
3498
|
+
const s = !this.quiet && !this.json ? spinner2() : null;
|
|
3499
|
+
s?.start("Analyzing with reasoning engine...");
|
|
3500
|
+
try {
|
|
3501
|
+
const engine = new ReasoningRecommendationEngine();
|
|
3502
|
+
engine.loadIndex(index);
|
|
3503
|
+
await engine.initReasoning();
|
|
3504
|
+
const result = await engine.recommendWithReasoning(profile, {
|
|
3505
|
+
limit: this.limit ? parseInt(this.limit, 10) : 10,
|
|
3506
|
+
minScore: this.minScore ? parseInt(this.minScore, 10) : 30,
|
|
3507
|
+
categories: this.category,
|
|
3508
|
+
excludeInstalled: !this.includeInstalled,
|
|
3509
|
+
includeReasons: this.verbose,
|
|
3510
|
+
reasoning: this.reasoning,
|
|
3511
|
+
explainResults: this.explain,
|
|
3512
|
+
useTree: true
|
|
3513
|
+
});
|
|
3514
|
+
s?.stop("Analysis complete");
|
|
3515
|
+
if (this.json) {
|
|
3516
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3517
|
+
return 0;
|
|
3518
|
+
}
|
|
3519
|
+
this.displayExplainedRecommendations(
|
|
3520
|
+
result.recommendations,
|
|
3521
|
+
profile,
|
|
3522
|
+
result.totalSkillsScanned,
|
|
3523
|
+
result.reasoningSummary
|
|
3524
|
+
);
|
|
3525
|
+
return 0;
|
|
3526
|
+
} catch (err) {
|
|
3527
|
+
s?.stop(colors.error("Reasoning analysis failed"));
|
|
3528
|
+
console.log(colors.muted(err instanceof Error ? err.message : String(err)));
|
|
3529
|
+
console.log(colors.muted("Falling back to standard recommendations..."));
|
|
3530
|
+
console.log("");
|
|
3531
|
+
const engine = new RecommendationEngine();
|
|
3532
|
+
engine.loadIndex(index);
|
|
3533
|
+
const result = engine.recommend(profile, {
|
|
3534
|
+
limit: this.limit ? parseInt(this.limit, 10) : 10,
|
|
3535
|
+
minScore: this.minScore ? parseInt(this.minScore, 10) : 30,
|
|
3536
|
+
categories: this.category,
|
|
3537
|
+
excludeInstalled: !this.includeInstalled,
|
|
3538
|
+
includeReasons: this.verbose
|
|
3539
|
+
});
|
|
3540
|
+
this.displayRecommendations(result.recommendations, profile, result.totalSkillsScanned);
|
|
3541
|
+
return 0;
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3460
3544
|
async getProjectProfile(projectPath) {
|
|
3461
3545
|
const manager = new ContextManager2(projectPath);
|
|
3462
3546
|
let context = manager.get();
|
|
@@ -3539,6 +3623,74 @@ var RecommendCommand = class extends Command15 {
|
|
|
3539
3623
|
}
|
|
3540
3624
|
console.log(colors.muted("Install with: skillkit install <source>"));
|
|
3541
3625
|
}
|
|
3626
|
+
displayExplainedRecommendations(recommendations, profile, totalScanned, reasoningSummary) {
|
|
3627
|
+
this.showProjectProfile(profile);
|
|
3628
|
+
if (reasoningSummary && !this.quiet) {
|
|
3629
|
+
console.log(colors.dim("Reasoning: ") + colors.muted(reasoningSummary));
|
|
3630
|
+
console.log("");
|
|
3631
|
+
}
|
|
3632
|
+
if (recommendations.length === 0) {
|
|
3633
|
+
warn("No matching skills found.");
|
|
3634
|
+
console.log(colors.muted("Try lowering the minimum score with --min-score"));
|
|
3635
|
+
return;
|
|
3636
|
+
}
|
|
3637
|
+
console.log(colors.bold(`Explained Recommendations (${recommendations.length} of ${totalScanned} scanned):`));
|
|
3638
|
+
console.log("");
|
|
3639
|
+
for (const rec of recommendations) {
|
|
3640
|
+
let scoreColor;
|
|
3641
|
+
if (rec.score >= 70) {
|
|
3642
|
+
scoreColor = colors.success;
|
|
3643
|
+
} else if (rec.score >= 50) {
|
|
3644
|
+
scoreColor = colors.warning;
|
|
3645
|
+
} else {
|
|
3646
|
+
scoreColor = colors.muted;
|
|
3647
|
+
}
|
|
3648
|
+
const scoreBar = progressBar(rec.score, 100, 10);
|
|
3649
|
+
const qualityScore = rec.skill.quality ?? null;
|
|
3650
|
+
const qualityDisplay = qualityScore !== null && qualityScore !== void 0 ? ` ${formatQualityBadge(qualityScore)}` : "";
|
|
3651
|
+
console.log(` ${scoreColor(`${rec.score}%`)} ${colors.dim(scoreBar)} ${colors.bold(rec.skill.name)}${qualityDisplay}`);
|
|
3652
|
+
if (this.showPath && rec.treePath && rec.treePath.length > 0) {
|
|
3653
|
+
console.log(` ${colors.accent("Path:")} ${rec.treePath.join(" > ")}`);
|
|
3654
|
+
}
|
|
3655
|
+
if (rec.skill.description) {
|
|
3656
|
+
console.log(` ${colors.muted(truncate3(rec.skill.description, 70))}`);
|
|
3657
|
+
}
|
|
3658
|
+
if (rec.skill.source) {
|
|
3659
|
+
console.log(` ${colors.dim("Source:")} ${rec.skill.source}`);
|
|
3660
|
+
}
|
|
3661
|
+
if (this.explain && rec.explanation) {
|
|
3662
|
+
console.log(colors.dim(" Why this skill:"));
|
|
3663
|
+
if (rec.explanation.matchedBecause.length > 0) {
|
|
3664
|
+
console.log(` ${colors.success("\u251C\u2500")} Matched: ${rec.explanation.matchedBecause.join(", ")}`);
|
|
3665
|
+
}
|
|
3666
|
+
if (rec.explanation.relevantFor.length > 0) {
|
|
3667
|
+
console.log(` ${colors.accent("\u251C\u2500")} Relevant for: ${rec.explanation.relevantFor.join(", ")}`);
|
|
3668
|
+
}
|
|
3669
|
+
if (rec.explanation.confidence) {
|
|
3670
|
+
const confidenceColor = rec.explanation.confidence === "high" ? colors.success : rec.explanation.confidence === "medium" ? colors.warning : colors.muted;
|
|
3671
|
+
console.log(` ${colors.dim("\u2514\u2500")} Confidence: ${confidenceColor(rec.explanation.confidence)}`);
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
if (this.verbose && rec.reasons.length > 0) {
|
|
3675
|
+
console.log(colors.dim(" Score breakdown:"));
|
|
3676
|
+
for (const reason of rec.reasons.filter((r) => r.weight > 0)) {
|
|
3677
|
+
console.log(` ${colors.muted(symbols.stepActive)} ${reason.description} (+${reason.weight})`);
|
|
3678
|
+
}
|
|
3679
|
+
if (qualityScore !== null && qualityScore !== void 0) {
|
|
3680
|
+
const grade = getQualityGradeFromScore(qualityScore);
|
|
3681
|
+
console.log(` ${colors.muted(symbols.stepActive)} Quality: ${qualityScore}/100 (${grade})`);
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
if (rec.warnings.length > 0) {
|
|
3685
|
+
for (const warning of rec.warnings) {
|
|
3686
|
+
console.log(` ${colors.warning(symbols.warning)} ${warning}`);
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
console.log("");
|
|
3690
|
+
}
|
|
3691
|
+
console.log(colors.muted("Install with: skillkit install <source>"));
|
|
3692
|
+
console.log(colors.muted("More details: skillkit recommend --explain --verbose"));
|
|
3693
|
+
}
|
|
3542
3694
|
handleSearch(engine, query) {
|
|
3543
3695
|
if (!this.quiet && !this.json) {
|
|
3544
3696
|
header(`Search: "${query}"`);
|
|
@@ -5506,12 +5658,12 @@ ${learning.title}
|
|
|
5506
5658
|
}
|
|
5507
5659
|
const outputPath = this.output || `.skillkit/exports/${skillName}/SKILL.md`;
|
|
5508
5660
|
const { dirname: dirname7 } = await import("path");
|
|
5509
|
-
const { existsSync: existsSync21, mkdirSync:
|
|
5661
|
+
const { existsSync: existsSync21, mkdirSync: mkdirSync10, writeFileSync: writeFileSync11 } = await import("fs");
|
|
5510
5662
|
const outputDir = dirname7(outputPath);
|
|
5511
5663
|
if (!existsSync21(outputDir)) {
|
|
5512
|
-
|
|
5664
|
+
mkdirSync10(outputDir, { recursive: true });
|
|
5513
5665
|
}
|
|
5514
|
-
|
|
5666
|
+
writeFileSync11(outputPath, skillContent, "utf-8");
|
|
5515
5667
|
console.log(chalk18.green(`\u2713 Exported learning as skill: ${skillName}`));
|
|
5516
5668
|
console.log(chalk18.gray(` Path: ${outputPath}`));
|
|
5517
5669
|
return 0;
|
|
@@ -5527,8 +5679,8 @@ ${learning.title}
|
|
|
5527
5679
|
return 1;
|
|
5528
5680
|
}
|
|
5529
5681
|
const { existsSync: existsSync21, readFileSync: readFileSync11 } = await import("fs");
|
|
5530
|
-
const { resolve:
|
|
5531
|
-
const fullPath =
|
|
5682
|
+
const { resolve: resolve21 } = await import("path");
|
|
5683
|
+
const fullPath = resolve21(inputPath);
|
|
5532
5684
|
if (!existsSync21(fullPath)) {
|
|
5533
5685
|
console.error(chalk18.red(`File not found: ${fullPath}`));
|
|
5534
5686
|
return 1;
|
|
@@ -5786,7 +5938,7 @@ ${learning.title}
|
|
|
5786
5938
|
// src/commands/settings.ts
|
|
5787
5939
|
import { Command as Command27, Option as Option26 } from "clipanion";
|
|
5788
5940
|
import chalk19 from "chalk";
|
|
5789
|
-
import { loadConfig as
|
|
5941
|
+
import { loadConfig as loadConfig2, saveConfig } from "@skillkit/core";
|
|
5790
5942
|
var VALID_AGENTS = [
|
|
5791
5943
|
"claude-code",
|
|
5792
5944
|
"cursor",
|
|
@@ -5846,7 +5998,7 @@ var SettingsCommand = class extends Command27 {
|
|
|
5846
5998
|
description: "Reset all settings to defaults"
|
|
5847
5999
|
});
|
|
5848
6000
|
async execute() {
|
|
5849
|
-
const config =
|
|
6001
|
+
const config = loadConfig2(this.global);
|
|
5850
6002
|
if (this.reset) {
|
|
5851
6003
|
const defaultConfig = {
|
|
5852
6004
|
version: 1,
|
|
@@ -6608,14 +6760,14 @@ var TeamCommand = class extends Command29 {
|
|
|
6608
6760
|
}
|
|
6609
6761
|
const projectPath = process.cwd();
|
|
6610
6762
|
const bundlePath = join12(projectPath, ".skillkit", "bundles", `${this.name}.json`);
|
|
6611
|
-
const { existsSync: existsSync21, readFileSync: readFileSync11, writeFileSync:
|
|
6763
|
+
const { existsSync: existsSync21, readFileSync: readFileSync11, writeFileSync: writeFileSync11 } = await import("fs");
|
|
6612
6764
|
if (!existsSync21(bundlePath)) {
|
|
6613
6765
|
this.context.stderr.write(chalk21.red(`Bundle "${this.name}" not found. Create it first with bundle-create.
|
|
6614
6766
|
`));
|
|
6615
6767
|
return 1;
|
|
6616
6768
|
}
|
|
6617
6769
|
const content = readFileSync11(bundlePath, "utf-8");
|
|
6618
|
-
|
|
6770
|
+
writeFileSync11(this.output, content, "utf-8");
|
|
6619
6771
|
this.context.stdout.write(chalk21.green(`\u2713 Bundle exported to: ${this.output}
|
|
6620
6772
|
`));
|
|
6621
6773
|
return 0;
|
|
@@ -6806,8 +6958,8 @@ var PluginCommand = class extends Command30 {
|
|
|
6806
6958
|
if (!existsSync15(pluginsDir)) {
|
|
6807
6959
|
mkdirSync7(pluginsDir, { recursive: true });
|
|
6808
6960
|
}
|
|
6809
|
-
const { statSync:
|
|
6810
|
-
const sourceStat =
|
|
6961
|
+
const { statSync: statSync4 } = await import("fs");
|
|
6962
|
+
const sourceStat = statSync4(resolvedSource);
|
|
6811
6963
|
if (sourceStat.isDirectory()) {
|
|
6812
6964
|
cpSync3(resolvedSource, targetDir, { recursive: true });
|
|
6813
6965
|
} else {
|
|
@@ -9148,27 +9300,245 @@ Recent Errors (${stats.recentErrors.length}):`));
|
|
|
9148
9300
|
};
|
|
9149
9301
|
|
|
9150
9302
|
// 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";
|
|
9303
|
+
import { existsSync as existsSync17, readFileSync as readFileSync9, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
9304
|
+
import { join as join16, basename as basename4, dirname as dirname6, resolve as resolve18 } from "path";
|
|
9154
9305
|
import chalk27 from "chalk";
|
|
9155
9306
|
import { Command as Command37, Option as Option36 } from "clipanion";
|
|
9307
|
+
import { generateWellKnownIndex } from "@skillkit/core";
|
|
9308
|
+
function sanitizeSkillName(name) {
|
|
9309
|
+
if (!name || typeof name !== "string") return null;
|
|
9310
|
+
const base = basename4(name);
|
|
9311
|
+
if (base !== name || name.includes("..") || name.includes("/") || name.includes("\\")) {
|
|
9312
|
+
return null;
|
|
9313
|
+
}
|
|
9314
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(name)) {
|
|
9315
|
+
return null;
|
|
9316
|
+
}
|
|
9317
|
+
return name;
|
|
9318
|
+
}
|
|
9156
9319
|
var PublishCommand = class extends Command37 {
|
|
9157
9320
|
static paths = [["publish"]];
|
|
9158
9321
|
static usage = Command37.Usage({
|
|
9159
|
-
description: "
|
|
9322
|
+
description: "Generate well-known skills structure for hosting",
|
|
9323
|
+
details: `
|
|
9324
|
+
This command generates the RFC 8615 well-known URI structure for hosting skills.
|
|
9325
|
+
|
|
9326
|
+
The output includes:
|
|
9327
|
+
- .well-known/skills/index.json - Skill manifest for auto-discovery
|
|
9328
|
+
- .well-known/skills/{skill-name}/SKILL.md - Individual skill files
|
|
9329
|
+
|
|
9330
|
+
Users can then install skills via: skillkit add https://your-domain.com
|
|
9331
|
+
`,
|
|
9332
|
+
examples: [
|
|
9333
|
+
["Generate from current directory", "$0 publish"],
|
|
9334
|
+
["Generate from specific path", "$0 publish ./my-skills"],
|
|
9335
|
+
["Generate to custom output directory", "$0 publish --output ./public"],
|
|
9336
|
+
["Preview without writing", "$0 publish --dry-run"]
|
|
9337
|
+
]
|
|
9338
|
+
});
|
|
9339
|
+
skillPath = Option36.String({ required: false, name: "path" });
|
|
9340
|
+
output = Option36.String("--output,-o", {
|
|
9341
|
+
description: "Output directory for well-known structure (default: current directory)"
|
|
9342
|
+
});
|
|
9343
|
+
dryRun = Option36.Boolean("--dry-run,-n", false, {
|
|
9344
|
+
description: "Show what would be generated without writing files"
|
|
9345
|
+
});
|
|
9346
|
+
async execute() {
|
|
9347
|
+
const basePath = this.skillPath || process.cwd();
|
|
9348
|
+
const outputDir = this.output || basePath;
|
|
9349
|
+
console.log(chalk27.cyan("Generating well-known skills structure...\n"));
|
|
9350
|
+
const discoveredSkills = this.discoverSkills(basePath);
|
|
9351
|
+
if (discoveredSkills.length === 0) {
|
|
9352
|
+
console.error(chalk27.red("No skills found"));
|
|
9353
|
+
console.error(chalk27.dim("Skills must contain a SKILL.md file with frontmatter"));
|
|
9354
|
+
return 1;
|
|
9355
|
+
}
|
|
9356
|
+
console.log(chalk27.white(`Found ${discoveredSkills.length} skill(s):
|
|
9357
|
+
`));
|
|
9358
|
+
const wellKnownSkills = [];
|
|
9359
|
+
const validSkills = [];
|
|
9360
|
+
for (const skill of discoveredSkills) {
|
|
9361
|
+
const safeName = sanitizeSkillName(skill.name);
|
|
9362
|
+
if (!safeName) {
|
|
9363
|
+
console.log(chalk27.yellow(` ${chalk27.yellow("\u26A0")} Skipping "${skill.name}" (invalid name - must be alphanumeric with hyphens/underscores)`));
|
|
9364
|
+
continue;
|
|
9365
|
+
}
|
|
9366
|
+
const files = this.getSkillFiles(skill.path);
|
|
9367
|
+
console.log(chalk27.dim(` ${chalk27.green("\u25CF")} ${safeName}`));
|
|
9368
|
+
console.log(chalk27.dim(` Description: ${skill.description || "No description"}`));
|
|
9369
|
+
console.log(chalk27.dim(` Files: ${files.join(", ")}`));
|
|
9370
|
+
validSkills.push({ name: skill.name, safeName, description: skill.description, path: skill.path });
|
|
9371
|
+
wellKnownSkills.push({
|
|
9372
|
+
name: safeName,
|
|
9373
|
+
description: skill.description,
|
|
9374
|
+
files
|
|
9375
|
+
});
|
|
9376
|
+
}
|
|
9377
|
+
if (validSkills.length === 0) {
|
|
9378
|
+
console.error(chalk27.red("\nNo valid skills to publish"));
|
|
9379
|
+
return 1;
|
|
9380
|
+
}
|
|
9381
|
+
console.log("");
|
|
9382
|
+
if (this.dryRun) {
|
|
9383
|
+
console.log(chalk27.yellow("Dry run - not writing files\n"));
|
|
9384
|
+
console.log(chalk27.white("Would generate:"));
|
|
9385
|
+
console.log(chalk27.dim(` ${outputDir}/.well-known/skills/index.json`));
|
|
9386
|
+
for (const skill of wellKnownSkills) {
|
|
9387
|
+
for (const file of skill.files) {
|
|
9388
|
+
console.log(chalk27.dim(` ${outputDir}/.well-known/skills/${skill.name}/${file}`));
|
|
9389
|
+
}
|
|
9390
|
+
}
|
|
9391
|
+
console.log("");
|
|
9392
|
+
console.log(chalk27.white("index.json preview:"));
|
|
9393
|
+
console.log(JSON.stringify(generateWellKnownIndex(wellKnownSkills), null, 2));
|
|
9394
|
+
return 0;
|
|
9395
|
+
}
|
|
9396
|
+
const wellKnownDir = join16(outputDir, ".well-known", "skills");
|
|
9397
|
+
mkdirSync8(wellKnownDir, { recursive: true });
|
|
9398
|
+
for (const skill of validSkills) {
|
|
9399
|
+
const skillDir = join16(wellKnownDir, skill.safeName);
|
|
9400
|
+
const resolvedSkillDir = resolve18(skillDir);
|
|
9401
|
+
const resolvedWellKnownDir = resolve18(wellKnownDir);
|
|
9402
|
+
if (!resolvedSkillDir.startsWith(resolvedWellKnownDir)) {
|
|
9403
|
+
console.log(chalk27.yellow(` Skipping "${skill.name}" (path traversal detected)`));
|
|
9404
|
+
continue;
|
|
9405
|
+
}
|
|
9406
|
+
mkdirSync8(skillDir, { recursive: true });
|
|
9407
|
+
const files = this.getSkillFiles(skill.path);
|
|
9408
|
+
for (const file of files) {
|
|
9409
|
+
const safeFile = basename4(file);
|
|
9410
|
+
const sourcePath = join16(skill.path, file);
|
|
9411
|
+
const destPath = join16(skillDir, safeFile);
|
|
9412
|
+
const content = readFileSync9(sourcePath, "utf-8");
|
|
9413
|
+
writeFileSync8(destPath, content);
|
|
9414
|
+
}
|
|
9415
|
+
}
|
|
9416
|
+
const index = generateWellKnownIndex(wellKnownSkills);
|
|
9417
|
+
writeFileSync8(join16(wellKnownDir, "index.json"), JSON.stringify(index, null, 2));
|
|
9418
|
+
console.log(chalk27.green("Generated well-known structure:\n"));
|
|
9419
|
+
console.log(chalk27.dim(` ${wellKnownDir}/index.json`));
|
|
9420
|
+
for (const skill of wellKnownSkills) {
|
|
9421
|
+
console.log(chalk27.dim(` ${wellKnownDir}/${skill.name}/`));
|
|
9422
|
+
}
|
|
9423
|
+
console.log("");
|
|
9424
|
+
console.log(chalk27.cyan("Next steps:"));
|
|
9425
|
+
console.log(chalk27.dim(" 1. Deploy the .well-known directory to your web server"));
|
|
9426
|
+
console.log(chalk27.dim(" 2. Users can install via: skillkit add https://your-domain.com"));
|
|
9427
|
+
console.log(chalk27.dim(" 3. Skills auto-discovered from /.well-known/skills/index.json"));
|
|
9428
|
+
return 0;
|
|
9429
|
+
}
|
|
9430
|
+
discoverSkills(basePath) {
|
|
9431
|
+
const skills = [];
|
|
9432
|
+
const skillMdPath = join16(basePath, "SKILL.md");
|
|
9433
|
+
if (existsSync17(skillMdPath)) {
|
|
9434
|
+
const content = readFileSync9(skillMdPath, "utf-8");
|
|
9435
|
+
const frontmatter = this.parseFrontmatter(content);
|
|
9436
|
+
skills.push({
|
|
9437
|
+
name: frontmatter.name || basename4(basePath),
|
|
9438
|
+
description: frontmatter.description,
|
|
9439
|
+
path: basePath
|
|
9440
|
+
});
|
|
9441
|
+
return skills;
|
|
9442
|
+
}
|
|
9443
|
+
const searchDirs = [
|
|
9444
|
+
basePath,
|
|
9445
|
+
join16(basePath, "skills"),
|
|
9446
|
+
join16(basePath, ".claude", "skills")
|
|
9447
|
+
];
|
|
9448
|
+
for (const searchDir of searchDirs) {
|
|
9449
|
+
if (!existsSync17(searchDir)) continue;
|
|
9450
|
+
const entries = readdirSync3(searchDir);
|
|
9451
|
+
for (const entry of entries) {
|
|
9452
|
+
const entryPath = join16(searchDir, entry);
|
|
9453
|
+
if (!statSync3(entryPath).isDirectory()) continue;
|
|
9454
|
+
const entrySkillMd = join16(entryPath, "SKILL.md");
|
|
9455
|
+
if (existsSync17(entrySkillMd)) {
|
|
9456
|
+
const content = readFileSync9(entrySkillMd, "utf-8");
|
|
9457
|
+
const frontmatter = this.parseFrontmatter(content);
|
|
9458
|
+
skills.push({
|
|
9459
|
+
name: frontmatter.name || entry,
|
|
9460
|
+
description: frontmatter.description,
|
|
9461
|
+
path: entryPath
|
|
9462
|
+
});
|
|
9463
|
+
}
|
|
9464
|
+
}
|
|
9465
|
+
}
|
|
9466
|
+
return skills;
|
|
9467
|
+
}
|
|
9468
|
+
getSkillFiles(skillPath) {
|
|
9469
|
+
const files = [];
|
|
9470
|
+
const entries = readdirSync3(skillPath);
|
|
9471
|
+
for (const entry of entries) {
|
|
9472
|
+
const entryPath = join16(skillPath, entry);
|
|
9473
|
+
if (statSync3(entryPath).isFile()) {
|
|
9474
|
+
if (entry.startsWith(".") || entry === ".skillkit-metadata.json") continue;
|
|
9475
|
+
files.push(entry);
|
|
9476
|
+
}
|
|
9477
|
+
}
|
|
9478
|
+
if (!files.includes("SKILL.md")) {
|
|
9479
|
+
files.unshift("SKILL.md");
|
|
9480
|
+
}
|
|
9481
|
+
return files;
|
|
9482
|
+
}
|
|
9483
|
+
parseFrontmatter(content) {
|
|
9484
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
9485
|
+
if (!match) return {};
|
|
9486
|
+
const frontmatter = {};
|
|
9487
|
+
const lines = match[1].split(/\r?\n/);
|
|
9488
|
+
let inTagsList = false;
|
|
9489
|
+
for (const line of lines) {
|
|
9490
|
+
if (inTagsList) {
|
|
9491
|
+
const tagMatch = line.match(/^\s*-\s*(.+)$/);
|
|
9492
|
+
if (tagMatch) {
|
|
9493
|
+
frontmatter.tags ??= [];
|
|
9494
|
+
frontmatter.tags.push(tagMatch[1].trim().replace(/^["']|["']$/g, ""));
|
|
9495
|
+
continue;
|
|
9496
|
+
}
|
|
9497
|
+
if (line.trim() === "") continue;
|
|
9498
|
+
inTagsList = false;
|
|
9499
|
+
}
|
|
9500
|
+
const colonIdx = line.indexOf(":");
|
|
9501
|
+
if (colonIdx === -1) continue;
|
|
9502
|
+
const key = line.slice(0, colonIdx).trim();
|
|
9503
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
9504
|
+
switch (key) {
|
|
9505
|
+
case "name":
|
|
9506
|
+
frontmatter.name = value.replace(/^["']|["']$/g, "");
|
|
9507
|
+
break;
|
|
9508
|
+
case "description":
|
|
9509
|
+
frontmatter.description = value.replace(/^["']|["']$/g, "");
|
|
9510
|
+
break;
|
|
9511
|
+
case "version":
|
|
9512
|
+
frontmatter.version = value.replace(/^["']|["']$/g, "");
|
|
9513
|
+
break;
|
|
9514
|
+
case "tags":
|
|
9515
|
+
if (value.startsWith("[")) {
|
|
9516
|
+
frontmatter.tags = value.slice(1, -1).split(",").map((t) => t.trim().replace(/^["']|["']$/g, "")).filter((t) => t.length > 0);
|
|
9517
|
+
} else if (value === "") {
|
|
9518
|
+
inTagsList = true;
|
|
9519
|
+
frontmatter.tags = [];
|
|
9520
|
+
}
|
|
9521
|
+
break;
|
|
9522
|
+
}
|
|
9523
|
+
}
|
|
9524
|
+
return frontmatter;
|
|
9525
|
+
}
|
|
9526
|
+
};
|
|
9527
|
+
var PublishSubmitCommand = class extends Command37 {
|
|
9528
|
+
static paths = [["publish", "submit"]];
|
|
9529
|
+
static usage = Command37.Usage({
|
|
9530
|
+
description: "Submit skill to SkillKit marketplace (requires review)",
|
|
9160
9531
|
examples: [
|
|
9161
|
-
["
|
|
9162
|
-
["
|
|
9163
|
-
["Publish with custom name", "$0 publish --name my-awesome-skill"]
|
|
9532
|
+
["Submit skill from current directory", "$0 publish submit"],
|
|
9533
|
+
["Submit with custom name", "$0 publish submit --name my-skill"]
|
|
9164
9534
|
]
|
|
9165
9535
|
});
|
|
9166
9536
|
skillPath = Option36.String({ required: false, name: "path" });
|
|
9167
9537
|
name = Option36.String("--name,-n", {
|
|
9168
|
-
description: "Custom skill name
|
|
9538
|
+
description: "Custom skill name"
|
|
9169
9539
|
});
|
|
9170
9540
|
dryRun = Option36.Boolean("--dry-run", false, {
|
|
9171
|
-
description: "Show what would be
|
|
9541
|
+
description: "Show what would be submitted"
|
|
9172
9542
|
});
|
|
9173
9543
|
async execute() {
|
|
9174
9544
|
const skillPath = this.skillPath || process.cwd();
|
|
@@ -9176,10 +9546,9 @@ var PublishCommand = class extends Command37 {
|
|
|
9176
9546
|
if (!skillMdPath) {
|
|
9177
9547
|
console.error(chalk27.red("No SKILL.md found"));
|
|
9178
9548
|
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
9549
|
return 1;
|
|
9181
9550
|
}
|
|
9182
|
-
console.log(chalk27.cyan("
|
|
9551
|
+
console.log(chalk27.cyan("Submitting skill to SkillKit marketplace...\n"));
|
|
9183
9552
|
const content = readFileSync9(skillMdPath, "utf-8");
|
|
9184
9553
|
const frontmatter = this.parseFrontmatter(content);
|
|
9185
9554
|
const skillName = this.name || frontmatter.name || basename4(dirname6(skillMdPath));
|
|
@@ -9198,9 +9567,9 @@ var PublishCommand = class extends Command37 {
|
|
|
9198
9567
|
const skillEntry = {
|
|
9199
9568
|
id: `${repoInfo.owner}/${repoInfo.repo}/${skillSlug}`,
|
|
9200
9569
|
name: this.formatName(skillName),
|
|
9201
|
-
description: frontmatter.description || `Best practices
|
|
9570
|
+
description: frontmatter.description || `Best practices for ${this.formatName(skillName)}`,
|
|
9202
9571
|
source: `${repoInfo.owner}/${repoInfo.repo}`,
|
|
9203
|
-
tags: frontmatter.tags ||
|
|
9572
|
+
tags: frontmatter.tags || ["general"]
|
|
9204
9573
|
};
|
|
9205
9574
|
console.log(chalk27.white("Skill details:"));
|
|
9206
9575
|
console.log(chalk27.dim(` ID: ${skillEntry.id}`));
|
|
@@ -9210,8 +9579,7 @@ var PublishCommand = class extends Command37 {
|
|
|
9210
9579
|
console.log(chalk27.dim(` Tags: ${skillEntry.tags.join(", ")}`));
|
|
9211
9580
|
console.log();
|
|
9212
9581
|
if (this.dryRun) {
|
|
9213
|
-
console.log(chalk27.yellow("Dry run - not
|
|
9214
|
-
console.log(chalk27.dim("JSON entry that would be added:"));
|
|
9582
|
+
console.log(chalk27.yellow("Dry run - not submitting"));
|
|
9215
9583
|
console.log(JSON.stringify(skillEntry, null, 2));
|
|
9216
9584
|
return 0;
|
|
9217
9585
|
}
|
|
@@ -9221,20 +9589,16 @@ var PublishCommand = class extends Command37 {
|
|
|
9221
9589
|
const issueUrl = `https://github.com/rohitg00/skillkit/issues/new?title=${issueTitle}&body=${issueBodyEncoded}&labels=skill-submission,publish`;
|
|
9222
9590
|
console.log(chalk27.green("Opening GitHub to submit your skill...\n"));
|
|
9223
9591
|
try {
|
|
9592
|
+
const { execSync } = await import("child_process");
|
|
9224
9593
|
const openCmd = process.platform === "darwin" ? `open "${issueUrl}"` : process.platform === "win32" ? `cmd /c start "" "${issueUrl}"` : `xdg-open "${issueUrl}"`;
|
|
9225
9594
|
execSync(openCmd, { stdio: "ignore" });
|
|
9226
9595
|
console.log(chalk27.green("GitHub issue page opened!"));
|
|
9227
|
-
console.log(chalk27.dim("Review and submit the issue
|
|
9596
|
+
console.log(chalk27.dim("Review and submit the issue."));
|
|
9228
9597
|
} catch {
|
|
9229
9598
|
console.log(chalk27.yellow("Could not open browser automatically."));
|
|
9230
9599
|
console.log(chalk27.dim("Please open this URL manually:\n"));
|
|
9231
9600
|
console.log(chalk27.cyan(issueUrl));
|
|
9232
9601
|
}
|
|
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
9602
|
return 0;
|
|
9239
9603
|
}
|
|
9240
9604
|
findSkillMd(basePath) {
|
|
@@ -9247,8 +9611,7 @@ var PublishCommand = class extends Command37 {
|
|
|
9247
9611
|
}
|
|
9248
9612
|
const locations = [
|
|
9249
9613
|
join16(basePath, "skills", "SKILL.md"),
|
|
9250
|
-
join16(basePath, ".claude", "skills", "SKILL.md")
|
|
9251
|
-
join16(basePath, ".cursor", "skills", "SKILL.md")
|
|
9614
|
+
join16(basePath, ".claude", "skills", "SKILL.md")
|
|
9252
9615
|
];
|
|
9253
9616
|
for (const loc of locations) {
|
|
9254
9617
|
if (existsSync17(loc)) {
|
|
@@ -9262,18 +9625,7 @@ var PublishCommand = class extends Command37 {
|
|
|
9262
9625
|
if (!match) return {};
|
|
9263
9626
|
const frontmatter = {};
|
|
9264
9627
|
const lines = match[1].split(/\r?\n/);
|
|
9265
|
-
let inTagsList = false;
|
|
9266
9628
|
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
9629
|
const colonIdx = line.indexOf(":");
|
|
9278
9630
|
if (colonIdx === -1) continue;
|
|
9279
9631
|
const key = line.slice(0, colonIdx).trim();
|
|
@@ -9291,9 +9643,6 @@ var PublishCommand = class extends Command37 {
|
|
|
9291
9643
|
case "tags":
|
|
9292
9644
|
if (value.startsWith("[")) {
|
|
9293
9645
|
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
9646
|
}
|
|
9298
9647
|
break;
|
|
9299
9648
|
}
|
|
@@ -9305,6 +9654,7 @@ var PublishCommand = class extends Command37 {
|
|
|
9305
9654
|
}
|
|
9306
9655
|
getRepoInfo(dir) {
|
|
9307
9656
|
try {
|
|
9657
|
+
const { execSync } = __require("child_process");
|
|
9308
9658
|
const remote = execSync("git remote get-url origin", {
|
|
9309
9659
|
cwd: dir,
|
|
9310
9660
|
encoding: "utf-8",
|
|
@@ -9321,27 +9671,6 @@ var PublishCommand = class extends Command37 {
|
|
|
9321
9671
|
formatName(name) {
|
|
9322
9672
|
return name.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
9323
9673
|
}
|
|
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
9674
|
createIssueBody(skill) {
|
|
9346
9675
|
return `## Publish Skill Request
|
|
9347
9676
|
|
|
@@ -9364,15 +9693,15 @@ ${JSON.stringify(skill, null, 2)}
|
|
|
9364
9693
|
- [ ] Tags are appropriate
|
|
9365
9694
|
|
|
9366
9695
|
---
|
|
9367
|
-
Submitted via \`skillkit publish\``;
|
|
9696
|
+
Submitted via \`skillkit publish submit\``;
|
|
9368
9697
|
}
|
|
9369
9698
|
};
|
|
9370
9699
|
|
|
9371
9700
|
// src/commands/agent.ts
|
|
9372
9701
|
import chalk28 from "chalk";
|
|
9373
9702
|
import { Command as Command38, Option as Option37 } from "clipanion";
|
|
9374
|
-
import { existsSync as existsSync18, mkdirSync as
|
|
9375
|
-
import { join as join17 } from "path";
|
|
9703
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
9704
|
+
import { join as join17, basename as basename5 } from "path";
|
|
9376
9705
|
import { homedir as homedir3 } from "os";
|
|
9377
9706
|
import {
|
|
9378
9707
|
findAllAgents,
|
|
@@ -9381,7 +9710,10 @@ import {
|
|
|
9381
9710
|
discoverAgentsFromPath,
|
|
9382
9711
|
validateAgent,
|
|
9383
9712
|
translateAgent,
|
|
9384
|
-
getAgentTargetDirectory
|
|
9713
|
+
getAgentTargetDirectory,
|
|
9714
|
+
discoverSkills as discoverSkills2,
|
|
9715
|
+
readSkillContent as readSkillContent2,
|
|
9716
|
+
generateSubagentFromSkill
|
|
9385
9717
|
} from "@skillkit/core";
|
|
9386
9718
|
import {
|
|
9387
9719
|
getBundledAgents,
|
|
@@ -9402,17 +9734,19 @@ var AgentCommand = class extends Command38 {
|
|
|
9402
9734
|
that can be invoked with @mentions or the --agent flag.
|
|
9403
9735
|
|
|
9404
9736
|
Sub-commands:
|
|
9405
|
-
agent list
|
|
9406
|
-
agent show
|
|
9407
|
-
agent create
|
|
9408
|
-
agent
|
|
9409
|
-
agent
|
|
9410
|
-
agent
|
|
9737
|
+
agent list - List all installed agents
|
|
9738
|
+
agent show - Show agent details
|
|
9739
|
+
agent create - Create a new agent
|
|
9740
|
+
agent from-skill - Convert a skill to a subagent
|
|
9741
|
+
agent translate - Translate agents between formats
|
|
9742
|
+
agent sync - Sync agents to target AI agent
|
|
9743
|
+
agent validate - Validate agent definitions
|
|
9411
9744
|
`,
|
|
9412
9745
|
examples: [
|
|
9413
9746
|
["List all agents", "$0 agent list"],
|
|
9414
9747
|
["Show agent details", "$0 agent show architect"],
|
|
9415
9748
|
["Create new agent", "$0 agent create security-reviewer"],
|
|
9749
|
+
["Convert skill to subagent", "$0 agent from-skill code-simplifier"],
|
|
9416
9750
|
["Translate to Cursor format", "$0 agent translate --to cursor"],
|
|
9417
9751
|
["Sync agents", "$0 agent sync --agent claude-code"]
|
|
9418
9752
|
]
|
|
@@ -9422,6 +9756,7 @@ var AgentCommand = class extends Command38 {
|
|
|
9422
9756
|
console.log(" agent list List all installed agents");
|
|
9423
9757
|
console.log(" agent show <name> Show agent details");
|
|
9424
9758
|
console.log(" agent create <name> Create a new agent");
|
|
9759
|
+
console.log(" agent from-skill <name> Convert a skill to a subagent");
|
|
9425
9760
|
console.log(" agent translate Translate agents between formats");
|
|
9426
9761
|
console.log(" agent sync Sync agents to target AI agent");
|
|
9427
9762
|
console.log(" agent validate [path] Validate agent definitions");
|
|
@@ -9595,7 +9930,7 @@ var AgentCreateCommand = class extends Command38 {
|
|
|
9595
9930
|
targetDir = join17(process.cwd(), ".claude", "agents");
|
|
9596
9931
|
}
|
|
9597
9932
|
if (!existsSync18(targetDir)) {
|
|
9598
|
-
|
|
9933
|
+
mkdirSync9(targetDir, { recursive: true });
|
|
9599
9934
|
}
|
|
9600
9935
|
const agentPath = join17(targetDir, `${this.name}.md`);
|
|
9601
9936
|
if (existsSync18(agentPath)) {
|
|
@@ -9604,7 +9939,7 @@ var AgentCreateCommand = class extends Command38 {
|
|
|
9604
9939
|
}
|
|
9605
9940
|
const description = this.description || `${this.name} agent`;
|
|
9606
9941
|
const content = generateAgentTemplate(this.name, description, this.model);
|
|
9607
|
-
|
|
9942
|
+
writeFileSync9(agentPath, content);
|
|
9608
9943
|
console.log(chalk28.green(`Created agent: ${agentPath}`));
|
|
9609
9944
|
console.log();
|
|
9610
9945
|
console.log(chalk28.dim("Edit the file to customize the agent system prompt."));
|
|
@@ -9713,9 +10048,9 @@ var AgentTranslateCommand = class extends Command38 {
|
|
|
9713
10048
|
}
|
|
9714
10049
|
} else {
|
|
9715
10050
|
if (!existsSync18(outputDir)) {
|
|
9716
|
-
|
|
10051
|
+
mkdirSync9(outputDir, { recursive: true });
|
|
9717
10052
|
}
|
|
9718
|
-
|
|
10053
|
+
writeFileSync9(outputPath, result.content);
|
|
9719
10054
|
console.log(chalk28.green(`\u2713 ${agent.name} \u2192 ${outputPath}`));
|
|
9720
10055
|
}
|
|
9721
10056
|
successCount++;
|
|
@@ -9759,13 +10094,13 @@ var AgentSyncCommand = class extends Command38 {
|
|
|
9759
10094
|
const outputDir = getAgentTargetDirectory(process.cwd(), targetAgent);
|
|
9760
10095
|
console.log(chalk28.blue(`\u2192 ${targetAgent} (${outputDir})`));
|
|
9761
10096
|
if (!existsSync18(outputDir)) {
|
|
9762
|
-
|
|
10097
|
+
mkdirSync9(outputDir, { recursive: true });
|
|
9763
10098
|
}
|
|
9764
10099
|
for (const agent of agents) {
|
|
9765
10100
|
const result = translateAgent(agent, targetAgent);
|
|
9766
10101
|
if (result.success) {
|
|
9767
10102
|
const outputPath = join17(outputDir, result.filename);
|
|
9768
|
-
|
|
10103
|
+
writeFileSync9(outputPath, result.content);
|
|
9769
10104
|
console.log(chalk28.green(` \u2713 ${agent.name}`));
|
|
9770
10105
|
} else {
|
|
9771
10106
|
console.log(chalk28.red(` \u2717 ${agent.name}`));
|
|
@@ -10039,9 +10374,148 @@ var AgentAvailableCommand = class extends Command38 {
|
|
|
10039
10374
|
return 0;
|
|
10040
10375
|
}
|
|
10041
10376
|
};
|
|
10377
|
+
var AgentFromSkillCommand = class extends Command38 {
|
|
10378
|
+
static paths = [["agent", "from-skill"]];
|
|
10379
|
+
static usage = Command38.Usage({
|
|
10380
|
+
description: "Convert a skill into a Claude Code subagent",
|
|
10381
|
+
details: `
|
|
10382
|
+
Converts a SkillKit skill into a Claude Code native subagent format.
|
|
10383
|
+
The generated .md file can be used with @mentions in Claude Code.
|
|
10384
|
+
|
|
10385
|
+
By default, the subagent references the skill (skills: [skill-name]).
|
|
10386
|
+
Use --inline to embed the full skill content in the system prompt.
|
|
10387
|
+
`,
|
|
10388
|
+
examples: [
|
|
10389
|
+
["Convert skill to subagent", "$0 agent from-skill code-simplifier"],
|
|
10390
|
+
["Create global subagent", "$0 agent from-skill code-simplifier --global"],
|
|
10391
|
+
["Embed skill content inline", "$0 agent from-skill code-simplifier --inline"],
|
|
10392
|
+
["Set model for subagent", "$0 agent from-skill code-simplifier --model opus"],
|
|
10393
|
+
["Preview without writing", "$0 agent from-skill code-simplifier --dry-run"]
|
|
10394
|
+
]
|
|
10395
|
+
});
|
|
10396
|
+
skillName = Option37.String({ required: true });
|
|
10397
|
+
inline = Option37.Boolean("--inline,-i", false, {
|
|
10398
|
+
description: "Embed full skill content in system prompt"
|
|
10399
|
+
});
|
|
10400
|
+
model = Option37.String("--model,-m", {
|
|
10401
|
+
description: "Model to use (sonnet, opus, haiku, inherit)"
|
|
10402
|
+
});
|
|
10403
|
+
permission = Option37.String("--permission,-p", {
|
|
10404
|
+
description: "Permission mode (default, plan, auto-edit, full-auto, bypassPermissions)"
|
|
10405
|
+
});
|
|
10406
|
+
global = Option37.Boolean("--global,-g", false, {
|
|
10407
|
+
description: "Create in ~/.claude/agents/ instead of .claude/agents/"
|
|
10408
|
+
});
|
|
10409
|
+
output = Option37.String("--output,-o", {
|
|
10410
|
+
description: "Custom output filename (without .md)"
|
|
10411
|
+
});
|
|
10412
|
+
dryRun = Option37.Boolean("--dry-run,-n", false, {
|
|
10413
|
+
description: "Preview without writing files"
|
|
10414
|
+
});
|
|
10415
|
+
async execute() {
|
|
10416
|
+
const skills = discoverSkills2(process.cwd());
|
|
10417
|
+
const skill = skills.find((s) => s.name === this.skillName);
|
|
10418
|
+
if (!skill) {
|
|
10419
|
+
console.log(chalk28.red(`Skill not found: ${this.skillName}`));
|
|
10420
|
+
console.log(chalk28.dim("Available skills:"));
|
|
10421
|
+
for (const s of skills.slice(0, 10)) {
|
|
10422
|
+
console.log(chalk28.dim(` - ${s.name}`));
|
|
10423
|
+
}
|
|
10424
|
+
if (skills.length > 10) {
|
|
10425
|
+
console.log(chalk28.dim(` ... and ${skills.length - 10} more`));
|
|
10426
|
+
}
|
|
10427
|
+
return 1;
|
|
10428
|
+
}
|
|
10429
|
+
const skillContent = readSkillContent2(skill.path);
|
|
10430
|
+
if (!skillContent) {
|
|
10431
|
+
console.log(chalk28.red(`Could not read skill content: ${skill.path}`));
|
|
10432
|
+
return 1;
|
|
10433
|
+
}
|
|
10434
|
+
const options = {
|
|
10435
|
+
inline: this.inline
|
|
10436
|
+
};
|
|
10437
|
+
if (this.model) {
|
|
10438
|
+
const validModels = ["sonnet", "opus", "haiku", "inherit"];
|
|
10439
|
+
if (!validModels.includes(this.model)) {
|
|
10440
|
+
console.log(chalk28.red(`Invalid model: ${this.model}`));
|
|
10441
|
+
console.log(chalk28.dim(`Valid options: ${validModels.join(", ")}`));
|
|
10442
|
+
return 1;
|
|
10443
|
+
}
|
|
10444
|
+
options.model = this.model;
|
|
10445
|
+
}
|
|
10446
|
+
if (this.permission) {
|
|
10447
|
+
const validModes = ["default", "plan", "auto-edit", "full-auto", "bypassPermissions"];
|
|
10448
|
+
if (!validModes.includes(this.permission)) {
|
|
10449
|
+
console.log(chalk28.red(`Invalid permission mode: ${this.permission}`));
|
|
10450
|
+
console.log(chalk28.dim(`Valid options: ${validModes.join(", ")}`));
|
|
10451
|
+
return 1;
|
|
10452
|
+
}
|
|
10453
|
+
options.permissionMode = this.permission;
|
|
10454
|
+
}
|
|
10455
|
+
const content = generateSubagentFromSkill(skill, skillContent, options);
|
|
10456
|
+
const targetDir = this.global ? join17(homedir3(), ".claude", "agents") : join17(process.cwd(), ".claude", "agents");
|
|
10457
|
+
let filename;
|
|
10458
|
+
if (this.output) {
|
|
10459
|
+
const sanitized = sanitizeFilename(this.output);
|
|
10460
|
+
if (!sanitized) {
|
|
10461
|
+
console.log(chalk28.red(`Invalid output filename: ${this.output}`));
|
|
10462
|
+
console.log(chalk28.dim("Filename must contain only alphanumeric characters, hyphens, and underscores"));
|
|
10463
|
+
return 1;
|
|
10464
|
+
}
|
|
10465
|
+
filename = `${sanitized}.md`;
|
|
10466
|
+
} else {
|
|
10467
|
+
const sanitized = sanitizeFilename(skill.name);
|
|
10468
|
+
if (!sanitized) {
|
|
10469
|
+
console.log(chalk28.red(`Invalid skill name for filename: ${skill.name}`));
|
|
10470
|
+
console.log(chalk28.dim("Skill name must contain only alphanumeric characters, hyphens, and underscores"));
|
|
10471
|
+
return 1;
|
|
10472
|
+
}
|
|
10473
|
+
filename = `${sanitized}.md`;
|
|
10474
|
+
}
|
|
10475
|
+
const outputPath = join17(targetDir, filename);
|
|
10476
|
+
if (this.dryRun) {
|
|
10477
|
+
console.log(chalk28.cyan("Preview (dry run):\n"));
|
|
10478
|
+
console.log(chalk28.dim(`Would write to: ${outputPath}`));
|
|
10479
|
+
console.log(chalk28.dim("\u2500".repeat(50)));
|
|
10480
|
+
console.log(content);
|
|
10481
|
+
console.log(chalk28.dim("\u2500".repeat(50)));
|
|
10482
|
+
return 0;
|
|
10483
|
+
}
|
|
10484
|
+
if (!existsSync18(targetDir)) {
|
|
10485
|
+
mkdirSync9(targetDir, { recursive: true });
|
|
10486
|
+
}
|
|
10487
|
+
if (existsSync18(outputPath)) {
|
|
10488
|
+
console.log(chalk28.yellow(`Overwriting existing file: ${outputPath}`));
|
|
10489
|
+
}
|
|
10490
|
+
writeFileSync9(outputPath, content);
|
|
10491
|
+
console.log(chalk28.green(`Created subagent: ${outputPath}`));
|
|
10492
|
+
console.log();
|
|
10493
|
+
console.log(chalk28.dim(`Invoke with: @${skill.name}`));
|
|
10494
|
+
if (!this.inline) {
|
|
10495
|
+
console.log(chalk28.dim(`Skills referenced: ${skill.name}`));
|
|
10496
|
+
} else {
|
|
10497
|
+
console.log(chalk28.dim("Skill content embedded inline"));
|
|
10498
|
+
}
|
|
10499
|
+
return 0;
|
|
10500
|
+
}
|
|
10501
|
+
};
|
|
10042
10502
|
function formatCategoryName(category) {
|
|
10043
10503
|
return category.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
10044
10504
|
}
|
|
10505
|
+
function sanitizeFilename(input) {
|
|
10506
|
+
const base = basename5(input);
|
|
10507
|
+
const stem = base.replace(/\.md$/i, "");
|
|
10508
|
+
if (!stem || stem.startsWith(".") || stem.startsWith("-")) {
|
|
10509
|
+
return null;
|
|
10510
|
+
}
|
|
10511
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(stem)) {
|
|
10512
|
+
return null;
|
|
10513
|
+
}
|
|
10514
|
+
if (stem.length > 64) {
|
|
10515
|
+
return null;
|
|
10516
|
+
}
|
|
10517
|
+
return stem;
|
|
10518
|
+
}
|
|
10045
10519
|
|
|
10046
10520
|
// src/commands/check.ts
|
|
10047
10521
|
init_helpers();
|
|
@@ -10129,9 +10603,9 @@ var CheckCommand = class extends Command39 {
|
|
|
10129
10603
|
const sourceSkillMd = join18(localPath, "SKILL.md");
|
|
10130
10604
|
const installedSkillMd = join18(skill.path, "SKILL.md");
|
|
10131
10605
|
if (existsSync19(sourceSkillMd) && existsSync19(installedSkillMd)) {
|
|
10132
|
-
const { statSync:
|
|
10133
|
-
const sourceTime =
|
|
10134
|
-
const installedTime =
|
|
10606
|
+
const { statSync: statSync4 } = await import("fs");
|
|
10607
|
+
const sourceTime = statSync4(sourceSkillMd).mtime;
|
|
10608
|
+
const installedTime = statSync4(installedSkillMd).mtime;
|
|
10135
10609
|
if (sourceTime > installedTime) {
|
|
10136
10610
|
results.push({ name: skill.name, hasUpdate: true });
|
|
10137
10611
|
updatesAvailable++;
|
|
@@ -136210,7 +136684,7 @@ var ManifestGenerateCommand = class extends Command41 {
|
|
|
136210
136684
|
|
|
136211
136685
|
// src/commands/primer.ts
|
|
136212
136686
|
import { Command as Command42, Option as Option41 } from "clipanion";
|
|
136213
|
-
import { resolve as
|
|
136687
|
+
import { resolve as resolve19 } from "path";
|
|
136214
136688
|
import chalk29 from "chalk";
|
|
136215
136689
|
import {
|
|
136216
136690
|
AgentType as AgentTypeSchema,
|
|
@@ -136278,7 +136752,7 @@ var PrimerCommand = class extends Command42 {
|
|
|
136278
136752
|
description: "Number of commits to analyze for learning (default: 100)"
|
|
136279
136753
|
});
|
|
136280
136754
|
async execute() {
|
|
136281
|
-
const projectPath =
|
|
136755
|
+
const projectPath = resolve19(this.directory || process.cwd());
|
|
136282
136756
|
if (this.analyzeOnly) {
|
|
136283
136757
|
return this.runAnalysis(projectPath);
|
|
136284
136758
|
}
|
|
@@ -136347,7 +136821,7 @@ var PrimerCommand = class extends Command42 {
|
|
|
136347
136821
|
const result = generatePrimer(projectPath, {
|
|
136348
136822
|
agents,
|
|
136349
136823
|
allAgents: this.allAgents,
|
|
136350
|
-
outputDir: this.output ?
|
|
136824
|
+
outputDir: this.output ? resolve19(this.output) : void 0,
|
|
136351
136825
|
dryRun: this.dryRun,
|
|
136352
136826
|
verbose: this.verbose,
|
|
136353
136827
|
includeExamples: this.includeExamples
|
|
@@ -137558,7 +138032,7 @@ Sent Messages (${messages.length})
|
|
|
137558
138032
|
|
|
137559
138033
|
// src/commands/learn.ts
|
|
137560
138034
|
import { Command as Command45, Option as Option44 } from "clipanion";
|
|
137561
|
-
import { resolve as
|
|
138035
|
+
import { resolve as resolve20 } from "path";
|
|
137562
138036
|
import chalk32 from "chalk";
|
|
137563
138037
|
import {
|
|
137564
138038
|
analyzeGitHistory as analyzeGitHistory2,
|
|
@@ -137578,7 +138052,7 @@ import {
|
|
|
137578
138052
|
getPatternStats,
|
|
137579
138053
|
clusterPatterns
|
|
137580
138054
|
} from "@skillkit/core";
|
|
137581
|
-
import { writeFileSync as
|
|
138055
|
+
import { writeFileSync as writeFileSync10, readFileSync as readFileSync10, existsSync as existsSync20 } from "fs";
|
|
137582
138056
|
var LearnCommand = class extends Command45 {
|
|
137583
138057
|
static paths = [["learn"]];
|
|
137584
138058
|
static usage = Command45.Usage({
|
|
@@ -137615,7 +138089,7 @@ var LearnCommand = class extends Command45 {
|
|
|
137615
138089
|
description: "Project directory to analyze (default: current directory)"
|
|
137616
138090
|
});
|
|
137617
138091
|
async execute() {
|
|
137618
|
-
const projectPath =
|
|
138092
|
+
const projectPath = resolve20(this.directory || process.cwd());
|
|
137619
138093
|
if (this.show) {
|
|
137620
138094
|
return this.showPatterns();
|
|
137621
138095
|
}
|
|
@@ -137862,7 +138336,7 @@ var PatternExportCommand = class extends Command45 {
|
|
|
137862
138336
|
content = exportPatternsAsJson(patterns);
|
|
137863
138337
|
}
|
|
137864
138338
|
if (this.output) {
|
|
137865
|
-
|
|
138339
|
+
writeFileSync10(this.output, content);
|
|
137866
138340
|
console.log(chalk32.green(`\u2713 Exported ${patterns.length} patterns to ${this.output}`));
|
|
137867
138341
|
} else {
|
|
137868
138342
|
console.log(content);
|
|
@@ -138584,6 +139058,208 @@ function formatCategoryName2(category) {
|
|
|
138584
139058
|
return category.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
138585
139059
|
}
|
|
138586
139060
|
|
|
139061
|
+
// src/commands/tree.ts
|
|
139062
|
+
init_onboarding();
|
|
139063
|
+
import { Command as Command49, Option as Option48 } from "clipanion";
|
|
139064
|
+
import { join as join19 } from "path";
|
|
139065
|
+
import { homedir as homedir4 } from "os";
|
|
139066
|
+
import {
|
|
139067
|
+
loadIndex as loadIndexFromCache3,
|
|
139068
|
+
generateSkillTree,
|
|
139069
|
+
saveTree,
|
|
139070
|
+
loadTree
|
|
139071
|
+
} from "@skillkit/core";
|
|
139072
|
+
var TREE_PATH = join19(homedir4(), ".skillkit", "skill-tree.json");
|
|
139073
|
+
var TreeCommand = class extends Command49 {
|
|
139074
|
+
static paths = [["tree"]];
|
|
139075
|
+
static usage = Command49.Usage({
|
|
139076
|
+
description: "Browse skills in a hierarchical tree structure",
|
|
139077
|
+
details: `
|
|
139078
|
+
The tree command displays skills organized in a hierarchical taxonomy.
|
|
139079
|
+
Navigate through categories like Development, Testing, DevOps, AI/ML, etc.
|
|
139080
|
+
|
|
139081
|
+
Features:
|
|
139082
|
+
- Visual tree structure of all skills
|
|
139083
|
+
- Filter by category path (e.g., "Frontend > React")
|
|
139084
|
+
- Generate tree from skill index
|
|
139085
|
+
- Export to markdown format
|
|
139086
|
+
`,
|
|
139087
|
+
examples: [
|
|
139088
|
+
["Show full tree", "$0 tree"],
|
|
139089
|
+
["Show specific category", "$0 tree Frontend"],
|
|
139090
|
+
["Show subcategory", '$0 tree "Frontend > React"'],
|
|
139091
|
+
["Limit depth", "$0 tree --depth 2"],
|
|
139092
|
+
["Generate/update tree", "$0 tree --generate"],
|
|
139093
|
+
["Export to markdown", "$0 tree --markdown"],
|
|
139094
|
+
["Show tree stats", "$0 tree --stats"]
|
|
139095
|
+
]
|
|
139096
|
+
});
|
|
139097
|
+
treePath = Option48.String({ required: false });
|
|
139098
|
+
depth = Option48.String("--depth,-d", {
|
|
139099
|
+
description: "Maximum depth to display"
|
|
139100
|
+
});
|
|
139101
|
+
generate = Option48.Boolean("--generate,-g", false, {
|
|
139102
|
+
description: "Generate/update tree from skill index"
|
|
139103
|
+
});
|
|
139104
|
+
markdown = Option48.Boolean("--markdown,-m", false, {
|
|
139105
|
+
description: "Output in markdown format"
|
|
139106
|
+
});
|
|
139107
|
+
stats = Option48.Boolean("--stats,-s", false, {
|
|
139108
|
+
description: "Show tree statistics"
|
|
139109
|
+
});
|
|
139110
|
+
json = Option48.Boolean("--json,-j", false, {
|
|
139111
|
+
description: "Output in JSON format"
|
|
139112
|
+
});
|
|
139113
|
+
quiet = Option48.Boolean("--quiet,-q", false, {
|
|
139114
|
+
description: "Minimal output"
|
|
139115
|
+
});
|
|
139116
|
+
async execute() {
|
|
139117
|
+
if (this.generate) {
|
|
139118
|
+
return await this.generateTree();
|
|
139119
|
+
}
|
|
139120
|
+
const tree = this.loadOrGenerateTree();
|
|
139121
|
+
if (!tree) {
|
|
139122
|
+
warn('No skill tree found. Run "skillkit tree --generate" first.');
|
|
139123
|
+
return 1;
|
|
139124
|
+
}
|
|
139125
|
+
if (this.stats) {
|
|
139126
|
+
return this.showStats(tree);
|
|
139127
|
+
}
|
|
139128
|
+
if (this.json) {
|
|
139129
|
+
console.log(JSON.stringify(tree, null, 2));
|
|
139130
|
+
return 0;
|
|
139131
|
+
}
|
|
139132
|
+
if (this.markdown) {
|
|
139133
|
+
const { treeToMarkdown } = await import("@skillkit/core");
|
|
139134
|
+
console.log(treeToMarkdown(tree));
|
|
139135
|
+
return 0;
|
|
139136
|
+
}
|
|
139137
|
+
return this.displayTree(tree);
|
|
139138
|
+
}
|
|
139139
|
+
async generateTree() {
|
|
139140
|
+
if (!this.quiet) {
|
|
139141
|
+
header("Generate Skill Tree");
|
|
139142
|
+
}
|
|
139143
|
+
const index = loadIndexFromCache3();
|
|
139144
|
+
if (!index || index.skills.length === 0) {
|
|
139145
|
+
warn('No skill index found. Run "skillkit recommend --update" first.');
|
|
139146
|
+
return 1;
|
|
139147
|
+
}
|
|
139148
|
+
const s = spinner2();
|
|
139149
|
+
s.start("Generating skill tree...");
|
|
139150
|
+
try {
|
|
139151
|
+
const tree = generateSkillTree(index.skills);
|
|
139152
|
+
saveTree(tree, TREE_PATH);
|
|
139153
|
+
s.stop(`Generated tree with ${tree.totalCategories} categories`);
|
|
139154
|
+
console.log("");
|
|
139155
|
+
console.log(colors.success(`${symbols.success} Tree generated successfully`));
|
|
139156
|
+
console.log(colors.muted(` Total skills: ${tree.totalSkills}`));
|
|
139157
|
+
console.log(colors.muted(` Categories: ${tree.totalCategories}`));
|
|
139158
|
+
console.log(colors.muted(` Max depth: ${tree.maxDepth}`));
|
|
139159
|
+
console.log(colors.muted(` Saved to: ${TREE_PATH}`));
|
|
139160
|
+
console.log("");
|
|
139161
|
+
return 0;
|
|
139162
|
+
} catch (err) {
|
|
139163
|
+
s.stop(colors.error("Failed to generate tree"));
|
|
139164
|
+
console.log(colors.muted(err instanceof Error ? err.message : String(err)));
|
|
139165
|
+
return 1;
|
|
139166
|
+
}
|
|
139167
|
+
}
|
|
139168
|
+
loadOrGenerateTree() {
|
|
139169
|
+
let tree = loadTree(TREE_PATH);
|
|
139170
|
+
if (!tree) {
|
|
139171
|
+
const index = loadIndexFromCache3();
|
|
139172
|
+
if (index && index.skills.length > 0) {
|
|
139173
|
+
tree = generateSkillTree(index.skills);
|
|
139174
|
+
saveTree(tree, TREE_PATH);
|
|
139175
|
+
}
|
|
139176
|
+
}
|
|
139177
|
+
return tree;
|
|
139178
|
+
}
|
|
139179
|
+
showStats(tree) {
|
|
139180
|
+
if (!this.quiet) {
|
|
139181
|
+
header("Skill Tree Statistics");
|
|
139182
|
+
}
|
|
139183
|
+
console.log("");
|
|
139184
|
+
console.log(colors.bold("Overview:"));
|
|
139185
|
+
console.log(` Total Skills: ${colors.accent(String(tree.totalSkills))}`);
|
|
139186
|
+
console.log(` Categories: ${colors.accent(String(tree.totalCategories))}`);
|
|
139187
|
+
console.log(` Max Depth: ${colors.accent(String(tree.maxDepth))}`);
|
|
139188
|
+
console.log(` Generated: ${colors.muted(tree.generatedAt)}`);
|
|
139189
|
+
console.log("");
|
|
139190
|
+
console.log(colors.bold("Top-Level Categories:"));
|
|
139191
|
+
for (const child of tree.rootNode.children) {
|
|
139192
|
+
const percentage = tree.totalSkills > 0 ? (child.skillCount / tree.totalSkills * 100).toFixed(1) : "0.0";
|
|
139193
|
+
console.log(
|
|
139194
|
+
` ${colors.accent(child.name.padEnd(15))} ${String(child.skillCount).padStart(6)} skills (${percentage}%)`
|
|
139195
|
+
);
|
|
139196
|
+
if (child.children.length > 0) {
|
|
139197
|
+
const subcats = child.children.sort((a, b) => b.skillCount - a.skillCount).slice(0, 3).map((c) => `${c.name} (${c.skillCount})`).join(", ");
|
|
139198
|
+
console.log(` ${colors.muted(subcats)}`);
|
|
139199
|
+
}
|
|
139200
|
+
}
|
|
139201
|
+
console.log("");
|
|
139202
|
+
return 0;
|
|
139203
|
+
}
|
|
139204
|
+
displayTree(tree) {
|
|
139205
|
+
if (!this.quiet) {
|
|
139206
|
+
header("Skill Tree");
|
|
139207
|
+
console.log(colors.muted(`${tree.totalSkills} skills in ${tree.totalCategories} categories`));
|
|
139208
|
+
console.log("");
|
|
139209
|
+
}
|
|
139210
|
+
let targetNode = tree.rootNode;
|
|
139211
|
+
if (this.treePath) {
|
|
139212
|
+
const segments = this.treePath.split(">").map((s) => s.trim());
|
|
139213
|
+
let current = tree.rootNode;
|
|
139214
|
+
for (const segment of segments) {
|
|
139215
|
+
const child = current.children.find(
|
|
139216
|
+
(c) => c.name.toLowerCase() === segment.toLowerCase()
|
|
139217
|
+
);
|
|
139218
|
+
if (!child) {
|
|
139219
|
+
warn(`Category not found: ${segment}`);
|
|
139220
|
+
console.log(colors.muted(`Available categories: ${current.children.map((c) => c.name).join(", ")}`));
|
|
139221
|
+
return 1;
|
|
139222
|
+
}
|
|
139223
|
+
current = child;
|
|
139224
|
+
}
|
|
139225
|
+
targetNode = current;
|
|
139226
|
+
}
|
|
139227
|
+
let maxDepth = this.depth ? parseInt(this.depth, 10) : 3;
|
|
139228
|
+
if (Number.isNaN(maxDepth) || maxDepth < 0) {
|
|
139229
|
+
warn("Invalid depth value. Using default depth of 3.");
|
|
139230
|
+
maxDepth = 3;
|
|
139231
|
+
}
|
|
139232
|
+
this.renderNode(targetNode, "", true, 0, maxDepth);
|
|
139233
|
+
console.log("");
|
|
139234
|
+
console.log(colors.muted('Navigate: skillkit tree "Category > Subcategory"'));
|
|
139235
|
+
console.log(colors.muted("Generate: skillkit tree --generate"));
|
|
139236
|
+
return 0;
|
|
139237
|
+
}
|
|
139238
|
+
renderNode(node, prefix, isLast, depth, maxDepth) {
|
|
139239
|
+
if (depth > maxDepth) return;
|
|
139240
|
+
const connector = depth === 0 ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
139241
|
+
const icon = node.children.length > 0 ? "\u{1F4C1} " : "\u{1F4C4} ";
|
|
139242
|
+
const skillInfo = node.skillCount > 0 ? colors.muted(` (${node.skillCount})`) : "";
|
|
139243
|
+
const nameColor = depth === 0 ? colors.bold : depth === 1 ? colors.accent : colors.dim;
|
|
139244
|
+
console.log(`${prefix}${connector}${icon}${nameColor(node.name)}${skillInfo}`);
|
|
139245
|
+
if (depth === maxDepth && node.skills.length > 0 && node.skills.length <= 5) {
|
|
139246
|
+
const childPrefix = prefix + (isLast ? " " : "\u2502 ");
|
|
139247
|
+
for (const skill of node.skills) {
|
|
139248
|
+
console.log(`${childPrefix} \u2022 ${colors.muted(skill)}`);
|
|
139249
|
+
}
|
|
139250
|
+
}
|
|
139251
|
+
const newPrefix = prefix + (depth === 0 ? "" : isLast ? " " : "\u2502 ");
|
|
139252
|
+
if (depth < maxDepth) {
|
|
139253
|
+
node.children.forEach((child, index) => {
|
|
139254
|
+
const childIsLast = index === node.children.length - 1;
|
|
139255
|
+
this.renderNode(child, newPrefix, childIsLast, depth + 1, maxDepth);
|
|
139256
|
+
});
|
|
139257
|
+
} else if (node.children.length > 0) {
|
|
139258
|
+
console.log(`${newPrefix} ${colors.muted(`... ${node.children.length} more subcategories`)}`);
|
|
139259
|
+
}
|
|
139260
|
+
}
|
|
139261
|
+
};
|
|
139262
|
+
|
|
138587
139263
|
// src/index.ts
|
|
138588
139264
|
init_helpers();
|
|
138589
139265
|
export {
|
|
@@ -138591,6 +139267,7 @@ export {
|
|
|
138591
139267
|
AgentAvailableCommand,
|
|
138592
139268
|
AgentCommand,
|
|
138593
139269
|
AgentCreateCommand,
|
|
139270
|
+
AgentFromSkillCommand,
|
|
138594
139271
|
AgentInstallCommand,
|
|
138595
139272
|
AgentListCommand,
|
|
138596
139273
|
AgentShowCommand,
|
|
@@ -138651,6 +139328,7 @@ export {
|
|
|
138651
139328
|
ProfileListCommand,
|
|
138652
139329
|
ProfileRemoveCommand,
|
|
138653
139330
|
PublishCommand,
|
|
139331
|
+
PublishSubmitCommand,
|
|
138654
139332
|
ReadCommand,
|
|
138655
139333
|
RecommendCommand,
|
|
138656
139334
|
RemoveCommand,
|
|
@@ -138670,6 +139348,7 @@ export {
|
|
|
138670
139348
|
TeamCommand,
|
|
138671
139349
|
TestCommand,
|
|
138672
139350
|
TranslateCommand,
|
|
139351
|
+
TreeCommand,
|
|
138673
139352
|
UICommand,
|
|
138674
139353
|
UpdateCommand,
|
|
138675
139354
|
ValidateCommand,
|