skild 0.6.1 → 0.7.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/README.md +3 -0
- package/dist/index.js +457 -46
- package/package.json +2 -2
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk17 from "chalk";
|
|
6
6
|
import { createRequire } from "module";
|
|
7
7
|
|
|
8
8
|
// src/commands/install.ts
|
|
@@ -71,10 +71,10 @@ var logger = {
|
|
|
71
71
|
/**
|
|
72
72
|
* Log a skill entry with status indicator.
|
|
73
73
|
*/
|
|
74
|
-
skillEntry: (name,
|
|
74
|
+
skillEntry: (name, path6, hasSkillMd) => {
|
|
75
75
|
const status = hasSkillMd ? chalk.green("\u2713") : chalk.yellow("\u26A0");
|
|
76
76
|
console.log(` ${status} ${chalk.cyan(name)}`);
|
|
77
|
-
console.log(chalk.dim(` \u2514\u2500 ${
|
|
77
|
+
console.log(chalk.dim(` \u2514\u2500 ${path6}`));
|
|
78
78
|
},
|
|
79
79
|
/**
|
|
80
80
|
* Log installation result details.
|
|
@@ -337,6 +337,19 @@ function formatTreeNode(node, selection, isCursor, options = {}) {
|
|
|
337
337
|
}
|
|
338
338
|
return `${cursorMark}${indent}${checkbox} ${name}${count}${suffix}${hint}`;
|
|
339
339
|
}
|
|
340
|
+
function truncateDescription(value, maxLen) {
|
|
341
|
+
const trimmed = value.trim();
|
|
342
|
+
if (trimmed.length <= maxLen) return trimmed;
|
|
343
|
+
return `${trimmed.slice(0, maxLen - 3).trimEnd()}...`;
|
|
344
|
+
}
|
|
345
|
+
function getSkillDescriptionSuffix(skills, node, isCursor) {
|
|
346
|
+
if (!isCursor || !node.isLeaf || node.leafIndices.length !== 1) return "";
|
|
347
|
+
const skill = skills[node.leafIndices[0]];
|
|
348
|
+
if (!skill?.description) return "";
|
|
349
|
+
const description = truncateDescription(skill.description, 72);
|
|
350
|
+
if (!description) return "";
|
|
351
|
+
return chalk2.dim(` - ${description}`);
|
|
352
|
+
}
|
|
340
353
|
async function promptSkillsInteractive(skills, options = {}) {
|
|
341
354
|
if (skills.length === 0) return null;
|
|
342
355
|
const targetPlatforms = options.targetPlatforms || [];
|
|
@@ -355,19 +368,20 @@ async function promptSkillsInteractive(skills, options = {}) {
|
|
|
355
368
|
subtitle: "\u2191\u2193 navigate \u2022 Space toggle \u2022 Enter confirm",
|
|
356
369
|
buildTree: buildSkillTree,
|
|
357
370
|
formatNode: (node, selection, isCursor) => {
|
|
358
|
-
let
|
|
371
|
+
let installedSuffix = "";
|
|
359
372
|
if (node.isLeaf && node.leafIndices.length === 1) {
|
|
360
373
|
const skill = skills[node.leafIndices[0]];
|
|
361
374
|
if (skill?.installedPlatforms?.length) {
|
|
362
375
|
if (skill.installedPlatforms.length === targetPlatforms.length && targetPlatforms.length > 0) {
|
|
363
|
-
|
|
376
|
+
installedSuffix = chalk2.dim(" [installed]");
|
|
364
377
|
} else if (skill.installedPlatforms.length > 0) {
|
|
365
|
-
|
|
378
|
+
installedSuffix = chalk2.dim(` [installed on ${skill.installedPlatforms.length}]`);
|
|
366
379
|
}
|
|
367
380
|
}
|
|
368
381
|
}
|
|
369
|
-
const
|
|
370
|
-
|
|
382
|
+
const descriptionSuffix = getSkillDescriptionSuffix(skills, node, isCursor);
|
|
383
|
+
const formatted = formatTreeNode(node, selection, isCursor, { suffix: `${installedSuffix}${descriptionSuffix}` });
|
|
384
|
+
if (isCursor && installedSuffix && selection.state !== "all") {
|
|
371
385
|
return formatted.replace("\u2190 Space to select", "\u2190 Space to reinstall");
|
|
372
386
|
}
|
|
373
387
|
return formatted;
|
|
@@ -388,7 +402,10 @@ async function promptSkillsTreeInteractive(skills, tree, options = {}) {
|
|
|
388
402
|
title: "Select skills from markdown",
|
|
389
403
|
subtitle: "\u2191\u2193 navigate \u2022 Space toggle \u2022 Enter confirm",
|
|
390
404
|
buildTree: () => buildTreeFromSkillNodes(tree, skills.length),
|
|
391
|
-
formatNode: (node, selection, isCursor) =>
|
|
405
|
+
formatNode: (node, selection, isCursor) => {
|
|
406
|
+
const descriptionSuffix = getSkillDescriptionSuffix(skills, node, isCursor);
|
|
407
|
+
return formatTreeNode(node, selection, isCursor, { suffix: descriptionSuffix });
|
|
408
|
+
},
|
|
392
409
|
defaultAll: options.defaultAll !== false
|
|
393
410
|
});
|
|
394
411
|
if (!selectedIndices) return null;
|
|
@@ -425,6 +442,7 @@ async function promptPlatformsInteractive(options = {}) {
|
|
|
425
442
|
// src/commands/install-discovery.ts
|
|
426
443
|
import fs from "fs";
|
|
427
444
|
import path from "path";
|
|
445
|
+
import { parseSkillFrontmatter, readSkillMd } from "@skild/core";
|
|
428
446
|
function parsePositiveInt(input, fallback) {
|
|
429
447
|
if (input == null) return fallback;
|
|
430
448
|
const n = typeof input === "number" ? input : Number(String(input).trim());
|
|
@@ -437,6 +455,19 @@ function parseNonNegativeInt(input, fallback) {
|
|
|
437
455
|
if (!Number.isFinite(n) || n < 0) return fallback;
|
|
438
456
|
return Math.floor(n);
|
|
439
457
|
}
|
|
458
|
+
function extractSkillMetadata(skillMdContent) {
|
|
459
|
+
const frontmatter = parseSkillFrontmatter(skillMdContent);
|
|
460
|
+
if (!frontmatter) return null;
|
|
461
|
+
const name = typeof frontmatter.name === "string" ? frontmatter.name.trim() : void 0;
|
|
462
|
+
const description = typeof frontmatter.description === "string" ? frontmatter.description.trim() : void 0;
|
|
463
|
+
if (!name && !description) return null;
|
|
464
|
+
return { name, description };
|
|
465
|
+
}
|
|
466
|
+
function readSkillMetadata(skillDir) {
|
|
467
|
+
const content = readSkillMd(skillDir);
|
|
468
|
+
if (!content) return null;
|
|
469
|
+
return extractSkillMetadata(content);
|
|
470
|
+
}
|
|
440
471
|
function normalizeRelPath(relPath) {
|
|
441
472
|
return relPath.split(path.sep).join("/").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
442
473
|
}
|
|
@@ -510,7 +541,7 @@ function discoverSkillDirsWithHeuristics(rootDir, options) {
|
|
|
510
541
|
return discoverSkillDirs(root, options);
|
|
511
542
|
}
|
|
512
543
|
|
|
513
|
-
// src/commands/
|
|
544
|
+
// src/commands/markdown-discovery.ts
|
|
514
545
|
import fs2 from "fs";
|
|
515
546
|
import path2 from "path";
|
|
516
547
|
import { unified } from "unified";
|
|
@@ -520,7 +551,8 @@ import { deriveChildSource, fetchWithTimeout, materializeSourceToTemp } from "@s
|
|
|
520
551
|
var README_CANDIDATES = ["README.md", "readme.md", "Readme.md", "README.markdown", "readme.markdown"];
|
|
521
552
|
async function discoverMarkdownSkillsFromSource(input) {
|
|
522
553
|
const ctx = {
|
|
523
|
-
|
|
554
|
+
maxDocDepth: input.maxDocDepth,
|
|
555
|
+
maxSkillDepth: input.maxSkillDepth,
|
|
524
556
|
maxSkills: input.maxSkills,
|
|
525
557
|
maxLinks: Math.min(400, Math.max(200, input.maxSkills * 2)),
|
|
526
558
|
linkLimitReached: false,
|
|
@@ -564,7 +596,7 @@ function cleanupRepoCache(ctx) {
|
|
|
564
596
|
ctx.repoCache.clear();
|
|
565
597
|
}
|
|
566
598
|
async function parseMarkdownDoc(doc, ctx, depth, visitedDocs) {
|
|
567
|
-
if (depth > ctx.
|
|
599
|
+
if (depth > ctx.maxDocDepth) return [];
|
|
568
600
|
const docKey = `${doc.repo.owner}/${doc.repo.repo}#${doc.repo.ref || ""}:${doc.docPath}`;
|
|
569
601
|
if (visitedDocs.has(docKey)) return [];
|
|
570
602
|
visitedDocs.add(docKey);
|
|
@@ -652,7 +684,7 @@ async function parseInlineLinks(node, parent, doc, ctx, depth, visitedDocs) {
|
|
|
652
684
|
if (!resolved) continue;
|
|
653
685
|
const label = normalizeLabel(link.label) || resolved.displayName;
|
|
654
686
|
const maybeMarkdown = !isLikelyFilePath(resolved.pathHint) || looksLikeMarkdownPath(resolved.pathHint);
|
|
655
|
-
const canRecurseMarkdown = depth < ctx.
|
|
687
|
+
const canRecurseMarkdown = depth < ctx.maxDocDepth;
|
|
656
688
|
if (maybeMarkdown && canRecurseMarkdown) {
|
|
657
689
|
const childDoc = await resolveMarkdownDoc(resolved.source, ctx);
|
|
658
690
|
if (childDoc) {
|
|
@@ -673,10 +705,7 @@ async function parseInlineLinks(node, parent, doc, ctx, depth, visitedDocs) {
|
|
|
673
705
|
parent.children.push(createSkillLeaf(ctx, skillIndices[0], label));
|
|
674
706
|
} else {
|
|
675
707
|
const groupLabel = label || resolved.displayName || "Skills";
|
|
676
|
-
const groupNode =
|
|
677
|
-
for (const skillIndex of skillIndices) {
|
|
678
|
-
groupNode.children.push(createSkillLeaf(ctx, skillIndex));
|
|
679
|
-
}
|
|
708
|
+
const groupNode = buildSkillPathTree(ctx, skillIndices, groupLabel);
|
|
680
709
|
parent.children.push(groupNode);
|
|
681
710
|
}
|
|
682
711
|
}
|
|
@@ -697,6 +726,50 @@ function createSkillLeaf(ctx, skillIndex, labelOverride) {
|
|
|
697
726
|
node.skillIndex = skillIndex;
|
|
698
727
|
return node;
|
|
699
728
|
}
|
|
729
|
+
function buildSkillPathTree(ctx, skillIndices, label) {
|
|
730
|
+
const root = createNode(ctx, label || "Skills", "list", []);
|
|
731
|
+
const childMap = /* @__PURE__ */ new WeakMap();
|
|
732
|
+
const ensureChild = (parent, segment) => {
|
|
733
|
+
let map = childMap.get(parent);
|
|
734
|
+
if (!map) {
|
|
735
|
+
map = /* @__PURE__ */ new Map();
|
|
736
|
+
childMap.set(parent, map);
|
|
737
|
+
}
|
|
738
|
+
const existing = map.get(segment);
|
|
739
|
+
if (existing) return existing;
|
|
740
|
+
const node = createNode(ctx, segment, "list", []);
|
|
741
|
+
parent.children.push(node);
|
|
742
|
+
map.set(segment, node);
|
|
743
|
+
return node;
|
|
744
|
+
};
|
|
745
|
+
for (const skillIndex of skillIndices) {
|
|
746
|
+
const skill = ctx.skills[skillIndex];
|
|
747
|
+
if (!skill) continue;
|
|
748
|
+
let relPath = skill.relPath || "";
|
|
749
|
+
if (relPath === "." || relPath === "./") {
|
|
750
|
+
root.children.push(createSkillLeaf(ctx, skillIndex, skill.displayName));
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
if (relPath.startsWith("./")) relPath = relPath.slice(2);
|
|
754
|
+
relPath = relPath.replace(/^\/+/, "");
|
|
755
|
+
if (!relPath) {
|
|
756
|
+
root.children.push(createSkillLeaf(ctx, skillIndex, skill.displayName));
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
const segments = relPath.split("/").filter(Boolean);
|
|
760
|
+
if (segments.length === 0) {
|
|
761
|
+
root.children.push(createSkillLeaf(ctx, skillIndex, skill.displayName));
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
let parent = root;
|
|
765
|
+
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
766
|
+
parent = ensureChild(parent, segments[i]);
|
|
767
|
+
}
|
|
768
|
+
const leafLabel = skill.displayName || segments[segments.length - 1];
|
|
769
|
+
parent.children.push(createSkillLeaf(ctx, skillIndex, leafLabel));
|
|
770
|
+
}
|
|
771
|
+
return root;
|
|
772
|
+
}
|
|
700
773
|
function collapseSingleChildNodes(nodes) {
|
|
701
774
|
const collapsed = [];
|
|
702
775
|
for (const node of nodes) {
|
|
@@ -780,17 +853,19 @@ async function resolveSkillsFromSource(source, displayName, ctx, repoHint) {
|
|
|
780
853
|
}
|
|
781
854
|
const skillMd = path2.join(materializedDir, "SKILL.md");
|
|
782
855
|
if (fs2.existsSync(skillMd)) {
|
|
856
|
+
const metadata = readSkillMetadata(materializedDir);
|
|
783
857
|
const skillIndex = registerSkill(ctx, {
|
|
784
858
|
relPath: ".",
|
|
785
859
|
suggestedSource: source,
|
|
786
860
|
materializedDir,
|
|
787
|
-
displayName: displayName || deriveDisplayName(source)
|
|
861
|
+
displayName: metadata?.name || displayName || deriveDisplayName(source),
|
|
862
|
+
description: metadata?.description
|
|
788
863
|
});
|
|
789
864
|
const indices2 = [skillIndex];
|
|
790
865
|
ctx.skillCache.set(source, indices2);
|
|
791
866
|
return indices2;
|
|
792
867
|
}
|
|
793
|
-
const discovered = discoverSkillDirsWithHeuristics(materializedDir, { maxDepth: ctx.
|
|
868
|
+
const discovered = discoverSkillDirsWithHeuristics(materializedDir, { maxDepth: ctx.maxSkillDepth, maxSkills: ctx.maxSkills });
|
|
794
869
|
if (discovered.length === 0) {
|
|
795
870
|
ctx.skillCache.set(source, []);
|
|
796
871
|
return [];
|
|
@@ -798,11 +873,13 @@ async function resolveSkillsFromSource(source, displayName, ctx, repoHint) {
|
|
|
798
873
|
const indices = [];
|
|
799
874
|
for (const skill of discovered) {
|
|
800
875
|
const childSource = deriveChildSource(source, skill.relPath);
|
|
876
|
+
const metadata = readSkillMetadata(skill.absDir);
|
|
801
877
|
const skillIndex = registerSkill(ctx, {
|
|
802
878
|
relPath: skill.relPath,
|
|
803
879
|
suggestedSource: childSource,
|
|
804
880
|
materializedDir: skill.absDir,
|
|
805
|
-
displayName: deriveDisplayName(childSource)
|
|
881
|
+
displayName: metadata?.name || deriveDisplayName(childSource),
|
|
882
|
+
description: metadata?.description
|
|
806
883
|
});
|
|
807
884
|
indices.push(skillIndex);
|
|
808
885
|
}
|
|
@@ -815,23 +892,27 @@ function resolveSkillsFromLocal(repoHint, source, displayName, ctx) {
|
|
|
815
892
|
if (!fs2.existsSync(base)) return [];
|
|
816
893
|
const skillMd = path2.join(base, "SKILL.md");
|
|
817
894
|
if (fs2.existsSync(skillMd)) {
|
|
895
|
+
const metadata = readSkillMetadata(base);
|
|
818
896
|
return [
|
|
819
897
|
registerSkill(ctx, {
|
|
820
898
|
relPath: ".",
|
|
821
899
|
suggestedSource: source,
|
|
822
|
-
displayName: displayName || deriveDisplayName(source)
|
|
900
|
+
displayName: metadata?.name || displayName || deriveDisplayName(source),
|
|
901
|
+
description: metadata?.description
|
|
823
902
|
})
|
|
824
903
|
];
|
|
825
904
|
}
|
|
826
|
-
const discovered = discoverSkillDirsWithHeuristics(base, { maxDepth: ctx.
|
|
905
|
+
const discovered = discoverSkillDirsWithHeuristics(base, { maxDepth: ctx.maxSkillDepth, maxSkills: ctx.maxSkills });
|
|
827
906
|
if (discovered.length === 0) return [];
|
|
828
907
|
const indices = [];
|
|
829
908
|
for (const skill of discovered) {
|
|
830
909
|
const childSource = deriveChildSource(source, skill.relPath);
|
|
910
|
+
const metadata = readSkillMetadata(skill.absDir);
|
|
831
911
|
const skillIndex = registerSkill(ctx, {
|
|
832
912
|
relPath: skill.relPath,
|
|
833
913
|
suggestedSource: childSource,
|
|
834
|
-
displayName: deriveDisplayName(childSource)
|
|
914
|
+
displayName: metadata?.name || deriveDisplayName(childSource),
|
|
915
|
+
description: metadata?.description
|
|
835
916
|
});
|
|
836
917
|
indices.push(skillIndex);
|
|
837
918
|
}
|
|
@@ -849,16 +930,19 @@ async function tryFetchSkillManifest(source, displayName, ctx) {
|
|
|
849
930
|
if (res.status === 404) return [];
|
|
850
931
|
return [];
|
|
851
932
|
}
|
|
933
|
+
const content = await res.text();
|
|
934
|
+
const metadata = extractSkillMetadata(content);
|
|
935
|
+
return [
|
|
936
|
+
registerSkill(ctx, {
|
|
937
|
+
relPath: ".",
|
|
938
|
+
suggestedSource: source,
|
|
939
|
+
displayName: metadata?.name || displayName || deriveDisplayName(source),
|
|
940
|
+
description: metadata?.description
|
|
941
|
+
})
|
|
942
|
+
];
|
|
852
943
|
} catch {
|
|
853
944
|
return [];
|
|
854
945
|
}
|
|
855
|
-
return [
|
|
856
|
-
registerSkill(ctx, {
|
|
857
|
-
relPath: ".",
|
|
858
|
-
suggestedSource: source,
|
|
859
|
-
displayName: displayName || deriveDisplayName(source)
|
|
860
|
-
})
|
|
861
|
-
];
|
|
862
946
|
}
|
|
863
947
|
function registerSkill(ctx, skill) {
|
|
864
948
|
const key = skill.suggestedSource;
|
|
@@ -1053,12 +1137,18 @@ function getPlatformPromptList(scope) {
|
|
|
1053
1137
|
const installed = getInstalledPlatforms(scope);
|
|
1054
1138
|
return installed.length > 0 ? installed : [...PLATFORMS2];
|
|
1055
1139
|
}
|
|
1056
|
-
function asDiscoveredSkills(discovered, toSuggestedSource, toMaterializedDir) {
|
|
1057
|
-
return discovered.map((d) =>
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1140
|
+
function asDiscoveredSkills(discovered, toSuggestedSource, toMaterializedDir, toMetadataDir) {
|
|
1141
|
+
return discovered.map((d) => {
|
|
1142
|
+
const metadataDir = toMetadataDir ? toMetadataDir(d) : void 0;
|
|
1143
|
+
const metadata = metadataDir ? readSkillMetadata(metadataDir) : null;
|
|
1144
|
+
return {
|
|
1145
|
+
relPath: d.relPath,
|
|
1146
|
+
suggestedSource: toSuggestedSource(d),
|
|
1147
|
+
materializedDir: toMaterializedDir ? toMaterializedDir(d) : void 0,
|
|
1148
|
+
displayName: metadata?.name,
|
|
1149
|
+
description: metadata?.description
|
|
1150
|
+
};
|
|
1151
|
+
});
|
|
1062
1152
|
}
|
|
1063
1153
|
function createContext(source, options) {
|
|
1064
1154
|
const scope = options.local ? "project" : "global";
|
|
@@ -1089,7 +1179,8 @@ function createContext(source, options) {
|
|
|
1089
1179
|
jsonOnly,
|
|
1090
1180
|
interactive,
|
|
1091
1181
|
yes,
|
|
1092
|
-
|
|
1182
|
+
markdownDepth: parseNonNegativeInt(options.depth, 0),
|
|
1183
|
+
scanDepth: parseNonNegativeInt(options.scanDepth, 6),
|
|
1093
1184
|
maxSkills: parsePositiveInt(options.maxSkills, 200),
|
|
1094
1185
|
resolvedSource: source.trim(),
|
|
1095
1186
|
targets,
|
|
@@ -1136,7 +1227,7 @@ async function resolveSource(ctx) {
|
|
|
1136
1227
|
return true;
|
|
1137
1228
|
}
|
|
1138
1229
|
async function discoverSkills(ctx) {
|
|
1139
|
-
const { resolvedSource,
|
|
1230
|
+
const { resolvedSource, markdownDepth, scanDepth, maxSkills, jsonOnly } = ctx;
|
|
1140
1231
|
if (ctx.spinner) {
|
|
1141
1232
|
ctx.spinner.text = `Discovery at ${chalk3.cyan(ctx.source)}...`;
|
|
1142
1233
|
}
|
|
@@ -1151,11 +1242,17 @@ async function discoverSkills(ctx) {
|
|
|
1151
1242
|
if (isLocal) {
|
|
1152
1243
|
const hasSkillMd2 = fs3.existsSync(path3.join(maybeLocalRoot, "SKILL.md"));
|
|
1153
1244
|
if (hasSkillMd2) {
|
|
1245
|
+
const metadata = readSkillMetadata(maybeLocalRoot);
|
|
1154
1246
|
ctx.isSingleSkill = true;
|
|
1155
|
-
ctx.selectedSkills = [{
|
|
1247
|
+
ctx.selectedSkills = [{
|
|
1248
|
+
relPath: maybeLocalRoot,
|
|
1249
|
+
suggestedSource: resolvedSource,
|
|
1250
|
+
displayName: metadata?.name,
|
|
1251
|
+
description: metadata?.description
|
|
1252
|
+
}];
|
|
1156
1253
|
return true;
|
|
1157
1254
|
}
|
|
1158
|
-
const discovered2 = discoverSkillDirsWithHeuristics(maybeLocalRoot, { maxDepth, maxSkills });
|
|
1255
|
+
const discovered2 = discoverSkillDirsWithHeuristics(maybeLocalRoot, { maxDepth: scanDepth, maxSkills });
|
|
1159
1256
|
if (discovered2.length === 0) {
|
|
1160
1257
|
const message = `No SKILL.md found at ${maybeLocalRoot} (or within subdirectories).`;
|
|
1161
1258
|
if (jsonOnly) {
|
|
@@ -1167,12 +1264,18 @@ async function discoverSkills(ctx) {
|
|
|
1167
1264
|
process.exitCode = 1;
|
|
1168
1265
|
return false;
|
|
1169
1266
|
}
|
|
1170
|
-
ctx.discoveredSkills = asDiscoveredSkills(
|
|
1267
|
+
ctx.discoveredSkills = asDiscoveredSkills(
|
|
1268
|
+
discovered2,
|
|
1269
|
+
(d) => path3.join(maybeLocalRoot, d.relPath),
|
|
1270
|
+
void 0,
|
|
1271
|
+
(d) => d.absDir
|
|
1272
|
+
);
|
|
1171
1273
|
return true;
|
|
1172
1274
|
}
|
|
1173
1275
|
const markdownResult = await discoverMarkdownSkillsFromSource({
|
|
1174
1276
|
source: resolvedSource,
|
|
1175
|
-
|
|
1277
|
+
maxDocDepth: markdownDepth,
|
|
1278
|
+
maxSkillDepth: scanDepth,
|
|
1176
1279
|
maxSkills,
|
|
1177
1280
|
onProgress: (update2) => {
|
|
1178
1281
|
if (ctx.spinner) {
|
|
@@ -1195,11 +1298,18 @@ async function discoverSkills(ctx) {
|
|
|
1195
1298
|
ctx.cleanupMaterialized = appendCleanup(ctx.cleanupMaterialized, materialized.cleanup);
|
|
1196
1299
|
const hasSkillMd = fs3.existsSync(path3.join(ctx.materializedDir, "SKILL.md"));
|
|
1197
1300
|
if (hasSkillMd) {
|
|
1301
|
+
const metadata = readSkillMetadata(ctx.materializedDir);
|
|
1198
1302
|
ctx.isSingleSkill = true;
|
|
1199
|
-
ctx.selectedSkills = [{
|
|
1303
|
+
ctx.selectedSkills = [{
|
|
1304
|
+
relPath: ".",
|
|
1305
|
+
suggestedSource: resolvedSource,
|
|
1306
|
+
materializedDir: ctx.materializedDir,
|
|
1307
|
+
displayName: metadata?.name,
|
|
1308
|
+
description: metadata?.description
|
|
1309
|
+
}];
|
|
1200
1310
|
return true;
|
|
1201
1311
|
}
|
|
1202
|
-
const discovered = discoverSkillDirsWithHeuristics(ctx.materializedDir, { maxDepth, maxSkills });
|
|
1312
|
+
const discovered = discoverSkillDirsWithHeuristics(ctx.materializedDir, { maxDepth: scanDepth, maxSkills });
|
|
1203
1313
|
if (discovered.length === 0) {
|
|
1204
1314
|
const message = `No SKILL.md found in source "${resolvedSource}".`;
|
|
1205
1315
|
if (jsonOnly) {
|
|
@@ -1214,6 +1324,7 @@ async function discoverSkills(ctx) {
|
|
|
1214
1324
|
ctx.discoveredSkills = asDiscoveredSkills(
|
|
1215
1325
|
discovered,
|
|
1216
1326
|
(d) => deriveChildSource2(resolvedSource, d.relPath),
|
|
1327
|
+
(d) => d.absDir,
|
|
1217
1328
|
(d) => d.absDir
|
|
1218
1329
|
);
|
|
1219
1330
|
return true;
|
|
@@ -2257,13 +2368,310 @@ async function search(query, options = {}) {
|
|
|
2257
2368
|
}
|
|
2258
2369
|
}
|
|
2259
2370
|
|
|
2371
|
+
// src/commands/extract-github-skills.ts
|
|
2372
|
+
import fs5 from "fs";
|
|
2373
|
+
import path5 from "path";
|
|
2374
|
+
import os2 from "os";
|
|
2375
|
+
import chalk16 from "chalk";
|
|
2376
|
+
import {
|
|
2377
|
+
deriveChildSource as deriveChildSource3,
|
|
2378
|
+
materializeSourceToTemp as materializeSourceToTemp3,
|
|
2379
|
+
SkildError as SkildError11
|
|
2380
|
+
} from "@skild/core";
|
|
2381
|
+
async function extractGithubSkills(source, options) {
|
|
2382
|
+
const resolvedSource = source.trim();
|
|
2383
|
+
const jsonOnly = Boolean(options.json);
|
|
2384
|
+
if (!parseGitHubSource(resolvedSource)) {
|
|
2385
|
+
const message = `Only GitHub sources are supported for extract-github-skills: "${resolvedSource}"`;
|
|
2386
|
+
if (jsonOnly) {
|
|
2387
|
+
process.stdout.write(JSON.stringify({ ok: false, error: message }, null, 2) + "\n");
|
|
2388
|
+
} else {
|
|
2389
|
+
console.error(chalk16.red(message));
|
|
2390
|
+
}
|
|
2391
|
+
process.exitCode = 1;
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
const maxDocDepth = parseNonNegativeInt(options.depth, 0);
|
|
2395
|
+
const maxSkillDepth = parseNonNegativeInt(options.scanDepth, 6);
|
|
2396
|
+
const maxSkills = parsePositiveInt(options.maxSkills, 200);
|
|
2397
|
+
const outDir = resolveOutDir(options.out);
|
|
2398
|
+
const force = Boolean(options.force);
|
|
2399
|
+
try {
|
|
2400
|
+
prepareOutputDir(outDir, force);
|
|
2401
|
+
} catch (error) {
|
|
2402
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2403
|
+
if (jsonOnly) {
|
|
2404
|
+
process.stdout.write(JSON.stringify({ ok: false, error: message }, null, 2) + "\n");
|
|
2405
|
+
} else {
|
|
2406
|
+
console.error(chalk16.red(message));
|
|
2407
|
+
}
|
|
2408
|
+
process.exitCode = 1;
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
const spinner = createSpinner(`Parsing markdown at ${chalk16.cyan(resolvedSource)}...`);
|
|
2412
|
+
if (!jsonOnly) spinner.start();
|
|
2413
|
+
let skills = [];
|
|
2414
|
+
let tree = [];
|
|
2415
|
+
let cleanup = null;
|
|
2416
|
+
try {
|
|
2417
|
+
const markdownResult = await discoverMarkdownSkillsFromSource({
|
|
2418
|
+
source: resolvedSource,
|
|
2419
|
+
maxDocDepth,
|
|
2420
|
+
maxSkillDepth,
|
|
2421
|
+
maxSkills,
|
|
2422
|
+
onProgress: (update2) => {
|
|
2423
|
+
if (!spinner) return;
|
|
2424
|
+
const current = update2.current ? ` \xB7 ${update2.current}` : "";
|
|
2425
|
+
const capped = update2.linkLimitReached ? " \xB7 link cap reached" : "";
|
|
2426
|
+
spinner.text = `Parsing markdown (${update2.docsScanned} docs, ${update2.linksChecked} links, ${update2.skillsFound} skills)${current}${capped}`;
|
|
2427
|
+
}
|
|
2428
|
+
});
|
|
2429
|
+
if (markdownResult && markdownResult.skills.length > 0) {
|
|
2430
|
+
skills = markdownResult.skills;
|
|
2431
|
+
tree = markdownResult.tree;
|
|
2432
|
+
cleanup = markdownResult.cleanup;
|
|
2433
|
+
} else {
|
|
2434
|
+
if (!jsonOnly) spinner.text = `Scanning repository at ${chalk16.cyan(resolvedSource)}...`;
|
|
2435
|
+
const materialized = await materializeSourceToTemp3(resolvedSource);
|
|
2436
|
+
cleanup = materialized.cleanup;
|
|
2437
|
+
const discovered = discoverSkillDirsWithHeuristics(materialized.dir, { maxDepth: maxSkillDepth, maxSkills });
|
|
2438
|
+
if (discovered.length === 0) {
|
|
2439
|
+
throw new SkildError11("SKILL_NOT_FOUND", `No SKILL.md found in source "${resolvedSource}".`);
|
|
2440
|
+
}
|
|
2441
|
+
skills = discovered.map((d) => {
|
|
2442
|
+
const metadata = readSkillMetadata(d.absDir);
|
|
2443
|
+
return {
|
|
2444
|
+
relPath: d.relPath,
|
|
2445
|
+
suggestedSource: d.relPath === "." ? resolvedSource : deriveChildSource3(resolvedSource, d.relPath),
|
|
2446
|
+
materializedDir: d.absDir,
|
|
2447
|
+
displayName: metadata?.name,
|
|
2448
|
+
description: metadata?.description
|
|
2449
|
+
};
|
|
2450
|
+
});
|
|
2451
|
+
tree = buildTreeFromRelPaths(skills);
|
|
2452
|
+
}
|
|
2453
|
+
if (skills.length > maxSkills) {
|
|
2454
|
+
throw new SkildError11("INVALID_SOURCE", `Found more than ${maxSkills} skills. Increase --max-skills to proceed.`);
|
|
2455
|
+
}
|
|
2456
|
+
const exportPaths = buildExportPathMap(tree, skills);
|
|
2457
|
+
const repoCache = /* @__PURE__ */ new Map();
|
|
2458
|
+
for (let i = 0; i < skills.length; i++) {
|
|
2459
|
+
const skill = skills[i];
|
|
2460
|
+
const exportSegments = exportPaths.get(i);
|
|
2461
|
+
if (!exportSegments || exportSegments.length === 0) continue;
|
|
2462
|
+
const exportPath = path5.join(outDir, ...exportSegments);
|
|
2463
|
+
const localDir = await resolveSkillDirectory(skill, repoCache);
|
|
2464
|
+
copyDirectory(localDir, exportPath);
|
|
2465
|
+
const metadata = readSkillMetadata(localDir) || { name: skill.displayName, description: skill.description };
|
|
2466
|
+
const skillJson = {
|
|
2467
|
+
name: metadata?.name || skill.displayName || skill.relPath || void 0,
|
|
2468
|
+
description: metadata?.description || skill.description || void 0,
|
|
2469
|
+
source: skill.suggestedSource,
|
|
2470
|
+
relPath: skill.relPath,
|
|
2471
|
+
exportPath: path5.relative(outDir, exportPath).split(path5.sep).join("/")
|
|
2472
|
+
};
|
|
2473
|
+
fs5.writeFileSync(path5.join(exportPath, "skill.json"), JSON.stringify(skillJson, null, 2));
|
|
2474
|
+
}
|
|
2475
|
+
const catalogSkills = skills.map((skill, index) => {
|
|
2476
|
+
const exportSegments = exportPaths.get(index) || [];
|
|
2477
|
+
const exportPath = exportSegments.length > 0 ? exportSegments.join("/") : slugifySegment(skill.displayName || skill.relPath || "skill", "skill");
|
|
2478
|
+
return {
|
|
2479
|
+
index,
|
|
2480
|
+
name: skill.displayName || void 0,
|
|
2481
|
+
description: skill.description || void 0,
|
|
2482
|
+
source: skill.suggestedSource,
|
|
2483
|
+
relPath: skill.relPath,
|
|
2484
|
+
exportPath
|
|
2485
|
+
};
|
|
2486
|
+
});
|
|
2487
|
+
const catalog = {
|
|
2488
|
+
schemaVersion: 1,
|
|
2489
|
+
source: resolvedSource,
|
|
2490
|
+
exportRoot: outDir,
|
|
2491
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2492
|
+
tree,
|
|
2493
|
+
skills: catalogSkills
|
|
2494
|
+
};
|
|
2495
|
+
fs5.writeFileSync(path5.join(outDir, "catalog.json"), JSON.stringify(catalog, null, 2));
|
|
2496
|
+
for (const entry of repoCache.values()) entry.cleanup();
|
|
2497
|
+
cleanup?.();
|
|
2498
|
+
if (!jsonOnly) {
|
|
2499
|
+
spinner.succeed(`Exported ${skills.length} skill${skills.length > 1 ? "s" : ""} to ${chalk16.cyan(outDir)}`);
|
|
2500
|
+
} else {
|
|
2501
|
+
process.stdout.write(JSON.stringify({ ok: true, outDir, skills: catalogSkills }, null, 2) + "\n");
|
|
2502
|
+
}
|
|
2503
|
+
} catch (error) {
|
|
2504
|
+
cleanup?.();
|
|
2505
|
+
if (!jsonOnly) spinner.stop();
|
|
2506
|
+
const message = error instanceof SkildError11 ? error.message : error instanceof Error ? error.message : String(error);
|
|
2507
|
+
if (jsonOnly) {
|
|
2508
|
+
process.stdout.write(JSON.stringify({ ok: false, error: message }, null, 2) + "\n");
|
|
2509
|
+
} else {
|
|
2510
|
+
console.error(chalk16.red(message));
|
|
2511
|
+
}
|
|
2512
|
+
process.exitCode = 1;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
function resolveOutDir(out) {
|
|
2516
|
+
if (!out || !out.trim()) {
|
|
2517
|
+
return path5.resolve(process.cwd(), "skild-github-skills");
|
|
2518
|
+
}
|
|
2519
|
+
const trimmed = out.trim();
|
|
2520
|
+
if (trimmed.startsWith("~")) {
|
|
2521
|
+
return path5.resolve(os2.homedir(), trimmed.slice(1));
|
|
2522
|
+
}
|
|
2523
|
+
return path5.resolve(process.cwd(), trimmed);
|
|
2524
|
+
}
|
|
2525
|
+
function prepareOutputDir(outDir, force) {
|
|
2526
|
+
if (fs5.existsSync(outDir)) {
|
|
2527
|
+
const entries = fs5.readdirSync(outDir);
|
|
2528
|
+
if (entries.length > 0) {
|
|
2529
|
+
if (!force) {
|
|
2530
|
+
throw new Error(`Output directory is not empty: ${outDir}. Use --force to overwrite.`);
|
|
2531
|
+
}
|
|
2532
|
+
fs5.rmSync(outDir, { recursive: true, force: true });
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
fs5.mkdirSync(outDir, { recursive: true });
|
|
2536
|
+
}
|
|
2537
|
+
async function resolveSkillDirectory(skill, repoCache) {
|
|
2538
|
+
if (skill.materializedDir && fs5.existsSync(skill.materializedDir)) return skill.materializedDir;
|
|
2539
|
+
const parsed = parseGitHubSource(skill.suggestedSource);
|
|
2540
|
+
if (!parsed) throw new SkildError11("INVALID_SOURCE", `Unsupported skill source: ${skill.suggestedSource}`);
|
|
2541
|
+
const ref = parsed.ref || "HEAD";
|
|
2542
|
+
const repoKey = `${parsed.owner}/${parsed.repo}#${ref}`;
|
|
2543
|
+
let cached = repoCache.get(repoKey);
|
|
2544
|
+
if (!cached) {
|
|
2545
|
+
const materialized = await materializeSourceToTemp3(repoKey);
|
|
2546
|
+
cached = { dir: materialized.dir, cleanup: materialized.cleanup };
|
|
2547
|
+
repoCache.set(repoKey, cached);
|
|
2548
|
+
}
|
|
2549
|
+
const relPath = parsed.path ? parsed.path.replace(/^\/+/, "") : "";
|
|
2550
|
+
const resolved = relPath ? path5.join(cached.dir, relPath) : cached.dir;
|
|
2551
|
+
if (!fs5.existsSync(resolved)) {
|
|
2552
|
+
throw new SkildError11("SKILL_NOT_FOUND", `Skill path missing in repo: ${skill.suggestedSource}`);
|
|
2553
|
+
}
|
|
2554
|
+
return resolved;
|
|
2555
|
+
}
|
|
2556
|
+
function copyDirectory(fromDir, toDir) {
|
|
2557
|
+
fs5.mkdirSync(path5.dirname(toDir), { recursive: true });
|
|
2558
|
+
fs5.cpSync(fromDir, toDir, { recursive: true, errorOnExist: false, force: true });
|
|
2559
|
+
}
|
|
2560
|
+
function buildTreeFromRelPaths(skills) {
|
|
2561
|
+
let nodeId = 0;
|
|
2562
|
+
const root = { id: "root", label: "root", kind: "list", children: [] };
|
|
2563
|
+
const ensureChild = (parent, label) => {
|
|
2564
|
+
const found = parent.children.find((child2) => child2.label === label);
|
|
2565
|
+
if (found) return found;
|
|
2566
|
+
nodeId += 1;
|
|
2567
|
+
const child = { id: `rel-${nodeId}`, label, kind: "list", children: [] };
|
|
2568
|
+
parent.children.push(child);
|
|
2569
|
+
return child;
|
|
2570
|
+
};
|
|
2571
|
+
skills.forEach((skill, index) => {
|
|
2572
|
+
const relPath = skill.relPath === "." ? "" : skill.relPath;
|
|
2573
|
+
const segments = relPath.split("/").filter(Boolean);
|
|
2574
|
+
let parent = root;
|
|
2575
|
+
if (segments.length === 0) {
|
|
2576
|
+
nodeId += 1;
|
|
2577
|
+
parent.children.push({
|
|
2578
|
+
id: `rel-${nodeId}`,
|
|
2579
|
+
label: skill.displayName || "skill",
|
|
2580
|
+
kind: "skill",
|
|
2581
|
+
skillIndex: index,
|
|
2582
|
+
children: []
|
|
2583
|
+
});
|
|
2584
|
+
return;
|
|
2585
|
+
}
|
|
2586
|
+
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
2587
|
+
parent = ensureChild(parent, segments[i]);
|
|
2588
|
+
}
|
|
2589
|
+
nodeId += 1;
|
|
2590
|
+
parent.children.push({
|
|
2591
|
+
id: `rel-${nodeId}`,
|
|
2592
|
+
label: skill.displayName || segments[segments.length - 1],
|
|
2593
|
+
kind: "skill",
|
|
2594
|
+
skillIndex: index,
|
|
2595
|
+
children: []
|
|
2596
|
+
});
|
|
2597
|
+
});
|
|
2598
|
+
return collapseSingleChildNodes2(root.children);
|
|
2599
|
+
}
|
|
2600
|
+
function buildExportPathMap(tree, skills) {
|
|
2601
|
+
const paths = /* @__PURE__ */ new Map();
|
|
2602
|
+
const usedPaths = /* @__PURE__ */ new Set();
|
|
2603
|
+
const walk = (nodes, parentSegments) => {
|
|
2604
|
+
const siblingCounts = /* @__PURE__ */ new Map();
|
|
2605
|
+
for (const node of nodes) {
|
|
2606
|
+
const base = slugifySegment(node.label, node.kind === "skill" ? "skill" : "section");
|
|
2607
|
+
const segment = ensureUniqueSegment(base, siblingCounts);
|
|
2608
|
+
if (node.kind === "skill" && node.skillIndex != null) {
|
|
2609
|
+
const segments = ensureUniquePath([...parentSegments, segment], usedPaths);
|
|
2610
|
+
paths.set(node.skillIndex, segments);
|
|
2611
|
+
usedPaths.add(segments.join("/"));
|
|
2612
|
+
continue;
|
|
2613
|
+
}
|
|
2614
|
+
if (node.children.length > 0) {
|
|
2615
|
+
walk(node.children, [...parentSegments, segment]);
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2619
|
+
walk(tree, []);
|
|
2620
|
+
for (let i = 0; i < skills.length; i++) {
|
|
2621
|
+
if (paths.has(i)) continue;
|
|
2622
|
+
const skill = skills[i];
|
|
2623
|
+
const fallbackSegments = relPathSegments(skill);
|
|
2624
|
+
const segments = ensureUniquePath(fallbackSegments, usedPaths);
|
|
2625
|
+
paths.set(i, segments);
|
|
2626
|
+
usedPaths.add(segments.join("/"));
|
|
2627
|
+
}
|
|
2628
|
+
return paths;
|
|
2629
|
+
}
|
|
2630
|
+
function relPathSegments(skill) {
|
|
2631
|
+
if (skill.relPath && skill.relPath !== ".") {
|
|
2632
|
+
return skill.relPath.split("/").filter(Boolean).map((segment) => slugifySegment(segment, "skill"));
|
|
2633
|
+
}
|
|
2634
|
+
return [slugifySegment(skill.displayName || skill.relPath || "skill", "skill")];
|
|
2635
|
+
}
|
|
2636
|
+
function ensureUniqueSegment(base, counts) {
|
|
2637
|
+
const next = (counts.get(base) || 0) + 1;
|
|
2638
|
+
counts.set(base, next);
|
|
2639
|
+
return next === 1 ? base : `${base}-${next}`;
|
|
2640
|
+
}
|
|
2641
|
+
function ensureUniquePath(segments, usedPaths) {
|
|
2642
|
+
let candidate = segments.join("/");
|
|
2643
|
+
if (!usedPaths.has(candidate)) return segments;
|
|
2644
|
+
let suffix = 2;
|
|
2645
|
+
const baseSegments = [...segments];
|
|
2646
|
+
while (usedPaths.has(candidate)) {
|
|
2647
|
+
candidate = [...baseSegments.slice(0, -1), `${baseSegments[baseSegments.length - 1]}-${suffix}`].join("/");
|
|
2648
|
+
suffix += 1;
|
|
2649
|
+
}
|
|
2650
|
+
return candidate.split("/");
|
|
2651
|
+
}
|
|
2652
|
+
function slugifySegment(label, fallback) {
|
|
2653
|
+
const normalized = label.trim().toLowerCase();
|
|
2654
|
+
const slug = normalized.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2655
|
+
return slug || fallback;
|
|
2656
|
+
}
|
|
2657
|
+
function collapseSingleChildNodes2(nodes) {
|
|
2658
|
+
return nodes.map((node) => collapseNode2(node)).filter(Boolean);
|
|
2659
|
+
}
|
|
2660
|
+
function collapseNode2(node) {
|
|
2661
|
+
node.children = node.children.map((child) => collapseNode2(child)).filter(Boolean);
|
|
2662
|
+
if (node.kind !== "heading" && node.kind !== "skill" && node.children.length === 1 && !node.skillIndex) {
|
|
2663
|
+
return node.children[0];
|
|
2664
|
+
}
|
|
2665
|
+
return node;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2260
2668
|
// src/index.ts
|
|
2261
2669
|
import { PLATFORMS as PLATFORMS4 } from "@skild/core";
|
|
2262
2670
|
var require2 = createRequire(import.meta.url);
|
|
2263
2671
|
var { version } = require2("../package.json");
|
|
2264
2672
|
var program = new Command();
|
|
2265
2673
|
program.name("skild").description("The npm for Agent Skills \u2014 Discover, install, manage, and publish AI Agent Skills with ease.").version(version);
|
|
2266
|
-
program.command("install <source>").alias("i").description("Install a Skill from a Git URL, degit shorthand, or local directory").option("-t, --target <platform>", `Target platform: ${PLATFORMS4.join(", ")}`).option("--all", `Install to all platforms: ${PLATFORMS4.join(", ")}`).option("--recursive", "If source is a multi-skill directory/repo, install all discovered skills").option("-y, --yes", "Skip confirmation prompts (assume yes)").option("--depth <n>", "Max directory depth to scan for SKILL.md (default:
|
|
2674
|
+
program.command("install <source>").alias("i").description("Install a Skill from a Git URL, degit shorthand, or local directory").option("-t, --target <platform>", `Target platform: ${PLATFORMS4.join(", ")}`).option("--all", `Install to all platforms: ${PLATFORMS4.join(", ")}`).option("--recursive", "If source is a multi-skill directory/repo, install all discovered skills").option("-y, --yes", "Skip confirmation prompts (assume yes)").option("--depth <n>", "Max markdown recursion depth (default: 0)", "0").option("--scan-depth <n>", "Max directory depth to scan for SKILL.md (default: 6)", "6").option("--max-skills <n>", "Max discovered skills to install (default: 200)", "200").option("-l, --local", "Install to project-level directory instead of global").option("-f, --force", "Overwrite existing installation").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--json", "Output JSON").action(async (source, options) => {
|
|
2267
2675
|
await install(source, options);
|
|
2268
2676
|
});
|
|
2269
2677
|
program.command("list").alias("ls").description("List installed Skills").option("-t, --target <platform>", `Target platform: ${PLATFORMS4.join(", ")} (optional; omit to list all)`).option("-l, --local", "List project-level directory instead of global").option("--paths", "Show install paths").option("--verbose", "Show skillset dependency details").option("--json", "Output JSON").action(async (options) => list(options));
|
|
@@ -2278,8 +2686,11 @@ program.command("logout").description("Remove stored registry credentials").acti
|
|
|
2278
2686
|
program.command("whoami").description("Show current registry identity").action(async () => whoami());
|
|
2279
2687
|
program.command("publish").description("Publish a Skill directory to the registry (hosted tarball)").option("--dir <path>", "Skill directory (defaults to cwd)").option("--name <@publisher/skill>", "Override skill name (defaults to SKILL.md frontmatter)").option("--skill-version <semver>", "Override version (defaults to SKILL.md frontmatter)").option("--alias <alias>", "Optional short identifier (global unique) for `skild install <alias>`").option("--description <text>", "Override description (defaults to SKILL.md frontmatter)").option("--targets <list>", "Comma-separated target platforms metadata (optional)").option("--tag <tag>", "Dist-tag (default: latest)", "latest").option("--registry <url>", "Registry base URL (defaults to saved login)").option("--json", "Output JSON").action(async (options) => publish(options));
|
|
2280
2688
|
program.command("search <query>").description("Search Skills in the registry").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--limit <n>", "Max results (default: 50)", "50").option("--json", "Output JSON").action(async (query, options) => search(query, options));
|
|
2689
|
+
program.command("extract-github-skills <source>").description("Extract GitHub skills into a local catalog directory").option("--out <dir>", "Output directory (default: ./skild-github-skills)").option("-f, --force", "Overwrite existing output directory").option("--depth <n>", "Max markdown recursion depth (default: 0)", "0").option("--scan-depth <n>", "Max directory depth to scan for SKILL.md (default: 6)", "6").option("--max-skills <n>", "Max discovered skills to export (default: 200)", "200").option("--json", "Output JSON").action(async (source, options) => {
|
|
2690
|
+
await extractGithubSkills(source, options);
|
|
2691
|
+
});
|
|
2281
2692
|
program.action(() => {
|
|
2282
|
-
console.log(
|
|
2693
|
+
console.log(chalk17.bold("\n\u{1F6E1}\uFE0F skild \u2014 Get your agents skilled.\n"));
|
|
2283
2694
|
program.outputHelp();
|
|
2284
2695
|
});
|
|
2285
2696
|
var argv = process.argv.slice();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skild",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "The npm for Agent Skills — Discover, install, manage, and publish AI Agent Skills with ease.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"remark-parse": "^11.0.0",
|
|
42
42
|
"tar": "^7.4.3",
|
|
43
43
|
"unified": "^11.0.4",
|
|
44
|
-
"@skild/core": "^0.
|
|
44
|
+
"@skild/core": "^0.7.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/mdast": "^4.0.4",
|