@skillkit/cli 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5790,12 +5790,12 @@ ${learning.title}
5790
5790
  }
5791
5791
  const outputPath = this.output || `.skillkit/exports/${skillName}/SKILL.md`;
5792
5792
  const { dirname: dirname7 } = await import("path");
5793
- const { existsSync: existsSync21, mkdirSync: mkdirSync10, writeFileSync: writeFileSync11 } = await import("fs");
5793
+ const { existsSync: existsSync23, mkdirSync: mkdirSync11, writeFileSync: writeFileSync12 } = await import("fs");
5794
5794
  const outputDir = dirname7(outputPath);
5795
- if (!existsSync21(outputDir)) {
5796
- mkdirSync10(outputDir, { recursive: true });
5795
+ if (!existsSync23(outputDir)) {
5796
+ mkdirSync11(outputDir, { recursive: true });
5797
5797
  }
5798
- writeFileSync11(outputPath, skillContent, "utf-8");
5798
+ writeFileSync12(outputPath, skillContent, "utf-8");
5799
5799
  console.log(chalk18.green(`\u2713 Exported learning as skill: ${skillName}`));
5800
5800
  console.log(chalk18.gray(` Path: ${outputPath}`));
5801
5801
  return 0;
@@ -5810,10 +5810,10 @@ ${learning.title}
5810
5810
  console.log(chalk18.gray("Usage: skillkit memory import --input <path>"));
5811
5811
  return 1;
5812
5812
  }
5813
- const { existsSync: existsSync21, readFileSync: readFileSync11 } = await import("fs");
5814
- const { resolve: resolve21 } = await import("path");
5815
- const fullPath = resolve21(inputPath);
5816
- if (!existsSync21(fullPath)) {
5813
+ const { existsSync: existsSync23, readFileSync: readFileSync12 } = await import("fs");
5814
+ const { resolve: resolve23 } = await import("path");
5815
+ const fullPath = resolve23(inputPath);
5816
+ if (!existsSync23(fullPath)) {
5817
5817
  console.error(chalk18.red(`File not found: ${fullPath}`));
5818
5818
  return 1;
5819
5819
  }
@@ -5823,7 +5823,7 @@ ${learning.title}
5823
5823
  this.global ? void 0 : projectPath
5824
5824
  );
5825
5825
  try {
5826
- const content = readFileSync11(fullPath, "utf-8");
5826
+ const content = readFileSync12(fullPath, "utf-8");
5827
5827
  const { parse: parseYaml2 } = await import("yaml");
5828
5828
  const data = parseYaml2(content);
5829
5829
  if (!data.learnings || !Array.isArray(data.learnings)) {
@@ -6892,14 +6892,14 @@ var TeamCommand = class extends Command29 {
6892
6892
  }
6893
6893
  const projectPath = process.cwd();
6894
6894
  const bundlePath = join12(projectPath, ".skillkit", "bundles", `${this.name}.json`);
6895
- const { existsSync: existsSync21, readFileSync: readFileSync11, writeFileSync: writeFileSync11 } = await import("fs");
6896
- if (!existsSync21(bundlePath)) {
6895
+ const { existsSync: existsSync23, readFileSync: readFileSync12, writeFileSync: writeFileSync12 } = await import("fs");
6896
+ if (!existsSync23(bundlePath)) {
6897
6897
  this.context.stderr.write(chalk21.red(`Bundle "${this.name}" not found. Create it first with bundle-create.
6898
6898
  `));
6899
6899
  return 1;
6900
6900
  }
6901
- const content = readFileSync11(bundlePath, "utf-8");
6902
- writeFileSync11(this.output, content, "utf-8");
6901
+ const content = readFileSync12(bundlePath, "utf-8");
6902
+ writeFileSync12(this.output, content, "utf-8");
6903
6903
  this.context.stdout.write(chalk21.green(`\u2713 Bundle exported to: ${this.output}
6904
6904
  `));
6905
6905
  return 0;
@@ -6909,8 +6909,8 @@ var TeamCommand = class extends Command29 {
6909
6909
  this.context.stderr.write(chalk21.red("--source <path> is required for bundle-import\n"));
6910
6910
  return 1;
6911
6911
  }
6912
- const { existsSync: existsSync21 } = await import("fs");
6913
- if (!existsSync21(this.source)) {
6912
+ const { existsSync: existsSync23 } = await import("fs");
6913
+ if (!existsSync23(this.source)) {
6914
6914
  this.context.stderr.write(chalk21.red(`Bundle file not found: ${this.source}
6915
6915
  `));
6916
6916
  return 1;
@@ -7078,8 +7078,8 @@ var PluginCommand = class extends Command30 {
7078
7078
  }
7079
7079
  const projectPath = this.global ? join13(homedir2(), ".skillkit") : process.cwd();
7080
7080
  const pluginsDir = this.global ? join13(projectPath, "plugins") : join13(projectPath, ".skillkit", "plugins");
7081
- const isLocalPath4 = this.source.startsWith("./") || this.source.startsWith("../") || this.source.startsWith("/") || this.source.startsWith("~") || this.source.includes("\\") || isAbsolute(this.source);
7082
- if (isLocalPath4 && existsSync15(resolvedSource)) {
7081
+ const isLocalPath5 = this.source.startsWith("./") || this.source.startsWith("../") || this.source.startsWith("/") || this.source.startsWith("~") || this.source.includes("\\") || isAbsolute(this.source);
7082
+ if (isLocalPath5 && existsSync15(resolvedSource)) {
7083
7083
  const targetDir = join13(pluginsDir, pluginName);
7084
7084
  const resolvedTarget = resolve13(targetDir);
7085
7085
  const resolvedPluginsDir = resolve13(pluginsDir);
@@ -10815,6 +10815,7 @@ var CheckCommand = class extends Command39 {
10815
10815
  // src/commands/find.ts
10816
10816
  init_onboarding();
10817
10817
  import { Command as Command40, Option as Option39 } from "clipanion";
10818
+ import { FederatedSearch, GitHubSkillRegistry, RateLimitError } from "@skillkit/core";
10818
10819
 
10819
10820
  // ../../marketplace/skills.json
10820
10821
  var skills_default = {
@@ -136493,6 +136494,9 @@ var FindCommand = class extends Command40 {
136493
136494
  quiet = Option39.Boolean("--quiet,-q", false, {
136494
136495
  description: "Minimal output (just list skills)"
136495
136496
  });
136497
+ federated = Option39.Boolean("--federated,-f", false, {
136498
+ description: "Search external registries (GitHub SKILL.md files)"
136499
+ });
136496
136500
  async execute() {
136497
136501
  const s = spinner2();
136498
136502
  const limit = parseInt(this.limit, 10) || 10;
@@ -136542,12 +136546,42 @@ var FindCommand = class extends Command40 {
136542
136546
  results = allSkills.slice(0, limit);
136543
136547
  }
136544
136548
  }
136549
+ if (this.federated && this.query) {
136550
+ s.start("Searching external registries...");
136551
+ const fedSearch = new FederatedSearch();
136552
+ fedSearch.addRegistry(new GitHubSkillRegistry());
136553
+ try {
136554
+ const fedResult = await fedSearch.search(this.query, { limit: parseInt(this.limit, 10) || 10 });
136555
+ s.stop(`Found ${fedResult.total} external skill(s) from ${fedResult.registries.join(", ") || "none"}`);
136556
+ if (fedResult.skills.length > 0) {
136557
+ console.log("");
136558
+ console.log(colors.bold("External Skills (SKILL.md):"));
136559
+ for (const skill of fedResult.skills) {
136560
+ const stars = typeof skill.stars === "number" ? colors.muted(` \u2605${skill.stars}`) : "";
136561
+ const desc = skill.description ? colors.muted(` - ${skill.description.slice(0, 50)}${skill.description.length > 50 ? "..." : ""}`) : "";
136562
+ console.log(` ${colors.cyan(symbols.bullet)} ${colors.primary(skill.name)}${stars}${desc}`);
136563
+ if (!this.quiet) {
136564
+ console.log(` ${colors.muted(skill.source)}`);
136565
+ }
136566
+ }
136567
+ console.log("");
136568
+ }
136569
+ } catch (err) {
136570
+ if (err instanceof RateLimitError) {
136571
+ s.stop(colors.warning("Rate limited by GitHub API"));
136572
+ console.log(colors.muted("Set GITHUB_TOKEN env var or wait before retrying."));
136573
+ } else {
136574
+ s.stop(colors.warning("Federated search failed"));
136575
+ }
136576
+ }
136577
+ }
136545
136578
  if (results.length === 0) {
136546
136579
  console.log(colors.muted("No skills found matching your search"));
136547
136580
  console.log("");
136548
136581
  console.log(colors.muted("Try:"));
136549
- console.log(colors.muted(" skillkit find --top # Show featured skills"));
136550
- console.log(colors.muted(" skillkit ui # Browse in TUI"));
136582
+ console.log(colors.muted(" skillkit find --top # Show featured skills"));
136583
+ console.log(colors.muted(" skillkit find -f <query> # Search external registries"));
136584
+ console.log(colors.muted(" skillkit ui # Browse in TUI"));
136551
136585
  return 0;
136552
136586
  }
136553
136587
  console.log("");
@@ -139409,6 +139443,631 @@ var TreeCommand = class extends Command49 {
139409
139443
  }
139410
139444
  };
139411
139445
 
139446
+ // src/commands/quick.ts
139447
+ init_helpers();
139448
+ init_onboarding();
139449
+ import { existsSync as existsSync21, mkdirSync as mkdirSync10, cpSync as cpSync4, rmSync as rmSync5, symlinkSync as symlinkSync2 } from "fs";
139450
+ import { join as join20, resolve as resolve21, normalize } from "path";
139451
+ import { execFile as execFile2 } from "child_process";
139452
+ import { promisify as promisify2 } from "util";
139453
+ import { Command as Command50 } from "clipanion";
139454
+ import {
139455
+ ContextManager as ContextManager3,
139456
+ RecommendationEngine as RecommendationEngine2,
139457
+ loadIndex as loadIndexFromCache4,
139458
+ isIndexStale as isIndexStale2,
139459
+ buildSkillIndex as buildSkillIndex2,
139460
+ saveIndex as saveIndex2,
139461
+ KNOWN_SKILL_REPOS as KNOWN_SKILL_REPOS2,
139462
+ isPathInside as isPathInside2,
139463
+ isLocalPath as isLocalPath4,
139464
+ detectProvider as detectProvider4
139465
+ } from "@skillkit/core";
139466
+ import { getAdapter as getAdapter6, detectAgent as detectAgent5, getAllAdapters as getAllAdapters6 } from "@skillkit/agents";
139467
+ var execFileAsync2 = promisify2(execFile2);
139468
+ function truncate6(str, maxLen) {
139469
+ if (str.length <= maxLen) return str;
139470
+ return str.slice(0, maxLen - 3) + "...";
139471
+ }
139472
+ var QuickCommand = class extends Command50 {
139473
+ static paths = [["quick"]];
139474
+ static usage = Command50.Usage({
139475
+ description: "Zero-friction skill setup: detect agents, analyze project, recommend and install",
139476
+ examples: [
139477
+ ["Quick setup", "$0 quick"]
139478
+ ]
139479
+ });
139480
+ async execute() {
139481
+ const s = spinner2();
139482
+ try {
139483
+ welcome();
139484
+ step(`${colors.bold("Quick Setup")} - zero-friction skill installation`);
139485
+ console.log("");
139486
+ s.start("Detecting installed AI agents...");
139487
+ const allAdapters = getAllAdapters6();
139488
+ const detectedAgents = [];
139489
+ for (const adapter of allAdapters) {
139490
+ try {
139491
+ const installDir = getInstallDir(false, adapter.type);
139492
+ const globalDir = getInstallDir(true, adapter.type);
139493
+ if (existsSync21(installDir) || existsSync21(globalDir) || existsSync21(adapter.configFile)) {
139494
+ detectedAgents.push(adapter.type);
139495
+ }
139496
+ } catch {
139497
+ }
139498
+ }
139499
+ if (detectedAgents.length === 0) {
139500
+ const fallback = await detectAgent5();
139501
+ detectedAgents.push(fallback);
139502
+ }
139503
+ s.stop(`Detected ${detectedAgents.length} agent${detectedAgents.length !== 1 ? "s" : ""}: ${detectedAgents.map(formatAgent).join(", ")}`);
139504
+ s.start("Analyzing project...");
139505
+ const targetPath = resolve21(process.cwd());
139506
+ const manager = new ContextManager3(targetPath);
139507
+ let context = manager.get();
139508
+ if (!context) {
139509
+ context = manager.init();
139510
+ }
139511
+ s.stop("Project analyzed");
139512
+ if (!context) {
139513
+ error("Failed to analyze project");
139514
+ return 1;
139515
+ }
139516
+ const profile = {
139517
+ name: context.project.name,
139518
+ type: context.project.type,
139519
+ stack: context.stack,
139520
+ patterns: context.patterns,
139521
+ installedSkills: context.skills?.installed || [],
139522
+ excludedSkills: context.skills?.excluded || []
139523
+ };
139524
+ const languages = profile.stack.languages.map((l) => `${l.name}${l.version ? ` ${l.version}` : ""}`);
139525
+ const frameworks = profile.stack.frameworks.map((f) => `${f.name}${f.version ? ` ${f.version}` : ""}`);
139526
+ showProjectSummary({
139527
+ name: profile.name,
139528
+ type: profile.type,
139529
+ languages,
139530
+ frameworks
139531
+ });
139532
+ let index = loadIndexFromCache4();
139533
+ if (!index || index.skills.length === 0) {
139534
+ s.start("Building skill index from known sources...");
139535
+ try {
139536
+ const result2 = await buildSkillIndex2(KNOWN_SKILL_REPOS2, (message) => {
139537
+ s.message(message);
139538
+ });
139539
+ index = result2.index;
139540
+ saveIndex2(index);
139541
+ s.stop(`Indexed ${index.skills.length} skills`);
139542
+ } catch {
139543
+ s.stop(colors.error("Failed to build index"));
139544
+ warn("Could not fetch skill index. Try running: skillkit recommend --update");
139545
+ return 1;
139546
+ }
139547
+ } else if (isIndexStale2(index)) {
139548
+ step(colors.muted('Skill index is stale. Run "skillkit recommend --update" to refresh.'));
139549
+ }
139550
+ const engine = new RecommendationEngine2();
139551
+ engine.loadIndex(index);
139552
+ const result = engine.recommend(profile, {
139553
+ limit: 5,
139554
+ minScore: 20,
139555
+ excludeInstalled: true,
139556
+ includeReasons: true
139557
+ });
139558
+ const recommendations = result.recommendations;
139559
+ if (recommendations.length === 0) {
139560
+ warn("No matching skills found for your project.");
139561
+ console.log(colors.muted("Try running: skillkit recommend --update"));
139562
+ return 0;
139563
+ }
139564
+ console.log("");
139565
+ console.log(colors.bold(`Top ${recommendations.length} Recommended Skills:`));
139566
+ console.log("");
139567
+ const options = [];
139568
+ for (let i = 0; i < recommendations.length; i++) {
139569
+ const rec = recommendations[i];
139570
+ let scoreColor;
139571
+ if (rec.score >= 70) {
139572
+ scoreColor = colors.success;
139573
+ } else if (rec.score >= 50) {
139574
+ scoreColor = colors.warning;
139575
+ } else {
139576
+ scoreColor = colors.muted;
139577
+ }
139578
+ const scoreBar = progressBar(rec.score, 100, 10);
139579
+ const qualityScore = rec.skill.quality ?? null;
139580
+ const qualityDisplay = typeof qualityScore === "number" && Number.isFinite(qualityScore) ? ` ${formatQualityBadge(qualityScore)}` : "";
139581
+ console.log(` ${colors.bold(`${i + 1}.`)} ${scoreColor(`${rec.score}%`)} ${colors.dim(scoreBar)} ${colors.bold(rec.skill.name)}${qualityDisplay}`);
139582
+ if (rec.skill.description) {
139583
+ console.log(` ${colors.muted(truncate6(rec.skill.description, 70))}`);
139584
+ }
139585
+ options.push({
139586
+ value: `${i}`,
139587
+ label: `${rec.skill.name} (${rec.score}% match)`,
139588
+ hint: rec.skill.source || void 0
139589
+ });
139590
+ }
139591
+ console.log("");
139592
+ const skipOption = { value: "skip", label: "Skip installation" };
139593
+ const selectResult = await select2({
139594
+ message: "Select a skill to install",
139595
+ options: [...options, skipOption]
139596
+ });
139597
+ if (isCancel2(selectResult) || selectResult === "skip") {
139598
+ cancel2("Quick setup complete (no installation)");
139599
+ return 0;
139600
+ }
139601
+ const selectedIndex = parseInt(selectResult, 10);
139602
+ const selectedSkill = recommendations[selectedIndex];
139603
+ if (!selectedSkill || !selectedSkill.skill.source) {
139604
+ error("Selected skill has no installable source");
139605
+ return 1;
139606
+ }
139607
+ const agentDisplay = detectedAgents.length <= 3 ? detectedAgents.map(formatAgent).join(", ") : `${detectedAgents.slice(0, 2).map(formatAgent).join(", ")} +${detectedAgents.length - 2} more`;
139608
+ const confirmResult = await confirm2({
139609
+ message: `Install ${colors.bold(selectedSkill.skill.name)} to ${agentDisplay}?`,
139610
+ initialValue: true
139611
+ });
139612
+ if (isCancel2(confirmResult) || !confirmResult) {
139613
+ cancel2("Installation cancelled");
139614
+ return 0;
139615
+ }
139616
+ saveLastAgents(detectedAgents);
139617
+ const providerAdapter = detectProvider4(selectedSkill.skill.source);
139618
+ if (!providerAdapter) {
139619
+ error(`Could not detect provider for: ${selectedSkill.skill.source}`);
139620
+ return 1;
139621
+ }
139622
+ s.start(`Fetching ${selectedSkill.skill.name}...`);
139623
+ const cloneResult = await providerAdapter.clone(selectedSkill.skill.source, "", { depth: 1 });
139624
+ if (!cloneResult.success || !cloneResult.path) {
139625
+ s.stop(colors.error(cloneResult.error || "Failed to fetch source"));
139626
+ return 1;
139627
+ }
139628
+ const discoveredSkills = cloneResult.discoveredSkills || [];
139629
+ const matchedSkill = discoveredSkills.find((ds) => ds.name === selectedSkill.skill.name) || discoveredSkills[0];
139630
+ if (!matchedSkill) {
139631
+ s.stop(colors.error("Skill not found in repository"));
139632
+ return 1;
139633
+ }
139634
+ s.stop(`Found ${selectedSkill.skill.name}`);
139635
+ let primaryPath = null;
139636
+ const installedAgents = [];
139637
+ for (const agentType of detectedAgents) {
139638
+ const adapter = getAdapter6(agentType);
139639
+ const installDir = getInstallDir(false, agentType);
139640
+ if (!existsSync21(installDir)) {
139641
+ mkdirSync10(installDir, { recursive: true });
139642
+ }
139643
+ const sanitizedName = matchedSkill.name.split(/[/\\]/).pop() || matchedSkill.name;
139644
+ const targetInstallPath = normalize(join20(installDir, sanitizedName));
139645
+ if (!isPathInside2(targetInstallPath, installDir)) {
139646
+ error(`Skipping ${matchedSkill.name} for ${adapter.name} (install path escapes target directory)`);
139647
+ continue;
139648
+ }
139649
+ const securityRoot = cloneResult.tempRoot || cloneResult.path;
139650
+ if (!isPathInside2(matchedSkill.path, securityRoot)) {
139651
+ error(`Skipping ${matchedSkill.name} for ${adapter.name} (path traversal detected)`);
139652
+ continue;
139653
+ }
139654
+ const useSymlink = detectedAgents.length > 1 && primaryPath !== null;
139655
+ s.start(`Installing to ${adapter.name}${useSymlink ? " (symlink)" : ""}...`);
139656
+ try {
139657
+ if (existsSync21(targetInstallPath)) {
139658
+ rmSync5(targetInstallPath, { recursive: true, force: true });
139659
+ }
139660
+ if (useSymlink && primaryPath) {
139661
+ symlinkSync2(primaryPath, targetInstallPath, "dir");
139662
+ } else {
139663
+ cpSync4(matchedSkill.path, targetInstallPath, { recursive: true, dereference: true });
139664
+ if (detectedAgents.length > 1 && primaryPath === null) {
139665
+ primaryPath = targetInstallPath;
139666
+ }
139667
+ const packageJsonPath = join20(targetInstallPath, "package.json");
139668
+ if (existsSync21(packageJsonPath)) {
139669
+ s.stop(`Installed to ${adapter.name}`);
139670
+ s.start("Installing npm dependencies...");
139671
+ try {
139672
+ await execFileAsync2("npm", ["install", "--production"], { cwd: targetInstallPath });
139673
+ s.stop("Dependencies installed");
139674
+ } catch {
139675
+ s.stop(colors.warning("Dependencies failed"));
139676
+ console.log(colors.muted("Run manually: npm install in " + targetInstallPath));
139677
+ }
139678
+ s.start("Finishing installation...");
139679
+ }
139680
+ }
139681
+ const metadata = {
139682
+ name: matchedSkill.name,
139683
+ description: selectedSkill.skill.description || "",
139684
+ source: selectedSkill.skill.source,
139685
+ sourceType: providerAdapter.type,
139686
+ subpath: matchedSkill.name,
139687
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
139688
+ enabled: true
139689
+ };
139690
+ saveSkillMetadata(targetInstallPath, metadata);
139691
+ installedAgents.push(agentType);
139692
+ s.stop(`Installed to ${adapter.name}${useSymlink ? " (symlink)" : ""}`);
139693
+ } catch (err) {
139694
+ s.stop(colors.error(`Failed to install to ${adapter.name}`));
139695
+ console.log(colors.muted(err instanceof Error ? err.message : String(err)));
139696
+ }
139697
+ }
139698
+ const cleanupPath = cloneResult.tempRoot || cloneResult.path;
139699
+ if (!isLocalPath4(selectedSkill.skill.source) && cleanupPath && existsSync21(cleanupPath)) {
139700
+ rmSync5(cleanupPath, { recursive: true, force: true });
139701
+ }
139702
+ if (installedAgents.length > 0) {
139703
+ console.log("");
139704
+ success(`Installed ${colors.bold(matchedSkill.name)} to ${installedAgents.length} agent${installedAgents.length !== 1 ? "s" : ""}`);
139705
+ for (const a of installedAgents) {
139706
+ console.log(colors.muted(` ${symbols.success} ${getAgentIcon(a)} ${formatAgent(a)}`));
139707
+ }
139708
+ console.log("");
139709
+ const badgeUrl = `https://img.shields.io/badge/SkillKit-${encodeURIComponent(matchedSkill.name)}-black?style=flat-square`;
139710
+ console.log(colors.dim("Add to your README:"));
139711
+ console.log(colors.muted(` [![SkillKit](${badgeUrl})](https://agenstskills.com)`));
139712
+ outro2("Quick setup complete!");
139713
+ console.log("");
139714
+ console.log(colors.muted("Next steps:"));
139715
+ console.log(colors.muted(` ${symbols.arrowRight} Run ${colors.cyan("skillkit sync")} to update agent configs`));
139716
+ console.log(colors.muted(` ${symbols.arrowRight} Run ${colors.cyan("skillkit recommend")} for more suggestions`));
139717
+ console.log(colors.muted(` ${symbols.arrowRight} Run ${colors.cyan("skillkit list")} to see installed skills`));
139718
+ console.log("");
139719
+ } else {
139720
+ warn("No agents were installed");
139721
+ }
139722
+ return 0;
139723
+ } catch (err) {
139724
+ s.stop(colors.error("Quick setup failed"));
139725
+ console.log(colors.muted(err instanceof Error ? err.message : String(err)));
139726
+ return 1;
139727
+ }
139728
+ }
139729
+ };
139730
+
139731
+ // src/commands/skillmd.ts
139732
+ init_onboarding();
139733
+ import { existsSync as existsSync22, readFileSync as readFileSync11, writeFileSync as writeFileSync11, readdirSync as readdirSync4 } from "fs";
139734
+ import { join as join21, resolve as resolve22, basename as basename6, relative } from "path";
139735
+ import { Command as Command51, Option as Option49 } from "clipanion";
139736
+ function validateSkillMd(filePath) {
139737
+ const result = {
139738
+ path: filePath,
139739
+ exists: false,
139740
+ hasName: false,
139741
+ hasDescription: false,
139742
+ hasTriggers: false,
139743
+ hasCapabilities: false,
139744
+ triggerCount: 0,
139745
+ capabilityCount: 0,
139746
+ score: 0,
139747
+ issues: []
139748
+ };
139749
+ if (!existsSync22(filePath)) {
139750
+ result.issues.push("File does not exist");
139751
+ return result;
139752
+ }
139753
+ result.exists = true;
139754
+ const content = readFileSync11(filePath, "utf-8");
139755
+ if (!content.trim()) {
139756
+ result.issues.push("File is empty");
139757
+ return result;
139758
+ }
139759
+ const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
139760
+ const bodyContent = frontmatterMatch ? content.slice(frontmatterMatch[0].length) : content;
139761
+ if (frontmatterMatch) {
139762
+ const frontmatter = frontmatterMatch[1];
139763
+ if (/^name:\s*.+$/m.test(frontmatter)) result.hasName = true;
139764
+ if (/^description:\s*.+$/m.test(frontmatter)) result.hasDescription = true;
139765
+ }
139766
+ if (!result.hasName) {
139767
+ if (/^#\s+.+$/m.test(bodyContent)) result.hasName = true;
139768
+ }
139769
+ if (!result.hasDescription) {
139770
+ if (/^##?\s*(description|about|overview)\b/im.test(bodyContent)) {
139771
+ result.hasDescription = true;
139772
+ } else {
139773
+ const lines = bodyContent.split(/\r?\n/).filter((l) => l.trim());
139774
+ const nonHeaderLines = lines.filter((l) => !l.startsWith("#"));
139775
+ if (nonHeaderLines.length >= 2) result.hasDescription = true;
139776
+ }
139777
+ }
139778
+ const triggerPatterns = [
139779
+ /triggers?\s*when/i,
139780
+ /when\s*to\s*use/i,
139781
+ /use\s*this\s*when/i,
139782
+ /\*\*triggers?\b/i,
139783
+ /^[-*]\s+.+(when|if|pattern|trigger)/im,
139784
+ /glob:/i
139785
+ ];
139786
+ for (const pattern of triggerPatterns) {
139787
+ if (pattern.test(content)) {
139788
+ result.hasTriggers = true;
139789
+ break;
139790
+ }
139791
+ }
139792
+ if (result.hasTriggers) {
139793
+ const triggerSection = content.match(/triggers?\s*when[:\s]*\n((?:[-*]\s+.+\n?)+)/i);
139794
+ result.triggerCount = triggerSection ? (triggerSection[1].match(/^[-*]\s+/gm) || []).length : 1;
139795
+ }
139796
+ const capabilityPatterns = [
139797
+ /capabilities?[:\s]/i,
139798
+ /features?[:\s]/i,
139799
+ /what\s*(it|this)\s*(does|can)/i,
139800
+ /^##?\s*capabilities/im,
139801
+ /^##?\s*features/im
139802
+ ];
139803
+ for (const pattern of capabilityPatterns) {
139804
+ if (pattern.test(content)) {
139805
+ result.hasCapabilities = true;
139806
+ break;
139807
+ }
139808
+ }
139809
+ if (result.hasCapabilities) {
139810
+ const capSection = content.match(/(?:capabilities?|features?)[:\s]*\n((?:[-*]\s+.+\n?)+)/i);
139811
+ result.capabilityCount = capSection ? (capSection[1].match(/^[-*]\s+/gm) || []).length : 1;
139812
+ }
139813
+ if (!result.hasName) result.issues.push("Missing name field");
139814
+ if (!result.hasDescription) result.issues.push("Missing description section");
139815
+ if (!result.hasTriggers) result.issues.push("No trigger patterns found");
139816
+ if (!result.hasCapabilities) result.issues.push("No capabilities list found");
139817
+ let score = 0;
139818
+ if (result.exists) score += 10;
139819
+ if (result.hasName) score += 20;
139820
+ if (result.hasDescription) score += 25;
139821
+ if (result.hasTriggers) score += 20;
139822
+ if (result.hasCapabilities) score += 15;
139823
+ if (result.triggerCount >= 3) score += 5;
139824
+ if (result.capabilityCount >= 3) score += 5;
139825
+ if (frontmatterMatch) {
139826
+ const fm = frontmatterMatch[1];
139827
+ if (/^version:/m.test(fm)) score += 3;
139828
+ if (/^license:/m.test(fm)) score += 2;
139829
+ if (/^tags:/m.test(fm)) score += 3;
139830
+ if (/^metadata:/m.test(fm)) score += 2;
139831
+ }
139832
+ result.score = Math.min(100, score);
139833
+ return result;
139834
+ }
139835
+ function printValidation(validation, verbose = false) {
139836
+ const scoreColor = validation.score >= 80 ? colors.success : validation.score >= 60 ? colors.warning : colors.error;
139837
+ console.log(` ${colors.muted("Score:")} ${scoreColor(`${validation.score}`)}/100`);
139838
+ console.log(` ${validation.hasName ? colors.success(symbols.success) : colors.error(symbols.error)} Name field`);
139839
+ console.log(` ${validation.hasDescription ? colors.success(symbols.success) : colors.error(symbols.error)} Description section`);
139840
+ console.log(` ${validation.hasTriggers ? colors.success(symbols.success) : colors.error(symbols.error)} Trigger patterns${validation.triggerCount > 0 ? ` (${validation.triggerCount})` : ""}`);
139841
+ console.log(` ${validation.hasCapabilities ? colors.success(symbols.success) : colors.error(symbols.error)} Capabilities list${validation.capabilityCount > 0 ? ` (${validation.capabilityCount})` : ""}`);
139842
+ if (verbose && validation.issues.length > 0) {
139843
+ console.log("");
139844
+ console.log(` ${colors.warning("Issues:")}`);
139845
+ for (const issue of validation.issues) {
139846
+ console.log(` ${colors.warning(symbols.warning)} ${issue}`);
139847
+ }
139848
+ }
139849
+ }
139850
+ var SkillMdValidateCommand = class extends Command51 {
139851
+ static paths = [["skillmd", "validate"], ["skill-md", "validate"]];
139852
+ static usage = Command51.Usage({
139853
+ description: "Validate a SKILL.md file against the standard",
139854
+ examples: [
139855
+ ["Validate SKILL.md in current directory", "$0 skillmd validate"],
139856
+ ["Validate specific file", "$0 skillmd validate ./path/to/SKILL.md"]
139857
+ ]
139858
+ });
139859
+ targetPath = Option49.String({ required: false, name: "path" });
139860
+ verbose = Option49.Boolean("--verbose,-v", false, {
139861
+ description: "Show detailed validation results"
139862
+ });
139863
+ json = Option49.Boolean("--json", false, {
139864
+ description: "Output as JSON"
139865
+ });
139866
+ async execute() {
139867
+ const targetPath = this.targetPath || join21(process.cwd(), "SKILL.md");
139868
+ const resolvedPath = resolve22(targetPath);
139869
+ const filePath = resolvedPath.endsWith("SKILL.md") ? resolvedPath : join21(resolvedPath, "SKILL.md");
139870
+ if (!this.json) {
139871
+ header("SKILL.md Validation");
139872
+ step(`Validating: ${colors.cyan(filePath)}`);
139873
+ console.log("");
139874
+ }
139875
+ const validation = validateSkillMd(filePath);
139876
+ if (this.json) {
139877
+ console.log(JSON.stringify(validation, null, 2));
139878
+ return validation.score >= 60 ? 0 : 1;
139879
+ }
139880
+ console.log(colors.primary(basename6(filePath)));
139881
+ printValidation(validation, this.verbose);
139882
+ console.log("");
139883
+ if (validation.score >= 80) {
139884
+ success(`SKILL.md is compliant (score: ${validation.score}/100)`);
139885
+ } else if (validation.score >= 60) {
139886
+ warn(`SKILL.md needs improvement (score: ${validation.score}/100)`);
139887
+ } else {
139888
+ error(`SKILL.md does not meet minimum standard (score: ${validation.score}/100)`);
139889
+ }
139890
+ return validation.score >= 60 ? 0 : 1;
139891
+ }
139892
+ };
139893
+ var SkillMdInitCommand = class extends Command51 {
139894
+ static paths = [["skillmd", "init"], ["skill-md", "init"]];
139895
+ static usage = Command51.Usage({
139896
+ description: "Create a template SKILL.md in the current directory",
139897
+ examples: [
139898
+ ["Create SKILL.md template", "$0 skillmd init"],
139899
+ ["Create with custom name", "$0 skillmd init --name my-skill"]
139900
+ ]
139901
+ });
139902
+ name = Option49.String("--name,-n", {
139903
+ description: "Skill name"
139904
+ });
139905
+ force = Option49.Boolean("--force,-f", false, {
139906
+ description: "Overwrite existing SKILL.md"
139907
+ });
139908
+ async execute() {
139909
+ const outputPath = join21(process.cwd(), "SKILL.md");
139910
+ if (existsSync22(outputPath) && !this.force) {
139911
+ warn("SKILL.md already exists");
139912
+ console.log(colors.muted("Use --force to overwrite"));
139913
+ return 1;
139914
+ }
139915
+ const skillName = this.name || basename6(process.cwd());
139916
+ const formattedName = skillName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
139917
+ const template = [
139918
+ "---",
139919
+ `name: ${skillName}`,
139920
+ "description: What this skill does and when to use it",
139921
+ 'version: "1.0"',
139922
+ "license: MIT",
139923
+ "tags: [general]",
139924
+ "metadata:",
139925
+ " author: your-name",
139926
+ "---",
139927
+ "",
139928
+ `# ${formattedName}`,
139929
+ "",
139930
+ "Brief description of what this skill provides to AI coding agents.",
139931
+ "",
139932
+ "## Triggers when:",
139933
+ `- User asks about ${skillName} related tasks`,
139934
+ `- Project contains ${skillName} configuration files`,
139935
+ `- Code changes involve ${skillName} patterns`,
139936
+ "",
139937
+ "## Capabilities:",
139938
+ `- Provides best practices for ${skillName}`,
139939
+ "- Generates boilerplate code and configurations",
139940
+ "- Validates existing implementations",
139941
+ "- Suggests improvements and optimizations",
139942
+ "",
139943
+ "## Steps",
139944
+ "1. First step the AI agent should take",
139945
+ "2. Second step with specific instructions",
139946
+ "3. Third step with expected outcome",
139947
+ "",
139948
+ "## Examples",
139949
+ "",
139950
+ "```",
139951
+ "Example code or configuration here",
139952
+ "```",
139953
+ ""
139954
+ ].join("\n");
139955
+ writeFileSync11(outputPath, template);
139956
+ success(`Created SKILL.md for "${skillName}"`);
139957
+ console.log("");
139958
+ console.log(colors.muted("Next steps:"));
139959
+ console.log(` ${colors.cyan("1.")} Edit SKILL.md with your skill instructions`);
139960
+ console.log(` ${colors.cyan("2.")} Run ${colors.cyan("skillkit skillmd validate")} to check compliance`);
139961
+ console.log(` ${colors.cyan("3.")} Run ${colors.cyan("skillkit publish submit")} to share with the marketplace`);
139962
+ return 0;
139963
+ }
139964
+ };
139965
+ var SkillMdCheckCommand = class extends Command51 {
139966
+ static paths = [["skillmd", "check"], ["skill-md", "check"]];
139967
+ static usage = Command51.Usage({
139968
+ description: "Check all SKILL.md files in the project for compliance",
139969
+ examples: [
139970
+ ["Check current directory", "$0 skillmd check"],
139971
+ ["Check specific directory", "$0 skillmd check ./skills"]
139972
+ ]
139973
+ });
139974
+ targetPath = Option49.String({ required: false, name: "path" });
139975
+ verbose = Option49.Boolean("--verbose,-v", false, {
139976
+ description: "Show detailed validation results"
139977
+ });
139978
+ async execute() {
139979
+ const targetPath = resolve22(this.targetPath || process.cwd());
139980
+ header("SKILL.md Compliance Check");
139981
+ const s = spinner2();
139982
+ s.start("Scanning for SKILL.md files...");
139983
+ const skillMdFiles = this.findSkillMdFiles(targetPath);
139984
+ s.stop(`Found ${skillMdFiles.length} SKILL.md file(s)`);
139985
+ if (skillMdFiles.length === 0) {
139986
+ warn("No SKILL.md files found");
139987
+ console.log(colors.muted(`Searched: ${targetPath}`));
139988
+ console.log("");
139989
+ console.log(colors.muted("Create one with:"));
139990
+ console.log(` ${colors.cyan("skillkit skillmd init")}`);
139991
+ return 0;
139992
+ }
139993
+ const validations = skillMdFiles.map((f) => validateSkillMd(f));
139994
+ validations.sort((a, b) => b.score - a.score);
139995
+ console.log("");
139996
+ for (const validation of validations) {
139997
+ const relativePath = relative(targetPath, validation.path);
139998
+ const scoreColor = validation.score >= 80 ? colors.success : validation.score >= 60 ? colors.warning : colors.error;
139999
+ console.log(`${scoreColor(symbols.bullet)} ${colors.primary(relativePath)} ${colors.muted(`(${validation.score}/100)`)}`);
140000
+ if (this.verbose) {
140001
+ printValidation(validation, true);
140002
+ console.log("");
140003
+ }
140004
+ }
140005
+ console.log("");
140006
+ const passing = validations.filter((v) => v.score >= 60).length;
140007
+ const failing = validations.filter((v) => v.score < 60).length;
140008
+ const avgScore = Math.round(validations.reduce((sum, v) => sum + v.score, 0) / validations.length);
140009
+ console.log(colors.muted("Summary:"));
140010
+ console.log(` ${colors.muted("Total:")} ${validations.length}`);
140011
+ console.log(` ${colors.success(symbols.success)} Passing (60+): ${passing}`);
140012
+ if (failing > 0) {
140013
+ console.log(` ${colors.error(symbols.error)} Failing: ${failing}`);
140014
+ }
140015
+ console.log(` ${colors.muted("Average score:")} ${avgScore}/100`);
140016
+ console.log("");
140017
+ if (failing > 0) {
140018
+ error(`${failing} SKILL.md file(s) below minimum standard`);
140019
+ return 1;
140020
+ }
140021
+ success("All SKILL.md files are compliant");
140022
+ return 0;
140023
+ }
140024
+ findSkillMdFiles(dir) {
140025
+ const results = [];
140026
+ const directFile = join21(dir, "SKILL.md");
140027
+ if (existsSync22(directFile)) results.push(directFile);
140028
+ const searchDirs = [
140029
+ join21(dir, "skills"),
140030
+ join21(dir, ".claude", "skills"),
140031
+ join21(dir, ".cursor", "skills"),
140032
+ join21(dir, ".codex", "skills"),
140033
+ join21(dir, ".github", "skills")
140034
+ ];
140035
+ for (const searchDir of searchDirs) {
140036
+ if (!existsSync22(searchDir)) continue;
140037
+ this.scanDir(searchDir, results, 0);
140038
+ }
140039
+ try {
140040
+ const entries = readdirSync4(dir, { withFileTypes: true });
140041
+ for (const entry of entries) {
140042
+ if (!entry.isDirectory()) continue;
140043
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
140044
+ const entrySkillMd = join21(dir, entry.name, "SKILL.md");
140045
+ if (existsSync22(entrySkillMd) && !results.includes(entrySkillMd)) {
140046
+ results.push(entrySkillMd);
140047
+ }
140048
+ }
140049
+ } catch {
140050
+ }
140051
+ return results;
140052
+ }
140053
+ scanDir(dir, results, depth) {
140054
+ if (depth > 3) return;
140055
+ try {
140056
+ const entries = readdirSync4(dir, { withFileTypes: true });
140057
+ for (const entry of entries) {
140058
+ const entryPath = join21(dir, entry.name);
140059
+ if (entry.isFile() && entry.name === "SKILL.md") {
140060
+ if (!results.includes(entryPath)) results.push(entryPath);
140061
+ }
140062
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
140063
+ this.scanDir(entryPath, results, depth + 1);
140064
+ }
140065
+ }
140066
+ } catch {
140067
+ }
140068
+ }
140069
+ };
140070
+
139412
140071
  // src/index.ts
139413
140072
  init_helpers();
139414
140073
  export {
@@ -139478,6 +140137,7 @@ export {
139478
140137
  ProfileRemoveCommand,
139479
140138
  PublishCommand,
139480
140139
  PublishSubmitCommand,
140140
+ QuickCommand,
139481
140141
  ReadCommand,
139482
140142
  RecommendCommand,
139483
140143
  RemoveCommand,
@@ -139492,6 +140152,9 @@ export {
139492
140152
  SessionStartCommand,
139493
140153
  SessionStatusCommand,
139494
140154
  SettingsCommand,
140155
+ SkillMdCheckCommand,
140156
+ SkillMdInitCommand,
140157
+ SkillMdValidateCommand,
139495
140158
  StatusCommand,
139496
140159
  SyncCommand,
139497
140160
  TeamCommand,