skillspp 0.2.0 → 1.3.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/background-executor.js +3895 -0
- package/dist/background-executor.js.map +7 -0
- package/dist/background-worker.js +59 -0
- package/dist/background-worker.js.map +7 -0
- package/dist/cli.js +762 -913
- package/dist/cli.js.map +4 -4
- package/package.json +8 -2
- package/src/assets/ascii/logo/skillspp-logo.session.json +14630 -0
- package/src/assets/ascii/logo/skillspp-logo.txt +9 -0
package/dist/cli.js
CHANGED
|
@@ -20,9 +20,7 @@ function parseSource(input) {
|
|
|
20
20
|
if (isLocalPath(input)) {
|
|
21
21
|
return { type: "local", localPath: path.resolve(input) };
|
|
22
22
|
}
|
|
23
|
-
const githubTreeWithPath = input.match(
|
|
24
|
-
/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/
|
|
25
|
-
);
|
|
23
|
+
const githubTreeWithPath = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/);
|
|
26
24
|
if (githubTreeWithPath) {
|
|
27
25
|
const [, owner, repo, ref, subpath] = githubTreeWithPath;
|
|
28
26
|
return {
|
|
@@ -32,9 +30,7 @@ function parseSource(input) {
|
|
|
32
30
|
subpath
|
|
33
31
|
};
|
|
34
32
|
}
|
|
35
|
-
const githubTree = input.match(
|
|
36
|
-
/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/
|
|
37
|
-
);
|
|
33
|
+
const githubTree = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/);
|
|
38
34
|
if (githubTree) {
|
|
39
35
|
const [, owner, repo, ref] = githubTree;
|
|
40
36
|
return {
|
|
@@ -81,13 +77,7 @@ function parseSource(input) {
|
|
|
81
77
|
import fs from "node:fs";
|
|
82
78
|
import path2 from "node:path";
|
|
83
79
|
import matter from "gray-matter";
|
|
84
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
85
|
-
".git",
|
|
86
|
-
"node_modules",
|
|
87
|
-
"dist",
|
|
88
|
-
"build",
|
|
89
|
-
"__pycache__"
|
|
90
|
-
]);
|
|
80
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "__pycache__"]);
|
|
91
81
|
function resolveSourceLabel(parsedSource) {
|
|
92
82
|
switch (parsedSource.type) {
|
|
93
83
|
case "local":
|
|
@@ -136,12 +126,7 @@ function findSkillDirsRecursive(dir, depth, maxDepth, out) {
|
|
|
136
126
|
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) {
|
|
137
127
|
continue;
|
|
138
128
|
}
|
|
139
|
-
findSkillDirsRecursive(
|
|
140
|
-
path2.join(dir, entry.name),
|
|
141
|
-
depth + 1,
|
|
142
|
-
maxDepth,
|
|
143
|
-
out
|
|
144
|
-
);
|
|
129
|
+
findSkillDirsRecursive(path2.join(dir, entry.name), depth + 1, maxDepth, out);
|
|
145
130
|
}
|
|
146
131
|
}
|
|
147
132
|
function discoverSkills(basePath) {
|
|
@@ -498,9 +483,7 @@ function normalizeAgentSelectionInput(values, cwd = process.cwd()) {
|
|
|
498
483
|
if (unknown.length === 0) {
|
|
499
484
|
return values;
|
|
500
485
|
}
|
|
501
|
-
const expandedFromGlob = unknown.every(
|
|
502
|
-
(value) => fs2.existsSync(path3.resolve(cwd, value))
|
|
503
|
-
);
|
|
486
|
+
const expandedFromGlob = unknown.every((value) => fs2.existsSync(path3.resolve(cwd, value)));
|
|
504
487
|
if (!expandedFromGlob) {
|
|
505
488
|
return values;
|
|
506
489
|
}
|
|
@@ -544,22 +527,20 @@ function isAgentInstalled(agent, cwd) {
|
|
|
544
527
|
return false;
|
|
545
528
|
}
|
|
546
529
|
|
|
547
|
-
// ../../packages/
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
return sanitized || "unnamed-skill";
|
|
551
|
-
}
|
|
530
|
+
// ../../packages/cli-shared/src/ui/selection-step.tsx
|
|
531
|
+
import { useMemo, useState as useState2 } from "react";
|
|
532
|
+
import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
|
|
552
533
|
|
|
553
|
-
// src/ui/screens.tsx
|
|
534
|
+
// ../../packages/cli-shared/src/ui/screens.tsx
|
|
554
535
|
import { stripVTControlCharacters } from "node:util";
|
|
555
536
|
import React, { useEffect, useState } from "react";
|
|
556
537
|
import { Box, Text, render, useInput, useStdout } from "ink";
|
|
557
538
|
|
|
558
|
-
// src/ui/format.ts
|
|
539
|
+
// ../../packages/cli-shared/src/ui/format.ts
|
|
559
540
|
import os2 from "node:os";
|
|
560
541
|
import path4 from "node:path";
|
|
561
542
|
|
|
562
|
-
// src/ui/colors.ts
|
|
543
|
+
// ../../packages/cli-shared/src/ui/colors.ts
|
|
563
544
|
var ANSI_ESCAPE = "\x1B[";
|
|
564
545
|
var ANSI_RESET = "\x1B[0m";
|
|
565
546
|
var COLOR_TOKENS = {
|
|
@@ -593,7 +574,7 @@ function dim(text, colorEnabled) {
|
|
|
593
574
|
return ansiStyle(text, "2", colorEnabled);
|
|
594
575
|
}
|
|
595
576
|
|
|
596
|
-
// src/ui/format.ts
|
|
577
|
+
// ../../packages/cli-shared/src/ui/format.ts
|
|
597
578
|
function shortenHomePath(value, homeDir = os2.homedir()) {
|
|
598
579
|
if (value === homeDir || value.startsWith(`${homeDir}${path4.sep}`)) {
|
|
599
580
|
return `~${value.slice(homeDir.length)}`;
|
|
@@ -620,25 +601,31 @@ function formatDriftChips(options) {
|
|
|
620
601
|
)}`;
|
|
621
602
|
}
|
|
622
603
|
|
|
623
|
-
// src/ui/logo.ts
|
|
604
|
+
// ../../packages/cli-shared/src/ui/logo.ts
|
|
624
605
|
import fs3 from "node:fs";
|
|
625
|
-
import path5 from "node:path";
|
|
626
|
-
import { fileURLToPath } from "node:url";
|
|
627
606
|
var DEFAULT_LOGO_FPS = 12;
|
|
628
607
|
var EMPTY_TEXT_ONLY_LOGO = [];
|
|
629
608
|
var animatedCache;
|
|
630
609
|
var staticCache;
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
610
|
+
var configuredSessionPath = null;
|
|
611
|
+
var configuredStaticPath = null;
|
|
612
|
+
function normalizeConfiguredPath(filePath) {
|
|
613
|
+
if (typeof filePath !== "string") {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
const trimmed = filePath.trim();
|
|
617
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
618
|
+
}
|
|
619
|
+
function configureLogoAssetPaths(paths) {
|
|
620
|
+
configuredSessionPath = normalizeConfiguredPath(paths.sessionPath);
|
|
621
|
+
configuredStaticPath = normalizeConfiguredPath(paths.textPath);
|
|
622
|
+
resetLogoCache();
|
|
634
623
|
}
|
|
635
624
|
function resolveSessionPath() {
|
|
636
|
-
|
|
637
|
-
return customPath || path5.join(resolveLogoDir(), "skillspp-logo.session.json");
|
|
625
|
+
return normalizeConfiguredPath(process.env.SKILLSPP_LOGO_SESSION_PATH) ?? configuredSessionPath;
|
|
638
626
|
}
|
|
639
627
|
function resolveStaticPath() {
|
|
640
|
-
|
|
641
|
-
return customPath || path5.join(resolveLogoDir(), "skillspp-logo.txt");
|
|
628
|
+
return normalizeConfiguredPath(process.env.SKILLSPP_LOGO_TEXT_PATH) ?? configuredStaticPath;
|
|
642
629
|
}
|
|
643
630
|
function trimOuterEmptyRows(lines) {
|
|
644
631
|
let start = 0;
|
|
@@ -747,6 +734,10 @@ function readFileSafe(filePath) {
|
|
|
747
734
|
return null;
|
|
748
735
|
}
|
|
749
736
|
}
|
|
737
|
+
function resetLogoCache() {
|
|
738
|
+
animatedCache = void 0;
|
|
739
|
+
staticCache = void 0;
|
|
740
|
+
}
|
|
750
741
|
function getAnimatedLogoFrames() {
|
|
751
742
|
if (animatedCache !== void 0) {
|
|
752
743
|
return animatedCache;
|
|
@@ -783,7 +774,7 @@ function getBannerLogoLines() {
|
|
|
783
774
|
return getStaticLogoLines() ?? EMPTY_TEXT_ONLY_LOGO;
|
|
784
775
|
}
|
|
785
776
|
|
|
786
|
-
// src/ui/screens.tsx
|
|
777
|
+
// ../../packages/cli-shared/src/ui/screens.tsx
|
|
787
778
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
788
779
|
var DEFAULT_BANNER_WIDTH = 78;
|
|
789
780
|
var DEFAULT_PANEL_MIN_WIDTH = 58;
|
|
@@ -906,10 +897,7 @@ function resolveTerminalColumns() {
|
|
|
906
897
|
return Math.max(MIN_TERMINAL_WIDTH, process.stdout.columns || 80);
|
|
907
898
|
}
|
|
908
899
|
function resolveMaxInnerWidth(indent = " ") {
|
|
909
|
-
return Math.max(
|
|
910
|
-
MIN_INNER_WIDTH,
|
|
911
|
-
resolveTerminalColumns() - indent.length - FRAME_OVERHEAD
|
|
912
|
-
);
|
|
900
|
+
return Math.max(MIN_INNER_WIDTH, resolveTerminalColumns() - indent.length - FRAME_OVERHEAD);
|
|
913
901
|
}
|
|
914
902
|
function clampToTerminalInnerWidth(idealWidth, minWidth, indent = " ") {
|
|
915
903
|
const maxInnerWidth = resolveMaxInnerWidth(indent);
|
|
@@ -917,9 +905,7 @@ function clampToTerminalInnerWidth(idealWidth, minWidth, indent = " ") {
|
|
|
917
905
|
return Math.max(lowerBound, Math.min(idealWidth, maxInnerWidth));
|
|
918
906
|
}
|
|
919
907
|
function resolvePanelWidth(options) {
|
|
920
|
-
const lineLengths = options.lines.map(
|
|
921
|
-
(line) => visibleLength(singleLine(line))
|
|
922
|
-
);
|
|
908
|
+
const lineLengths = options.lines.map((line) => visibleLength(singleLine(line)));
|
|
923
909
|
return Math.max(
|
|
924
910
|
options.minWidth ?? DEFAULT_PANEL_MIN_WIDTH,
|
|
925
911
|
singleLine(options.title).length + 4,
|
|
@@ -928,10 +914,7 @@ function resolvePanelWidth(options) {
|
|
|
928
914
|
}
|
|
929
915
|
function resolveSelectionColumnWidths(innerWidth, targetLabelWidth, targetDescWidth) {
|
|
930
916
|
const availableColumns = Math.max(8, innerWidth - SELECTION_ROW_OVERHEAD);
|
|
931
|
-
const targetTotal = Math.max(
|
|
932
|
-
8,
|
|
933
|
-
targetLabelWidth + Math.max(0, targetDescWidth)
|
|
934
|
-
);
|
|
917
|
+
const targetTotal = Math.max(8, targetLabelWidth + Math.max(0, targetDescWidth));
|
|
935
918
|
if (availableColumns >= targetTotal) {
|
|
936
919
|
return {
|
|
937
920
|
labelWidth: targetLabelWidth,
|
|
@@ -957,25 +940,13 @@ function renderFramedPanel(options) {
|
|
|
957
940
|
const bottomLeft = options.style === "rounded" ? "\u2570" : "\u2514";
|
|
958
941
|
const bottomRight = options.style === "rounded" ? "\u256F" : "\u2518";
|
|
959
942
|
const out = [];
|
|
960
|
-
out.push(
|
|
961
|
-
`${options.indent}${dim(
|
|
962
|
-
`${topLeft}\u2500 ${title} ${"\u2500".repeat(topTail)}${topRight}`
|
|
963
|
-
)}`
|
|
964
|
-
);
|
|
943
|
+
out.push(`${options.indent}${dim(`${topLeft}\u2500 ${title} ${"\u2500".repeat(topTail)}${topRight}`)}`);
|
|
965
944
|
for (const line of options.lines) {
|
|
966
945
|
for (const wrappedLine of wrapVisibleLine(line, width)) {
|
|
967
|
-
out.push(
|
|
968
|
-
`${options.indent}${dim("\u2502")} ${padRight(wrappedLine, width)} ${dim(
|
|
969
|
-
"\u2502"
|
|
970
|
-
)}`
|
|
971
|
-
);
|
|
946
|
+
out.push(`${options.indent}${dim("\u2502")} ${padRight(wrappedLine, width)} ${dim("\u2502")}`);
|
|
972
947
|
}
|
|
973
948
|
}
|
|
974
|
-
out.push(
|
|
975
|
-
`${options.indent}${dim(
|
|
976
|
-
`${bottomLeft}${"\u2500".repeat(bottomWidth)}${bottomRight}`
|
|
977
|
-
)}`
|
|
978
|
-
);
|
|
949
|
+
out.push(`${options.indent}${dim(`${bottomLeft}${"\u2500".repeat(bottomWidth)}${bottomRight}`)}`);
|
|
979
950
|
return out.join("\n");
|
|
980
951
|
}
|
|
981
952
|
function toSelectedRows(rows, selectedIds) {
|
|
@@ -1022,11 +993,7 @@ function resolveSelectionPanelLayout(options) {
|
|
|
1022
993
|
options.minWidth ?? DEFAULT_PANEL_MIN_WIDTH,
|
|
1023
994
|
indent
|
|
1024
995
|
);
|
|
1025
|
-
const columns = resolveSelectionColumnWidths(
|
|
1026
|
-
width,
|
|
1027
|
-
options.labelWidth,
|
|
1028
|
-
options.descWidth
|
|
1029
|
-
);
|
|
996
|
+
const columns = resolveSelectionColumnWidths(width, options.labelWidth, options.descWidth);
|
|
1030
997
|
return {
|
|
1031
998
|
width,
|
|
1032
999
|
labelWidth: columns.labelWidth,
|
|
@@ -1045,10 +1012,7 @@ function renderSectionToText(section) {
|
|
|
1045
1012
|
switch (section.type) {
|
|
1046
1013
|
case "banner": {
|
|
1047
1014
|
const logoLines = getBannerLogoLines();
|
|
1048
|
-
const widestLogoLine = logoLines.reduce(
|
|
1049
|
-
(max, line) => Math.max(max, visibleLength(line)),
|
|
1050
|
-
0
|
|
1051
|
-
);
|
|
1015
|
+
const widestLogoLine = logoLines.reduce((max, line) => Math.max(max, visibleLength(line)), 0);
|
|
1052
1016
|
const resolvedWidth = clampToTerminalInnerWidth(
|
|
1053
1017
|
Math.max(
|
|
1054
1018
|
section.width ?? DEFAULT_BANNER_WIDTH,
|
|
@@ -1075,7 +1039,7 @@ function renderSectionToText(section) {
|
|
|
1075
1039
|
return renderPanelText(section);
|
|
1076
1040
|
}
|
|
1077
1041
|
case "source":
|
|
1078
|
-
return finalizeUiBlock(`
|
|
1042
|
+
return finalizeUiBlock(` ${section.source}`);
|
|
1079
1043
|
case "lines":
|
|
1080
1044
|
return finalizeUiBlock(section.lines.join("\n"));
|
|
1081
1045
|
case "text":
|
|
@@ -1096,10 +1060,7 @@ function HistoryText({ text }) {
|
|
|
1096
1060
|
return /* @__PURE__ */ jsx(Text, { children: text.replace(/\n$/, "") });
|
|
1097
1061
|
}
|
|
1098
1062
|
function renderPinnedLogoHeaderText(lines) {
|
|
1099
|
-
const widestLogoLine = lines.reduce(
|
|
1100
|
-
(max, line) => Math.max(max, visibleLength(line)),
|
|
1101
|
-
0
|
|
1102
|
-
);
|
|
1063
|
+
const widestLogoLine = lines.reduce((max, line) => Math.max(max, visibleLength(line)), 0);
|
|
1103
1064
|
const resolvedWidth = clampToTerminalInnerWidth(
|
|
1104
1065
|
Math.max(PINNED_LOGO_HEADER_WIDTH, widestLogoLine),
|
|
1105
1066
|
Math.max(1, widestLogoLine)
|
|
@@ -1271,10 +1232,7 @@ function renderSectionToTextWithLogoLines(section, logoLinesOverride) {
|
|
|
1271
1232
|
return renderSectionToText(section);
|
|
1272
1233
|
}
|
|
1273
1234
|
const logoLines = logoLinesOverride ?? getBannerLogoLines();
|
|
1274
|
-
const widestLogoLine = logoLines.reduce(
|
|
1275
|
-
(max, line) => Math.max(max, visibleLength(line)),
|
|
1276
|
-
0
|
|
1277
|
-
);
|
|
1235
|
+
const widestLogoLine = logoLines.reduce((max, line) => Math.max(max, visibleLength(line)), 0);
|
|
1278
1236
|
const resolvedWidth = clampToTerminalInnerWidth(
|
|
1279
1237
|
Math.max(
|
|
1280
1238
|
section.width ?? DEFAULT_BANNER_WIDTH,
|
|
@@ -1290,11 +1248,7 @@ function renderSectionToTextWithLogoLines(section, logoLinesOverride) {
|
|
|
1290
1248
|
center(section.title, resolvedWidth)
|
|
1291
1249
|
];
|
|
1292
1250
|
return finalizeUiBlock(
|
|
1293
|
-
[
|
|
1294
|
-
`\u256D${border}\u256E`,
|
|
1295
|
-
...bodyLines.map((line) => `\u2502${line}\u2502`),
|
|
1296
|
-
`\u2570${border}\u256F`
|
|
1297
|
-
].join("\n")
|
|
1251
|
+
[`\u256D${border}\u256E`, ...bodyLines.map((line) => `\u2502${line}\u2502`), `\u2570${border}\u256F`].join("\n")
|
|
1298
1252
|
);
|
|
1299
1253
|
}
|
|
1300
1254
|
function freezeBannerSections(sections, logoLines) {
|
|
@@ -1350,17 +1304,13 @@ function statusStepsSection(steps) {
|
|
|
1350
1304
|
);
|
|
1351
1305
|
}
|
|
1352
1306
|
function completedStepsSection(steps) {
|
|
1353
|
-
return statusStepsSection(
|
|
1354
|
-
steps.map((step) => ({ status: "completed", label: step }))
|
|
1355
|
-
);
|
|
1307
|
+
return statusStepsSection(steps.map((step) => ({ status: "completed", label: step })));
|
|
1356
1308
|
}
|
|
1357
1309
|
function failedStepsSection(steps) {
|
|
1358
|
-
return statusStepsSection(
|
|
1359
|
-
steps.map((step) => ({ status: "failed", label: step }))
|
|
1360
|
-
);
|
|
1310
|
+
return statusStepsSection(steps.map((step) => ({ status: "failed", label: step })));
|
|
1361
1311
|
}
|
|
1362
|
-
function sourceSection(source) {
|
|
1363
|
-
return { type: "source", source };
|
|
1312
|
+
function sourceSection(source, label = "Skills source") {
|
|
1313
|
+
return { type: "source", source: `${label}: ${source}` };
|
|
1364
1314
|
}
|
|
1365
1315
|
function completionSummarySection(options) {
|
|
1366
1316
|
const skillLabel = options.skillCount === 1 ? "skill" : "skills";
|
|
@@ -1383,10 +1333,7 @@ function installationSummarySection(options) {
|
|
|
1383
1333
|
)} ${bold("Agents:")} ${colorToken(
|
|
1384
1334
|
options.agentCount.toString(),
|
|
1385
1335
|
"primary"
|
|
1386
|
-
)} ${bold("Targets:")} ${colorToken(
|
|
1387
|
-
options.targetCount.toString(),
|
|
1388
|
-
"primary"
|
|
1389
|
-
)}`,
|
|
1336
|
+
)} ${bold("Targets:")} ${colorToken(options.targetCount.toString(), "primary")}`,
|
|
1390
1337
|
"",
|
|
1391
1338
|
bold("Targets:")
|
|
1392
1339
|
];
|
|
@@ -1411,13 +1358,14 @@ function installationSummarySection(options) {
|
|
|
1411
1358
|
});
|
|
1412
1359
|
}
|
|
1413
1360
|
function uninstallSummarySection(options) {
|
|
1361
|
+
const itemLabel = options.itemLabel || "Skills";
|
|
1414
1362
|
return panelSection({
|
|
1415
1363
|
title: "Uninstall Summary",
|
|
1416
1364
|
lines: [
|
|
1417
1365
|
`Scope: ${options.globalInstall ? "global" : "current project"}`,
|
|
1418
1366
|
"",
|
|
1419
|
-
|
|
1420
|
-
...options.
|
|
1367
|
+
`${itemLabel} (${options.itemNames.length}):`,
|
|
1368
|
+
...options.itemNames.map((name) => ` - ${name}`),
|
|
1421
1369
|
"",
|
|
1422
1370
|
`Agents (${options.agentDisplayNames.length}): ${compactAgentDisplayNames(
|
|
1423
1371
|
[...options.agentDisplayNames].sort((a, b) => a.localeCompare(b)),
|
|
@@ -1534,9 +1482,7 @@ function Spinner({ label }) {
|
|
|
1534
1482
|
return /* @__PURE__ */ jsx(Text, { children: ` ${colorToken(frames[frameIndex], "primary")} ${label}` });
|
|
1535
1483
|
}
|
|
1536
1484
|
|
|
1537
|
-
// src/ui/selection-step.tsx
|
|
1538
|
-
import { useMemo, useState as useState2 } from "react";
|
|
1539
|
-
import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
|
|
1485
|
+
// ../../packages/cli-shared/src/ui/selection-step.tsx
|
|
1540
1486
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1541
1487
|
var DEFAULT_REQUIRED_MESSAGE = "At least one choice must be selected";
|
|
1542
1488
|
function assertPromptAllowed(shouldPrompt, interactive) {
|
|
@@ -1550,9 +1496,7 @@ function filterSelectionRowIndexes(rows, searchTerm) {
|
|
|
1550
1496
|
return rows.map((_, index) => index);
|
|
1551
1497
|
}
|
|
1552
1498
|
return rows.reduce((acc, row, index) => {
|
|
1553
|
-
const haystack = `${row.label} ${row.description || ""}`.toLocaleLowerCase(
|
|
1554
|
-
"en-US"
|
|
1555
|
-
);
|
|
1499
|
+
const haystack = `${row.label} ${row.description || ""}`.toLocaleLowerCase("en-US");
|
|
1556
1500
|
if (haystack.includes(normalized)) {
|
|
1557
1501
|
acc.push(index);
|
|
1558
1502
|
}
|
|
@@ -1603,6 +1547,31 @@ function toVisibleRows(model) {
|
|
|
1603
1547
|
};
|
|
1604
1548
|
});
|
|
1605
1549
|
}
|
|
1550
|
+
function resolveVisibleSelectionViewport(rows, activeIndex, maxVisibleRows) {
|
|
1551
|
+
const limit = Math.max(0, Math.floor(maxVisibleRows ?? 0));
|
|
1552
|
+
if (limit === 0 || rows.length <= limit) {
|
|
1553
|
+
return {
|
|
1554
|
+
rows,
|
|
1555
|
+
truncatedTop: false,
|
|
1556
|
+
truncatedBottom: false
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
const clampedActiveIndex = clampActiveVisibleIndex(activeIndex, rows.length);
|
|
1560
|
+
const maxStart = rows.length - limit;
|
|
1561
|
+
let start = clampedActiveIndex - Math.floor(limit / 2);
|
|
1562
|
+
if (start < 0) {
|
|
1563
|
+
start = 0;
|
|
1564
|
+
}
|
|
1565
|
+
if (start > maxStart) {
|
|
1566
|
+
start = maxStart;
|
|
1567
|
+
}
|
|
1568
|
+
const end = start + limit;
|
|
1569
|
+
return {
|
|
1570
|
+
rows: rows.slice(start, end),
|
|
1571
|
+
truncatedTop: start > 0,
|
|
1572
|
+
truncatedBottom: end < rows.length
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1606
1575
|
function buildRenderModel(options) {
|
|
1607
1576
|
return {
|
|
1608
1577
|
title: options.request.title,
|
|
@@ -1611,9 +1580,7 @@ function buildRenderModel(options) {
|
|
|
1611
1580
|
label: row.label,
|
|
1612
1581
|
description: row.description
|
|
1613
1582
|
})),
|
|
1614
|
-
visibleRowIds: options.visibleIndexes.map(
|
|
1615
|
-
(index) => options.rows[index].id
|
|
1616
|
-
),
|
|
1583
|
+
visibleRowIds: options.visibleIndexes.map((index) => options.rows[index].id),
|
|
1617
1584
|
activeVisibleIndex: options.activeVisibleIndex,
|
|
1618
1585
|
selectedIds: options.selectedIds ?? selectedRowIds(options.rows),
|
|
1619
1586
|
searchable: Boolean(options.request.searchable),
|
|
@@ -1630,7 +1597,11 @@ function appendSearchChar(current, text) {
|
|
|
1630
1597
|
return normalizeSearchTerm(`${current}${text}`);
|
|
1631
1598
|
}
|
|
1632
1599
|
function renderManySelectionOpenPanel(config, model) {
|
|
1633
|
-
const
|
|
1600
|
+
const viewport = resolveVisibleSelectionViewport(
|
|
1601
|
+
toVisibleRows(model),
|
|
1602
|
+
model.activeVisibleIndex,
|
|
1603
|
+
config.maxVisibleRows
|
|
1604
|
+
);
|
|
1634
1605
|
const hintLine = selectionHintsText(
|
|
1635
1606
|
model.keyHints.length > 0 ? model.keyHints : config.defaultHints
|
|
1636
1607
|
);
|
|
@@ -1658,10 +1629,13 @@ function renderManySelectionOpenPanel(config, model) {
|
|
|
1658
1629
|
lines.push(`Search: ${model.searchTerm}`);
|
|
1659
1630
|
lines.push("");
|
|
1660
1631
|
}
|
|
1661
|
-
if (
|
|
1632
|
+
if (viewport.rows.length === 0) {
|
|
1662
1633
|
lines.push(" (no matches)");
|
|
1663
1634
|
} else {
|
|
1664
|
-
|
|
1635
|
+
if (viewport.truncatedTop) {
|
|
1636
|
+
lines.push(" ...");
|
|
1637
|
+
}
|
|
1638
|
+
for (const row of viewport.rows) {
|
|
1665
1639
|
const prefix = ` ${row.active ? "\u203A" : " "} `;
|
|
1666
1640
|
const marker = row.selected ? colorToken("\u25CF", "primary") : "\u25CB";
|
|
1667
1641
|
const content = formatSelectionDisplayLine({
|
|
@@ -1674,6 +1648,9 @@ function renderManySelectionOpenPanel(config, model) {
|
|
|
1674
1648
|
const renderedRow = `${prefix}${marker} ${row.active || row.selected ? content : dim(content)}`;
|
|
1675
1649
|
lines.push(renderedRow);
|
|
1676
1650
|
}
|
|
1651
|
+
if (viewport.truncatedBottom) {
|
|
1652
|
+
lines.push(" ...");
|
|
1653
|
+
}
|
|
1677
1654
|
}
|
|
1678
1655
|
lines.push("");
|
|
1679
1656
|
lines.push(` ${hintLine}`);
|
|
@@ -1689,14 +1666,8 @@ function renderManySelectionOpenPanel(config, model) {
|
|
|
1689
1666
|
}
|
|
1690
1667
|
function renderSingleSelectionOpenPanel(config, model) {
|
|
1691
1668
|
const visibleRows = toVisibleRows(model);
|
|
1692
|
-
const maxLabelWidth = visibleRows.reduce(
|
|
1693
|
-
|
|
1694
|
-
8
|
|
1695
|
-
);
|
|
1696
|
-
const maxDescWidth = visibleRows.reduce(
|
|
1697
|
-
(max, row) => Math.max(max, row.description.length),
|
|
1698
|
-
0
|
|
1699
|
-
);
|
|
1669
|
+
const maxLabelWidth = visibleRows.reduce((max, row) => Math.max(max, row.label.length), 8);
|
|
1670
|
+
const maxDescWidth = visibleRows.reduce((max, row) => Math.max(max, row.description.length), 0);
|
|
1700
1671
|
const layout = resolveSelectionPanelLayout({
|
|
1701
1672
|
title: config.title,
|
|
1702
1673
|
staticLines: [config.instructionLine, "\u2191\u2193 navigate enter confirm"],
|
|
@@ -1731,9 +1702,7 @@ function SelectionRenderer({ content }) {
|
|
|
1731
1702
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: /* @__PURE__ */ jsx2(Text2, { children: content.replace(/\n$/, "") }) });
|
|
1732
1703
|
}
|
|
1733
1704
|
function MultiSelectPrompt(props) {
|
|
1734
|
-
const initialSelectedIds = new Set(
|
|
1735
|
-
props.options.initialSelectedIds || props.selectedIds || []
|
|
1736
|
-
);
|
|
1705
|
+
const initialSelectedIds = new Set(props.options.initialSelectedIds || props.selectedIds || []);
|
|
1737
1706
|
const [rows, setRows] = useState2(
|
|
1738
1707
|
props.rows.map((row) => ({
|
|
1739
1708
|
...row,
|
|
@@ -1744,16 +1713,10 @@ function MultiSelectPrompt(props) {
|
|
|
1744
1713
|
const [activeVisibleIndex, setActiveVisibleIndex] = useState2(0);
|
|
1745
1714
|
const [errorMessage, setErrorMessage] = useState2();
|
|
1746
1715
|
const visibleIndexes = useMemo(
|
|
1747
|
-
() => filterSelectionRowIndexes(
|
|
1748
|
-
rows,
|
|
1749
|
-
props.options.searchable !== false ? searchTerm : ""
|
|
1750
|
-
),
|
|
1716
|
+
() => filterSelectionRowIndexes(rows, props.options.searchable !== false ? searchTerm : ""),
|
|
1751
1717
|
[rows, searchTerm, props.options.searchable]
|
|
1752
1718
|
);
|
|
1753
|
-
const clampedIndex = clampActiveVisibleIndex(
|
|
1754
|
-
activeVisibleIndex,
|
|
1755
|
-
visibleIndexes.length
|
|
1756
|
-
);
|
|
1719
|
+
const clampedIndex = clampActiveVisibleIndex(activeVisibleIndex, visibleIndexes.length);
|
|
1757
1720
|
useInput2((input, key) => {
|
|
1758
1721
|
if (key.ctrl && input === "c") {
|
|
1759
1722
|
props.onCancel(new PromptCancelledError());
|
|
@@ -1765,9 +1728,7 @@ function MultiSelectPrompt(props) {
|
|
|
1765
1728
|
}
|
|
1766
1729
|
if (key.upArrow) {
|
|
1767
1730
|
if (visibleIndexes.length > 0) {
|
|
1768
|
-
setActiveVisibleIndex(
|
|
1769
|
-
(clampedIndex - 1 + visibleIndexes.length) % visibleIndexes.length
|
|
1770
|
-
);
|
|
1731
|
+
setActiveVisibleIndex((clampedIndex - 1 + visibleIndexes.length) % visibleIndexes.length);
|
|
1771
1732
|
}
|
|
1772
1733
|
setErrorMessage(void 0);
|
|
1773
1734
|
return;
|
|
@@ -1780,9 +1741,7 @@ function MultiSelectPrompt(props) {
|
|
|
1780
1741
|
return;
|
|
1781
1742
|
}
|
|
1782
1743
|
if (input === " ") {
|
|
1783
|
-
setRows(
|
|
1784
|
-
(prev) => toggleSelectionAtVisibleIndex(prev, visibleIndexes, clampedIndex)
|
|
1785
|
-
);
|
|
1744
|
+
setRows((prev) => toggleSelectionAtVisibleIndex(prev, visibleIndexes, clampedIndex));
|
|
1786
1745
|
setErrorMessage(void 0);
|
|
1787
1746
|
return;
|
|
1788
1747
|
}
|
|
@@ -1810,9 +1769,7 @@ function MultiSelectPrompt(props) {
|
|
|
1810
1769
|
if (key.return) {
|
|
1811
1770
|
const selectedIds = selectedRowIds(rows);
|
|
1812
1771
|
if ((props.options.required ?? true) && selectedIds.length === 0) {
|
|
1813
|
-
setErrorMessage(
|
|
1814
|
-
props.options.requiredMessage || DEFAULT_REQUIRED_MESSAGE
|
|
1815
|
-
);
|
|
1772
|
+
setErrorMessage(props.options.requiredMessage || DEFAULT_REQUIRED_MESSAGE);
|
|
1816
1773
|
return;
|
|
1817
1774
|
}
|
|
1818
1775
|
props.onSubmit(selectedIds);
|
|
@@ -1875,9 +1832,7 @@ function SingleSelectPrompt(props) {
|
|
|
1875
1832
|
if (key.return) {
|
|
1876
1833
|
const selectedId2 = props.rows[activeVisibleIndex]?.id || "";
|
|
1877
1834
|
if ((props.options.required ?? true) && !selectedId2) {
|
|
1878
|
-
setErrorMessage(
|
|
1879
|
-
props.options.requiredMessage || DEFAULT_REQUIRED_MESSAGE
|
|
1880
|
-
);
|
|
1835
|
+
setErrorMessage(props.options.requiredMessage || DEFAULT_REQUIRED_MESSAGE);
|
|
1881
1836
|
return;
|
|
1882
1837
|
}
|
|
1883
1838
|
props.onSubmit(selectedId2);
|
|
@@ -1959,7 +1914,148 @@ async function runOneSelectionStep(options) {
|
|
|
1959
1914
|
return selectedId;
|
|
1960
1915
|
}
|
|
1961
1916
|
|
|
1962
|
-
// src/
|
|
1917
|
+
// ../../packages/cli-shared/src/add-command.ts
|
|
1918
|
+
function parseAddLockFormatValue(value) {
|
|
1919
|
+
if (!value) {
|
|
1920
|
+
return void 0;
|
|
1921
|
+
}
|
|
1922
|
+
if (value !== "json" && value !== "yaml") {
|
|
1923
|
+
throw new Error(`Invalid --lock-format value: ${value}`);
|
|
1924
|
+
}
|
|
1925
|
+
return value;
|
|
1926
|
+
}
|
|
1927
|
+
function buildBaseAddOptions(input, presence = {}) {
|
|
1928
|
+
const maxDownloadBytes = input.maxDownloadBytes ? Number(input.maxDownloadBytes) : void 0;
|
|
1929
|
+
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
1930
|
+
throw new Error(`Invalid --max-download-bytes value: ${input.maxDownloadBytes}`);
|
|
1931
|
+
}
|
|
1932
|
+
const parsed = {
|
|
1933
|
+
global: Boolean(input.global),
|
|
1934
|
+
symlink: Boolean(input.symlink),
|
|
1935
|
+
yaml: Boolean(input.yaml),
|
|
1936
|
+
list: Boolean(input.list),
|
|
1937
|
+
all: Boolean(input.all),
|
|
1938
|
+
nonInteractive: Boolean(input.nonInteractive),
|
|
1939
|
+
trustWellKnown: Boolean(input.trustWellKnown),
|
|
1940
|
+
agent: normalizeAgentSelectionInput(input.agent),
|
|
1941
|
+
skill: input.selectedNames,
|
|
1942
|
+
allowHost: input.allowHost?.map((item) => item.toLowerCase()),
|
|
1943
|
+
denyHost: input.denyHost?.map((item) => item.toLowerCase()),
|
|
1944
|
+
maxDownloadBytes,
|
|
1945
|
+
policyMode: input.policyMode,
|
|
1946
|
+
lockFormat: parseAddLockFormatValue(input.lockFormat),
|
|
1947
|
+
experimental: input.experimental ?? false
|
|
1948
|
+
};
|
|
1949
|
+
if (parsed.agent && parsed.agent.length > 0) {
|
|
1950
|
+
parsed.agentFlagProvided = true;
|
|
1951
|
+
}
|
|
1952
|
+
if (presence.agentProvided) {
|
|
1953
|
+
parsed.agentFlagProvided = true;
|
|
1954
|
+
}
|
|
1955
|
+
if (presence.globalProvided) {
|
|
1956
|
+
parsed.globalFlagProvided = true;
|
|
1957
|
+
}
|
|
1958
|
+
if (presence.symlinkProvided) {
|
|
1959
|
+
parsed.symlinkFlagProvided = true;
|
|
1960
|
+
}
|
|
1961
|
+
if (parsed.all) {
|
|
1962
|
+
parsed.skill = ["*"];
|
|
1963
|
+
parsed.agent = ["*"];
|
|
1964
|
+
}
|
|
1965
|
+
return parsed;
|
|
1966
|
+
}
|
|
1967
|
+
function buildNamedAddSelectionRows(items) {
|
|
1968
|
+
return items.map((item) => ({
|
|
1969
|
+
id: item.name,
|
|
1970
|
+
label: item.name,
|
|
1971
|
+
description: item.description
|
|
1972
|
+
}));
|
|
1973
|
+
}
|
|
1974
|
+
function filterNamedAddSelection(items, requested) {
|
|
1975
|
+
if (!requested || requested.length === 0) {
|
|
1976
|
+
return [...items];
|
|
1977
|
+
}
|
|
1978
|
+
if (requested.includes("*")) {
|
|
1979
|
+
return [...items];
|
|
1980
|
+
}
|
|
1981
|
+
const wanted = new Set(requested.map((item) => item.toLowerCase()));
|
|
1982
|
+
return items.filter((item) => wanted.has(item.name.toLowerCase()));
|
|
1983
|
+
}
|
|
1984
|
+
async function resolveNamedAddSelection(options) {
|
|
1985
|
+
const {
|
|
1986
|
+
available,
|
|
1987
|
+
interactive,
|
|
1988
|
+
listMode,
|
|
1989
|
+
requested,
|
|
1990
|
+
rows,
|
|
1991
|
+
keyHints,
|
|
1992
|
+
view,
|
|
1993
|
+
promptTitle,
|
|
1994
|
+
requiredMessage,
|
|
1995
|
+
emptyMessage,
|
|
1996
|
+
multipleInNonInteractiveMessage,
|
|
1997
|
+
renderClosed
|
|
1998
|
+
} = options;
|
|
1999
|
+
if (listMode) {
|
|
2000
|
+
return filterNamedAddSelection(available, requested);
|
|
2001
|
+
}
|
|
2002
|
+
const prompt = {
|
|
2003
|
+
title: promptTitle,
|
|
2004
|
+
required: true,
|
|
2005
|
+
requiredMessage,
|
|
2006
|
+
searchable: true,
|
|
2007
|
+
keyHints: [...keyHints],
|
|
2008
|
+
view
|
|
2009
|
+
};
|
|
2010
|
+
if (requested) {
|
|
2011
|
+
const selected = filterNamedAddSelection(available, requested);
|
|
2012
|
+
if (selected.length > 0) {
|
|
2013
|
+
await runManySelectionStep({
|
|
2014
|
+
interactive,
|
|
2015
|
+
rows: [...rows],
|
|
2016
|
+
selectedIds: selected.map((item) => item.name),
|
|
2017
|
+
shouldPrompt: false,
|
|
2018
|
+
prompt,
|
|
2019
|
+
renderClosed
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
return selected;
|
|
2023
|
+
}
|
|
2024
|
+
if (available.length === 0) {
|
|
2025
|
+
throw new Error(emptyMessage);
|
|
2026
|
+
}
|
|
2027
|
+
if (available.length === 1) {
|
|
2028
|
+
await runManySelectionStep({
|
|
2029
|
+
interactive,
|
|
2030
|
+
rows: [...rows],
|
|
2031
|
+
selectedIds: [available[0].name],
|
|
2032
|
+
shouldPrompt: false,
|
|
2033
|
+
prompt,
|
|
2034
|
+
renderClosed
|
|
2035
|
+
});
|
|
2036
|
+
return [...available];
|
|
2037
|
+
}
|
|
2038
|
+
if (!interactive) {
|
|
2039
|
+
throw new Error(multipleInNonInteractiveMessage);
|
|
2040
|
+
}
|
|
2041
|
+
const selectedNames = await runManySelectionStep({
|
|
2042
|
+
interactive,
|
|
2043
|
+
rows: [...rows],
|
|
2044
|
+
shouldPrompt: true,
|
|
2045
|
+
prompt,
|
|
2046
|
+
renderClosed
|
|
2047
|
+
});
|
|
2048
|
+
const wanted = new Set(selectedNames);
|
|
2049
|
+
return available.filter((item) => wanted.has(item.name));
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
// ../../packages/core/src/runtime/installer.ts
|
|
2053
|
+
function sanitizeSkillName(name) {
|
|
2054
|
+
const sanitized = name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[.-]+|[.-]+$/g, "");
|
|
2055
|
+
return sanitized || "unnamed-skill";
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
// ../../packages/cli-shared/src/interactive.ts
|
|
1963
2059
|
function isPromptCancelledError(error) {
|
|
1964
2060
|
return error instanceof PromptCancelledError;
|
|
1965
2061
|
}
|
|
@@ -1997,7 +2093,7 @@ function parsePolicyMode(value) {
|
|
|
1997
2093
|
throw new Error(`Invalid --policy-mode value: ${value}`);
|
|
1998
2094
|
}
|
|
1999
2095
|
|
|
2000
|
-
// src/command-builder.ts
|
|
2096
|
+
// ../../packages/cli-shared/src/command-builder.ts
|
|
2001
2097
|
import { CommanderError } from "commander";
|
|
2002
2098
|
|
|
2003
2099
|
// ../../packages/core/src/runtime/telemetry.ts
|
|
@@ -2034,8 +2130,8 @@ function emitLifecycleEvent(emitter, event) {
|
|
|
2034
2130
|
emitter.publish(row);
|
|
2035
2131
|
}
|
|
2036
2132
|
|
|
2037
|
-
// src/command-builder.ts
|
|
2038
|
-
function createCliCommandContext(emitter, options) {
|
|
2133
|
+
// ../../packages/cli-shared/src/command-builder.ts
|
|
2134
|
+
function createCliCommandContext(emitter, options = {}) {
|
|
2039
2135
|
const emitCommandEvent = (command, event) => {
|
|
2040
2136
|
emitLifecycleEvent(emitter, {
|
|
2041
2137
|
eventType: event.eventType,
|
|
@@ -2048,7 +2144,7 @@ function createCliCommandContext(emitter, options) {
|
|
|
2048
2144
|
});
|
|
2049
2145
|
};
|
|
2050
2146
|
return {
|
|
2051
|
-
experimental: options.experimental,
|
|
2147
|
+
experimental: Boolean(options.experimental),
|
|
2052
2148
|
emitCommandEvent,
|
|
2053
2149
|
wrapAction: (command, action) => async (...args) => {
|
|
2054
2150
|
emitCommandEvent(command, {
|
|
@@ -2075,12 +2171,52 @@ function createCliCommandContext(emitter, options) {
|
|
|
2075
2171
|
}
|
|
2076
2172
|
};
|
|
2077
2173
|
}
|
|
2174
|
+
function applyExitOverride(command) {
|
|
2175
|
+
command.exitOverride((error) => {
|
|
2176
|
+
throw error;
|
|
2177
|
+
});
|
|
2178
|
+
for (const subcommand of command.commands) {
|
|
2179
|
+
applyExitOverride(subcommand);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
function isGracefulCommanderExit(error) {
|
|
2183
|
+
return error instanceof CommanderError && (error.code === "commander.helpDisplayed" || error.code === "commander.version");
|
|
2184
|
+
}
|
|
2185
|
+
function inferCommandSource(argv, options = {}) {
|
|
2186
|
+
const valueFlags = new Set(options.valueFlags ?? []);
|
|
2187
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
2188
|
+
const arg = argv[i];
|
|
2189
|
+
if (valueFlags.has(arg)) {
|
|
2190
|
+
i += 1;
|
|
2191
|
+
continue;
|
|
2192
|
+
}
|
|
2193
|
+
if (arg.startsWith("-")) {
|
|
2194
|
+
continue;
|
|
2195
|
+
}
|
|
2196
|
+
return arg;
|
|
2197
|
+
}
|
|
2198
|
+
return options.fallbackSource ?? "cli";
|
|
2199
|
+
}
|
|
2200
|
+
function emitCommanderParseErrorTelemetry(emitter, argv, error, options = {}) {
|
|
2201
|
+
const source = inferCommandSource(argv, options);
|
|
2202
|
+
emitLifecycleEvent(emitter, {
|
|
2203
|
+
eventType: `${source}_failed`,
|
|
2204
|
+
source,
|
|
2205
|
+
reason: "commander_parse_error",
|
|
2206
|
+
command: source,
|
|
2207
|
+
status: "error",
|
|
2208
|
+
error: error.message,
|
|
2209
|
+
metadata: {
|
|
2210
|
+
commanderCode: error.code
|
|
2211
|
+
}
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2078
2214
|
|
|
2079
2215
|
// ../../packages/platform-node/src/background-runner.ts
|
|
2080
2216
|
import fs4 from "node:fs";
|
|
2081
|
-
import
|
|
2217
|
+
import path5 from "node:path";
|
|
2082
2218
|
import { spawn } from "node:child_process";
|
|
2083
|
-
import { fileURLToPath
|
|
2219
|
+
import { fileURLToPath } from "node:url";
|
|
2084
2220
|
var activeChildren = /* @__PURE__ */ new Set();
|
|
2085
2221
|
var cleanupHandlersInstalled = false;
|
|
2086
2222
|
function cleanupActiveChildren() {
|
|
@@ -2110,12 +2246,12 @@ function installCleanupHandlers() {
|
|
|
2110
2246
|
}
|
|
2111
2247
|
}
|
|
2112
2248
|
function resolveWorkerEntry() {
|
|
2113
|
-
const dir =
|
|
2114
|
-
const tsPath =
|
|
2249
|
+
const dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
2250
|
+
const tsPath = path5.join(dir, "background-worker.ts");
|
|
2115
2251
|
if (fs4.existsSync(tsPath)) {
|
|
2116
2252
|
return tsPath;
|
|
2117
2253
|
}
|
|
2118
|
-
return
|
|
2254
|
+
return path5.join(dir, "background-worker.js");
|
|
2119
2255
|
}
|
|
2120
2256
|
function appendOutputChunk(chunks, chunk) {
|
|
2121
2257
|
const next = chunk.toString();
|
|
@@ -2131,18 +2267,14 @@ async function runBackgroundTask(request, options) {
|
|
|
2131
2267
|
installCleanupHandlers();
|
|
2132
2268
|
return new Promise((resolve, reject) => {
|
|
2133
2269
|
const childCwd = typeof request === "object" && request !== null && "payload" in request && typeof request.payload?.cwd === "string" ? request.payload.cwd : process.cwd();
|
|
2134
|
-
const child = spawn(
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
},
|
|
2143
|
-
stdio: ["ignore", "pipe", "pipe", "ipc"]
|
|
2144
|
-
}
|
|
2145
|
-
);
|
|
2270
|
+
const child = spawn(process.execPath, [...process.execArgv, resolveWorkerEntry()], {
|
|
2271
|
+
cwd: childCwd,
|
|
2272
|
+
env: {
|
|
2273
|
+
...process.env,
|
|
2274
|
+
SKILLSPP_BG_EXECUTOR: options.executorModule
|
|
2275
|
+
},
|
|
2276
|
+
stdio: ["ignore", "pipe", "pipe", "ipc"]
|
|
2277
|
+
});
|
|
2146
2278
|
activeChildren.add(child);
|
|
2147
2279
|
const stdoutChunks = [];
|
|
2148
2280
|
const stderrChunks = [];
|
|
@@ -2204,13 +2336,34 @@ async function runBackgroundTask(request, options) {
|
|
|
2204
2336
|
});
|
|
2205
2337
|
}
|
|
2206
2338
|
|
|
2207
|
-
// src/runtime/background-runner.ts
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2339
|
+
// ../../packages/cli-shared/src/runtime/background-runner.ts
|
|
2340
|
+
import fs5 from "node:fs";
|
|
2341
|
+
import path6 from "node:path";
|
|
2342
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
2343
|
+
function resolveExecutorModule(importMetaUrl) {
|
|
2344
|
+
const runtimeDir = path6.dirname(fileURLToPath2(importMetaUrl));
|
|
2345
|
+
const localCandidates = [
|
|
2346
|
+
path6.join(runtimeDir, "background-executor.js"),
|
|
2347
|
+
path6.join(runtimeDir, "background-executor.ts")
|
|
2348
|
+
];
|
|
2349
|
+
for (const candidate of localCandidates) {
|
|
2350
|
+
if (fs5.existsSync(candidate)) {
|
|
2351
|
+
return pathToFileURL(candidate).href;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
return "@skillspp/core/runtime/background-tasks";
|
|
2213
2355
|
}
|
|
2356
|
+
function createBackgroundTaskRunner(importMetaUrl) {
|
|
2357
|
+
return async function runBackgroundTask3(request, options = {}) {
|
|
2358
|
+
return runBackgroundTask(request, {
|
|
2359
|
+
onProgress: options.onProgress,
|
|
2360
|
+
executorModule: resolveExecutorModule(importMetaUrl)
|
|
2361
|
+
});
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
// src/runtime/background-runner.ts
|
|
2366
|
+
var runBackgroundTask2 = createBackgroundTaskRunner(import.meta.url);
|
|
2214
2367
|
|
|
2215
2368
|
// src/commands/add.ts
|
|
2216
2369
|
var SKILL_NAME_WIDTH = 32;
|
|
@@ -2238,6 +2391,7 @@ var ADD_SKILLS_SELECTION_VIEW = {
|
|
|
2238
2391
|
instructionLine: "Select skills (space to toggle)",
|
|
2239
2392
|
labelWidth: SKILL_NAME_WIDTH,
|
|
2240
2393
|
descWidth: SKILL_DESC_WIDTH,
|
|
2394
|
+
maxVisibleRows: 10,
|
|
2241
2395
|
minWidth: 74,
|
|
2242
2396
|
defaultHints: ADD_SKILLS_KEY_HINTS
|
|
2243
2397
|
};
|
|
@@ -2247,6 +2401,7 @@ var ADD_AGENTS_SELECTION_VIEW = {
|
|
|
2247
2401
|
instructionLine: "Select agents (space to toggle)",
|
|
2248
2402
|
labelWidth: AGENT_NAME_WIDTH,
|
|
2249
2403
|
descWidth: AGENT_DESC_WIDTH,
|
|
2404
|
+
maxVisibleRows: 10,
|
|
2250
2405
|
minWidth: 74,
|
|
2251
2406
|
defaultHints: ADD_AGENTS_KEY_HINTS
|
|
2252
2407
|
};
|
|
@@ -2267,17 +2422,10 @@ var ADD_SCOPE_SELECTION_ROWS = [
|
|
|
2267
2422
|
description: "Install into home-directory skills directories"
|
|
2268
2423
|
}
|
|
2269
2424
|
];
|
|
2270
|
-
function buildAddSkillSelectionRows(skills) {
|
|
2271
|
-
return skills.map((skill) => ({
|
|
2272
|
-
id: skill.name,
|
|
2273
|
-
label: skill.name,
|
|
2274
|
-
description: skill.description
|
|
2275
|
-
}));
|
|
2276
|
-
}
|
|
2277
2425
|
function renderAddSkillsSection(options) {
|
|
2278
2426
|
return manySelectionClosedSection(
|
|
2279
2427
|
ADD_SKILLS_SELECTION_VIEW,
|
|
2280
|
-
|
|
2428
|
+
buildNamedAddSelectionRows(options.skills),
|
|
2281
2429
|
options.selectedNames
|
|
2282
2430
|
);
|
|
2283
2431
|
}
|
|
@@ -2294,11 +2442,7 @@ function buildInstallationSummaryRows(options) {
|
|
|
2294
2442
|
if (!canonicalAgent) {
|
|
2295
2443
|
return rows;
|
|
2296
2444
|
}
|
|
2297
|
-
const canonicalBase = getAgentSkillsDir(
|
|
2298
|
-
canonicalAgent,
|
|
2299
|
-
options.globalInstall,
|
|
2300
|
-
options.cwd
|
|
2301
|
-
);
|
|
2445
|
+
const canonicalBase = getAgentSkillsDir(canonicalAgent, options.globalInstall, options.cwd);
|
|
2302
2446
|
for (const rawSkillName of options.skillNames) {
|
|
2303
2447
|
const skillName = sanitizeSkillName(rawSkillName);
|
|
2304
2448
|
const canonicalDir = path7.join(canonicalBase, skillName);
|
|
@@ -2354,95 +2498,33 @@ async function printAddListScreen(options) {
|
|
|
2354
2498
|
]);
|
|
2355
2499
|
}
|
|
2356
2500
|
async function resolveAddSkills(available, merged, interactive) {
|
|
2357
|
-
const
|
|
2358
|
-
if (!requested || requested.length === 0) {
|
|
2359
|
-
return items;
|
|
2360
|
-
}
|
|
2361
|
-
if (requested.includes("*")) {
|
|
2362
|
-
return items;
|
|
2363
|
-
}
|
|
2364
|
-
const wanted2 = new Set(requested.map((item) => item.toLowerCase()));
|
|
2365
|
-
return items.filter((item) => wanted2.has(item.name.toLowerCase()));
|
|
2366
|
-
};
|
|
2367
|
-
if (merged.list) {
|
|
2368
|
-
return filterByRequestedName(available, merged.skill);
|
|
2369
|
-
}
|
|
2370
|
-
const skillRows = buildAddSkillSelectionRows(
|
|
2501
|
+
const skillRows = buildNamedAddSelectionRows(
|
|
2371
2502
|
available.map((item) => ({
|
|
2372
2503
|
name: item.name,
|
|
2373
2504
|
description: item.description
|
|
2374
2505
|
}))
|
|
2375
2506
|
);
|
|
2376
|
-
const renderClosed = (
|
|
2507
|
+
const renderClosed = (selectedNames) => renderAddSkillsSection({
|
|
2377
2508
|
skills: available.map((item) => ({
|
|
2378
2509
|
name: item.name,
|
|
2379
2510
|
description: item.description
|
|
2380
2511
|
})),
|
|
2381
|
-
selectedNames
|
|
2512
|
+
selectedNames
|
|
2382
2513
|
});
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
if (selected.length > 0) {
|
|
2386
|
-
await runManySelectionStep({
|
|
2387
|
-
interactive,
|
|
2388
|
-
rows: skillRows,
|
|
2389
|
-
selectedIds: selected.map((item) => item.name),
|
|
2390
|
-
shouldPrompt: false,
|
|
2391
|
-
prompt: {
|
|
2392
|
-
title: "Choose Skills",
|
|
2393
|
-
required: true,
|
|
2394
|
-
requiredMessage: "At least one skill must be selected",
|
|
2395
|
-
searchable: true,
|
|
2396
|
-
keyHints: ADD_SKILLS_KEY_HINTS,
|
|
2397
|
-
view: ADD_SKILLS_SELECTION_VIEW
|
|
2398
|
-
},
|
|
2399
|
-
renderClosed
|
|
2400
|
-
});
|
|
2401
|
-
}
|
|
2402
|
-
return selected;
|
|
2403
|
-
}
|
|
2404
|
-
if (available.length === 0) {
|
|
2405
|
-
throw new Error("No skills available");
|
|
2406
|
-
}
|
|
2407
|
-
if (available.length === 1) {
|
|
2408
|
-
await runManySelectionStep({
|
|
2409
|
-
interactive,
|
|
2410
|
-
rows: skillRows,
|
|
2411
|
-
selectedIds: [available[0].name],
|
|
2412
|
-
shouldPrompt: false,
|
|
2413
|
-
prompt: {
|
|
2414
|
-
title: "Choose Skills",
|
|
2415
|
-
required: true,
|
|
2416
|
-
requiredMessage: "At least one skill must be selected",
|
|
2417
|
-
searchable: true,
|
|
2418
|
-
keyHints: ADD_SKILLS_KEY_HINTS,
|
|
2419
|
-
view: ADD_SKILLS_SELECTION_VIEW
|
|
2420
|
-
},
|
|
2421
|
-
renderClosed
|
|
2422
|
-
});
|
|
2423
|
-
return available;
|
|
2424
|
-
}
|
|
2425
|
-
if (!interactive) {
|
|
2426
|
-
throw new Error(
|
|
2427
|
-
"Multiple skills found. Use --skill <name> or run in TTY without --non-interactive."
|
|
2428
|
-
);
|
|
2429
|
-
}
|
|
2430
|
-
const selectedNames = await runManySelectionStep({
|
|
2514
|
+
return resolveNamedAddSelection({
|
|
2515
|
+
available,
|
|
2431
2516
|
interactive,
|
|
2517
|
+
listMode: merged.list,
|
|
2518
|
+
requested: merged.skill,
|
|
2432
2519
|
rows: skillRows,
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
keyHints: ADD_SKILLS_KEY_HINTS,
|
|
2440
|
-
view: ADD_SKILLS_SELECTION_VIEW
|
|
2441
|
-
},
|
|
2520
|
+
keyHints: ADD_SKILLS_KEY_HINTS,
|
|
2521
|
+
view: ADD_SKILLS_SELECTION_VIEW,
|
|
2522
|
+
promptTitle: "Choose Skills",
|
|
2523
|
+
requiredMessage: "At least one skill must be selected",
|
|
2524
|
+
emptyMessage: "No skills available",
|
|
2525
|
+
multipleInNonInteractiveMessage: "Multiple skills found. Use --skill <name> or run in TTY without --non-interactive.",
|
|
2442
2526
|
renderClosed
|
|
2443
2527
|
});
|
|
2444
|
-
const wanted = new Set(selectedNames);
|
|
2445
|
-
return available.filter((item) => wanted.has(item.name));
|
|
2446
2528
|
}
|
|
2447
2529
|
async function resolveAddAgents(merged, globalInstall, interactive) {
|
|
2448
2530
|
const rows = resolveAddAgentSelectionRows(globalInstall ? "global" : "local");
|
|
@@ -2490,9 +2572,7 @@ async function resolveAddAgents(merged, globalInstall, interactive) {
|
|
|
2490
2572
|
return selectedAgents;
|
|
2491
2573
|
}
|
|
2492
2574
|
if (!interactive) {
|
|
2493
|
-
throw new Error(
|
|
2494
|
-
"Missing --agent in non-interactive mode. Provide at least one agent."
|
|
2495
|
-
);
|
|
2575
|
+
throw new Error("Missing --agent in non-interactive mode. Provide at least one agent.");
|
|
2496
2576
|
}
|
|
2497
2577
|
if (allForScope.length === 1) {
|
|
2498
2578
|
await runManySelectionStep({
|
|
@@ -2563,56 +2643,27 @@ function resolveAddInstallMode(merged) {
|
|
|
2563
2643
|
}
|
|
2564
2644
|
return "copy";
|
|
2565
2645
|
}
|
|
2566
|
-
function parseLockFormatValue(value) {
|
|
2567
|
-
if (!value) {
|
|
2568
|
-
return void 0;
|
|
2569
|
-
}
|
|
2570
|
-
if (value !== "json" && value !== "yaml") {
|
|
2571
|
-
throw new Error(`Invalid --lock-format value: ${value}`);
|
|
2572
|
-
}
|
|
2573
|
-
return value;
|
|
2574
|
-
}
|
|
2575
2646
|
function toAddOptions(options, presence = {}) {
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
lockFormat: parseLockFormatValue(options.lockFormat),
|
|
2597
|
-
experimental: false
|
|
2598
|
-
};
|
|
2599
|
-
if (parsed.agent && parsed.agent.length > 0) {
|
|
2600
|
-
parsed.agentFlagProvided = true;
|
|
2601
|
-
}
|
|
2602
|
-
if (presence.agentProvided) {
|
|
2603
|
-
parsed.agentFlagProvided = true;
|
|
2604
|
-
}
|
|
2605
|
-
if (presence.globalProvided) {
|
|
2606
|
-
parsed.globalFlagProvided = true;
|
|
2607
|
-
}
|
|
2608
|
-
if (presence.symlinkProvided) {
|
|
2609
|
-
parsed.symlinkFlagProvided = true;
|
|
2610
|
-
}
|
|
2611
|
-
if (parsed.all) {
|
|
2612
|
-
parsed.skill = ["*"];
|
|
2613
|
-
parsed.agent = ["*"];
|
|
2614
|
-
}
|
|
2615
|
-
return parsed;
|
|
2647
|
+
return buildBaseAddOptions(
|
|
2648
|
+
{
|
|
2649
|
+
global: options.global,
|
|
2650
|
+
symlink: options.symlink,
|
|
2651
|
+
yaml: options.yaml,
|
|
2652
|
+
list: options.list,
|
|
2653
|
+
all: options.all,
|
|
2654
|
+
nonInteractive: options.nonInteractive,
|
|
2655
|
+
trustWellKnown: options.trustWellKnown,
|
|
2656
|
+
agent: options.agent,
|
|
2657
|
+
selectedNames: options.skill,
|
|
2658
|
+
allowHost: options.allowHost,
|
|
2659
|
+
denyHost: options.denyHost,
|
|
2660
|
+
maxDownloadBytes: options.maxDownloadBytes,
|
|
2661
|
+
policyMode: parsePolicyMode(options.policyMode),
|
|
2662
|
+
lockFormat: options.lockFormat,
|
|
2663
|
+
experimental: false
|
|
2664
|
+
},
|
|
2665
|
+
presence
|
|
2666
|
+
);
|
|
2616
2667
|
}
|
|
2617
2668
|
async function executeAdd(sourceInput, merged) {
|
|
2618
2669
|
const interactive = canUseInteractive(merged.nonInteractive);
|
|
@@ -2625,9 +2676,7 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2625
2676
|
sourceLabel = resolveSourceLabel(parsedSource);
|
|
2626
2677
|
} catch (error) {
|
|
2627
2678
|
hideLoader();
|
|
2628
|
-
await renderStaticScreen([
|
|
2629
|
-
failedStepsSection(["failed to parse source"])
|
|
2630
|
-
]);
|
|
2679
|
+
await renderStaticScreen([failedStepsSection(["failed to parse source"])]);
|
|
2631
2680
|
throw error;
|
|
2632
2681
|
}
|
|
2633
2682
|
hideLoader();
|
|
@@ -2653,26 +2702,17 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2653
2702
|
);
|
|
2654
2703
|
} catch (error) {
|
|
2655
2704
|
hideLoader();
|
|
2656
|
-
await renderStaticScreen([
|
|
2657
|
-
failedStepsSection(["failed to fetch skill index"])
|
|
2658
|
-
]);
|
|
2705
|
+
await renderStaticScreen([failedStepsSection(["failed to fetch skill index"])]);
|
|
2659
2706
|
throw error;
|
|
2660
2707
|
}
|
|
2661
2708
|
hideLoader();
|
|
2662
2709
|
await renderStaticScreen([
|
|
2663
|
-
completedStepsSection([
|
|
2664
|
-
"skill index fetched",
|
|
2665
|
-
"interactive session ready"
|
|
2666
|
-
])
|
|
2710
|
+
completedStepsSection(["skill index fetched", "interactive session ready"])
|
|
2667
2711
|
]);
|
|
2668
2712
|
if (!merged.list) {
|
|
2669
2713
|
await renderStaticScreen([sourceSection(shortenHomePath(sourceLabel))]);
|
|
2670
2714
|
}
|
|
2671
|
-
const selected = await resolveAddSkills(
|
|
2672
|
-
discovered.skills,
|
|
2673
|
-
merged,
|
|
2674
|
-
interactive
|
|
2675
|
-
);
|
|
2715
|
+
const selected = await resolveAddSkills(discovered.skills, merged, interactive);
|
|
2676
2716
|
if (selected.length === 0) {
|
|
2677
2717
|
if (parsedSource.type === "well-known" || parsedSource.type === "catalog") {
|
|
2678
2718
|
throw new Error("No matching well-known skills found in source");
|
|
@@ -2695,11 +2735,7 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2695
2735
|
global: globalInstall,
|
|
2696
2736
|
globalFlagProvided: true
|
|
2697
2737
|
};
|
|
2698
|
-
const agents = await resolveAddAgents(
|
|
2699
|
-
installOptions,
|
|
2700
|
-
globalInstall,
|
|
2701
|
-
interactive
|
|
2702
|
-
);
|
|
2738
|
+
const agents = await resolveAddAgents(installOptions, globalInstall, interactive);
|
|
2703
2739
|
const mode = resolveAddInstallMode(merged);
|
|
2704
2740
|
await printInstallationSummary({
|
|
2705
2741
|
skillNames: selected.map((item) => item.name),
|
|
@@ -2731,9 +2767,7 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2731
2767
|
);
|
|
2732
2768
|
} catch (error) {
|
|
2733
2769
|
hideLoader();
|
|
2734
|
-
await renderStaticScreen([
|
|
2735
|
-
failedStepsSection(["failed to install skills"])
|
|
2736
|
-
]);
|
|
2770
|
+
await renderStaticScreen([failedStepsSection(["failed to install skills"])]);
|
|
2737
2771
|
throw error;
|
|
2738
2772
|
}
|
|
2739
2773
|
hideLoader();
|
|
@@ -2754,13 +2788,7 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2754
2788
|
}
|
|
2755
2789
|
}
|
|
2756
2790
|
function configureAddCommand(command, action) {
|
|
2757
|
-
return command.description("Install skills from local path or git source").argument("<source>", "Source path or URL").option("-a, --agent <agents...>", "Target agent(s) for installation").option("-s, --skill <skills...>", "Install only selected skill(s)").option("-l, --list", "List skills from source without installing").option("--symlink", "Install by symlinking files to all agents").option(
|
|
2758
|
-
"--yaml",
|
|
2759
|
-
"Create skill-installer.yaml when scaffolding missing installer config"
|
|
2760
|
-
).option("-g, --global", "Install globally").option("--trust-well-known", "Allow hook commands for well-known source").option("--allow-host <hosts...>", "Restrict well-known hosts to allowlist").option("--deny-host <hosts...>", "Block specific well-known hosts").option("--max-download-bytes <n>", "Set well-known download budget").option("--policy-mode <mode>", "Policy mode (enforce|warn)").option("--lock-format <format>", "Lockfile format output (json|yaml)").option(
|
|
2761
|
-
"--non-interactive",
|
|
2762
|
-
"Disable prompts and require explicit selection"
|
|
2763
|
-
).option("--all", "Install all skills and known agents").action(action);
|
|
2791
|
+
return command.description("Install skills from local path or git source").argument("<source>", "Source path or URL").option("-a, --agent <agents...>", "Target agent(s) for installation").option("-s, --skill <skills...>", "Install only selected skill(s)").option("-l, --list", "List skills from source without installing").option("--symlink", "Install by symlinking files to all agents").option("--yaml", "Create skill-installer.yaml when scaffolding missing installer config").option("-g, --global", "Install globally").option("--trust-well-known", "Allow hook commands for well-known source").option("--allow-host <hosts...>", "Restrict well-known hosts to allowlist").option("--deny-host <hosts...>", "Block specific well-known hosts").option("--max-download-bytes <n>", "Set well-known download budget").option("--policy-mode <mode>", "Policy mode (enforce|warn)").option("--lock-format <format>", "Lockfile format output (json|yaml)").option("--non-interactive", "Disable prompts and require explicit selection").option("--all", "Install all skills and known agents").action(action);
|
|
2764
2792
|
}
|
|
2765
2793
|
function registerAddCommand(program, ctx) {
|
|
2766
2794
|
configureAddCommand(
|
|
@@ -2786,7 +2814,7 @@ function registerAddCommand(program, ctx) {
|
|
|
2786
2814
|
import { Command as Command3 } from "commander";
|
|
2787
2815
|
|
|
2788
2816
|
// ../../packages/core/src/sources/git.ts
|
|
2789
|
-
import
|
|
2817
|
+
import fs6 from "node:fs";
|
|
2790
2818
|
import os3 from "node:os";
|
|
2791
2819
|
import path8 from "node:path";
|
|
2792
2820
|
import { spawn as spawn2, spawnSync } from "node:child_process";
|
|
@@ -2812,12 +2840,12 @@ function applyCheckoutRefSync(repoDir, ref) {
|
|
|
2812
2840
|
}
|
|
2813
2841
|
function prepareSourceDir(parsed) {
|
|
2814
2842
|
if (parsed.type === "local") {
|
|
2815
|
-
if (!
|
|
2843
|
+
if (!fs6.existsSync(parsed.localPath)) {
|
|
2816
2844
|
throw new Error(`Local source not found: ${parsed.localPath}`);
|
|
2817
2845
|
}
|
|
2818
2846
|
return { basePath: parsed.localPath };
|
|
2819
2847
|
}
|
|
2820
|
-
const tmp =
|
|
2848
|
+
const tmp = fs6.mkdtempSync(path8.join(os3.tmpdir(), "skillspp-cli-"));
|
|
2821
2849
|
runGit(["clone", "--depth", "1", parsed.repoUrl, tmp]);
|
|
2822
2850
|
const ref = parsed.type === "github" ? parsed.ref : void 0;
|
|
2823
2851
|
applyCheckoutRefSync(tmp, ref);
|
|
@@ -2825,7 +2853,7 @@ function prepareSourceDir(parsed) {
|
|
|
2825
2853
|
return {
|
|
2826
2854
|
basePath,
|
|
2827
2855
|
cleanup: () => {
|
|
2828
|
-
|
|
2856
|
+
fs6.rmSync(tmp, { recursive: true, force: true });
|
|
2829
2857
|
}
|
|
2830
2858
|
};
|
|
2831
2859
|
}
|
|
@@ -2872,11 +2900,33 @@ var DEFAULT_OPTIONS = {
|
|
|
2872
2900
|
maxFilesPerSkill: 128,
|
|
2873
2901
|
maxSkillFileBytes: 512 * 1024
|
|
2874
2902
|
};
|
|
2875
|
-
var EXCLUDED_HOSTS = /* @__PURE__ */ new Set([
|
|
2876
|
-
|
|
2877
|
-
"
|
|
2878
|
-
"
|
|
2879
|
-
|
|
2903
|
+
var EXCLUDED_HOSTS = /* @__PURE__ */ new Set(["github.com", "gitlab.com", "raw.githubusercontent.com"]);
|
|
2904
|
+
var SKILL_CONFIG = {
|
|
2905
|
+
kind: "skills",
|
|
2906
|
+
displayLabel: "well-known skills",
|
|
2907
|
+
indexPath: "/.well-known/skills/index.json",
|
|
2908
|
+
entryLabel: "skill",
|
|
2909
|
+
requireDescription: true,
|
|
2910
|
+
missingManifestMessage: (name) => `Well-known skill '${name}' is missing SKILL.md`,
|
|
2911
|
+
validateName(name) {
|
|
2912
|
+
if (!/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/.test(name)) {
|
|
2913
|
+
throw new Error(`Invalid well-known skill name: ${name}`);
|
|
2914
|
+
}
|
|
2915
|
+
},
|
|
2916
|
+
hasRequiredManifest(filePath) {
|
|
2917
|
+
return filePath.toLowerCase() === "skill.md";
|
|
2918
|
+
},
|
|
2919
|
+
buildRemoteResult({ entry, files, sourceUrl }) {
|
|
2920
|
+
return {
|
|
2921
|
+
name: entry.name,
|
|
2922
|
+
description: entry.description || "",
|
|
2923
|
+
installName: entry.name,
|
|
2924
|
+
sourceUrl,
|
|
2925
|
+
sourceType: "well-known",
|
|
2926
|
+
files
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2929
|
+
};
|
|
2880
2930
|
var SecureWellKnownProvider = class {
|
|
2881
2931
|
id = "well-known";
|
|
2882
2932
|
displayName = "Secure Well-Known Skills";
|
|
@@ -2896,10 +2946,13 @@ var SecureWellKnownProvider = class {
|
|
|
2896
2946
|
}
|
|
2897
2947
|
getSourceIdentifier(url) {
|
|
2898
2948
|
const parsed = new URL(url);
|
|
2899
|
-
const
|
|
2900
|
-
return
|
|
2949
|
+
const pathname = parsed.pathname.replace(/\/$/, "");
|
|
2950
|
+
return pathname && pathname !== "/" ? `wellknown/${parsed.hostname}${pathname}` : `wellknown/${parsed.hostname}`;
|
|
2901
2951
|
}
|
|
2902
2952
|
async fetchAllSkills(url, options = {}) {
|
|
2953
|
+
return this.fetchAllResources(url, options, SKILL_CONFIG);
|
|
2954
|
+
}
|
|
2955
|
+
async fetchAllResources(url, options, config) {
|
|
2903
2956
|
const normalized = this.normalizeOptions(options);
|
|
2904
2957
|
const budget = { remaining: normalized.maxDownloadBytes };
|
|
2905
2958
|
const parsed = new URL(url);
|
|
@@ -2907,29 +2960,19 @@ var SecureWellKnownProvider = class {
|
|
|
2907
2960
|
throw new Error("Well-known provider requires HTTPS URLs");
|
|
2908
2961
|
}
|
|
2909
2962
|
await this.assertHostAllowed(parsed.hostname, normalized);
|
|
2910
|
-
const { index, resolvedBase } = await this.fetchIndex(
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
const skills = [];
|
|
2916
|
-
for (const entry of index.skills) {
|
|
2917
|
-
const skill = await this.fetchSkillByEntry(
|
|
2918
|
-
resolvedBase,
|
|
2919
|
-
entry,
|
|
2920
|
-
normalized,
|
|
2921
|
-
budget
|
|
2963
|
+
const { index, resolvedBase } = await this.fetchIndex(parsed, normalized, budget, config);
|
|
2964
|
+
const resources = [];
|
|
2965
|
+
for (const entry of index) {
|
|
2966
|
+
resources.push(
|
|
2967
|
+
await this.fetchResourceByEntry(resolvedBase, entry, normalized, budget, config)
|
|
2922
2968
|
);
|
|
2923
|
-
if (skill) {
|
|
2924
|
-
skills.push(skill);
|
|
2925
|
-
}
|
|
2926
2969
|
}
|
|
2927
|
-
return
|
|
2970
|
+
return resources;
|
|
2928
2971
|
}
|
|
2929
2972
|
normalizeOptions(options) {
|
|
2930
2973
|
return {
|
|
2931
|
-
allowHosts: (options.allowHosts || []).map((
|
|
2932
|
-
denyHosts: (options.denyHosts || []).map((
|
|
2974
|
+
allowHosts: (options.allowHosts || []).map((value) => value.trim().toLowerCase()).filter(Boolean),
|
|
2975
|
+
denyHosts: (options.denyHosts || []).map((value) => value.trim().toLowerCase()).filter(Boolean),
|
|
2933
2976
|
maxDownloadBytes: options.maxDownloadBytes ?? DEFAULT_OPTIONS.maxDownloadBytes,
|
|
2934
2977
|
timeoutMs: options.timeoutMs ?? DEFAULT_OPTIONS.timeoutMs,
|
|
2935
2978
|
maxRedirects: options.maxRedirects ?? DEFAULT_OPTIONS.maxRedirects,
|
|
@@ -2937,10 +2980,10 @@ var SecureWellKnownProvider = class {
|
|
|
2937
2980
|
maxSkillFileBytes: options.maxSkillFileBytes ?? DEFAULT_OPTIONS.maxSkillFileBytes
|
|
2938
2981
|
};
|
|
2939
2982
|
}
|
|
2940
|
-
async fetchIndex(parsedUrl, options, budget) {
|
|
2941
|
-
const candidates = this.buildBaseCandidates(parsedUrl);
|
|
2983
|
+
async fetchIndex(parsedUrl, options, budget, config) {
|
|
2984
|
+
const candidates = this.buildBaseCandidates(parsedUrl, config.indexPath);
|
|
2942
2985
|
for (const base of candidates) {
|
|
2943
|
-
const indexUrl = `${base}
|
|
2986
|
+
const indexUrl = `${base}${config.indexPath}`;
|
|
2944
2987
|
try {
|
|
2945
2988
|
const jsonText = await this.fetchTextWithLimit(
|
|
2946
2989
|
indexUrl,
|
|
@@ -2949,20 +2992,18 @@ var SecureWellKnownProvider = class {
|
|
|
2949
2992
|
budget
|
|
2950
2993
|
);
|
|
2951
2994
|
const parsed = JSON.parse(jsonText);
|
|
2952
|
-
const validated = this.validateIndex(parsed, options.maxFilesPerSkill);
|
|
2995
|
+
const validated = this.validateIndex(parsed, options.maxFilesPerSkill, config);
|
|
2953
2996
|
return { index: validated, resolvedBase: base };
|
|
2954
2997
|
} catch {
|
|
2955
2998
|
continue;
|
|
2956
2999
|
}
|
|
2957
3000
|
}
|
|
2958
|
-
throw new Error(
|
|
2959
|
-
"No valid well-known skills index found at /.well-known/skills/index.json"
|
|
2960
|
-
);
|
|
3001
|
+
throw new Error(`No valid ${config.displayLabel} index found at ${config.indexPath}`);
|
|
2961
3002
|
}
|
|
2962
|
-
buildBaseCandidates(parsed) {
|
|
3003
|
+
buildBaseCandidates(parsed, indexPath) {
|
|
2963
3004
|
const origin = parsed.origin;
|
|
2964
3005
|
const pathname = parsed.pathname.replace(/\/$/, "");
|
|
2965
|
-
const marker = "
|
|
3006
|
+
const marker = indexPath.replace(/\/index\.json$/, "");
|
|
2966
3007
|
const out = [];
|
|
2967
3008
|
if (pathname.includes(marker)) {
|
|
2968
3009
|
const prefix = pathname.slice(0, pathname.indexOf(marker));
|
|
@@ -2977,53 +3018,52 @@ var SecureWellKnownProvider = class {
|
|
|
2977
3018
|
}
|
|
2978
3019
|
}
|
|
2979
3020
|
return [
|
|
2980
|
-
...new Set(out.map((
|
|
3021
|
+
...new Set(out.map((value) => value.endsWith("/") ? value.slice(0, -1) : value))
|
|
2981
3022
|
].filter(Boolean);
|
|
2982
3023
|
}
|
|
2983
|
-
validateIndex(raw, maxFilesPerSkill) {
|
|
3024
|
+
validateIndex(raw, maxFilesPerSkill, config) {
|
|
2984
3025
|
if (!raw || typeof raw !== "object") {
|
|
2985
3026
|
throw new Error("Invalid well-known index: expected object");
|
|
2986
3027
|
}
|
|
2987
3028
|
const data = raw;
|
|
2988
|
-
|
|
2989
|
-
|
|
3029
|
+
const rows = data[config.kind];
|
|
3030
|
+
if (!Array.isArray(rows)) {
|
|
3031
|
+
throw new Error(`Invalid well-known index: '${config.kind}' must be an array`);
|
|
2990
3032
|
}
|
|
2991
|
-
|
|
3033
|
+
return rows.map((item, idx) => {
|
|
2992
3034
|
if (!item || typeof item !== "object") {
|
|
2993
3035
|
throw new Error(`Invalid well-known index entry[${idx}]`);
|
|
2994
3036
|
}
|
|
2995
3037
|
const row = item;
|
|
2996
3038
|
const name = String(row.name || "").trim();
|
|
2997
|
-
const description =
|
|
2998
|
-
const files = Array.isArray(row.files) ? row.files.map((
|
|
2999
|
-
if (!name ||
|
|
3000
|
-
throw new Error(
|
|
3001
|
-
`Invalid well-known index entry[${idx}]: missing required fields`
|
|
3002
|
-
);
|
|
3039
|
+
const description = typeof row.description === "string" ? row.description.trim() : void 0;
|
|
3040
|
+
const files = Array.isArray(row.files) ? row.files.map((value) => String(value)) : [];
|
|
3041
|
+
if (!name || files.length === 0) {
|
|
3042
|
+
throw new Error(`Invalid well-known index entry[${idx}]: missing required fields`);
|
|
3003
3043
|
}
|
|
3004
|
-
if (
|
|
3005
|
-
throw new Error(`Invalid well-known
|
|
3044
|
+
if (config.requireDescription && !description) {
|
|
3045
|
+
throw new Error(`Invalid well-known index entry[${idx}]: missing required fields`);
|
|
3006
3046
|
}
|
|
3047
|
+
config.validateName?.(name);
|
|
3007
3048
|
if (files.length > maxFilesPerSkill) {
|
|
3008
|
-
throw new Error(`Too many files in well-known
|
|
3049
|
+
throw new Error(`Too many files in well-known ${config.entryLabel} '${name}'`);
|
|
3009
3050
|
}
|
|
3010
|
-
if (!files.some((
|
|
3011
|
-
throw new Error(
|
|
3051
|
+
if (!files.some((filePath) => config.hasRequiredManifest(filePath))) {
|
|
3052
|
+
throw new Error(config.missingManifestMessage(name));
|
|
3012
3053
|
}
|
|
3013
3054
|
for (const file of files) {
|
|
3014
3055
|
this.assertSafeRelativePath(file);
|
|
3015
3056
|
}
|
|
3016
3057
|
return { name, description, files };
|
|
3017
3058
|
});
|
|
3018
|
-
return { skills };
|
|
3019
3059
|
}
|
|
3020
3060
|
assertSafeRelativePath(filePath) {
|
|
3021
3061
|
if (!filePath || filePath.startsWith("/") || filePath.startsWith("\\") || filePath.includes("..") || filePath.includes("\\")) {
|
|
3022
3062
|
throw new Error(`Unsafe well-known file path: ${filePath}`);
|
|
3023
3063
|
}
|
|
3024
3064
|
}
|
|
3025
|
-
async
|
|
3026
|
-
const baseUrl = `${resolvedBase}/.well-known
|
|
3065
|
+
async fetchResourceByEntry(resolvedBase, entry, options, budget, config) {
|
|
3066
|
+
const baseUrl = `${resolvedBase}/.well-known/${config.kind}/${entry.name}`;
|
|
3027
3067
|
const files = /* @__PURE__ */ new Map();
|
|
3028
3068
|
for (const filePath of entry.files) {
|
|
3029
3069
|
this.assertSafeRelativePath(filePath);
|
|
@@ -3035,24 +3075,31 @@ var SecureWellKnownProvider = class {
|
|
|
3035
3075
|
budget
|
|
3036
3076
|
);
|
|
3037
3077
|
if (text.includes("\0")) {
|
|
3038
|
-
throw new Error(
|
|
3039
|
-
`Binary content is not allowed in well-known file: ${filePath}`
|
|
3040
|
-
);
|
|
3078
|
+
throw new Error(`Binary content is not allowed in well-known file: ${filePath}`);
|
|
3041
3079
|
}
|
|
3042
3080
|
files.set(filePath, text);
|
|
3043
3081
|
}
|
|
3044
|
-
const
|
|
3045
|
-
|
|
3046
|
-
|
|
3082
|
+
const manifestPath = this.pickPrimaryManifestPath(entry.files, config);
|
|
3083
|
+
return config.buildRemoteResult({
|
|
3084
|
+
entry,
|
|
3085
|
+
files,
|
|
3086
|
+
sourceUrl: `${baseUrl}/${manifestPath}`
|
|
3087
|
+
});
|
|
3088
|
+
}
|
|
3089
|
+
pickPrimaryManifestPath(filePaths, config) {
|
|
3090
|
+
const manifests = filePaths.filter((filePath) => config.hasRequiredManifest(filePath)).sort((left, right) => {
|
|
3091
|
+
const leftDepth = left.split("/").length;
|
|
3092
|
+
const rightDepth = right.split("/").length;
|
|
3093
|
+
if (leftDepth !== rightDepth) {
|
|
3094
|
+
return leftDepth - rightDepth;
|
|
3095
|
+
}
|
|
3096
|
+
return left.localeCompare(right);
|
|
3097
|
+
});
|
|
3098
|
+
const manifestPath = manifests[0];
|
|
3099
|
+
if (!manifestPath) {
|
|
3100
|
+
throw new Error("Missing required manifest in well-known index entry");
|
|
3047
3101
|
}
|
|
3048
|
-
return
|
|
3049
|
-
name: entry.name,
|
|
3050
|
-
description: entry.description,
|
|
3051
|
-
installName: entry.name,
|
|
3052
|
-
sourceUrl: `${baseUrl}/SKILL.md`,
|
|
3053
|
-
sourceType: "well-known",
|
|
3054
|
-
files
|
|
3055
|
-
};
|
|
3102
|
+
return manifestPath;
|
|
3056
3103
|
}
|
|
3057
3104
|
async fetchTextWithLimit(url, maxPerRequestBytes, options, budget) {
|
|
3058
3105
|
let currentUrl = url;
|
|
@@ -3087,111 +3134,61 @@ var SecureWellKnownProvider = class {
|
|
|
3087
3134
|
continue;
|
|
3088
3135
|
}
|
|
3089
3136
|
if (!response.ok) {
|
|
3090
|
-
throw new Error(
|
|
3091
|
-
`Fetch failed (${response.status} ${response.statusText}) for ${currentUrl}`
|
|
3092
|
-
);
|
|
3093
|
-
}
|
|
3094
|
-
const contentLengthHeader = response.headers.get("content-length");
|
|
3095
|
-
if (contentLengthHeader) {
|
|
3096
|
-
const declared = Number(contentLengthHeader);
|
|
3097
|
-
if (Number.isFinite(declared) && declared > maxPerRequestBytes) {
|
|
3098
|
-
throw new Error(
|
|
3099
|
-
`Response exceeds per-file size limit for ${currentUrl}`
|
|
3100
|
-
);
|
|
3101
|
-
}
|
|
3102
|
-
if (Number.isFinite(declared) && declared > budget.remaining) {
|
|
3103
|
-
throw new Error(
|
|
3104
|
-
`Response exceeds remaining download budget for ${currentUrl}`
|
|
3105
|
-
);
|
|
3106
|
-
}
|
|
3107
|
-
}
|
|
3108
|
-
const reader = response.body?.getReader();
|
|
3109
|
-
if (!reader) {
|
|
3110
|
-
return "";
|
|
3137
|
+
throw new Error(`Failed to fetch ${currentUrl}: ${response.status} ${response.statusText}`);
|
|
3111
3138
|
}
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
const result = await reader.read();
|
|
3116
|
-
if (result.done) {
|
|
3117
|
-
break;
|
|
3118
|
-
}
|
|
3119
|
-
const chunk = result.value;
|
|
3120
|
-
received += chunk.byteLength;
|
|
3121
|
-
if (received > maxPerRequestBytes) {
|
|
3122
|
-
throw new Error(
|
|
3123
|
-
`Response exceeded per-file size limit for ${currentUrl}`
|
|
3124
|
-
);
|
|
3125
|
-
}
|
|
3126
|
-
if (received > budget.remaining) {
|
|
3127
|
-
throw new Error(
|
|
3128
|
-
`Response exceeded remaining download budget for ${currentUrl}`
|
|
3129
|
-
);
|
|
3130
|
-
}
|
|
3131
|
-
chunks.push(chunk);
|
|
3139
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
3140
|
+
if (bytes.byteLength > maxPerRequestBytes) {
|
|
3141
|
+
throw new Error(`Exceeded per-file download limit for ${currentUrl}`);
|
|
3132
3142
|
}
|
|
3133
|
-
budget.remaining -=
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
for (const chunk of chunks) {
|
|
3137
|
-
total.set(chunk, offset);
|
|
3138
|
-
offset += chunk.byteLength;
|
|
3143
|
+
budget.remaining -= bytes.byteLength;
|
|
3144
|
+
if (budget.remaining < 0) {
|
|
3145
|
+
throw new Error(`Exceeded total download budget while fetching ${url}`);
|
|
3139
3146
|
}
|
|
3140
|
-
return new TextDecoder("
|
|
3147
|
+
return new TextDecoder("utf8").decode(bytes);
|
|
3141
3148
|
}
|
|
3142
3149
|
}
|
|
3143
3150
|
async assertHostAllowed(hostname, options) {
|
|
3144
|
-
const
|
|
3145
|
-
if (options.denyHosts.includes(
|
|
3146
|
-
throw new Error(`
|
|
3151
|
+
const lowerHost = hostname.toLowerCase();
|
|
3152
|
+
if (options.denyHosts.includes(lowerHost)) {
|
|
3153
|
+
throw new Error(`Host '${hostname}' is explicitly denied`);
|
|
3147
3154
|
}
|
|
3148
|
-
if (options.allowHosts.length > 0 && !options.allowHosts.includes(
|
|
3149
|
-
throw new Error(`
|
|
3155
|
+
if (options.allowHosts.length > 0 && !options.allowHosts.includes(lowerHost)) {
|
|
3156
|
+
throw new Error(`Host '${hostname}' is not in allowed host list`);
|
|
3150
3157
|
}
|
|
3151
|
-
if (
|
|
3152
|
-
throw new Error(`
|
|
3158
|
+
if (isLocalHostname(lowerHost)) {
|
|
3159
|
+
throw new Error(`Local or loopback host '${hostname}' is not allowed`);
|
|
3153
3160
|
}
|
|
3154
|
-
const
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
throw new Error(
|
|
3158
|
-
`Well-known host resolves to private/local address: ${hostname}`
|
|
3159
|
-
);
|
|
3160
|
-
}
|
|
3161
|
+
const ips = await resolveHostIps(hostname);
|
|
3162
|
+
if (ips.some((ip) => isPrivateOrLocalIp(ip))) {
|
|
3163
|
+
throw new Error(`Host '${hostname}' resolves to a private/local address`);
|
|
3161
3164
|
}
|
|
3162
3165
|
}
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
} catch {
|
|
3166
|
+
};
|
|
3167
|
+
function isLocalHostname(hostname) {
|
|
3168
|
+
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname.endsWith(".local");
|
|
3169
|
+
}
|
|
3170
|
+
async function resolveHostIps(hostname) {
|
|
3171
|
+
const out = /* @__PURE__ */ new Set();
|
|
3172
|
+
try {
|
|
3173
|
+
const entries = await dns.lookup(hostname, { all: true });
|
|
3174
|
+
for (const entry of entries) {
|
|
3175
|
+
out.add(entry.address);
|
|
3174
3176
|
}
|
|
3175
|
-
|
|
3177
|
+
} catch {
|
|
3176
3178
|
}
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
const [a, b] = parts;
|
|
3184
|
-
if (a === 10 || a === 127 || a === 0) return true;
|
|
3185
|
-
if (a === 169 && b === 254) return true;
|
|
3186
|
-
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
3187
|
-
if (a === 192 && b === 168) return true;
|
|
3188
|
-
if (a >= 224) return true;
|
|
3189
|
-
return false;
|
|
3190
|
-
}
|
|
3191
|
-
const value = ip.toLowerCase();
|
|
3192
|
-
return value === "::1" || value === "::" || value.startsWith("fc") || value.startsWith("fd") || value.startsWith("fe80:");
|
|
3179
|
+
return [...out];
|
|
3180
|
+
}
|
|
3181
|
+
function isPrivateOrLocalIp(ip) {
|
|
3182
|
+
const family = net.isIP(ip);
|
|
3183
|
+
if (family === 4) {
|
|
3184
|
+
return ip.startsWith("10.") || ip.startsWith("127.") || ip.startsWith("169.254.") || ip.startsWith("192.168.") || /^172\.(1[6-9]|2\d|3[0-1])\./.test(ip);
|
|
3193
3185
|
}
|
|
3194
|
-
|
|
3186
|
+
if (family === 6) {
|
|
3187
|
+
const normalized = ip.toLowerCase();
|
|
3188
|
+
return normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe80:");
|
|
3189
|
+
}
|
|
3190
|
+
return false;
|
|
3191
|
+
}
|
|
3195
3192
|
var wellKnownProvider = new SecureWellKnownProvider();
|
|
3196
3193
|
|
|
3197
3194
|
// ../../packages/core/src/providers/catalog.ts
|
|
@@ -3220,6 +3217,33 @@ var HttpCatalogProvider = class {
|
|
|
3220
3217
|
return `catalog/${parsed.host}${parsed.pathname.replace(/\/+$/, "")}`;
|
|
3221
3218
|
}
|
|
3222
3219
|
async fetchAllSkills(url, options = {}) {
|
|
3220
|
+
return this.fetchAllResources(url, options, {
|
|
3221
|
+
kind: "skills",
|
|
3222
|
+
indexLabel: "catalog skills",
|
|
3223
|
+
resolveIndexUrl(parsed) {
|
|
3224
|
+
return parsed.pathname.endsWith(".json") ? parsed.toString() : new URL(
|
|
3225
|
+
"index.json",
|
|
3226
|
+
parsed.toString().endsWith("/") ? parsed.toString() : `${parsed.toString()}/`
|
|
3227
|
+
).toString();
|
|
3228
|
+
},
|
|
3229
|
+
requireDescription: true,
|
|
3230
|
+
missingManifestMessage: (name) => `Catalog skill '${name}' is missing SKILL.md`,
|
|
3231
|
+
hasRequiredManifest(filePath) {
|
|
3232
|
+
return filePath.toLowerCase() === "skill.md";
|
|
3233
|
+
},
|
|
3234
|
+
buildRemoteResult({ entry, files, sourceUrl }) {
|
|
3235
|
+
return {
|
|
3236
|
+
name: entry.name,
|
|
3237
|
+
description: entry.description || "",
|
|
3238
|
+
installName: entry.name,
|
|
3239
|
+
sourceUrl,
|
|
3240
|
+
sourceType: "catalog",
|
|
3241
|
+
files
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
async fetchAllResources(url, options, config) {
|
|
3223
3247
|
const parsed = new URL(url);
|
|
3224
3248
|
if (parsed.protocol !== "https:") {
|
|
3225
3249
|
throw new Error("Catalog provider requires HTTPS URLs");
|
|
@@ -3228,23 +3252,17 @@ var HttpCatalogProvider = class {
|
|
|
3228
3252
|
const timeoutMs = options.timeoutMs ?? DEFAULT_OPTIONS2.timeoutMs;
|
|
3229
3253
|
const maxFilesPerSkill = options.maxFilesPerSkill ?? DEFAULT_OPTIONS2.maxFilesPerSkill;
|
|
3230
3254
|
const maxSkillFileBytes = options.maxSkillFileBytes ?? DEFAULT_OPTIONS2.maxSkillFileBytes;
|
|
3231
|
-
const indexUrl =
|
|
3232
|
-
"index.json",
|
|
3233
|
-
parsed.toString().endsWith("/") ? parsed.toString() : `${parsed.toString()}/`
|
|
3234
|
-
).toString();
|
|
3255
|
+
const indexUrl = config.resolveIndexUrl(parsed);
|
|
3235
3256
|
const indexText = await this.fetchTextWithLimit(
|
|
3236
3257
|
indexUrl,
|
|
3237
3258
|
Math.min(maxDownloadBytes, maxSkillFileBytes),
|
|
3238
3259
|
timeoutMs
|
|
3239
3260
|
);
|
|
3240
|
-
const index = this.validateIndex(
|
|
3241
|
-
JSON.parse(indexText),
|
|
3242
|
-
maxFilesPerSkill
|
|
3243
|
-
);
|
|
3261
|
+
const index = this.validateIndex(JSON.parse(indexText), maxFilesPerSkill, config);
|
|
3244
3262
|
const out = [];
|
|
3245
3263
|
let remaining = maxDownloadBytes - indexText.length;
|
|
3246
3264
|
const indexBase = indexUrl.slice(0, indexUrl.lastIndexOf("/") + 1);
|
|
3247
|
-
for (const row of index
|
|
3265
|
+
for (const row of index) {
|
|
3248
3266
|
const files = /* @__PURE__ */ new Map();
|
|
3249
3267
|
for (const rel of row.files) {
|
|
3250
3268
|
this.assertSafeRelativePath(rel);
|
|
@@ -3260,14 +3278,16 @@ var HttpCatalogProvider = class {
|
|
|
3260
3278
|
remaining -= text.length;
|
|
3261
3279
|
files.set(rel, text);
|
|
3262
3280
|
}
|
|
3263
|
-
out.push(
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3281
|
+
out.push(
|
|
3282
|
+
config.buildRemoteResult({
|
|
3283
|
+
entry: row,
|
|
3284
|
+
files,
|
|
3285
|
+
sourceUrl: new URL(
|
|
3286
|
+
`${row.name}/${this.pickPrimaryManifestPath(row.files, config)}`,
|
|
3287
|
+
indexBase
|
|
3288
|
+
).toString()
|
|
3289
|
+
})
|
|
3290
|
+
);
|
|
3271
3291
|
}
|
|
3272
3292
|
return out;
|
|
3273
3293
|
}
|
|
@@ -3290,39 +3310,55 @@ var HttpCatalogProvider = class {
|
|
|
3290
3310
|
clearTimeout(timeout);
|
|
3291
3311
|
}
|
|
3292
3312
|
}
|
|
3293
|
-
validateIndex(raw, maxFilesPerSkill) {
|
|
3313
|
+
validateIndex(raw, maxFilesPerSkill, config) {
|
|
3294
3314
|
if (!raw || typeof raw !== "object") {
|
|
3295
3315
|
throw new Error("Invalid catalog index: expected object");
|
|
3296
3316
|
}
|
|
3297
3317
|
const data = raw;
|
|
3298
|
-
|
|
3299
|
-
|
|
3318
|
+
const rows = data[config.kind];
|
|
3319
|
+
if (!Array.isArray(rows)) {
|
|
3320
|
+
throw new Error(`Invalid catalog index: '${config.kind}' must be an array`);
|
|
3300
3321
|
}
|
|
3301
|
-
|
|
3322
|
+
return rows.map((item, idx) => {
|
|
3302
3323
|
if (!item || typeof item !== "object") {
|
|
3303
3324
|
throw new Error(`Invalid catalog index entry[${idx}]`);
|
|
3304
3325
|
}
|
|
3305
3326
|
const row = item;
|
|
3306
3327
|
const name = String(row.name || "").trim();
|
|
3307
|
-
const description =
|
|
3328
|
+
const description = typeof row.description === "string" ? row.description.trim() : void 0;
|
|
3308
3329
|
const files = Array.isArray(row.files) ? row.files.map((x) => String(x)) : [];
|
|
3309
|
-
if (!name ||
|
|
3310
|
-
throw new Error(
|
|
3311
|
-
|
|
3312
|
-
|
|
3330
|
+
if (!name || files.length === 0) {
|
|
3331
|
+
throw new Error(`Invalid catalog index entry[${idx}]: missing required fields`);
|
|
3332
|
+
}
|
|
3333
|
+
if (config.requireDescription && !description) {
|
|
3334
|
+
throw new Error(`Invalid catalog index entry[${idx}]: missing required fields`);
|
|
3313
3335
|
}
|
|
3314
3336
|
if (files.length > maxFilesPerSkill) {
|
|
3315
|
-
throw new Error(`Too many files in catalog
|
|
3337
|
+
throw new Error(`Too many files in catalog ${config.kind.slice(0, -1)} '${name}'`);
|
|
3316
3338
|
}
|
|
3317
|
-
if (!files.some((
|
|
3318
|
-
throw new Error(
|
|
3339
|
+
if (!files.some((filePath) => config.hasRequiredManifest(filePath))) {
|
|
3340
|
+
throw new Error(config.missingManifestMessage(name));
|
|
3319
3341
|
}
|
|
3320
3342
|
for (const file of files) {
|
|
3321
3343
|
this.assertSafeRelativePath(file);
|
|
3322
3344
|
}
|
|
3323
3345
|
return { name, description, files };
|
|
3324
3346
|
});
|
|
3325
|
-
|
|
3347
|
+
}
|
|
3348
|
+
pickPrimaryManifestPath(filePaths, config) {
|
|
3349
|
+
const manifests = filePaths.filter((filePath) => config.hasRequiredManifest(filePath)).sort((left, right) => {
|
|
3350
|
+
const leftDepth = left.split("/").length;
|
|
3351
|
+
const rightDepth = right.split("/").length;
|
|
3352
|
+
if (leftDepth !== rightDepth) {
|
|
3353
|
+
return leftDepth - rightDepth;
|
|
3354
|
+
}
|
|
3355
|
+
return left.localeCompare(right);
|
|
3356
|
+
});
|
|
3357
|
+
const manifestPath = manifests[0];
|
|
3358
|
+
if (!manifestPath) {
|
|
3359
|
+
throw new Error("Catalog entry is missing required manifest");
|
|
3360
|
+
}
|
|
3361
|
+
return manifestPath;
|
|
3326
3362
|
}
|
|
3327
3363
|
assertSafeRelativePath(filePath) {
|
|
3328
3364
|
if (!filePath || filePath.startsWith("/") || filePath.startsWith("\\") || filePath.includes("..") || filePath.includes("\\")) {
|
|
@@ -3349,9 +3385,7 @@ function assertExperimentalFeatureEnabled(feature, enabled) {
|
|
|
3349
3385
|
return;
|
|
3350
3386
|
}
|
|
3351
3387
|
if (feature === "catalog") {
|
|
3352
|
-
throw new Error(
|
|
3353
|
-
"Catalog source is experimental and requires explicit experimental mode."
|
|
3354
|
-
);
|
|
3388
|
+
throw new Error("Catalog source is experimental and requires explicit experimental mode.");
|
|
3355
3389
|
}
|
|
3356
3390
|
}
|
|
3357
3391
|
|
|
@@ -3385,7 +3419,7 @@ async function resolveCatalogSkills(sourceUrl, options) {
|
|
|
3385
3419
|
}
|
|
3386
3420
|
|
|
3387
3421
|
// ../../packages/core/src/runtime/lockfile.ts
|
|
3388
|
-
import
|
|
3422
|
+
import fs7 from "node:fs";
|
|
3389
3423
|
import path9 from "node:path";
|
|
3390
3424
|
import YAML from "yaml";
|
|
3391
3425
|
function perSkillLockfilePath(canonicalDir, format = "json") {
|
|
@@ -3404,7 +3438,7 @@ function parseLockPayload(text, format) {
|
|
|
3404
3438
|
function readPerSkillLockfile(canonicalDir) {
|
|
3405
3439
|
const jsonPath = perSkillLockfilePath(canonicalDir, "json");
|
|
3406
3440
|
const yamlPath = perSkillLockfilePath(canonicalDir, "yaml");
|
|
3407
|
-
const raw =
|
|
3441
|
+
const raw = fs7.existsSync(jsonPath) ? parseLockPayload(fs7.readFileSync(jsonPath, "utf8"), "json") : fs7.existsSync(yamlPath) ? parseLockPayload(fs7.readFileSync(yamlPath, "utf8"), "yaml") : null;
|
|
3408
3442
|
if (!raw) {
|
|
3409
3443
|
return null;
|
|
3410
3444
|
}
|
|
@@ -3419,18 +3453,18 @@ function readPerSkillLockfile(canonicalDir) {
|
|
|
3419
3453
|
function isSkillDirEntry(entry) {
|
|
3420
3454
|
return entry.isDirectory() || entry.isSymbolicLink();
|
|
3421
3455
|
}
|
|
3422
|
-
function
|
|
3456
|
+
function listInstalledResourceDirs(_kind, globalInstall, cwd) {
|
|
3423
3457
|
const out = /* @__PURE__ */ new Set();
|
|
3424
3458
|
for (const agent of Object.keys(AGENTS)) {
|
|
3425
|
-
const
|
|
3426
|
-
if (!
|
|
3459
|
+
const resourceRoot = getAgentSkillsDir(agent, globalInstall, cwd);
|
|
3460
|
+
if (!fs7.existsSync(resourceRoot) || !fs7.statSync(resourceRoot).isDirectory()) {
|
|
3427
3461
|
continue;
|
|
3428
3462
|
}
|
|
3429
|
-
for (const entry of
|
|
3463
|
+
for (const entry of fs7.readdirSync(resourceRoot, { withFileTypes: true })) {
|
|
3430
3464
|
if (!isSkillDirEntry(entry)) {
|
|
3431
3465
|
continue;
|
|
3432
3466
|
}
|
|
3433
|
-
out.add(path9.join(
|
|
3467
|
+
out.add(path9.join(resourceRoot, entry.name));
|
|
3434
3468
|
}
|
|
3435
3469
|
}
|
|
3436
3470
|
return [...out];
|
|
@@ -3440,8 +3474,11 @@ function lockEntrySortTime(entry) {
|
|
|
3440
3474
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
3441
3475
|
}
|
|
3442
3476
|
function readLockfile(globalInstall, cwd) {
|
|
3477
|
+
return readResourceLockfile("skill", globalInstall, cwd);
|
|
3478
|
+
}
|
|
3479
|
+
function readResourceLockfile(kind, globalInstall, cwd) {
|
|
3443
3480
|
const entriesBySkill = /* @__PURE__ */ new Map();
|
|
3444
|
-
for (const skillDir of
|
|
3481
|
+
for (const skillDir of listInstalledResourceDirs(kind, globalInstall, cwd)) {
|
|
3445
3482
|
const entry = readPerSkillLockfile(skillDir);
|
|
3446
3483
|
if (!entry || typeof entry.skillName !== "string") {
|
|
3447
3484
|
continue;
|
|
@@ -3453,9 +3490,7 @@ function readLockfile(globalInstall, cwd) {
|
|
|
3453
3490
|
}
|
|
3454
3491
|
return {
|
|
3455
3492
|
version: 1,
|
|
3456
|
-
entries: [...entriesBySkill.values()].sort(
|
|
3457
|
-
(a, b) => a.skillName.localeCompare(b.skillName)
|
|
3458
|
-
)
|
|
3493
|
+
entries: [...entriesBySkill.values()].sort((a, b) => a.skillName.localeCompare(b.skillName))
|
|
3459
3494
|
};
|
|
3460
3495
|
}
|
|
3461
3496
|
|
|
@@ -3492,9 +3527,7 @@ function buildCheckDriftSummaryLines(options) {
|
|
|
3492
3527
|
function toCheckOptions(options) {
|
|
3493
3528
|
const maxDownloadBytes = options.maxDownloadBytes ? Number(options.maxDownloadBytes) : void 0;
|
|
3494
3529
|
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
3495
|
-
throw new Error(
|
|
3496
|
-
`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`
|
|
3497
|
-
);
|
|
3530
|
+
throw new Error(`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`);
|
|
3498
3531
|
}
|
|
3499
3532
|
return {
|
|
3500
3533
|
global: Boolean(options.global),
|
|
@@ -3599,9 +3632,7 @@ async function executeCheck(options) {
|
|
|
3599
3632
|
detailLines.push("");
|
|
3600
3633
|
}
|
|
3601
3634
|
detailLines.push(` ${kind}`);
|
|
3602
|
-
for (const row of [...rows].sort(
|
|
3603
|
-
(a, b) => a.skillName.localeCompare(b.skillName)
|
|
3604
|
-
)) {
|
|
3635
|
+
for (const row of [...rows].sort((a, b) => a.skillName.localeCompare(b.skillName))) {
|
|
3605
3636
|
detailLines.push(` ${row.skillName}: ${row.detail}`);
|
|
3606
3637
|
}
|
|
3607
3638
|
}
|
|
@@ -3619,9 +3650,7 @@ async function executeCheck(options) {
|
|
|
3619
3650
|
if (conflicts.length > 0) {
|
|
3620
3651
|
conflictLines.push("Local/global conflicts (local preferred):");
|
|
3621
3652
|
for (const conflict of conflicts) {
|
|
3622
|
-
conflictLines.push(
|
|
3623
|
-
` ${conflict.skillName}: winner=${conflict.winner}`
|
|
3624
|
-
);
|
|
3653
|
+
conflictLines.push(` ${conflict.skillName}: winner=${conflict.winner}`);
|
|
3625
3654
|
}
|
|
3626
3655
|
}
|
|
3627
3656
|
if (transitiveConflicts.length > 0) {
|
|
@@ -3651,9 +3680,7 @@ async function executeCheck(options) {
|
|
|
3651
3680
|
}
|
|
3652
3681
|
const updateSkillNames = [
|
|
3653
3682
|
...new Set(
|
|
3654
|
-
drift.filter(
|
|
3655
|
-
(item) => item.kind === "changed-source" || item.kind === "local-modified"
|
|
3656
|
-
).map((item) => item.skillName)
|
|
3683
|
+
drift.filter((item) => item.kind === "changed-source" || item.kind === "local-modified").map((item) => item.skillName)
|
|
3657
3684
|
)
|
|
3658
3685
|
].sort((a, b) => a.localeCompare(b));
|
|
3659
3686
|
const migrateSkillNames = [
|
|
@@ -3666,9 +3693,7 @@ async function executeCheck(options) {
|
|
|
3666
3693
|
if (migrateSkillNames.length > 0) {
|
|
3667
3694
|
lines.push("Migration required:");
|
|
3668
3695
|
for (const skillName of migrateSkillNames) {
|
|
3669
|
-
lines.push(
|
|
3670
|
-
`skillspp update ${skillName} --migrate <new-skill-source>`
|
|
3671
|
-
);
|
|
3696
|
+
lines.push(`skillspp update ${skillName} --migrate <new-skill-source>`);
|
|
3672
3697
|
}
|
|
3673
3698
|
}
|
|
3674
3699
|
if (updateSkillNames.length > 0) {
|
|
@@ -3717,9 +3742,7 @@ import { Command as Command4 } from "commander";
|
|
|
3717
3742
|
function toFindOptions(options) {
|
|
3718
3743
|
const maxDownloadBytes = options.maxDownloadBytes ? Number(options.maxDownloadBytes) : void 0;
|
|
3719
3744
|
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
3720
|
-
throw new Error(
|
|
3721
|
-
`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`
|
|
3722
|
-
);
|
|
3745
|
+
throw new Error(`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`);
|
|
3723
3746
|
}
|
|
3724
3747
|
return {
|
|
3725
3748
|
allowHost: options.allowHost?.map((item) => item.toLowerCase()),
|
|
@@ -3793,18 +3816,12 @@ async function executeFind(source, query, options) {
|
|
|
3793
3816
|
filtered = inventory.skills.filter((item) => matchesQuery(item.name, item.description, query)).sort((a, b) => a.name.localeCompare(b.name));
|
|
3794
3817
|
} catch (error) {
|
|
3795
3818
|
hideLoader();
|
|
3796
|
-
await renderStaticScreen([
|
|
3797
|
-
failedStepsSection(["failed to apply query filter"])
|
|
3798
|
-
]);
|
|
3819
|
+
await renderStaticScreen([failedStepsSection(["failed to apply query filter"])]);
|
|
3799
3820
|
throw error;
|
|
3800
3821
|
}
|
|
3801
3822
|
hideLoader();
|
|
3802
3823
|
const flowSections = [
|
|
3803
|
-
completedStepsSection([
|
|
3804
|
-
"source parsed",
|
|
3805
|
-
"skill inventory fetched",
|
|
3806
|
-
"query filter applied"
|
|
3807
|
-
]),
|
|
3824
|
+
completedStepsSection(["source parsed", "skill inventory fetched", "query filter applied"]),
|
|
3808
3825
|
sourceSection(shortenHomePath(inventory.sourceLabel))
|
|
3809
3826
|
];
|
|
3810
3827
|
const queryTrimmed = query && query.trim().length > 0 ? query : "";
|
|
@@ -3905,7 +3922,7 @@ function registerFindCommand(program, ctx) {
|
|
|
3905
3922
|
}
|
|
3906
3923
|
|
|
3907
3924
|
// src/commands/init.ts
|
|
3908
|
-
import
|
|
3925
|
+
import fs9 from "node:fs";
|
|
3909
3926
|
import path11 from "node:path";
|
|
3910
3927
|
import { Command as Command5 } from "commander";
|
|
3911
3928
|
|
|
@@ -3961,7 +3978,7 @@ function buildAgentConfigScaffoldPlan(agents, input) {
|
|
|
3961
3978
|
}
|
|
3962
3979
|
|
|
3963
3980
|
// ../../packages/core/src/runtime/installer-scaffold.ts
|
|
3964
|
-
import
|
|
3981
|
+
import fs8 from "node:fs";
|
|
3965
3982
|
import path10 from "node:path";
|
|
3966
3983
|
import YAML3 from "yaml";
|
|
3967
3984
|
function installerConfigSkeleton() {
|
|
@@ -3973,7 +3990,7 @@ function installerConfigSkeleton() {
|
|
|
3973
3990
|
};
|
|
3974
3991
|
}
|
|
3975
3992
|
function isFile(filePath) {
|
|
3976
|
-
return
|
|
3993
|
+
return fs8.existsSync(filePath) && fs8.statSync(filePath).isFile();
|
|
3977
3994
|
}
|
|
3978
3995
|
function getInstallerConfigState(skillDir) {
|
|
3979
3996
|
const yamlPath = path10.join(skillDir, "skill-installer.yaml");
|
|
@@ -4000,7 +4017,7 @@ function scaffoldInstallerConfigFile(skillDir, format) {
|
|
|
4000
4017
|
}
|
|
4001
4018
|
const content = format === "yaml" ? YAML3.stringify(installerConfigSkeleton()) : JSON.stringify(installerConfigSkeleton(), null, 2);
|
|
4002
4019
|
const destinationPath = format === "yaml" ? state.yamlPath : state.jsonPath;
|
|
4003
|
-
|
|
4020
|
+
fs8.writeFileSync(destinationPath, `${content}
|
|
4004
4021
|
`, "utf8");
|
|
4005
4022
|
return { created: true, filePath: destinationPath };
|
|
4006
4023
|
}
|
|
@@ -4021,6 +4038,7 @@ var INIT_AGENTS_SELECTION_VIEW = {
|
|
|
4021
4038
|
instructionLine: "Select agents (space to toggle)",
|
|
4022
4039
|
labelWidth: 30,
|
|
4023
4040
|
descWidth: 40,
|
|
4041
|
+
maxVisibleRows: 10,
|
|
4024
4042
|
minWidth: 74,
|
|
4025
4043
|
defaultHints: INIT_AGENTS_KEY_HINTS
|
|
4026
4044
|
};
|
|
@@ -4068,9 +4086,7 @@ async function chooseInitOne(options) {
|
|
|
4068
4086
|
description: choice.description
|
|
4069
4087
|
}))
|
|
4070
4088
|
);
|
|
4071
|
-
const labelByValue = new Map(
|
|
4072
|
-
options.choices.map((choice) => [choice.value, choice.label])
|
|
4073
|
-
);
|
|
4089
|
+
const labelByValue = new Map(options.choices.map((choice) => [choice.value, choice.label]));
|
|
4074
4090
|
const selected = await runOneSelectionStep({
|
|
4075
4091
|
interactive: true,
|
|
4076
4092
|
rows,
|
|
@@ -4160,11 +4176,7 @@ async function collectAnswers(options) {
|
|
|
4160
4176
|
const answers = { ...defaults };
|
|
4161
4177
|
for (const question of initQuestions) {
|
|
4162
4178
|
if (!question.when(interactive)) {
|
|
4163
|
-
setAnswer(
|
|
4164
|
-
answers,
|
|
4165
|
-
question.id,
|
|
4166
|
-
question.normalize(answers[question.id], defaults)
|
|
4167
|
-
);
|
|
4179
|
+
setAnswer(answers, question.id, question.normalize(answers[question.id], defaults));
|
|
4168
4180
|
continue;
|
|
4169
4181
|
}
|
|
4170
4182
|
const value = await question.ask(defaults);
|
|
@@ -4198,9 +4210,7 @@ async function resolveInitAgents(options) {
|
|
|
4198
4210
|
return selectedIds;
|
|
4199
4211
|
}
|
|
4200
4212
|
if (!interactive) {
|
|
4201
|
-
throw new Error(
|
|
4202
|
-
"Missing --agent in non-interactive mode. Provide at least one agent."
|
|
4203
|
-
);
|
|
4213
|
+
throw new Error("Missing --agent in non-interactive mode. Provide at least one agent.");
|
|
4204
4214
|
}
|
|
4205
4215
|
const selected = await runManySelectionStep({
|
|
4206
4216
|
interactive,
|
|
@@ -4302,9 +4312,7 @@ async function executeInit(options, hooks) {
|
|
|
4302
4312
|
];
|
|
4303
4313
|
if (agentConfigPlan.unmapped.length > 0) {
|
|
4304
4314
|
summaryLines.push(
|
|
4305
|
-
`Unmapped agents (skipped): ${agentConfigPlan.unmapped.join(
|
|
4306
|
-
", "
|
|
4307
|
-
)} (no scaffold mapping)`
|
|
4315
|
+
`Unmapped agents (skipped): ${agentConfigPlan.unmapped.join(", ")} (no scaffold mapping)`
|
|
4308
4316
|
);
|
|
4309
4317
|
}
|
|
4310
4318
|
summaryLines.push("");
|
|
@@ -4333,16 +4341,16 @@ async function executeInit(options, hooks) {
|
|
|
4333
4341
|
const completedSteps = [];
|
|
4334
4342
|
let failedLabel = "failed to create directory";
|
|
4335
4343
|
try {
|
|
4336
|
-
const createDirectory = !
|
|
4337
|
-
|
|
4344
|
+
const createDirectory = !fs9.existsSync(skillDir);
|
|
4345
|
+
fs9.mkdirSync(skillDir, { recursive: true });
|
|
4338
4346
|
if (createDirectory) {
|
|
4339
4347
|
completedSteps.push("directory created");
|
|
4340
4348
|
}
|
|
4341
4349
|
failedLabel = "failed to write SKILL.md";
|
|
4342
|
-
if (
|
|
4350
|
+
if (fs9.existsSync(skillFile)) {
|
|
4343
4351
|
throw new Error(`SKILL.md already exists at: ${skillFile}`);
|
|
4344
4352
|
}
|
|
4345
|
-
|
|
4353
|
+
fs9.writeFileSync(skillFile, `${renderSkillContent(answers)}
|
|
4346
4354
|
`, "utf8");
|
|
4347
4355
|
completedSteps.push("SKILL.md written");
|
|
4348
4356
|
failedLabel = "failed to scaffold installer config";
|
|
@@ -4351,11 +4359,11 @@ async function executeInit(options, hooks) {
|
|
|
4351
4359
|
failedLabel = "failed to scaffold agent config";
|
|
4352
4360
|
for (const row of agentConfigPlan.mapped) {
|
|
4353
4361
|
const destination = ensureInsideSkillDir(skillDir, row.path);
|
|
4354
|
-
if (
|
|
4362
|
+
if (fs9.existsSync(destination)) {
|
|
4355
4363
|
throw new Error(`Agent config already exists at: ${destination}`);
|
|
4356
4364
|
}
|
|
4357
|
-
|
|
4358
|
-
|
|
4365
|
+
fs9.mkdirSync(path11.dirname(destination), { recursive: true });
|
|
4366
|
+
fs9.writeFileSync(destination, row.content, "utf8");
|
|
4359
4367
|
}
|
|
4360
4368
|
if (agentConfigPlan.mapped.length > 0) {
|
|
4361
4369
|
completedSteps.push("agent configs scaffolded");
|
|
@@ -4372,27 +4380,24 @@ async function executeInit(options, hooks) {
|
|
|
4372
4380
|
await renderStaticScreen(sections);
|
|
4373
4381
|
}
|
|
4374
4382
|
function configureInitCommand(command, action) {
|
|
4375
|
-
return command.description("Create a new SKILL.md template").argument("[name]", "Optional skill directory/name").option("-a, --agent <agents...>", "Target agent(s) for config scaffolding").option(
|
|
4376
|
-
"--yaml",
|
|
4377
|
-
"Create skill-installer.yaml when scaffolding installer config"
|
|
4378
|
-
).option("--non-interactive", "Disable prompts").action(action);
|
|
4383
|
+
return command.description("Create a new SKILL.md template").argument("[name]", "Optional skill directory/name").option("-a, --agent <agents...>", "Target agent(s) for config scaffolding").option("--yaml", "Create skill-installer.yaml when scaffolding installer config").option("--non-interactive", "Disable prompts").action(action);
|
|
4379
4384
|
}
|
|
4380
4385
|
function registerInitCommand(program, ctx) {
|
|
4381
4386
|
configureInitCommand(
|
|
4382
4387
|
program.command("init"),
|
|
4383
|
-
ctx.wrapAction(
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
await executeInit({
|
|
4388
|
+
ctx.wrapAction("init", async (name, options) => {
|
|
4389
|
+
await executeInit(
|
|
4390
|
+
{
|
|
4387
4391
|
nameArg: name,
|
|
4388
4392
|
nonInteractive: Boolean(options.nonInteractive),
|
|
4389
4393
|
yaml: Boolean(options.yaml),
|
|
4390
4394
|
agent: options.agent
|
|
4391
|
-
},
|
|
4395
|
+
},
|
|
4396
|
+
{
|
|
4392
4397
|
emitCommandEvent: (event) => ctx.emitCommandEvent("init", event)
|
|
4393
|
-
}
|
|
4394
|
-
|
|
4395
|
-
)
|
|
4398
|
+
}
|
|
4399
|
+
);
|
|
4400
|
+
})
|
|
4396
4401
|
);
|
|
4397
4402
|
}
|
|
4398
4403
|
|
|
@@ -4419,6 +4424,7 @@ var LIST_AGENTS_SELECTION_VIEW = {
|
|
|
4419
4424
|
instructionLine: "Select agents to list (space to toggle)",
|
|
4420
4425
|
labelWidth: AGENT_LABEL_WIDTH,
|
|
4421
4426
|
descWidth: AGENT_DESC_WIDTH2,
|
|
4427
|
+
maxVisibleRows: 10,
|
|
4422
4428
|
minWidth: 74,
|
|
4423
4429
|
defaultHints: LIST_AGENTS_KEY_HINTS
|
|
4424
4430
|
};
|
|
@@ -4450,10 +4456,7 @@ function renderListScopePanel(options) {
|
|
|
4450
4456
|
function renderListInventorySummary(skillCount) {
|
|
4451
4457
|
return panelSection({
|
|
4452
4458
|
title: "Inventory Summary",
|
|
4453
|
-
lines: [
|
|
4454
|
-
`${skillCount} unique skills found`,
|
|
4455
|
-
"Grouped by (skill name + resolved path)"
|
|
4456
|
-
],
|
|
4459
|
+
lines: [`${skillCount} unique skills found`, "Grouped by (skill name + resolved path)"],
|
|
4457
4460
|
style: "square",
|
|
4458
4461
|
minWidth: 74
|
|
4459
4462
|
});
|
|
@@ -4467,17 +4470,12 @@ function renderListInstalledSkillsPanel(rows) {
|
|
|
4467
4470
|
return a.resolvedPath.localeCompare(b.resolvedPath);
|
|
4468
4471
|
});
|
|
4469
4472
|
const uniqueSkillCount = new Set(sortedRows.map((row) => row.name)).size;
|
|
4470
|
-
const targetCount = sortedRows.reduce(
|
|
4471
|
-
(count, row) => count + row.agents.length,
|
|
4472
|
-
0
|
|
4473
|
-
);
|
|
4473
|
+
const targetCount = sortedRows.reduce((count, row) => count + row.agents.length, 0);
|
|
4474
4474
|
const lines = [];
|
|
4475
4475
|
lines.push(
|
|
4476
4476
|
`${bold("Skills:")} ${dim(uniqueSkillCount.toString())} ${bold(
|
|
4477
4477
|
"Entries:"
|
|
4478
|
-
)} ${dim(sortedRows.length.toString())} ${bold("Targets:")} ${dim(
|
|
4479
|
-
targetCount.toString()
|
|
4480
|
-
)}`
|
|
4478
|
+
)} ${dim(sortedRows.length.toString())} ${bold("Targets:")} ${dim(targetCount.toString())}`
|
|
4481
4479
|
);
|
|
4482
4480
|
lines.push("");
|
|
4483
4481
|
lines.push("Targets");
|
|
@@ -4490,11 +4488,7 @@ function renderListInstalledSkillsPanel(rows) {
|
|
|
4490
4488
|
}
|
|
4491
4489
|
const agents = [...row.agents].sort((a, b) => a.localeCompare(b));
|
|
4492
4490
|
for (const agent of agents) {
|
|
4493
|
-
lines.push(
|
|
4494
|
-
` - ${agent.padEnd(16, " ")} ${dim(
|
|
4495
|
-
shortenHomePath(row.resolvedPath)
|
|
4496
|
-
)}`
|
|
4497
|
-
);
|
|
4491
|
+
lines.push(` - ${agent.padEnd(16, " ")} ${dim(shortenHomePath(row.resolvedPath))}`);
|
|
4498
4492
|
}
|
|
4499
4493
|
}
|
|
4500
4494
|
return panelSection({
|
|
@@ -4616,7 +4610,7 @@ function registerListCommand(program, ctx) {
|
|
|
4616
4610
|
}
|
|
4617
4611
|
|
|
4618
4612
|
// src/commands/remove.ts
|
|
4619
|
-
import
|
|
4613
|
+
import fs10 from "node:fs";
|
|
4620
4614
|
import path12 from "node:path";
|
|
4621
4615
|
import { Command as Command7 } from "commander";
|
|
4622
4616
|
function toRemoveOptions(options) {
|
|
@@ -4658,6 +4652,7 @@ var REMOVE_SKILLS_SELECTION_VIEW = {
|
|
|
4658
4652
|
instructionLine: "Select skills to uninstall (space to toggle)",
|
|
4659
4653
|
labelWidth: SKILL_NAME_WIDTH2,
|
|
4660
4654
|
descWidth: SKILL_DESC_WIDTH2,
|
|
4655
|
+
maxVisibleRows: 10,
|
|
4661
4656
|
minWidth: 74,
|
|
4662
4657
|
defaultHints: REMOVE_SKILLS_KEY_HINTS
|
|
4663
4658
|
};
|
|
@@ -4667,6 +4662,7 @@ var REMOVE_AGENTS_SELECTION_VIEW = {
|
|
|
4667
4662
|
instructionLine: "Select agents to remove from (space to toggle)",
|
|
4668
4663
|
labelWidth: AGENT_NAME_WIDTH2,
|
|
4669
4664
|
descWidth: AGENT_DESC_WIDTH3,
|
|
4665
|
+
maxVisibleRows: 10,
|
|
4670
4666
|
minWidth: 74,
|
|
4671
4667
|
defaultHints: REMOVE_AGENTS_KEY_HINTS
|
|
4672
4668
|
};
|
|
@@ -4717,7 +4713,12 @@ function renderRemoveConfirmPanel(options) {
|
|
|
4717
4713
|
);
|
|
4718
4714
|
}
|
|
4719
4715
|
function renderRemoveUninstallSummaryBox(options) {
|
|
4720
|
-
return uninstallSummarySection(
|
|
4716
|
+
return uninstallSummarySection({
|
|
4717
|
+
globalInstall: options.globalInstall,
|
|
4718
|
+
itemNames: options.skillNames,
|
|
4719
|
+
itemLabel: "Skills",
|
|
4720
|
+
agentDisplayNames: options.agentDisplayNames
|
|
4721
|
+
});
|
|
4721
4722
|
}
|
|
4722
4723
|
function isSkillEntry(entry) {
|
|
4723
4724
|
return entry.isDirectory() || entry.isSymbolicLink();
|
|
@@ -4728,11 +4729,11 @@ function buildInstallIndex(globalInstall, cwd) {
|
|
|
4728
4729
|
const allAgents = Object.keys(AGENTS);
|
|
4729
4730
|
for (const agent of allAgents) {
|
|
4730
4731
|
const dir = getAgentSkillsDir(agent, globalInstall, cwd);
|
|
4731
|
-
if (!
|
|
4732
|
+
if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
|
|
4732
4733
|
continue;
|
|
4733
4734
|
}
|
|
4734
4735
|
const names = /* @__PURE__ */ new Set();
|
|
4735
|
-
for (const entry of
|
|
4736
|
+
for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
|
|
4736
4737
|
if (!isSkillEntry(entry)) {
|
|
4737
4738
|
continue;
|
|
4738
4739
|
}
|
|
@@ -4763,9 +4764,7 @@ function orderAgentsForDisplay(agents) {
|
|
|
4763
4764
|
}
|
|
4764
4765
|
function resolveCandidateAgentsForSkills(skillNames, index) {
|
|
4765
4766
|
return orderAgentsForDisplay(
|
|
4766
|
-
skillNames.flatMap(
|
|
4767
|
-
(name) => Array.from(index.agentsBySkill.get(name) ?? [])
|
|
4768
|
-
)
|
|
4767
|
+
skillNames.flatMap((name) => Array.from(index.agentsBySkill.get(name) ?? []))
|
|
4769
4768
|
);
|
|
4770
4769
|
}
|
|
4771
4770
|
async function executeRemove(positional, options) {
|
|
@@ -4777,9 +4776,7 @@ async function executeRemove(positional, options) {
|
|
|
4777
4776
|
try {
|
|
4778
4777
|
index = buildInstallIndex(globalInstall, cwd);
|
|
4779
4778
|
} catch (error) {
|
|
4780
|
-
await renderStaticScreen([
|
|
4781
|
-
failedStepsSection(["failed to index installed skills"])
|
|
4782
|
-
]);
|
|
4779
|
+
await renderStaticScreen([failedStepsSection(["failed to index installed skills"])]);
|
|
4783
4780
|
throw error;
|
|
4784
4781
|
}
|
|
4785
4782
|
if (index.agentsBySkill.size === 0) {
|
|
@@ -4816,20 +4813,14 @@ async function executeRemove(positional, options) {
|
|
|
4816
4813
|
try {
|
|
4817
4814
|
if (options.agent && options.agent.length > 0) {
|
|
4818
4815
|
if (options.agent.includes("*")) {
|
|
4819
|
-
candidateAgents = orderAgentsForDisplay(
|
|
4820
|
-
Array.from(index.skillsByAgent.keys())
|
|
4821
|
-
);
|
|
4816
|
+
candidateAgents = orderAgentsForDisplay(Array.from(index.skillsByAgent.keys()));
|
|
4822
4817
|
agents = candidateAgents;
|
|
4823
4818
|
} else {
|
|
4824
4819
|
const resolved = resolveAgents(options.agent);
|
|
4825
|
-
const outOfScopeAgents = resolved.filter(
|
|
4826
|
-
(agent) => !scopeAllowedAgents.has(agent)
|
|
4827
|
-
);
|
|
4820
|
+
const outOfScopeAgents = resolved.filter((agent) => !scopeAllowedAgents.has(agent));
|
|
4828
4821
|
if (outOfScopeAgents.length > 0) {
|
|
4829
4822
|
throw new Error(
|
|
4830
|
-
`Agent(s) not available in ${scope} scope: ${outOfScopeAgents.join(
|
|
4831
|
-
", "
|
|
4832
|
-
)}`
|
|
4823
|
+
`Agent(s) not available in ${scope} scope: ${outOfScopeAgents.join(", ")}`
|
|
4833
4824
|
);
|
|
4834
4825
|
}
|
|
4835
4826
|
candidateAgents = resolved;
|
|
@@ -4848,16 +4839,12 @@ async function executeRemove(positional, options) {
|
|
|
4848
4839
|
);
|
|
4849
4840
|
}
|
|
4850
4841
|
}
|
|
4851
|
-
candidateAgents = candidateAgents.filter(
|
|
4852
|
-
(agent) => scopeAllowedAgents.has(agent)
|
|
4853
|
-
);
|
|
4842
|
+
candidateAgents = candidateAgents.filter((agent) => scopeAllowedAgents.has(agent));
|
|
4854
4843
|
agents = agents.filter((agent) => scopeAllowedAgents.has(agent));
|
|
4855
4844
|
candidateAgents = orderAgentsForDisplay(candidateAgents);
|
|
4856
4845
|
agents = orderAgentsForDisplay(agents);
|
|
4857
4846
|
} catch (error) {
|
|
4858
|
-
await renderStaticScreen([
|
|
4859
|
-
failedStepsSection(["failed to resolve target candidates"])
|
|
4860
|
-
]);
|
|
4847
|
+
await renderStaticScreen([failedStepsSection(["failed to resolve target candidates"])]);
|
|
4861
4848
|
throw error;
|
|
4862
4849
|
}
|
|
4863
4850
|
await renderStaticScreen([
|
|
@@ -4893,9 +4880,7 @@ async function executeRemove(positional, options) {
|
|
|
4893
4880
|
if (!options.agent || options.agent.length === 0) {
|
|
4894
4881
|
if (!options.all) {
|
|
4895
4882
|
candidateAgents = resolveCandidateAgentsForSkills(finalSkills, index);
|
|
4896
|
-
candidateAgents = candidateAgents.filter(
|
|
4897
|
-
(agent) => scopeAllowedAgents.has(agent)
|
|
4898
|
-
);
|
|
4883
|
+
candidateAgents = candidateAgents.filter((agent) => scopeAllowedAgents.has(agent));
|
|
4899
4884
|
candidateAgents = orderAgentsForDisplay(candidateAgents);
|
|
4900
4885
|
}
|
|
4901
4886
|
if (candidateAgents.length === 0) {
|
|
@@ -4908,15 +4893,10 @@ async function executeRemove(positional, options) {
|
|
|
4908
4893
|
}
|
|
4909
4894
|
}
|
|
4910
4895
|
const shouldPromptAgents = interactive && (!options.agent || options.agent.length === 0);
|
|
4911
|
-
const visibleCandidateAgents = candidateAgents.filter(
|
|
4912
|
-
(agent) => scopeAllowedAgents.has(agent)
|
|
4913
|
-
);
|
|
4896
|
+
const visibleCandidateAgents = candidateAgents.filter((agent) => scopeAllowedAgents.has(agent));
|
|
4914
4897
|
const selectedAgentIds = await runManySelectionStep({
|
|
4915
4898
|
interactive,
|
|
4916
|
-
rows: buildRemoveAgentSelectionRows(
|
|
4917
|
-
visibleCandidateAgents,
|
|
4918
|
-
scopedAgentRowsById
|
|
4919
|
-
),
|
|
4899
|
+
rows: buildRemoveAgentSelectionRows(visibleCandidateAgents, scopedAgentRowsById),
|
|
4920
4900
|
selectedIds: agents,
|
|
4921
4901
|
shouldPrompt: shouldPromptAgents,
|
|
4922
4902
|
prompt: {
|
|
@@ -4934,9 +4914,7 @@ async function executeRemove(positional, options) {
|
|
|
4934
4914
|
})
|
|
4935
4915
|
});
|
|
4936
4916
|
const selectedAgentSet = new Set(selectedAgentIds);
|
|
4937
|
-
agents = visibleCandidateAgents.filter(
|
|
4938
|
-
(agent) => selectedAgentSet.has(agent)
|
|
4939
|
-
);
|
|
4917
|
+
agents = visibleCandidateAgents.filter((agent) => selectedAgentSet.has(agent));
|
|
4940
4918
|
if (options.all) {
|
|
4941
4919
|
const all = /* @__PURE__ */ new Set();
|
|
4942
4920
|
for (const agent of agents) {
|
|
@@ -5022,10 +5000,10 @@ async function executeRemove(positional, options) {
|
|
|
5022
5000
|
try {
|
|
5023
5001
|
for (const agent of agents) {
|
|
5024
5002
|
const dir = getAgentSkillsDir(agent, globalInstall, cwd);
|
|
5025
|
-
if (!
|
|
5003
|
+
if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
|
|
5026
5004
|
continue;
|
|
5027
5005
|
}
|
|
5028
|
-
for (const entry of
|
|
5006
|
+
for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
|
|
5029
5007
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
5030
5008
|
continue;
|
|
5031
5009
|
}
|
|
@@ -5034,7 +5012,7 @@ async function executeRemove(positional, options) {
|
|
|
5034
5012
|
}
|
|
5035
5013
|
const fullPath = path12.join(dir, entry.name);
|
|
5036
5014
|
failedLabel = `failed to remove ${entry.name} from ${agent}`;
|
|
5037
|
-
|
|
5015
|
+
fs10.rmSync(fullPath, { recursive: true, force: true });
|
|
5038
5016
|
removedCount += 1;
|
|
5039
5017
|
completedRemovalSteps.push(`removed ${entry.name} from ${agent}`);
|
|
5040
5018
|
}
|
|
@@ -5056,12 +5034,9 @@ function configureRemoveCommand(command, action) {
|
|
|
5056
5034
|
function registerRemoveCommand(program, ctx) {
|
|
5057
5035
|
configureRemoveCommand(
|
|
5058
5036
|
program.command("remove").alias("rm"),
|
|
5059
|
-
ctx.wrapAction(
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
await executeRemove(skills, toRemoveOptions(options));
|
|
5063
|
-
}
|
|
5064
|
-
)
|
|
5037
|
+
ctx.wrapAction("remove", async (skills, options) => {
|
|
5038
|
+
await executeRemove(skills, toRemoveOptions(options));
|
|
5039
|
+
})
|
|
5065
5040
|
);
|
|
5066
5041
|
}
|
|
5067
5042
|
|
|
@@ -5070,9 +5045,7 @@ import { Command as Command8 } from "commander";
|
|
|
5070
5045
|
function toUpdateOptions(options) {
|
|
5071
5046
|
const maxDownloadBytes = options.maxDownloadBytes ? Number(options.maxDownloadBytes) : void 0;
|
|
5072
5047
|
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
5073
|
-
throw new Error(
|
|
5074
|
-
`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`
|
|
5075
|
-
);
|
|
5048
|
+
throw new Error(`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`);
|
|
5076
5049
|
}
|
|
5077
5050
|
const lockFormat = options.lockFormat;
|
|
5078
5051
|
if (lockFormat && lockFormat !== "json" && lockFormat !== "yaml") {
|
|
@@ -5106,6 +5079,7 @@ var UPDATE_SKILLS_SELECTION_VIEW = {
|
|
|
5106
5079
|
instructionLine: "Select skills (space to toggle)",
|
|
5107
5080
|
labelWidth: 32,
|
|
5108
5081
|
descWidth: 28,
|
|
5082
|
+
maxVisibleRows: 10,
|
|
5109
5083
|
minWidth: 74,
|
|
5110
5084
|
defaultHints: UPDATE_SKILLS_KEY_HINTS
|
|
5111
5085
|
};
|
|
@@ -5120,11 +5094,7 @@ function buildUpdateRows(assessments) {
|
|
|
5120
5094
|
});
|
|
5121
5095
|
}
|
|
5122
5096
|
function renderUpdateSkillsClosedPanel(rows, selectedIds) {
|
|
5123
|
-
return manySelectionClosedSection(
|
|
5124
|
-
UPDATE_SKILLS_SELECTION_VIEW,
|
|
5125
|
-
rows,
|
|
5126
|
-
selectedIds
|
|
5127
|
-
);
|
|
5097
|
+
return manySelectionClosedSection(UPDATE_SKILLS_SELECTION_VIEW, rows, selectedIds);
|
|
5128
5098
|
}
|
|
5129
5099
|
function buildUpdateDriftSummaryLines(options) {
|
|
5130
5100
|
return [
|
|
@@ -5201,10 +5171,7 @@ async function executeMigrateUpdate(options) {
|
|
|
5201
5171
|
}
|
|
5202
5172
|
hideLoader();
|
|
5203
5173
|
await renderStaticScreen([
|
|
5204
|
-
completedStepsSection([
|
|
5205
|
-
`migrated ${options.skillName}`,
|
|
5206
|
-
"lockfile written"
|
|
5207
|
-
]),
|
|
5174
|
+
completedStepsSection([`migrated ${options.skillName}`, "lockfile written"]),
|
|
5208
5175
|
linesSection(["Migration complete.", `Updated ${options.skillName}.`])
|
|
5209
5176
|
]);
|
|
5210
5177
|
}
|
|
@@ -5216,9 +5183,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5216
5183
|
...options,
|
|
5217
5184
|
skill: mergedSkills
|
|
5218
5185
|
};
|
|
5219
|
-
const requestedSkills = (effectiveOptions.skill || []).filter(
|
|
5220
|
-
(skill) => skill !== "*"
|
|
5221
|
-
);
|
|
5186
|
+
const requestedSkills = (effectiveOptions.skill || []).filter((skill) => skill !== "*");
|
|
5222
5187
|
if (effectiveOptions.migrate) {
|
|
5223
5188
|
if (requestedSkills.length !== 1) {
|
|
5224
5189
|
throw new Error(
|
|
@@ -5264,9 +5229,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5264
5229
|
);
|
|
5265
5230
|
} catch (error) {
|
|
5266
5231
|
hideLoader();
|
|
5267
|
-
await renderStaticScreen([
|
|
5268
|
-
failedStepsSection(["failed to assess drift"])
|
|
5269
|
-
]);
|
|
5232
|
+
await renderStaticScreen([failedStepsSection(["failed to assess drift"])]);
|
|
5270
5233
|
throw error;
|
|
5271
5234
|
}
|
|
5272
5235
|
hideLoader();
|
|
@@ -5279,9 +5242,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5279
5242
|
(item) => item.kind === "changed-source" || item.kind === "local-modified"
|
|
5280
5243
|
);
|
|
5281
5244
|
});
|
|
5282
|
-
const migrateRequired = assessments.filter(
|
|
5283
|
-
(assessment) => assessment.drift.some((item) => item.kind === "migrate-required")
|
|
5284
|
-
).map((assessment) => assessment.entry.skillName).sort((a, b) => a.localeCompare(b));
|
|
5245
|
+
const migrateRequired = assessments.filter((assessment) => assessment.drift.some((item) => item.kind === "migrate-required")).map((assessment) => assessment.entry.skillName).sort((a, b) => a.localeCompare(b));
|
|
5285
5246
|
let changedSourceCount = 0;
|
|
5286
5247
|
let localModifiedCount = 0;
|
|
5287
5248
|
for (const assessment of candidateAssessments) {
|
|
@@ -5320,7 +5281,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5320
5281
|
panelSection({
|
|
5321
5282
|
title: "Migration Required",
|
|
5322
5283
|
lines: migrateRequired.map(
|
|
5323
|
-
(skillName) => `skillspp update ${skillName} --migrate <new-skill-source>`
|
|
5284
|
+
(skillName) => `skillspp update ${colorToken(skillName, "primary")} --migrate <new-skill-source>`
|
|
5324
5285
|
),
|
|
5325
5286
|
style: "square",
|
|
5326
5287
|
minWidth: 74
|
|
@@ -5375,9 +5336,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5375
5336
|
})
|
|
5376
5337
|
]);
|
|
5377
5338
|
if (effectiveOptions.dryRun) {
|
|
5378
|
-
await renderStaticScreen([
|
|
5379
|
-
linesSection(["Dry-run mode: no changes applied."])
|
|
5380
|
-
]);
|
|
5339
|
+
await renderStaticScreen([linesSection(["Dry-run mode: no changes applied."])]);
|
|
5381
5340
|
return;
|
|
5382
5341
|
}
|
|
5383
5342
|
showLoader("updating selected skills");
|
|
@@ -5391,9 +5350,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5391
5350
|
payload: {
|
|
5392
5351
|
cwd,
|
|
5393
5352
|
options: effectiveOptions,
|
|
5394
|
-
selectedSkillNames: selectedAssessments.map(
|
|
5395
|
-
(assessment) => assessment.entry.skillName
|
|
5396
|
-
),
|
|
5353
|
+
selectedSkillNames: selectedAssessments.map((assessment) => assessment.entry.skillName),
|
|
5397
5354
|
lockFormat
|
|
5398
5355
|
}
|
|
5399
5356
|
},
|
|
@@ -5402,9 +5359,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5402
5359
|
if (label === "writing lockfile") {
|
|
5403
5360
|
failedLabel = "failed to write lockfile";
|
|
5404
5361
|
} else if (label.startsWith("updating ")) {
|
|
5405
|
-
failedLabel = `failed to update ${label.slice(
|
|
5406
|
-
"updating ".length
|
|
5407
|
-
)}`;
|
|
5362
|
+
failedLabel = `failed to update ${label.slice("updating ".length)}`;
|
|
5408
5363
|
} else {
|
|
5409
5364
|
failedLabel = "failed to assess selected skills";
|
|
5410
5365
|
}
|
|
@@ -5424,10 +5379,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5424
5379
|
...applied.updatedSkillNames.map((skillName) => `updated ${skillName}`),
|
|
5425
5380
|
"lockfile written"
|
|
5426
5381
|
]),
|
|
5427
|
-
linesSection([
|
|
5428
|
-
"Update complete.",
|
|
5429
|
-
`Updated ${applied.updatedSkillNames.length} skills.`
|
|
5430
|
-
])
|
|
5382
|
+
linesSection(["Update complete.", `Updated ${applied.updatedSkillNames.length} skills.`])
|
|
5431
5383
|
]);
|
|
5432
5384
|
} finally {
|
|
5433
5385
|
hideLoader();
|
|
@@ -5439,23 +5391,20 @@ function configureUpdateCommand(command, action) {
|
|
|
5439
5391
|
function registerUpdateCommand(program, ctx) {
|
|
5440
5392
|
configureUpdateCommand(
|
|
5441
5393
|
program.command("update"),
|
|
5442
|
-
ctx.wrapAction(
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
);
|
|
5452
|
-
}
|
|
5453
|
-
)
|
|
5394
|
+
ctx.wrapAction("update", async (skill, options) => {
|
|
5395
|
+
await executeUpdate(
|
|
5396
|
+
{
|
|
5397
|
+
...toUpdateOptions(options),
|
|
5398
|
+
experimental: ctx.experimental
|
|
5399
|
+
},
|
|
5400
|
+
skill
|
|
5401
|
+
);
|
|
5402
|
+
})
|
|
5454
5403
|
);
|
|
5455
5404
|
}
|
|
5456
5405
|
|
|
5457
5406
|
// src/commands/validate.ts
|
|
5458
|
-
import
|
|
5407
|
+
import fs13 from "node:fs";
|
|
5459
5408
|
import path15 from "node:path";
|
|
5460
5409
|
import os5 from "node:os";
|
|
5461
5410
|
import { Command as Command9 } from "commander";
|
|
@@ -5463,14 +5412,14 @@ import matter2 from "gray-matter";
|
|
|
5463
5412
|
|
|
5464
5413
|
// ../../packages/core/src/runtime/skill-installer.ts
|
|
5465
5414
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
5466
|
-
import
|
|
5415
|
+
import fs12 from "node:fs";
|
|
5467
5416
|
import os4 from "node:os";
|
|
5468
5417
|
import path14 from "node:path";
|
|
5469
5418
|
import YAML4 from "yaml";
|
|
5470
5419
|
import { z } from "zod";
|
|
5471
5420
|
|
|
5472
5421
|
// ../../packages/core/src/runtime/installer-security.ts
|
|
5473
|
-
import
|
|
5422
|
+
import fs11 from "node:fs";
|
|
5474
5423
|
import path13 from "node:path";
|
|
5475
5424
|
function isInsideRoot(rootDir, candidatePath) {
|
|
5476
5425
|
const relative = path13.relative(rootDir, candidatePath);
|
|
@@ -5504,9 +5453,9 @@ function evaluateInstallerLocalDependency(input, _options = {}) {
|
|
|
5504
5453
|
violation: toEscapeViolation(input.source)
|
|
5505
5454
|
};
|
|
5506
5455
|
}
|
|
5507
|
-
if (
|
|
5508
|
-
const realRoot =
|
|
5509
|
-
const realSource =
|
|
5456
|
+
if (fs11.existsSync(resolvedSourcePath)) {
|
|
5457
|
+
const realRoot = fs11.realpathSync.native ? fs11.realpathSync.native(resolvedRoot) : fs11.realpathSync(resolvedRoot);
|
|
5458
|
+
const realSource = fs11.realpathSync.native ? fs11.realpathSync.native(resolvedSourcePath) : fs11.realpathSync(resolvedSourcePath);
|
|
5510
5459
|
if (!isInsideRoot(realRoot, realSource)) {
|
|
5511
5460
|
return {
|
|
5512
5461
|
ok: false,
|
|
@@ -5539,10 +5488,7 @@ function applyMode(result, mode) {
|
|
|
5539
5488
|
};
|
|
5540
5489
|
}
|
|
5541
5490
|
function evaluateInstallerLocalDependencyPolicy(input, mode) {
|
|
5542
|
-
return applyMode(
|
|
5543
|
-
evaluateInstallerLocalDependency(input, { policyMode: "fixed" }),
|
|
5544
|
-
mode
|
|
5545
|
-
);
|
|
5491
|
+
return applyMode(evaluateInstallerLocalDependency(input, { policyMode: "fixed" }), mode);
|
|
5546
5492
|
}
|
|
5547
5493
|
function evaluateHookTrustPolicy(input) {
|
|
5548
5494
|
if (input.sourceType !== "well-known") {
|
|
@@ -5628,9 +5574,7 @@ function sourceLooksLikeRepoShorthand(source) {
|
|
|
5628
5574
|
}
|
|
5629
5575
|
function parseRepoSource(source) {
|
|
5630
5576
|
const withoutProtocol = source.trim().replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
|
5631
|
-
const match = withoutProtocol.match(
|
|
5632
|
-
/^(github\.com|gitlab\.com)\/([^/]+)\/([^/]+?)(?:\.git)?$/
|
|
5633
|
-
);
|
|
5577
|
+
const match = withoutProtocol.match(/^(github\.com|gitlab\.com)\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
5634
5578
|
if (!match) {
|
|
5635
5579
|
throw new Error(`Unsupported repository dependency source: ${source}`);
|
|
5636
5580
|
}
|
|
@@ -5648,9 +5592,7 @@ function deriveDestinationNameFromSource(source) {
|
|
|
5648
5592
|
const parts = parsed.pathname.split("/").filter(Boolean);
|
|
5649
5593
|
const leaf2 = parts[parts.length - 1];
|
|
5650
5594
|
if (!leaf2) {
|
|
5651
|
-
throw new Error(
|
|
5652
|
-
`Cannot derive dependency name from URL source: ${source}`
|
|
5653
|
-
);
|
|
5595
|
+
throw new Error(`Cannot derive dependency name from URL source: ${source}`);
|
|
5654
5596
|
}
|
|
5655
5597
|
return leaf2;
|
|
5656
5598
|
}
|
|
@@ -5674,9 +5616,7 @@ function classifyDependencySource(source) {
|
|
|
5674
5616
|
}
|
|
5675
5617
|
function parseConfigObject(raw) {
|
|
5676
5618
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
5677
|
-
throw new Error(
|
|
5678
|
-
"Invalid skill-installer config: expected an object with top-level keys"
|
|
5679
|
-
);
|
|
5619
|
+
throw new Error("Invalid skill-installer config: expected an object with top-level keys");
|
|
5680
5620
|
}
|
|
5681
5621
|
const parsed = raw;
|
|
5682
5622
|
if (typeof parsed["skill-installer"] !== "undefined") {
|
|
@@ -5717,10 +5657,10 @@ function parseConfigObject(raw) {
|
|
|
5717
5657
|
}
|
|
5718
5658
|
function assertNoLegacyInstallerBlock(skillDir) {
|
|
5719
5659
|
const openAiYamlPath = path14.join(skillDir, "agents", "openai.yaml");
|
|
5720
|
-
if (!
|
|
5660
|
+
if (!fs12.existsSync(openAiYamlPath) || !fs12.statSync(openAiYamlPath).isFile()) {
|
|
5721
5661
|
return;
|
|
5722
5662
|
}
|
|
5723
|
-
const parsed = YAML4.parse(
|
|
5663
|
+
const parsed = YAML4.parse(fs12.readFileSync(openAiYamlPath, "utf8"));
|
|
5724
5664
|
if (parsed && typeof parsed === "object" && typeof parsed["skill-installer"] !== "undefined") {
|
|
5725
5665
|
throw new Error(
|
|
5726
5666
|
"Legacy skill-installer config detected in agents/openai.yaml. Move it to skill-installer.yaml or skill-installer.json."
|
|
@@ -5731,8 +5671,8 @@ function loadInstallerConfig(skillDir) {
|
|
|
5731
5671
|
assertNoLegacyInstallerBlock(skillDir);
|
|
5732
5672
|
const yamlPath = path14.join(skillDir, "skill-installer.yaml");
|
|
5733
5673
|
const jsonPath = path14.join(skillDir, "skill-installer.json");
|
|
5734
|
-
const hasYaml =
|
|
5735
|
-
const hasJson =
|
|
5674
|
+
const hasYaml = fs12.existsSync(yamlPath) && fs12.statSync(yamlPath).isFile();
|
|
5675
|
+
const hasJson = fs12.existsSync(jsonPath) && fs12.statSync(jsonPath).isFile();
|
|
5736
5676
|
if (hasYaml && hasJson) {
|
|
5737
5677
|
throw new Error(
|
|
5738
5678
|
"Both skill-installer.yaml and skill-installer.json exist. Keep only one installer config file."
|
|
@@ -5747,10 +5687,10 @@ function loadInstallerConfig(skillDir) {
|
|
|
5747
5687
|
};
|
|
5748
5688
|
}
|
|
5749
5689
|
if (hasYaml) {
|
|
5750
|
-
const parsed = YAML4.parse(
|
|
5690
|
+
const parsed = YAML4.parse(fs12.readFileSync(yamlPath, "utf8"));
|
|
5751
5691
|
return parseConfigObject(parsed);
|
|
5752
5692
|
}
|
|
5753
|
-
const raw = JSON.parse(
|
|
5693
|
+
const raw = JSON.parse(fs12.readFileSync(jsonPath, "utf8"));
|
|
5754
5694
|
return parseConfigObject(raw);
|
|
5755
5695
|
}
|
|
5756
5696
|
function resolveDependencySourceAndTarget(dep) {
|
|
@@ -5766,19 +5706,13 @@ function resolveDependencySourceAndTarget(dep) {
|
|
|
5766
5706
|
};
|
|
5767
5707
|
}
|
|
5768
5708
|
function runGitClone(repoUrl, targetDir) {
|
|
5769
|
-
const result = spawnSync2(
|
|
5770
|
-
"
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
encoding: "utf8",
|
|
5774
|
-
stdio: "pipe"
|
|
5775
|
-
}
|
|
5776
|
-
);
|
|
5709
|
+
const result = spawnSync2("git", ["clone", "--depth", "1", repoUrl, targetDir], {
|
|
5710
|
+
encoding: "utf8",
|
|
5711
|
+
stdio: "pipe"
|
|
5712
|
+
});
|
|
5777
5713
|
if (result.status !== 0) {
|
|
5778
5714
|
const message = (result.stderr || result.stdout || "git clone failed").trim().split(/\r?\n/)[0] || "git clone failed";
|
|
5779
|
-
throw new Error(
|
|
5780
|
-
`Repository dependency clone failed: ${repoUrl} (${message})`
|
|
5781
|
-
);
|
|
5715
|
+
throw new Error(`Repository dependency clone failed: ${repoUrl} (${message})`);
|
|
5782
5716
|
}
|
|
5783
5717
|
}
|
|
5784
5718
|
async function prepareDependency(source, targetPath, index, sourceCwd, stagingDir, policyMode, events) {
|
|
@@ -5794,9 +5728,7 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
|
|
|
5794
5728
|
if (!evaluation.ok) {
|
|
5795
5729
|
const violation = "violation" in evaluation ? evaluation.violation : void 0;
|
|
5796
5730
|
if (!violation) {
|
|
5797
|
-
throw new Error(
|
|
5798
|
-
`Dependency[${index}] policy evaluation failed for source: ${source}`
|
|
5799
|
-
);
|
|
5731
|
+
throw new Error(`Dependency[${index}] policy evaluation failed for source: ${source}`);
|
|
5800
5732
|
}
|
|
5801
5733
|
if (violation.blocking) {
|
|
5802
5734
|
throw new InstallerSecurityError(violation);
|
|
@@ -5807,12 +5739,10 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
|
|
|
5807
5739
|
});
|
|
5808
5740
|
}
|
|
5809
5741
|
const sourcePath = evaluation.ok ? evaluation.resolvedPath : path14.resolve(sourceCwd, source);
|
|
5810
|
-
if (!
|
|
5811
|
-
throw new Error(
|
|
5812
|
-
`Dependency[${index}] (local) source not found: ${source}`
|
|
5813
|
-
);
|
|
5742
|
+
if (!fs12.existsSync(sourcePath)) {
|
|
5743
|
+
throw new Error(`Dependency[${index}] (local) source not found: ${source}`);
|
|
5814
5744
|
}
|
|
5815
|
-
|
|
5745
|
+
fs12.accessSync(sourcePath, fs12.constants.R_OK);
|
|
5816
5746
|
events.push({
|
|
5817
5747
|
level: "info",
|
|
5818
5748
|
message: `[skills] dependency[${index}] preflight-validated: ${source}`
|
|
@@ -5846,9 +5776,7 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
|
|
|
5846
5776
|
}
|
|
5847
5777
|
const target = new URL(source);
|
|
5848
5778
|
if (target.protocol !== "http:" && target.protocol !== "https:") {
|
|
5849
|
-
throw new Error(
|
|
5850
|
-
`Dependency[${index}] (remote) unsupported protocol: ${target.protocol}`
|
|
5851
|
-
);
|
|
5779
|
+
throw new Error(`Dependency[${index}] (remote) unsupported protocol: ${target.protocol}`);
|
|
5852
5780
|
}
|
|
5853
5781
|
const response = await fetch(target.toString());
|
|
5854
5782
|
if (!response.ok) {
|
|
@@ -5860,8 +5788,8 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
|
|
|
5860
5788
|
stagingDir,
|
|
5861
5789
|
`remote-${index}-${path14.basename(targetPath)}`
|
|
5862
5790
|
);
|
|
5863
|
-
|
|
5864
|
-
|
|
5791
|
+
fs12.mkdirSync(path14.dirname(stagedFilePath), { recursive: true });
|
|
5792
|
+
fs12.writeFileSync(stagedFilePath, Buffer.from(await response.arrayBuffer()));
|
|
5865
5793
|
events.push({
|
|
5866
5794
|
level: "info",
|
|
5867
5795
|
message: `[skills] dependency[${index}] staged: ${source}`
|
|
@@ -5910,9 +5838,7 @@ async function prepareInstallerArtifacts(skillDir, sourceCwd, options = {}) {
|
|
|
5910
5838
|
events
|
|
5911
5839
|
};
|
|
5912
5840
|
}
|
|
5913
|
-
const stagingDir =
|
|
5914
|
-
path14.join(os4.tmpdir(), "skillspp-installer-stage-")
|
|
5915
|
-
);
|
|
5841
|
+
const stagingDir = fs12.mkdtempSync(path14.join(os4.tmpdir(), "skillspp-installer-stage-"));
|
|
5916
5842
|
const preparedDependencies = [];
|
|
5917
5843
|
const seenTargetPaths = /* @__PURE__ */ new Set();
|
|
5918
5844
|
try {
|
|
@@ -5921,13 +5847,11 @@ async function prepareInstallerArtifacts(skillDir, sourceCwd, options = {}) {
|
|
|
5921
5847
|
const { source, targetPath } = resolveDependencySourceAndTarget(dep);
|
|
5922
5848
|
const normalizedTarget = targetPath.replace(/\\/g, "/");
|
|
5923
5849
|
if (seenTargetPaths.has(normalizedTarget)) {
|
|
5924
|
-
throw new Error(
|
|
5925
|
-
`Dependency[${i}] duplicate target path: ${targetPath}`
|
|
5926
|
-
);
|
|
5850
|
+
throw new Error(`Dependency[${i}] duplicate target path: ${targetPath}`);
|
|
5927
5851
|
}
|
|
5928
5852
|
seenTargetPaths.add(normalizedTarget);
|
|
5929
5853
|
const destinationInSkill = ensureInsideRoot(skillDir, targetPath);
|
|
5930
|
-
if (
|
|
5854
|
+
if (fs12.existsSync(destinationInSkill)) {
|
|
5931
5855
|
throw new Error(
|
|
5932
5856
|
`Dependency[${i}] destination already exists: ${path14.relative(
|
|
5933
5857
|
skillDir,
|
|
@@ -5954,13 +5878,13 @@ async function prepareInstallerArtifacts(skillDir, sourceCwd, options = {}) {
|
|
|
5954
5878
|
events
|
|
5955
5879
|
};
|
|
5956
5880
|
} catch (error) {
|
|
5957
|
-
|
|
5881
|
+
fs12.rmSync(stagingDir, { recursive: true, force: true });
|
|
5958
5882
|
throw error;
|
|
5959
5883
|
}
|
|
5960
5884
|
}
|
|
5961
5885
|
function cleanupPreparedInstallerArtifacts(prepared) {
|
|
5962
5886
|
if (prepared.stagingDir) {
|
|
5963
|
-
|
|
5887
|
+
fs12.rmSync(prepared.stagingDir, { recursive: true, force: true });
|
|
5964
5888
|
}
|
|
5965
5889
|
}
|
|
5966
5890
|
|
|
@@ -5974,15 +5898,11 @@ function toValidateOptions(source, options) {
|
|
|
5974
5898
|
}
|
|
5975
5899
|
const maxDescriptionChars = options.maxDescriptionChars ? Number(options.maxDescriptionChars) : void 0;
|
|
5976
5900
|
if (typeof maxDescriptionChars === "number" && (!Number.isFinite(maxDescriptionChars) || maxDescriptionChars <= 0)) {
|
|
5977
|
-
throw new Error(
|
|
5978
|
-
`Invalid --max-description-chars value: ${options.maxDescriptionChars}`
|
|
5979
|
-
);
|
|
5901
|
+
throw new Error(`Invalid --max-description-chars value: ${options.maxDescriptionChars}`);
|
|
5980
5902
|
}
|
|
5981
5903
|
const maxDownloadBytes = options.maxDownloadBytes ? Number(options.maxDownloadBytes) : void 0;
|
|
5982
5904
|
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
5983
|
-
throw new Error(
|
|
5984
|
-
`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`
|
|
5985
|
-
);
|
|
5905
|
+
throw new Error(`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`);
|
|
5986
5906
|
}
|
|
5987
5907
|
return {
|
|
5988
5908
|
source,
|
|
@@ -6001,10 +5921,10 @@ function toValidateOptions(source, options) {
|
|
|
6001
5921
|
}
|
|
6002
5922
|
function loadRepoValidateConfig(cwd) {
|
|
6003
5923
|
const configPath = path15.join(cwd, ".skillspp-cli.json");
|
|
6004
|
-
if (!
|
|
5924
|
+
if (!fs13.existsSync(configPath) || !fs13.statSync(configPath).isFile()) {
|
|
6005
5925
|
return {};
|
|
6006
5926
|
}
|
|
6007
|
-
const parsed = JSON.parse(
|
|
5927
|
+
const parsed = JSON.parse(fs13.readFileSync(configPath, "utf8"));
|
|
6008
5928
|
return parsed.validate || {};
|
|
6009
5929
|
}
|
|
6010
5930
|
function resolveThresholds(options, cwd) {
|
|
@@ -6036,7 +5956,7 @@ var typeRules = [
|
|
|
6036
5956
|
when: (frontmatter) => frontmatter.type === "framework",
|
|
6037
5957
|
validate: ({ skillDir, skillName, diagnostics }) => {
|
|
6038
5958
|
const referencesDir = path15.join(skillDir, "references");
|
|
6039
|
-
if (!
|
|
5959
|
+
if (!fs13.existsSync(referencesDir) || !fs13.statSync(referencesDir).isDirectory()) {
|
|
6040
5960
|
addDiagnostic(diagnostics, {
|
|
6041
5961
|
severity: "error",
|
|
6042
5962
|
skill: skillName,
|
|
@@ -6049,9 +5969,7 @@ var typeRules = [
|
|
|
6049
5969
|
}
|
|
6050
5970
|
];
|
|
6051
5971
|
async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics, skillName) {
|
|
6052
|
-
const installerConfigPath =
|
|
6053
|
-
path15.join(skillDir, "skill-installer.yaml")
|
|
6054
|
-
) ? path15.join(skillDir, "skill-installer.yaml") : path15.join(skillDir, "skill-installer.json");
|
|
5972
|
+
const installerConfigPath = fs13.existsSync(path15.join(skillDir, "skill-installer.yaml")) ? path15.join(skillDir, "skill-installer.yaml") : path15.join(skillDir, "skill-installer.json");
|
|
6055
5973
|
try {
|
|
6056
5974
|
const installer = loadInstallerConfig(skillDir);
|
|
6057
5975
|
const fallbackRoots = Array.from(
|
|
@@ -6095,24 +6013,18 @@ async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics,
|
|
|
6095
6013
|
if (hasSecurityViolation) {
|
|
6096
6014
|
return;
|
|
6097
6015
|
}
|
|
6098
|
-
const tempSkillDir =
|
|
6099
|
-
path15.join(os5.tmpdir(), "skillspp-validate-installer-")
|
|
6100
|
-
);
|
|
6016
|
+
const tempSkillDir = fs13.mkdtempSync(path15.join(os5.tmpdir(), "skillspp-validate-installer-"));
|
|
6101
6017
|
try {
|
|
6102
|
-
const filesToCopy = [
|
|
6103
|
-
"SKILL.md",
|
|
6104
|
-
"skill-installer.yaml",
|
|
6105
|
-
"skill-installer.json"
|
|
6106
|
-
];
|
|
6018
|
+
const filesToCopy = ["SKILL.md", "skill-installer.yaml", "skill-installer.json"];
|
|
6107
6019
|
for (const fileName of filesToCopy) {
|
|
6108
6020
|
const src = path15.join(skillDir, fileName);
|
|
6109
|
-
if (
|
|
6110
|
-
|
|
6021
|
+
if (fs13.existsSync(src) && fs13.statSync(src).isFile()) {
|
|
6022
|
+
fs13.copyFileSync(src, path15.join(tempSkillDir, fileName));
|
|
6111
6023
|
}
|
|
6112
6024
|
}
|
|
6113
6025
|
const agentsDir = path15.join(skillDir, "agents");
|
|
6114
|
-
if (
|
|
6115
|
-
|
|
6026
|
+
if (fs13.existsSync(agentsDir) && fs13.statSync(agentsDir).isDirectory()) {
|
|
6027
|
+
fs13.cpSync(agentsDir, path15.join(tempSkillDir, "agents"), {
|
|
6116
6028
|
recursive: true,
|
|
6117
6029
|
force: true
|
|
6118
6030
|
});
|
|
@@ -6154,7 +6066,7 @@ async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics,
|
|
|
6154
6066
|
throw lastMissingSourceError;
|
|
6155
6067
|
}
|
|
6156
6068
|
} finally {
|
|
6157
|
-
|
|
6069
|
+
fs13.rmSync(tempSkillDir, { recursive: true, force: true });
|
|
6158
6070
|
}
|
|
6159
6071
|
} catch (error) {
|
|
6160
6072
|
if (isInstallerSecurityError(error)) {
|
|
@@ -6189,7 +6101,7 @@ async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics,
|
|
|
6189
6101
|
async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thresholds) {
|
|
6190
6102
|
const skillMd = path15.join(skillDir, "SKILL.md");
|
|
6191
6103
|
const skillName = path15.basename(skillDir);
|
|
6192
|
-
if (!
|
|
6104
|
+
if (!fs13.existsSync(skillMd)) {
|
|
6193
6105
|
addDiagnostic(diagnostics, {
|
|
6194
6106
|
severity: "error",
|
|
6195
6107
|
skill: skillName,
|
|
@@ -6199,7 +6111,7 @@ async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thres
|
|
|
6199
6111
|
});
|
|
6200
6112
|
return;
|
|
6201
6113
|
}
|
|
6202
|
-
const content =
|
|
6114
|
+
const content = fs13.readFileSync(skillMd, "utf8");
|
|
6203
6115
|
const lines = content.split(/\r?\n/);
|
|
6204
6116
|
if (lines.length > thresholds.maxLines) {
|
|
6205
6117
|
addDiagnostic(diagnostics, {
|
|
@@ -6270,7 +6182,7 @@ async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thres
|
|
|
6270
6182
|
if (!rel || rel.startsWith("..") || path15.isAbsolute(rel)) {
|
|
6271
6183
|
continue;
|
|
6272
6184
|
}
|
|
6273
|
-
if (!
|
|
6185
|
+
if (!fs13.existsSync(resolved)) {
|
|
6274
6186
|
addDiagnostic(diagnostics, {
|
|
6275
6187
|
severity: "error",
|
|
6276
6188
|
skill: skillName,
|
|
@@ -6285,18 +6197,13 @@ async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thres
|
|
|
6285
6197
|
rule.validate({ skillDir, skillName, diagnostics });
|
|
6286
6198
|
}
|
|
6287
6199
|
}
|
|
6288
|
-
await validateInstallerDependencies(
|
|
6289
|
-
skillDir,
|
|
6290
|
-
sourceRoot,
|
|
6291
|
-
diagnostics,
|
|
6292
|
-
skillName
|
|
6293
|
-
);
|
|
6200
|
+
await validateInstallerDependencies(skillDir, sourceRoot, diagnostics, skillName);
|
|
6294
6201
|
}
|
|
6295
6202
|
function discoverFallbackRoots(cwd) {
|
|
6296
6203
|
const candidates = [cwd, path15.join(cwd, "skills")];
|
|
6297
6204
|
const packagesDir = path15.join(cwd, "packages");
|
|
6298
|
-
if (
|
|
6299
|
-
for (const entry of
|
|
6205
|
+
if (fs13.existsSync(packagesDir) && fs13.statSync(packagesDir).isDirectory()) {
|
|
6206
|
+
for (const entry of fs13.readdirSync(packagesDir, { withFileTypes: true })) {
|
|
6300
6207
|
if (!entry.isDirectory()) {
|
|
6301
6208
|
continue;
|
|
6302
6209
|
}
|
|
@@ -6306,7 +6213,7 @@ function discoverFallbackRoots(cwd) {
|
|
|
6306
6213
|
const seen = /* @__PURE__ */ new Set();
|
|
6307
6214
|
const out = [];
|
|
6308
6215
|
for (const item of candidates) {
|
|
6309
|
-
if (!
|
|
6216
|
+
if (!fs13.existsSync(item)) {
|
|
6310
6217
|
continue;
|
|
6311
6218
|
}
|
|
6312
6219
|
const resolved = path15.resolve(item);
|
|
@@ -6321,7 +6228,7 @@ function discoverFallbackRoots(cwd) {
|
|
|
6321
6228
|
function discoverCiRoots(cwd) {
|
|
6322
6229
|
const config = loadRepoValidateConfig(cwd);
|
|
6323
6230
|
if (config.ciRoots && config.ciRoots.length > 0) {
|
|
6324
|
-
const roots = config.ciRoots.map((item) => path15.resolve(cwd, item)).filter((item) =>
|
|
6231
|
+
const roots = config.ciRoots.map((item) => path15.resolve(cwd, item)).filter((item) => fs13.existsSync(item) && fs13.statSync(item).isDirectory());
|
|
6325
6232
|
if (roots.length > 0) {
|
|
6326
6233
|
return roots;
|
|
6327
6234
|
}
|
|
@@ -6330,7 +6237,7 @@ function discoverCiRoots(cwd) {
|
|
|
6330
6237
|
}
|
|
6331
6238
|
async function stageAndValidateLocalRoot(rootPath, dependencyRoot, seenSkillPaths, diagnostics, strict, thresholds, emitProgress) {
|
|
6332
6239
|
const resolvedRoot = path15.resolve(rootPath);
|
|
6333
|
-
if (!
|
|
6240
|
+
if (!fs13.existsSync(resolvedRoot) || !fs13.statSync(resolvedRoot).isDirectory()) {
|
|
6334
6241
|
addDiagnostic(diagnostics, {
|
|
6335
6242
|
severity: "error",
|
|
6336
6243
|
skill: path15.basename(resolvedRoot),
|
|
@@ -6359,13 +6266,7 @@ async function stageAndValidateLocalRoot(rootPath, dependencyRoot, seenSkillPath
|
|
|
6359
6266
|
}
|
|
6360
6267
|
seenSkillPaths.add(resolvedSkillPath);
|
|
6361
6268
|
await emitProgress?.(`validating ${skill.name}`);
|
|
6362
|
-
await validateSkillDir(
|
|
6363
|
-
skill.path,
|
|
6364
|
-
dependencyRoot,
|
|
6365
|
-
diagnostics,
|
|
6366
|
-
strict,
|
|
6367
|
-
thresholds
|
|
6368
|
-
);
|
|
6269
|
+
await validateSkillDir(skill.path, dependencyRoot, diagnostics, strict, thresholds);
|
|
6369
6270
|
}
|
|
6370
6271
|
}
|
|
6371
6272
|
async function stageAndValidateSource(options, diagnostics, thresholds, emitProgress) {
|
|
@@ -6400,27 +6301,19 @@ async function stageAndValidateSource(options, diagnostics, thresholds, emitProg
|
|
|
6400
6301
|
const tempRoots = [];
|
|
6401
6302
|
try {
|
|
6402
6303
|
for (const remoteSkill of remote) {
|
|
6403
|
-
const tmp =
|
|
6404
|
-
path15.join(os5.tmpdir(), "skillspp-validate-")
|
|
6405
|
-
);
|
|
6304
|
+
const tmp = fs13.mkdtempSync(path15.join(os5.tmpdir(), "skillspp-validate-"));
|
|
6406
6305
|
tempRoots.push(tmp);
|
|
6407
6306
|
for (const [relativePath, content] of remoteSkill.files.entries()) {
|
|
6408
6307
|
const target = path15.join(tmp, relativePath);
|
|
6409
|
-
|
|
6410
|
-
|
|
6308
|
+
fs13.mkdirSync(path15.dirname(target), { recursive: true });
|
|
6309
|
+
fs13.writeFileSync(target, content, "utf8");
|
|
6411
6310
|
}
|
|
6412
6311
|
await emitProgress?.(`validating ${remoteSkill.installName}`);
|
|
6413
|
-
await validateSkillDir(
|
|
6414
|
-
tmp,
|
|
6415
|
-
tmp,
|
|
6416
|
-
diagnostics,
|
|
6417
|
-
Boolean(options.strict),
|
|
6418
|
-
thresholds
|
|
6419
|
-
);
|
|
6312
|
+
await validateSkillDir(tmp, tmp, diagnostics, Boolean(options.strict), thresholds);
|
|
6420
6313
|
}
|
|
6421
6314
|
} finally {
|
|
6422
6315
|
for (const tmp of tempRoots) {
|
|
6423
|
-
|
|
6316
|
+
fs13.rmSync(tmp, { recursive: true, force: true });
|
|
6424
6317
|
}
|
|
6425
6318
|
}
|
|
6426
6319
|
return;
|
|
@@ -6466,12 +6359,7 @@ async function runValidateAnalysis(options, emitProgress) {
|
|
|
6466
6359
|
);
|
|
6467
6360
|
}
|
|
6468
6361
|
} else {
|
|
6469
|
-
await stageAndValidateSource(
|
|
6470
|
-
options,
|
|
6471
|
-
diagnostics,
|
|
6472
|
-
thresholds,
|
|
6473
|
-
emitProgress
|
|
6474
|
-
);
|
|
6362
|
+
await stageAndValidateSource(options, diagnostics, thresholds, emitProgress);
|
|
6475
6363
|
}
|
|
6476
6364
|
await emitProgress?.("collecting diagnostics");
|
|
6477
6365
|
return { diagnostics };
|
|
@@ -6499,9 +6387,7 @@ function buildDiagnosticsPanelLines(diagnostics) {
|
|
|
6499
6387
|
return [colorToken("No diagnostics found.", "success")];
|
|
6500
6388
|
}
|
|
6501
6389
|
const sorted = [...diagnostics].sort(
|
|
6502
|
-
(a, b) => `${a.severity}:${a.skill}:${a.rule}`.localeCompare(
|
|
6503
|
-
`${b.severity}:${b.skill}:${b.rule}`
|
|
6504
|
-
)
|
|
6390
|
+
(a, b) => `${a.severity}:${a.skill}:${a.rule}`.localeCompare(`${b.severity}:${b.skill}:${b.rule}`)
|
|
6505
6391
|
);
|
|
6506
6392
|
return sorted.map((row) => {
|
|
6507
6393
|
const marker = row.severity === "error" ? colorToken("[error]", "danger") : colorToken("[warning]", "warning");
|
|
@@ -6572,38 +6458,23 @@ async function executeValidate(options) {
|
|
|
6572
6458
|
const errors = diagnostics.filter((item) => item.severity === "error");
|
|
6573
6459
|
const warnings = diagnostics.filter((item) => item.severity === "warning");
|
|
6574
6460
|
const validatingSteps = [...runtimeSteps].filter((step) => step.startsWith("validating ")).sort((a, b) => a.localeCompare(b));
|
|
6575
|
-
const runStepLines = [
|
|
6576
|
-
"staging source",
|
|
6577
|
-
...validatingSteps,
|
|
6578
|
-
"collecting diagnostics"
|
|
6579
|
-
];
|
|
6461
|
+
const runStepLines = ["staging source", ...validatingSteps, "collecting diagnostics"];
|
|
6580
6462
|
await renderStaticScreen([
|
|
6581
6463
|
completedStepsSection([
|
|
6582
6464
|
"source parsed",
|
|
6583
6465
|
"candidate skills discovered",
|
|
6584
6466
|
"validation session ready"
|
|
6585
6467
|
]),
|
|
6586
|
-
linesSection([
|
|
6587
|
-
` Validation target: ${resolveValidateTargetLabel(options)}`
|
|
6588
|
-
]),
|
|
6468
|
+
linesSection([` Validation target: ${resolveValidateTargetLabel(options)}`]),
|
|
6589
6469
|
panelSection({
|
|
6590
6470
|
title: "Checks Included",
|
|
6591
6471
|
lines: [
|
|
6592
|
-
` ${colorToken(
|
|
6593
|
-
"\u25CF",
|
|
6594
|
-
"primary"
|
|
6595
|
-
)} SKILL.md exists + frontmatter parseability`,
|
|
6472
|
+
` ${colorToken("\u25CF", "primary")} SKILL.md exists + frontmatter parseability`,
|
|
6596
6473
|
` ${colorToken("\u25CF", "primary")} required fields: name, description`,
|
|
6597
6474
|
` ${colorToken("\u25CF", "primary")} name \u2194 directory consistency`,
|
|
6598
6475
|
` ${colorToken("\u25CF", "primary")} markdown reference existence`,
|
|
6599
|
-
` ${colorToken(
|
|
6600
|
-
|
|
6601
|
-
"primary"
|
|
6602
|
-
)} type-specific rules (framework references dir)`,
|
|
6603
|
-
` ${colorToken(
|
|
6604
|
-
"\u25CF",
|
|
6605
|
-
"primary"
|
|
6606
|
-
)} installer dependency security + preflight`,
|
|
6476
|
+
` ${colorToken("\u25CF", "primary")} type-specific rules (framework references dir)`,
|
|
6477
|
+
` ${colorToken("\u25CF", "primary")} installer dependency security + preflight`,
|
|
6607
6478
|
` ${colorToken("\u25CF", "primary")} line/description budget thresholds`
|
|
6608
6479
|
],
|
|
6609
6480
|
style: "square",
|
|
@@ -6637,10 +6508,7 @@ async function executeValidate(options) {
|
|
|
6637
6508
|
}
|
|
6638
6509
|
}
|
|
6639
6510
|
function configureValidateCommand(command, action) {
|
|
6640
|
-
return command.description("Validate skill source structure and references").argument("[source]", "Source path or URL").option("--ci", "Validate multiple local roots for CI").option("--root <paths...>", "Root paths for CI mode").option("--strict", "Escalate warnings to errors").option("--json", "Emit JSON output").option("--max-lines <n>", "SKILL.md line budget threshold").option(
|
|
6641
|
-
"--max-description-chars <n>",
|
|
6642
|
-
"Description length budget threshold"
|
|
6643
|
-
).option("--allow-host <hosts...>", "Restrict well-known hosts to allowlist").option("--deny-host <hosts...>", "Block specific well-known hosts").option("--max-download-bytes <n>", "Set well-known download budget").option("--policy-mode <mode>", "Policy mode (enforce|warn)").action(action);
|
|
6511
|
+
return command.description("Validate skill source structure and references").argument("[source]", "Source path or URL").option("--ci", "Validate multiple local roots for CI").option("--root <paths...>", "Root paths for CI mode").option("--strict", "Escalate warnings to errors").option("--json", "Emit JSON output").option("--max-lines <n>", "SKILL.md line budget threshold").option("--max-description-chars <n>", "Description length budget threshold").option("--allow-host <hosts...>", "Restrict well-known hosts to allowlist").option("--deny-host <hosts...>", "Block specific well-known hosts").option("--max-download-bytes <n>", "Set well-known download budget").option("--policy-mode <mode>", "Policy mode (enforce|warn)").action(action);
|
|
6644
6512
|
}
|
|
6645
6513
|
function registerValidateCommand(program, ctx) {
|
|
6646
6514
|
configureValidateCommand(
|
|
@@ -6680,9 +6548,22 @@ function createCliTelemetryEmitter(sink) {
|
|
|
6680
6548
|
|
|
6681
6549
|
// src/cli.ts
|
|
6682
6550
|
import picocolors from "picocolors";
|
|
6551
|
+
import path16 from "node:path";
|
|
6552
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6683
6553
|
var require2 = createRequire(import.meta.url);
|
|
6684
6554
|
var packageJson = require2("../package.json");
|
|
6685
6555
|
var CLI_VERSION = typeof packageJson.version === "string" ? packageJson.version : "0.1.0";
|
|
6556
|
+
function resolveSkillsppLogoDir() {
|
|
6557
|
+
const moduleDir = path16.dirname(fileURLToPath3(import.meta.url));
|
|
6558
|
+
return path16.resolve(moduleDir, "../src/assets/ascii/logo");
|
|
6559
|
+
}
|
|
6560
|
+
function configureSkillsppLogoAssetPaths() {
|
|
6561
|
+
const logoDir = resolveSkillsppLogoDir();
|
|
6562
|
+
configureLogoAssetPaths({
|
|
6563
|
+
sessionPath: path16.join(logoDir, "skillspp-logo.session.json"),
|
|
6564
|
+
textPath: path16.join(logoDir, "skillspp-logo.txt")
|
|
6565
|
+
});
|
|
6566
|
+
}
|
|
6686
6567
|
function formatCliError(error) {
|
|
6687
6568
|
if (error && typeof error === "object" && "code" in error) {
|
|
6688
6569
|
const code = String(error.code);
|
|
@@ -6706,14 +6587,6 @@ function createProgram(emitter, experimental) {
|
|
|
6706
6587
|
applyExitOverride(program);
|
|
6707
6588
|
return program;
|
|
6708
6589
|
}
|
|
6709
|
-
function applyExitOverride(command) {
|
|
6710
|
-
command.exitOverride((error) => {
|
|
6711
|
-
throw error;
|
|
6712
|
-
});
|
|
6713
|
-
for (const subcommand of command.commands) {
|
|
6714
|
-
applyExitOverride(subcommand);
|
|
6715
|
-
}
|
|
6716
|
-
}
|
|
6717
6590
|
function parseTelemetryFromArgv(argv) {
|
|
6718
6591
|
for (let i = 0; i < argv.length; i += 1) {
|
|
6719
6592
|
if (argv[i] !== "--telemetry") {
|
|
@@ -6723,35 +6596,8 @@ function parseTelemetryFromArgv(argv) {
|
|
|
6723
6596
|
}
|
|
6724
6597
|
return void 0;
|
|
6725
6598
|
}
|
|
6726
|
-
function inferCommandSource(argv) {
|
|
6727
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
6728
|
-
const arg = argv[i];
|
|
6729
|
-
if (arg === "--telemetry") {
|
|
6730
|
-
i += 1;
|
|
6731
|
-
continue;
|
|
6732
|
-
}
|
|
6733
|
-
if (arg.startsWith("-")) {
|
|
6734
|
-
continue;
|
|
6735
|
-
}
|
|
6736
|
-
return arg;
|
|
6737
|
-
}
|
|
6738
|
-
return "cli";
|
|
6739
|
-
}
|
|
6740
|
-
function emitCommanderParseErrorTelemetry(emitter, argv, error) {
|
|
6741
|
-
const source = inferCommandSource(argv);
|
|
6742
|
-
emitLifecycleEvent(emitter, {
|
|
6743
|
-
eventType: `${source}_failed`,
|
|
6744
|
-
source,
|
|
6745
|
-
reason: "commander_parse_error",
|
|
6746
|
-
command: source,
|
|
6747
|
-
status: "error",
|
|
6748
|
-
error: error.message,
|
|
6749
|
-
metadata: {
|
|
6750
|
-
commanderCode: error.code
|
|
6751
|
-
}
|
|
6752
|
-
});
|
|
6753
|
-
}
|
|
6754
6599
|
async function runCli(argv) {
|
|
6600
|
+
configureSkillsppLogoAssetPaths();
|
|
6755
6601
|
const telemetrySink = parseTelemetrySink(parseTelemetryFromArgv(argv));
|
|
6756
6602
|
const emitter = createCliTelemetryEmitter(telemetrySink);
|
|
6757
6603
|
const experimental = argv.includes("--experimental");
|
|
@@ -6764,11 +6610,13 @@ async function runCli(argv) {
|
|
|
6764
6610
|
await program.parseAsync(argv, { from: "user" });
|
|
6765
6611
|
return 0;
|
|
6766
6612
|
} catch (error) {
|
|
6767
|
-
if (
|
|
6613
|
+
if (isGracefulCommanderExit(error)) {
|
|
6768
6614
|
return 0;
|
|
6769
6615
|
}
|
|
6770
6616
|
if (error instanceof CommanderError2) {
|
|
6771
|
-
emitCommanderParseErrorTelemetry(emitter, argv, error
|
|
6617
|
+
emitCommanderParseErrorTelemetry(emitter, argv, error, {
|
|
6618
|
+
valueFlags: ["--telemetry"]
|
|
6619
|
+
});
|
|
6772
6620
|
throw new Error(error.message);
|
|
6773
6621
|
}
|
|
6774
6622
|
throw error;
|
|
@@ -6797,6 +6645,7 @@ runCli(process.argv.slice(2)).then(
|
|
|
6797
6645
|
}
|
|
6798
6646
|
);
|
|
6799
6647
|
export {
|
|
6648
|
+
configureSkillsppLogoAssetPaths,
|
|
6800
6649
|
runCli
|
|
6801
6650
|
};
|
|
6802
6651
|
//# sourceMappingURL=cli.js.map
|