skild 0.6.0 → 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 +464 -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,12 +442,32 @@ 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());
|
|
431
449
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
432
450
|
return Math.floor(n);
|
|
433
451
|
}
|
|
452
|
+
function parseNonNegativeInt(input, fallback) {
|
|
453
|
+
if (input == null) return fallback;
|
|
454
|
+
const n = typeof input === "number" ? input : Number(String(input).trim());
|
|
455
|
+
if (!Number.isFinite(n) || n < 0) return fallback;
|
|
456
|
+
return Math.floor(n);
|
|
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
|
+
}
|
|
434
471
|
function normalizeRelPath(relPath) {
|
|
435
472
|
return relPath.split(path.sep).join("/").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
436
473
|
}
|
|
@@ -504,7 +541,7 @@ function discoverSkillDirsWithHeuristics(rootDir, options) {
|
|
|
504
541
|
return discoverSkillDirs(root, options);
|
|
505
542
|
}
|
|
506
543
|
|
|
507
|
-
// src/commands/
|
|
544
|
+
// src/commands/markdown-discovery.ts
|
|
508
545
|
import fs2 from "fs";
|
|
509
546
|
import path2 from "path";
|
|
510
547
|
import { unified } from "unified";
|
|
@@ -514,7 +551,8 @@ import { deriveChildSource, fetchWithTimeout, materializeSourceToTemp } from "@s
|
|
|
514
551
|
var README_CANDIDATES = ["README.md", "readme.md", "Readme.md", "README.markdown", "readme.markdown"];
|
|
515
552
|
async function discoverMarkdownSkillsFromSource(input) {
|
|
516
553
|
const ctx = {
|
|
517
|
-
|
|
554
|
+
maxDocDepth: input.maxDocDepth,
|
|
555
|
+
maxSkillDepth: input.maxSkillDepth,
|
|
518
556
|
maxSkills: input.maxSkills,
|
|
519
557
|
maxLinks: Math.min(400, Math.max(200, input.maxSkills * 2)),
|
|
520
558
|
linkLimitReached: false,
|
|
@@ -558,7 +596,7 @@ function cleanupRepoCache(ctx) {
|
|
|
558
596
|
ctx.repoCache.clear();
|
|
559
597
|
}
|
|
560
598
|
async function parseMarkdownDoc(doc, ctx, depth, visitedDocs) {
|
|
561
|
-
if (depth > ctx.
|
|
599
|
+
if (depth > ctx.maxDocDepth) return [];
|
|
562
600
|
const docKey = `${doc.repo.owner}/${doc.repo.repo}#${doc.repo.ref || ""}:${doc.docPath}`;
|
|
563
601
|
if (visitedDocs.has(docKey)) return [];
|
|
564
602
|
visitedDocs.add(docKey);
|
|
@@ -646,7 +684,8 @@ async function parseInlineLinks(node, parent, doc, ctx, depth, visitedDocs) {
|
|
|
646
684
|
if (!resolved) continue;
|
|
647
685
|
const label = normalizeLabel(link.label) || resolved.displayName;
|
|
648
686
|
const maybeMarkdown = !isLikelyFilePath(resolved.pathHint) || looksLikeMarkdownPath(resolved.pathHint);
|
|
649
|
-
|
|
687
|
+
const canRecurseMarkdown = depth < ctx.maxDocDepth;
|
|
688
|
+
if (maybeMarkdown && canRecurseMarkdown) {
|
|
650
689
|
const childDoc = await resolveMarkdownDoc(resolved.source, ctx);
|
|
651
690
|
if (childDoc) {
|
|
652
691
|
const childNodes = await parseMarkdownDoc(childDoc, ctx, depth + 1, visitedDocs);
|
|
@@ -666,10 +705,7 @@ async function parseInlineLinks(node, parent, doc, ctx, depth, visitedDocs) {
|
|
|
666
705
|
parent.children.push(createSkillLeaf(ctx, skillIndices[0], label));
|
|
667
706
|
} else {
|
|
668
707
|
const groupLabel = label || resolved.displayName || "Skills";
|
|
669
|
-
const groupNode =
|
|
670
|
-
for (const skillIndex of skillIndices) {
|
|
671
|
-
groupNode.children.push(createSkillLeaf(ctx, skillIndex));
|
|
672
|
-
}
|
|
708
|
+
const groupNode = buildSkillPathTree(ctx, skillIndices, groupLabel);
|
|
673
709
|
parent.children.push(groupNode);
|
|
674
710
|
}
|
|
675
711
|
}
|
|
@@ -690,6 +726,50 @@ function createSkillLeaf(ctx, skillIndex, labelOverride) {
|
|
|
690
726
|
node.skillIndex = skillIndex;
|
|
691
727
|
return node;
|
|
692
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
|
+
}
|
|
693
773
|
function collapseSingleChildNodes(nodes) {
|
|
694
774
|
const collapsed = [];
|
|
695
775
|
for (const node of nodes) {
|
|
@@ -773,17 +853,19 @@ async function resolveSkillsFromSource(source, displayName, ctx, repoHint) {
|
|
|
773
853
|
}
|
|
774
854
|
const skillMd = path2.join(materializedDir, "SKILL.md");
|
|
775
855
|
if (fs2.existsSync(skillMd)) {
|
|
856
|
+
const metadata = readSkillMetadata(materializedDir);
|
|
776
857
|
const skillIndex = registerSkill(ctx, {
|
|
777
858
|
relPath: ".",
|
|
778
859
|
suggestedSource: source,
|
|
779
860
|
materializedDir,
|
|
780
|
-
displayName: displayName || deriveDisplayName(source)
|
|
861
|
+
displayName: metadata?.name || displayName || deriveDisplayName(source),
|
|
862
|
+
description: metadata?.description
|
|
781
863
|
});
|
|
782
864
|
const indices2 = [skillIndex];
|
|
783
865
|
ctx.skillCache.set(source, indices2);
|
|
784
866
|
return indices2;
|
|
785
867
|
}
|
|
786
|
-
const discovered = discoverSkillDirsWithHeuristics(materializedDir, { maxDepth: ctx.
|
|
868
|
+
const discovered = discoverSkillDirsWithHeuristics(materializedDir, { maxDepth: ctx.maxSkillDepth, maxSkills: ctx.maxSkills });
|
|
787
869
|
if (discovered.length === 0) {
|
|
788
870
|
ctx.skillCache.set(source, []);
|
|
789
871
|
return [];
|
|
@@ -791,11 +873,13 @@ async function resolveSkillsFromSource(source, displayName, ctx, repoHint) {
|
|
|
791
873
|
const indices = [];
|
|
792
874
|
for (const skill of discovered) {
|
|
793
875
|
const childSource = deriveChildSource(source, skill.relPath);
|
|
876
|
+
const metadata = readSkillMetadata(skill.absDir);
|
|
794
877
|
const skillIndex = registerSkill(ctx, {
|
|
795
878
|
relPath: skill.relPath,
|
|
796
879
|
suggestedSource: childSource,
|
|
797
880
|
materializedDir: skill.absDir,
|
|
798
|
-
displayName: deriveDisplayName(childSource)
|
|
881
|
+
displayName: metadata?.name || deriveDisplayName(childSource),
|
|
882
|
+
description: metadata?.description
|
|
799
883
|
});
|
|
800
884
|
indices.push(skillIndex);
|
|
801
885
|
}
|
|
@@ -808,23 +892,27 @@ function resolveSkillsFromLocal(repoHint, source, displayName, ctx) {
|
|
|
808
892
|
if (!fs2.existsSync(base)) return [];
|
|
809
893
|
const skillMd = path2.join(base, "SKILL.md");
|
|
810
894
|
if (fs2.existsSync(skillMd)) {
|
|
895
|
+
const metadata = readSkillMetadata(base);
|
|
811
896
|
return [
|
|
812
897
|
registerSkill(ctx, {
|
|
813
898
|
relPath: ".",
|
|
814
899
|
suggestedSource: source,
|
|
815
|
-
displayName: displayName || deriveDisplayName(source)
|
|
900
|
+
displayName: metadata?.name || displayName || deriveDisplayName(source),
|
|
901
|
+
description: metadata?.description
|
|
816
902
|
})
|
|
817
903
|
];
|
|
818
904
|
}
|
|
819
|
-
const discovered = discoverSkillDirsWithHeuristics(base, { maxDepth: ctx.
|
|
905
|
+
const discovered = discoverSkillDirsWithHeuristics(base, { maxDepth: ctx.maxSkillDepth, maxSkills: ctx.maxSkills });
|
|
820
906
|
if (discovered.length === 0) return [];
|
|
821
907
|
const indices = [];
|
|
822
908
|
for (const skill of discovered) {
|
|
823
909
|
const childSource = deriveChildSource(source, skill.relPath);
|
|
910
|
+
const metadata = readSkillMetadata(skill.absDir);
|
|
824
911
|
const skillIndex = registerSkill(ctx, {
|
|
825
912
|
relPath: skill.relPath,
|
|
826
913
|
suggestedSource: childSource,
|
|
827
|
-
displayName: deriveDisplayName(childSource)
|
|
914
|
+
displayName: metadata?.name || deriveDisplayName(childSource),
|
|
915
|
+
description: metadata?.description
|
|
828
916
|
});
|
|
829
917
|
indices.push(skillIndex);
|
|
830
918
|
}
|
|
@@ -842,16 +930,19 @@ async function tryFetchSkillManifest(source, displayName, ctx) {
|
|
|
842
930
|
if (res.status === 404) return [];
|
|
843
931
|
return [];
|
|
844
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
|
+
];
|
|
845
943
|
} catch {
|
|
846
944
|
return [];
|
|
847
945
|
}
|
|
848
|
-
return [
|
|
849
|
-
registerSkill(ctx, {
|
|
850
|
-
relPath: ".",
|
|
851
|
-
suggestedSource: source,
|
|
852
|
-
displayName: displayName || deriveDisplayName(source)
|
|
853
|
-
})
|
|
854
|
-
];
|
|
855
946
|
}
|
|
856
947
|
function registerSkill(ctx, skill) {
|
|
857
948
|
const key = skill.suggestedSource;
|
|
@@ -1046,12 +1137,18 @@ function getPlatformPromptList(scope) {
|
|
|
1046
1137
|
const installed = getInstalledPlatforms(scope);
|
|
1047
1138
|
return installed.length > 0 ? installed : [...PLATFORMS2];
|
|
1048
1139
|
}
|
|
1049
|
-
function asDiscoveredSkills(discovered, toSuggestedSource, toMaterializedDir) {
|
|
1050
|
-
return discovered.map((d) =>
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
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
|
+
});
|
|
1055
1152
|
}
|
|
1056
1153
|
function createContext(source, options) {
|
|
1057
1154
|
const scope = options.local ? "project" : "global";
|
|
@@ -1082,7 +1179,8 @@ function createContext(source, options) {
|
|
|
1082
1179
|
jsonOnly,
|
|
1083
1180
|
interactive,
|
|
1084
1181
|
yes,
|
|
1085
|
-
|
|
1182
|
+
markdownDepth: parseNonNegativeInt(options.depth, 0),
|
|
1183
|
+
scanDepth: parseNonNegativeInt(options.scanDepth, 6),
|
|
1086
1184
|
maxSkills: parsePositiveInt(options.maxSkills, 200),
|
|
1087
1185
|
resolvedSource: source.trim(),
|
|
1088
1186
|
targets,
|
|
@@ -1129,7 +1227,7 @@ async function resolveSource(ctx) {
|
|
|
1129
1227
|
return true;
|
|
1130
1228
|
}
|
|
1131
1229
|
async function discoverSkills(ctx) {
|
|
1132
|
-
const { resolvedSource,
|
|
1230
|
+
const { resolvedSource, markdownDepth, scanDepth, maxSkills, jsonOnly } = ctx;
|
|
1133
1231
|
if (ctx.spinner) {
|
|
1134
1232
|
ctx.spinner.text = `Discovery at ${chalk3.cyan(ctx.source)}...`;
|
|
1135
1233
|
}
|
|
@@ -1144,11 +1242,17 @@ async function discoverSkills(ctx) {
|
|
|
1144
1242
|
if (isLocal) {
|
|
1145
1243
|
const hasSkillMd2 = fs3.existsSync(path3.join(maybeLocalRoot, "SKILL.md"));
|
|
1146
1244
|
if (hasSkillMd2) {
|
|
1245
|
+
const metadata = readSkillMetadata(maybeLocalRoot);
|
|
1147
1246
|
ctx.isSingleSkill = true;
|
|
1148
|
-
ctx.selectedSkills = [{
|
|
1247
|
+
ctx.selectedSkills = [{
|
|
1248
|
+
relPath: maybeLocalRoot,
|
|
1249
|
+
suggestedSource: resolvedSource,
|
|
1250
|
+
displayName: metadata?.name,
|
|
1251
|
+
description: metadata?.description
|
|
1252
|
+
}];
|
|
1149
1253
|
return true;
|
|
1150
1254
|
}
|
|
1151
|
-
const discovered2 = discoverSkillDirsWithHeuristics(maybeLocalRoot, { maxDepth, maxSkills });
|
|
1255
|
+
const discovered2 = discoverSkillDirsWithHeuristics(maybeLocalRoot, { maxDepth: scanDepth, maxSkills });
|
|
1152
1256
|
if (discovered2.length === 0) {
|
|
1153
1257
|
const message = `No SKILL.md found at ${maybeLocalRoot} (or within subdirectories).`;
|
|
1154
1258
|
if (jsonOnly) {
|
|
@@ -1160,12 +1264,18 @@ async function discoverSkills(ctx) {
|
|
|
1160
1264
|
process.exitCode = 1;
|
|
1161
1265
|
return false;
|
|
1162
1266
|
}
|
|
1163
|
-
ctx.discoveredSkills = asDiscoveredSkills(
|
|
1267
|
+
ctx.discoveredSkills = asDiscoveredSkills(
|
|
1268
|
+
discovered2,
|
|
1269
|
+
(d) => path3.join(maybeLocalRoot, d.relPath),
|
|
1270
|
+
void 0,
|
|
1271
|
+
(d) => d.absDir
|
|
1272
|
+
);
|
|
1164
1273
|
return true;
|
|
1165
1274
|
}
|
|
1166
1275
|
const markdownResult = await discoverMarkdownSkillsFromSource({
|
|
1167
1276
|
source: resolvedSource,
|
|
1168
|
-
|
|
1277
|
+
maxDocDepth: markdownDepth,
|
|
1278
|
+
maxSkillDepth: scanDepth,
|
|
1169
1279
|
maxSkills,
|
|
1170
1280
|
onProgress: (update2) => {
|
|
1171
1281
|
if (ctx.spinner) {
|
|
@@ -1188,11 +1298,18 @@ async function discoverSkills(ctx) {
|
|
|
1188
1298
|
ctx.cleanupMaterialized = appendCleanup(ctx.cleanupMaterialized, materialized.cleanup);
|
|
1189
1299
|
const hasSkillMd = fs3.existsSync(path3.join(ctx.materializedDir, "SKILL.md"));
|
|
1190
1300
|
if (hasSkillMd) {
|
|
1301
|
+
const metadata = readSkillMetadata(ctx.materializedDir);
|
|
1191
1302
|
ctx.isSingleSkill = true;
|
|
1192
|
-
ctx.selectedSkills = [{
|
|
1303
|
+
ctx.selectedSkills = [{
|
|
1304
|
+
relPath: ".",
|
|
1305
|
+
suggestedSource: resolvedSource,
|
|
1306
|
+
materializedDir: ctx.materializedDir,
|
|
1307
|
+
displayName: metadata?.name,
|
|
1308
|
+
description: metadata?.description
|
|
1309
|
+
}];
|
|
1193
1310
|
return true;
|
|
1194
1311
|
}
|
|
1195
|
-
const discovered = discoverSkillDirsWithHeuristics(ctx.materializedDir, { maxDepth, maxSkills });
|
|
1312
|
+
const discovered = discoverSkillDirsWithHeuristics(ctx.materializedDir, { maxDepth: scanDepth, maxSkills });
|
|
1196
1313
|
if (discovered.length === 0) {
|
|
1197
1314
|
const message = `No SKILL.md found in source "${resolvedSource}".`;
|
|
1198
1315
|
if (jsonOnly) {
|
|
@@ -1207,6 +1324,7 @@ async function discoverSkills(ctx) {
|
|
|
1207
1324
|
ctx.discoveredSkills = asDiscoveredSkills(
|
|
1208
1325
|
discovered,
|
|
1209
1326
|
(d) => deriveChildSource2(resolvedSource, d.relPath),
|
|
1327
|
+
(d) => d.absDir,
|
|
1210
1328
|
(d) => d.absDir
|
|
1211
1329
|
);
|
|
1212
1330
|
return true;
|
|
@@ -2250,13 +2368,310 @@ async function search(query, options = {}) {
|
|
|
2250
2368
|
}
|
|
2251
2369
|
}
|
|
2252
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
|
+
|
|
2253
2668
|
// src/index.ts
|
|
2254
2669
|
import { PLATFORMS as PLATFORMS4 } from "@skild/core";
|
|
2255
2670
|
var require2 = createRequire(import.meta.url);
|
|
2256
2671
|
var { version } = require2("../package.json");
|
|
2257
2672
|
var program = new Command();
|
|
2258
2673
|
program.name("skild").description("The npm for Agent Skills \u2014 Discover, install, manage, and publish AI Agent Skills with ease.").version(version);
|
|
2259
|
-
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: 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) => {
|
|
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) => {
|
|
2260
2675
|
await install(source, options);
|
|
2261
2676
|
});
|
|
2262
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));
|
|
@@ -2271,8 +2686,11 @@ program.command("logout").description("Remove stored registry credentials").acti
|
|
|
2271
2686
|
program.command("whoami").description("Show current registry identity").action(async () => whoami());
|
|
2272
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));
|
|
2273
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
|
+
});
|
|
2274
2692
|
program.action(() => {
|
|
2275
|
-
console.log(
|
|
2693
|
+
console.log(chalk17.bold("\n\u{1F6E1}\uFE0F skild \u2014 Get your agents skilled.\n"));
|
|
2276
2694
|
program.outputHelp();
|
|
2277
2695
|
});
|
|
2278
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",
|