skild 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +539 -220
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk16 from "chalk";
|
|
6
6
|
import { createRequire } from "module";
|
|
7
7
|
|
|
8
8
|
// src/commands/install.ts
|
|
9
9
|
import fs2 from "fs";
|
|
10
10
|
import path2 from "path";
|
|
11
|
-
import
|
|
12
|
-
import { deriveChildSource, fetchWithTimeout, installRegistrySkill, installSkill, isValidAlias, loadRegistryAuth, materializeSourceToTemp, resolveRegistryAlias, resolveRegistryUrl, SkildError, PLATFORMS } from "@skild/core";
|
|
11
|
+
import chalk3 from "chalk";
|
|
12
|
+
import { deriveChildSource, fetchWithTimeout, installRegistrySkill, installSkill, isValidAlias, loadRegistryAuth, materializeSourceToTemp, resolveRegistryAlias, resolveRegistryUrl, SkildError, PLATFORMS as PLATFORMS2 } from "@skild/core";
|
|
13
13
|
|
|
14
14
|
// src/utils/logger.ts
|
|
15
15
|
import chalk from "chalk";
|
|
@@ -71,72 +71,280 @@ var logger = {
|
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
// src/utils/
|
|
74
|
+
// src/utils/interactive-select.ts
|
|
75
75
|
import readline from "readline";
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
import chalk2 from "chalk";
|
|
77
|
+
import { PLATFORMS } from "@skild/core";
|
|
78
|
+
var PLATFORM_DISPLAY = {
|
|
79
|
+
claude: { name: "Claude" },
|
|
80
|
+
codex: { name: "Codex" },
|
|
81
|
+
copilot: { name: "Copilot" },
|
|
82
|
+
antigravity: { name: "Antigravity" }
|
|
83
|
+
};
|
|
84
|
+
function buildSkillTree(skills) {
|
|
85
|
+
const allNode = {
|
|
86
|
+
id: "all",
|
|
87
|
+
name: "All Skills",
|
|
88
|
+
depth: 1,
|
|
89
|
+
children: [],
|
|
90
|
+
leafIndices: [],
|
|
91
|
+
isLeaf: false
|
|
92
|
+
};
|
|
93
|
+
for (let i = 0; i < skills.length; i++) {
|
|
94
|
+
const relPath = skills[i].relPath;
|
|
95
|
+
const parts = relPath === "." ? ["."] : relPath.split("/").filter(Boolean);
|
|
96
|
+
allNode.leafIndices.push(i);
|
|
97
|
+
let current = allNode;
|
|
98
|
+
for (let d = 0; d < parts.length; d++) {
|
|
99
|
+
const part = parts[d];
|
|
100
|
+
const nodeId = parts.slice(0, d + 1).join("/");
|
|
101
|
+
let child = current.children.find((c2) => c2.name === part);
|
|
102
|
+
if (!child) {
|
|
103
|
+
child = {
|
|
104
|
+
id: nodeId,
|
|
105
|
+
name: part,
|
|
106
|
+
depth: d + 2,
|
|
107
|
+
// +2 because allNode is depth 1
|
|
108
|
+
children: [],
|
|
109
|
+
leafIndices: [],
|
|
110
|
+
isLeaf: d === parts.length - 1
|
|
111
|
+
};
|
|
112
|
+
current.children.push(child);
|
|
113
|
+
}
|
|
114
|
+
child.leafIndices.push(i);
|
|
115
|
+
current = child;
|
|
116
|
+
}
|
|
85
117
|
}
|
|
118
|
+
const root = { id: "", name: ".", depth: 0, children: [allNode], leafIndices: allNode.leafIndices.slice(), isLeaf: false };
|
|
119
|
+
return root;
|
|
86
120
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
function flattenTree(root) {
|
|
122
|
+
const result = [];
|
|
123
|
+
function walk(node) {
|
|
124
|
+
if (node.id !== "") {
|
|
125
|
+
result.push(node);
|
|
126
|
+
}
|
|
127
|
+
for (const child of node.children) {
|
|
128
|
+
walk(child);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const child of root.children) {
|
|
132
|
+
walk(child);
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
function getNodeState(node, selected) {
|
|
137
|
+
const total = node.leafIndices.length;
|
|
138
|
+
if (total === 0) return "none";
|
|
139
|
+
let count = 0;
|
|
140
|
+
for (const idx of node.leafIndices) {
|
|
141
|
+
if (selected.has(idx)) count++;
|
|
90
142
|
}
|
|
143
|
+
if (count === 0) return "none";
|
|
144
|
+
if (count === total) return "all";
|
|
145
|
+
return "partial";
|
|
146
|
+
}
|
|
147
|
+
async function interactiveTreeSelect(items, options) {
|
|
148
|
+
const { title, subtitle, buildTree, formatNode, defaultAll } = options;
|
|
149
|
+
const root = buildTree(items);
|
|
150
|
+
const flatNodes = flattenTree(root);
|
|
151
|
+
if (flatNodes.length === 0) return null;
|
|
152
|
+
const selected = /* @__PURE__ */ new Set();
|
|
153
|
+
if (defaultAll) {
|
|
154
|
+
for (let i = 0; i < items.length; i++) selected.add(i);
|
|
155
|
+
}
|
|
156
|
+
let cursor = 0;
|
|
91
157
|
const stdin = process.stdin;
|
|
92
158
|
const stdout = process.stdout;
|
|
93
|
-
stdout.
|
|
159
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
160
|
+
return defaultAll ? Array.from(selected) : null;
|
|
161
|
+
}
|
|
94
162
|
const wasRaw = Boolean(stdin.isRaw);
|
|
95
163
|
stdin.setRawMode(true);
|
|
96
164
|
stdin.resume();
|
|
97
165
|
readline.emitKeypressEvents(stdin);
|
|
98
|
-
|
|
99
|
-
|
|
166
|
+
function render() {
|
|
167
|
+
stdout.write("\x1B[?25l");
|
|
168
|
+
const totalLines = flatNodes.length + 4;
|
|
169
|
+
stdout.write(`\x1B[${totalLines}A`);
|
|
170
|
+
stdout.write("\x1B[0J");
|
|
171
|
+
stdout.write(`
|
|
172
|
+
${chalk2.bold.cyan(title)}
|
|
173
|
+
`);
|
|
174
|
+
stdout.write(`${chalk2.dim(subtitle)}
|
|
175
|
+
|
|
176
|
+
`);
|
|
177
|
+
for (let i = 0; i < flatNodes.length; i++) {
|
|
178
|
+
const node = flatNodes[i];
|
|
179
|
+
const state = getNodeState(node, selected);
|
|
180
|
+
const isCursor = i === cursor;
|
|
181
|
+
stdout.write(formatNode(node, state, isCursor) + "\n");
|
|
182
|
+
}
|
|
183
|
+
stdout.write("\n");
|
|
184
|
+
}
|
|
185
|
+
function initialRender() {
|
|
186
|
+
stdout.write(`
|
|
187
|
+
${chalk2.bold.cyan(title)}
|
|
188
|
+
`);
|
|
189
|
+
stdout.write(`${chalk2.dim(subtitle)}
|
|
190
|
+
|
|
191
|
+
`);
|
|
192
|
+
for (let i = 0; i < flatNodes.length; i++) {
|
|
193
|
+
const node = flatNodes[i];
|
|
194
|
+
const state = getNodeState(node, selected);
|
|
195
|
+
const isCursor = i === cursor;
|
|
196
|
+
stdout.write(formatNode(node, state, isCursor) + "\n");
|
|
197
|
+
}
|
|
198
|
+
stdout.write("\n");
|
|
199
|
+
}
|
|
200
|
+
initialRender();
|
|
201
|
+
return new Promise((resolve) => {
|
|
100
202
|
function cleanup() {
|
|
101
|
-
stdin.off("keypress", onKeypress);
|
|
102
203
|
stdin.setRawMode(wasRaw);
|
|
103
204
|
stdin.pause();
|
|
205
|
+
stdin.removeListener("keypress", onKeypress);
|
|
206
|
+
stdout.write("\x1B[?25h");
|
|
104
207
|
}
|
|
105
|
-
function onKeypress(
|
|
106
|
-
if (key
|
|
107
|
-
stdout.write("\n");
|
|
208
|
+
function onKeypress(_str, key) {
|
|
209
|
+
if (key.ctrl && key.name === "c") {
|
|
108
210
|
cleanup();
|
|
109
|
-
|
|
110
|
-
err.code = "PROMPT_CANCELLED";
|
|
111
|
-
reject(err);
|
|
211
|
+
resolve(null);
|
|
112
212
|
return;
|
|
113
213
|
}
|
|
114
|
-
if (key
|
|
115
|
-
stdout.write("\n");
|
|
214
|
+
if (key.name === "return" || key.name === "enter") {
|
|
116
215
|
cleanup();
|
|
117
|
-
|
|
216
|
+
const result = Array.from(selected);
|
|
217
|
+
if (result.length === 0) {
|
|
218
|
+
resolve(null);
|
|
219
|
+
} else {
|
|
220
|
+
resolve(result);
|
|
221
|
+
}
|
|
118
222
|
return;
|
|
119
223
|
}
|
|
120
|
-
if (key
|
|
121
|
-
|
|
224
|
+
if (key.name === "up") {
|
|
225
|
+
cursor = (cursor - 1 + flatNodes.length) % flatNodes.length;
|
|
226
|
+
render();
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (key.name === "down") {
|
|
230
|
+
cursor = (cursor + 1) % flatNodes.length;
|
|
231
|
+
render();
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (key.name === "space") {
|
|
235
|
+
const node = flatNodes[cursor];
|
|
236
|
+
const state = getNodeState(node, selected);
|
|
237
|
+
if (state === "all") {
|
|
238
|
+
for (const idx of node.leafIndices) {
|
|
239
|
+
selected.delete(idx);
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
for (const idx of node.leafIndices) {
|
|
243
|
+
selected.add(idx);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
render();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (key.name === "a") {
|
|
250
|
+
const allSelected = selected.size === items.length;
|
|
251
|
+
if (allSelected) {
|
|
252
|
+
selected.clear();
|
|
253
|
+
} else {
|
|
254
|
+
for (let i = 0; i < items.length; i++) selected.add(i);
|
|
255
|
+
}
|
|
256
|
+
render();
|
|
122
257
|
return;
|
|
123
258
|
}
|
|
124
|
-
if (!str) return;
|
|
125
|
-
if (key?.ctrl || key?.meta) return;
|
|
126
|
-
buf.push(str);
|
|
127
259
|
}
|
|
128
260
|
stdin.on("keypress", onKeypress);
|
|
129
261
|
});
|
|
130
262
|
}
|
|
131
|
-
async function
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
263
|
+
async function promptSkillsInteractive(skills, options = {}) {
|
|
264
|
+
if (skills.length === 0) return null;
|
|
265
|
+
const selectedIndices = await interactiveTreeSelect(skills, {
|
|
266
|
+
title: "Select skills to install",
|
|
267
|
+
subtitle: "\u2191\u2193 navigate \u2022 Space toggle \u2022 Enter confirm",
|
|
268
|
+
buildTree: buildSkillTree,
|
|
269
|
+
formatNode: (node, state, isCursor) => {
|
|
270
|
+
const indent = " ".repeat(node.depth - 1);
|
|
271
|
+
const checkbox = state === "all" ? chalk2.green("\u25CF") : state === "partial" ? chalk2.yellow("\u25D0") : chalk2.dim("\u25CB");
|
|
272
|
+
const name = isCursor ? chalk2.cyan.underline(node.name) : node.name;
|
|
273
|
+
const cursor = isCursor ? chalk2.cyan("\u203A ") : " ";
|
|
274
|
+
const count = node.leafIndices.length > 1 ? chalk2.dim(` (${node.leafIndices.length})`) : "";
|
|
275
|
+
let hint = "";
|
|
276
|
+
if (isCursor && node.leafIndices.length > 0) {
|
|
277
|
+
hint = state === "all" ? chalk2.dim(" \u2190 Space to deselect") : chalk2.dim(" \u2190 Space to select");
|
|
278
|
+
}
|
|
279
|
+
return `${cursor}${indent}${checkbox} ${name}${count}${hint}`;
|
|
280
|
+
},
|
|
281
|
+
defaultAll: options.defaultAll !== false
|
|
282
|
+
});
|
|
283
|
+
if (!selectedIndices || selectedIndices.length === 0) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
console.log(chalk2.green(`
|
|
287
|
+
\u2713 ${selectedIndices.length} skill${selectedIndices.length > 1 ? "s" : ""} selected
|
|
288
|
+
`));
|
|
289
|
+
return selectedIndices.map((i) => skills[i]);
|
|
290
|
+
}
|
|
291
|
+
async function promptPlatformsInteractive(options = {}) {
|
|
292
|
+
const platformItems = PLATFORMS.map((p) => ({ platform: p }));
|
|
293
|
+
const selectedIndices = await interactiveTreeSelect(platformItems, {
|
|
294
|
+
title: "Select target platforms",
|
|
295
|
+
subtitle: "\u2191\u2193 navigate \u2022 Space toggle \u2022 Enter confirm",
|
|
296
|
+
buildTree: (items) => {
|
|
297
|
+
const allNode = {
|
|
298
|
+
id: "all",
|
|
299
|
+
name: "All Platforms",
|
|
300
|
+
depth: 1,
|
|
301
|
+
children: [],
|
|
302
|
+
leafIndices: [],
|
|
303
|
+
isLeaf: false
|
|
304
|
+
};
|
|
305
|
+
for (let i = 0; i < items.length; i++) {
|
|
306
|
+
const p = items[i].platform;
|
|
307
|
+
allNode.children.push({
|
|
308
|
+
id: p,
|
|
309
|
+
name: p,
|
|
310
|
+
depth: 2,
|
|
311
|
+
children: [],
|
|
312
|
+
leafIndices: [i],
|
|
313
|
+
isLeaf: true
|
|
314
|
+
});
|
|
315
|
+
allNode.leafIndices.push(i);
|
|
316
|
+
}
|
|
317
|
+
const root = { id: "", name: ".", depth: 0, children: [allNode], leafIndices: allNode.leafIndices.slice(), isLeaf: false };
|
|
318
|
+
return root;
|
|
319
|
+
},
|
|
320
|
+
formatNode: (node, state, isCursor) => {
|
|
321
|
+
const indent = " ".repeat(node.depth - 1);
|
|
322
|
+
const checkbox = state === "all" ? chalk2.green("\u25CF") : state === "partial" ? chalk2.yellow("\u25D0") : chalk2.dim("\u25CB");
|
|
323
|
+
let displayName = node.name;
|
|
324
|
+
if (node.name !== "All Platforms") {
|
|
325
|
+
const platform = node.name;
|
|
326
|
+
const display = PLATFORM_DISPLAY[platform];
|
|
327
|
+
if (display) displayName = display.name;
|
|
328
|
+
}
|
|
329
|
+
const name = isCursor ? chalk2.cyan.underline(displayName) : displayName;
|
|
330
|
+
const cursor = isCursor ? chalk2.cyan("\u203A ") : " ";
|
|
331
|
+
const count = node.leafIndices.length > 1 ? chalk2.dim(` (${node.leafIndices.length})`) : "";
|
|
332
|
+
let hint = "";
|
|
333
|
+
if (isCursor && node.leafIndices.length > 0) {
|
|
334
|
+
hint = state === "all" ? chalk2.dim(" \u2190 Space to deselect") : chalk2.dim(" \u2190 Space to select");
|
|
335
|
+
}
|
|
336
|
+
return `${cursor}${indent}${checkbox} ${name}${count}${hint}`;
|
|
337
|
+
},
|
|
338
|
+
defaultAll: options.defaultAll !== false
|
|
339
|
+
});
|
|
340
|
+
if (!selectedIndices || selectedIndices.length === 0) {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
const selected = selectedIndices.map((i) => PLATFORMS[i]);
|
|
344
|
+
console.log(chalk2.green(`
|
|
345
|
+
\u2713 Installing to ${selected.length} platform${selected.length > 1 ? "s" : ""}
|
|
346
|
+
`));
|
|
347
|
+
return selected;
|
|
140
348
|
}
|
|
141
349
|
|
|
142
350
|
// src/commands/install-discovery.ts
|
|
@@ -233,10 +441,6 @@ function printJson(value) {
|
|
|
233
441
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
234
442
|
`);
|
|
235
443
|
}
|
|
236
|
-
function previewDiscovered(found, limit = 12) {
|
|
237
|
-
const preview = found.slice(0, limit).map((s) => ` - ${s.relPath}`).join("\n");
|
|
238
|
-
return `${preview}${found.length > limit ? "\n ..." : ""}`;
|
|
239
|
-
}
|
|
240
444
|
function asDiscoveredSkills(discovered, toSuggestedSource, toMaterializedDir) {
|
|
241
445
|
return discovered.map((d) => ({
|
|
242
446
|
relPath: d.relPath,
|
|
@@ -248,41 +452,55 @@ async function install(source, options = {}) {
|
|
|
248
452
|
const scope = options.local ? "project" : "global";
|
|
249
453
|
const auth = loadRegistryAuth();
|
|
250
454
|
const registryUrlForDeps = options.registry || auth?.registryUrl;
|
|
251
|
-
const all = Boolean(options.all);
|
|
252
455
|
const jsonOnly = Boolean(options.json);
|
|
253
456
|
const recursive = Boolean(options.recursive);
|
|
254
457
|
const yes = Boolean(options.yes);
|
|
255
458
|
const maxDepth = parsePositiveInt(options.depth, 6);
|
|
256
459
|
const maxSkills = parsePositiveInt(options.maxSkills, 200);
|
|
257
|
-
const
|
|
258
|
-
|
|
460
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY) && !jsonOnly;
|
|
461
|
+
const requestedAll = Boolean(options.all);
|
|
462
|
+
const requestedTarget = options.target;
|
|
463
|
+
if (requestedAll && options.target) {
|
|
259
464
|
const message = "Invalid options: use either --all or --target, not both.";
|
|
260
465
|
if (jsonOnly) printJson({ ok: false, error: message });
|
|
261
|
-
else console.error(
|
|
466
|
+
else console.error(chalk3.red(message));
|
|
262
467
|
process.exitCode = 1;
|
|
263
468
|
return;
|
|
264
469
|
}
|
|
470
|
+
let targets = [];
|
|
471
|
+
let deferPlatformSelection = false;
|
|
472
|
+
if (requestedAll) {
|
|
473
|
+
targets = [...PLATFORMS2];
|
|
474
|
+
} else if (requestedTarget) {
|
|
475
|
+
targets = [requestedTarget];
|
|
476
|
+
} else if (yes) {
|
|
477
|
+
targets = [...PLATFORMS2];
|
|
478
|
+
} else if (interactive) {
|
|
479
|
+
deferPlatformSelection = true;
|
|
480
|
+
targets = [...PLATFORMS2];
|
|
481
|
+
} else {
|
|
482
|
+
targets = ["claude"];
|
|
483
|
+
}
|
|
484
|
+
const allPlatformsSelected = targets.length === PLATFORMS2.length;
|
|
265
485
|
let resolvedSource = source.trim();
|
|
266
486
|
try {
|
|
267
487
|
if (looksLikeAlias(resolvedSource)) {
|
|
268
488
|
const registryUrl = resolveRegistryUrl(registryUrlForDeps);
|
|
269
489
|
const resolved = await resolveRegistryAlias(registryUrl, resolvedSource);
|
|
270
|
-
if (!jsonOnly) logger.info(`Resolved ${
|
|
490
|
+
if (!jsonOnly) logger.info(`Resolved ${chalk3.cyan(resolvedSource)} \u2192 ${chalk3.cyan(resolved.spec)} (${resolved.type})`);
|
|
271
491
|
resolvedSource = resolved.spec;
|
|
272
492
|
}
|
|
273
493
|
} catch (error) {
|
|
274
494
|
const message = error instanceof SkildError ? error.message : error instanceof Error ? error.message : String(error);
|
|
275
495
|
if (jsonOnly) printJson({ ok: false, error: message });
|
|
276
|
-
else console.error(
|
|
496
|
+
else console.error(chalk3.red(message));
|
|
277
497
|
process.exitCode = 1;
|
|
278
498
|
return;
|
|
279
499
|
}
|
|
280
|
-
const targets = all ? [...PLATFORMS] : [platform];
|
|
281
500
|
const results = [];
|
|
282
501
|
const errors = [];
|
|
283
502
|
let effectiveRecursive = recursive;
|
|
284
503
|
let recursiveSkillCount = null;
|
|
285
|
-
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY) && !jsonOnly;
|
|
286
504
|
async function installOne(inputSource, materializedDir) {
|
|
287
505
|
for (const targetPlatform of targets) {
|
|
288
506
|
try {
|
|
@@ -301,14 +519,14 @@ async function install(source, options = {}) {
|
|
|
301
519
|
}
|
|
302
520
|
}
|
|
303
521
|
}
|
|
304
|
-
async function
|
|
305
|
-
if (found.length === 0) return
|
|
522
|
+
async function resolveDiscoveredSelection(found, spinner) {
|
|
523
|
+
if (found.length === 0) return null;
|
|
306
524
|
if (found.length > maxSkills) {
|
|
307
525
|
const message = `Found more than ${maxSkills} skills. Increase --max-skills to proceed.`;
|
|
308
526
|
if (jsonOnly) printJson({ ok: false, error: "TOO_MANY_SKILLS", message, source, resolvedSource, maxSkills });
|
|
309
|
-
else console.error(
|
|
527
|
+
else console.error(chalk3.red(message));
|
|
310
528
|
process.exitCode = 1;
|
|
311
|
-
return
|
|
529
|
+
return null;
|
|
312
530
|
}
|
|
313
531
|
if (!effectiveRecursive) {
|
|
314
532
|
if (jsonOnly) {
|
|
@@ -322,36 +540,66 @@ async function install(source, options = {}) {
|
|
|
322
540
|
found: foundOutput
|
|
323
541
|
});
|
|
324
542
|
process.exitCode = 1;
|
|
325
|
-
return
|
|
543
|
+
return null;
|
|
326
544
|
}
|
|
327
545
|
if (spinner) spinner.stop();
|
|
328
|
-
const headline = found.length === 1 ? `No SKILL.md found at root. Found 1 skill
|
|
329
|
-
${
|
|
330
|
-
` : `No SKILL.md found at root. Found ${found.length} skills:
|
|
331
|
-
${previewDiscovered(found)}
|
|
546
|
+
const headline = found.length === 1 ? `No SKILL.md found at root. Found 1 skill.
|
|
547
|
+
` : `No SKILL.md found at root. Found ${found.length} skills.
|
|
332
548
|
`;
|
|
333
|
-
console.log(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
if (!confirm) {
|
|
337
|
-
console.log(chalk2.dim(`Tip: rerun with ${chalk2.cyan("skild install <source> --recursive")} to install all.`));
|
|
549
|
+
console.log(chalk3.yellow(headline));
|
|
550
|
+
if (!interactive && !yes) {
|
|
551
|
+
console.log(chalk3.dim(`Tip: rerun with ${chalk3.cyan("skild install <source> --recursive")} to install all.`));
|
|
338
552
|
process.exitCode = 1;
|
|
339
|
-
return
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
if (!yes) {
|
|
556
|
+
const selected = await promptSkillsInteractive(found, { defaultAll: true });
|
|
557
|
+
if (!selected) {
|
|
558
|
+
console.log(chalk3.red("No skills selected."));
|
|
559
|
+
process.exitCode = 1;
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
if (deferPlatformSelection) {
|
|
563
|
+
const selectedPlatforms = await promptPlatformsInteractive({ defaultAll: true });
|
|
564
|
+
if (!selectedPlatforms) {
|
|
565
|
+
console.log(chalk3.red("No platforms selected."));
|
|
566
|
+
process.exitCode = 1;
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
targets = selectedPlatforms;
|
|
570
|
+
deferPlatformSelection = false;
|
|
571
|
+
}
|
|
572
|
+
effectiveRecursive = true;
|
|
573
|
+
if (spinner) spinner.start();
|
|
574
|
+
recursiveSkillCount = selected.length;
|
|
575
|
+
return selected;
|
|
340
576
|
}
|
|
341
577
|
effectiveRecursive = true;
|
|
342
|
-
if (spinner) spinner.start();
|
|
343
578
|
}
|
|
344
579
|
recursiveSkillCount = found.length;
|
|
345
|
-
return
|
|
580
|
+
return found;
|
|
346
581
|
}
|
|
347
582
|
try {
|
|
348
583
|
const spinner = jsonOnly ? null : createSpinner(
|
|
349
|
-
|
|
584
|
+
allPlatformsSelected ? `Installing ${chalk3.cyan(source)} to ${chalk3.dim("all platforms")} (${scope})...` : targets.length > 1 ? `Installing ${chalk3.cyan(source)} to ${chalk3.dim(`${targets.length} platforms`)} (${scope})...` : `Installing ${chalk3.cyan(source)} to ${chalk3.dim(targets[0])} (${scope})...`
|
|
350
585
|
);
|
|
351
586
|
let cleanupMaterialized = null;
|
|
352
587
|
let materializedRoot = null;
|
|
353
588
|
try {
|
|
589
|
+
async function ensurePlatformSelection() {
|
|
590
|
+
if (!deferPlatformSelection) return true;
|
|
591
|
+
const selectedPlatforms = await promptPlatformsInteractive({ defaultAll: true });
|
|
592
|
+
if (!selectedPlatforms) {
|
|
593
|
+
console.log(chalk3.red("No platforms selected."));
|
|
594
|
+
process.exitCode = 1;
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
targets = selectedPlatforms;
|
|
598
|
+
deferPlatformSelection = false;
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
354
601
|
if (resolvedSource.startsWith("@") && resolvedSource.includes("/")) {
|
|
602
|
+
if (!await ensurePlatformSelection()) return;
|
|
355
603
|
await installOne(resolvedSource);
|
|
356
604
|
} else {
|
|
357
605
|
const maybeLocalRoot = path2.resolve(resolvedSource);
|
|
@@ -359,6 +607,7 @@ ${previewDiscovered(found)}
|
|
|
359
607
|
if (isLocal) {
|
|
360
608
|
const hasSkillMd = fs2.existsSync(path2.join(maybeLocalRoot, "SKILL.md"));
|
|
361
609
|
if (hasSkillMd) {
|
|
610
|
+
if (!await ensurePlatformSelection()) return;
|
|
362
611
|
await installOne(resolvedSource);
|
|
363
612
|
} else {
|
|
364
613
|
const discovered = discoverSkillDirsWithHeuristics(maybeLocalRoot, { maxDepth, maxSkills });
|
|
@@ -368,17 +617,17 @@ ${previewDiscovered(found)}
|
|
|
368
617
|
printJson({ ok: false, error: "SKILL_MD_NOT_FOUND", message, source, resolvedSource });
|
|
369
618
|
} else {
|
|
370
619
|
if (spinner) spinner.stop();
|
|
371
|
-
console.error(
|
|
620
|
+
console.error(chalk3.red(message));
|
|
372
621
|
}
|
|
373
622
|
process.exitCode = 1;
|
|
374
623
|
return;
|
|
375
624
|
}
|
|
376
625
|
const found = asDiscoveredSkills(discovered, (d) => path2.join(maybeLocalRoot, d.relPath));
|
|
377
|
-
const
|
|
378
|
-
if (
|
|
379
|
-
if (spinner) spinner.text = `Installing ${
|
|
380
|
-
for (const skill of
|
|
381
|
-
if (spinner) spinner.text = `Installing ${
|
|
626
|
+
const selected = await resolveDiscoveredSelection(found, spinner);
|
|
627
|
+
if (!selected) return;
|
|
628
|
+
if (spinner) spinner.text = `Installing ${chalk3.cyan(source)} \u2014 discovered ${selected.length} skills...`;
|
|
629
|
+
for (const skill of selected) {
|
|
630
|
+
if (spinner) spinner.text = `Installing ${chalk3.cyan(skill.relPath)} (${scope})...`;
|
|
382
631
|
await installOne(skill.suggestedSource, skill.materializedDir);
|
|
383
632
|
}
|
|
384
633
|
}
|
|
@@ -397,7 +646,7 @@ ${previewDiscovered(found)}
|
|
|
397
646
|
printJson({ ok: false, error: "SKILL_MD_NOT_FOUND", message, source, resolvedSource });
|
|
398
647
|
} else {
|
|
399
648
|
if (spinner) spinner.stop();
|
|
400
|
-
console.error(
|
|
649
|
+
console.error(chalk3.red(message));
|
|
401
650
|
}
|
|
402
651
|
process.exitCode = 1;
|
|
403
652
|
return;
|
|
@@ -407,11 +656,11 @@ ${previewDiscovered(found)}
|
|
|
407
656
|
(d) => deriveChildSource(resolvedSource, d.relPath),
|
|
408
657
|
(d) => d.absDir
|
|
409
658
|
);
|
|
410
|
-
const
|
|
411
|
-
if (
|
|
412
|
-
if (spinner) spinner.text = `Installing ${
|
|
413
|
-
for (const skill of
|
|
414
|
-
if (spinner) spinner.text = `Installing ${
|
|
659
|
+
const selected = await resolveDiscoveredSelection(found, spinner);
|
|
660
|
+
if (!selected) return;
|
|
661
|
+
if (spinner) spinner.text = `Installing ${chalk3.cyan(source)} \u2014 discovered ${selected.length} skills...`;
|
|
662
|
+
for (const skill of selected) {
|
|
663
|
+
if (spinner) spinner.text = `Installing ${chalk3.cyan(skill.relPath)} (${scope})...`;
|
|
415
664
|
await installOne(skill.suggestedSource, skill.materializedDir);
|
|
416
665
|
}
|
|
417
666
|
}
|
|
@@ -421,11 +670,21 @@ ${previewDiscovered(found)}
|
|
|
421
670
|
if (cleanupMaterialized) cleanupMaterialized();
|
|
422
671
|
}
|
|
423
672
|
if (jsonOnly) {
|
|
424
|
-
if (!
|
|
673
|
+
if (!effectiveRecursive && targets.length === 1) {
|
|
425
674
|
if (errors.length) printJson({ ok: false, error: errors[0]?.error || "Install failed." });
|
|
426
675
|
else printJson(results[0] ?? null);
|
|
427
676
|
} else {
|
|
428
|
-
printJson({
|
|
677
|
+
printJson({
|
|
678
|
+
ok: errors.length === 0,
|
|
679
|
+
source,
|
|
680
|
+
resolvedSource,
|
|
681
|
+
scope,
|
|
682
|
+
recursive: effectiveRecursive,
|
|
683
|
+
all: allPlatformsSelected,
|
|
684
|
+
recursiveSkillCount,
|
|
685
|
+
results,
|
|
686
|
+
errors
|
|
687
|
+
});
|
|
429
688
|
}
|
|
430
689
|
process.exitCode = errors.length ? 1 : 0;
|
|
431
690
|
return;
|
|
@@ -433,42 +692,42 @@ ${previewDiscovered(found)}
|
|
|
433
692
|
if (errors.length === 0) {
|
|
434
693
|
const displayName = results[0]?.canonicalName || results[0]?.name || source;
|
|
435
694
|
spinner.succeed(
|
|
436
|
-
effectiveRecursive ? `Installed ${
|
|
695
|
+
effectiveRecursive ? `Installed ${chalk3.green(String(recursiveSkillCount ?? results.length))}${chalk3.dim(" skills")} to ${chalk3.dim(`${targets.length} platforms`)}` : targets.length > 1 ? `Installed ${chalk3.green(displayName)} to ${chalk3.dim(`${results.length} platforms`)}` : `Installed ${chalk3.green(displayName)} to ${chalk3.dim(results[0]?.installDir || "")}`
|
|
437
696
|
);
|
|
438
697
|
} else {
|
|
439
698
|
const attempted = results.length + errors.length;
|
|
440
699
|
spinner.fail(
|
|
441
|
-
effectiveRecursive ? `Install had failures (${errors.length}/${attempted} installs failed)` : `Failed to install ${
|
|
700
|
+
effectiveRecursive ? `Install had failures (${errors.length}/${attempted} installs failed)` : `Failed to install ${chalk3.red(source)} to ${errors.length}/${targets.length} platforms`
|
|
442
701
|
);
|
|
443
702
|
process.exitCode = 1;
|
|
444
|
-
if (!
|
|
703
|
+
if (!effectiveRecursive && targets.length === 1 && errors[0]) console.error(chalk3.red(errors[0].error));
|
|
445
704
|
}
|
|
446
|
-
if (!effectiveRecursive &&
|
|
705
|
+
if (!effectiveRecursive && targets.length === 1 && results[0]) {
|
|
447
706
|
const record = results[0];
|
|
448
707
|
if (record.hasSkillMd) logger.installDetail("SKILL.md found \u2713");
|
|
449
708
|
else logger.installDetail("Warning: No SKILL.md found", true);
|
|
450
709
|
if (record.skill?.validation && !record.skill.validation.ok) {
|
|
451
|
-
logger.installDetail(`Validation: ${
|
|
710
|
+
logger.installDetail(`Validation: ${chalk3.yellow("failed")} (${record.skill.validation.issues.length} issues)`, true);
|
|
452
711
|
} else if (record.skill?.validation?.ok) {
|
|
453
|
-
logger.installDetail(`Validation: ${
|
|
712
|
+
logger.installDetail(`Validation: ${chalk3.green("ok")}`);
|
|
454
713
|
}
|
|
455
|
-
} else if (effectiveRecursive ||
|
|
714
|
+
} else if (effectiveRecursive || targets.length > 1) {
|
|
456
715
|
for (const r of results.slice(0, 60)) {
|
|
457
716
|
const displayName = r.canonicalName || r.name;
|
|
458
|
-
const suffix = r.hasSkillMd ?
|
|
459
|
-
console.log(` ${suffix} ${
|
|
717
|
+
const suffix = r.hasSkillMd ? chalk3.green("\u2713") : chalk3.yellow("\u26A0");
|
|
718
|
+
console.log(` ${suffix} ${chalk3.cyan(displayName)} \u2192 ${chalk3.dim(r.platform)}`);
|
|
460
719
|
}
|
|
461
|
-
if (results.length > 60) console.log(
|
|
720
|
+
if (results.length > 60) console.log(chalk3.dim(` ... and ${results.length - 60} more`));
|
|
462
721
|
if (errors.length) {
|
|
463
|
-
console.log(
|
|
464
|
-
for (const e of errors) console.log(
|
|
722
|
+
console.log(chalk3.yellow("\nFailures:"));
|
|
723
|
+
for (const e of errors) console.log(chalk3.yellow(` - ${e.platform}: ${e.error}`));
|
|
465
724
|
}
|
|
466
725
|
process.exitCode = errors.length ? 1 : 0;
|
|
467
726
|
}
|
|
468
727
|
} catch (error) {
|
|
469
728
|
const message = error instanceof SkildError ? error.message : error instanceof Error ? error.message : String(error);
|
|
470
729
|
if (jsonOnly) printJson({ ok: false, error: message });
|
|
471
|
-
else console.error(
|
|
730
|
+
else console.error(chalk3.red(message));
|
|
472
731
|
process.exitCode = 1;
|
|
473
732
|
}
|
|
474
733
|
}
|
|
@@ -504,8 +763,8 @@ async function reportDownload(record, registryOverride) {
|
|
|
504
763
|
}
|
|
505
764
|
|
|
506
765
|
// src/commands/list.ts
|
|
507
|
-
import
|
|
508
|
-
import { PLATFORMS as
|
|
766
|
+
import chalk4 from "chalk";
|
|
767
|
+
import { PLATFORMS as PLATFORMS3, listAllSkills, listSkills } from "@skild/core";
|
|
509
768
|
function isSkillset(skill) {
|
|
510
769
|
return Boolean(skill.record?.skillset || skill.record?.skill?.frontmatter?.skillset);
|
|
511
770
|
}
|
|
@@ -520,10 +779,10 @@ function buildNameToDisplay(skills) {
|
|
|
520
779
|
return mapping;
|
|
521
780
|
}
|
|
522
781
|
function statusIcon(skill) {
|
|
523
|
-
return skill.hasSkillMd ?
|
|
782
|
+
return skill.hasSkillMd ? chalk4.green("\u2713") : chalk4.yellow("\u26A0");
|
|
524
783
|
}
|
|
525
784
|
function missingSkillMdLabel(skill) {
|
|
526
|
-
return skill.hasSkillMd ? "" :
|
|
785
|
+
return skill.hasSkillMd ? "" : chalk4.yellow(" (missing SKILL.md)");
|
|
527
786
|
}
|
|
528
787
|
function formatDepName(dep, nameToDisplay) {
|
|
529
788
|
return dep.canonicalName || nameToDisplay.get(dep.name) || dep.name;
|
|
@@ -548,62 +807,62 @@ function summarizeDeps(record) {
|
|
|
548
807
|
return `deps: ${byType.total}${parts.length ? ` (${parts.join(", ")})` : ""}`;
|
|
549
808
|
}
|
|
550
809
|
function printSkillsetSection(skills, nameToDisplay, options) {
|
|
551
|
-
console.log(
|
|
810
|
+
console.log(chalk4.bold(` Skillsets (${skills.length})`));
|
|
552
811
|
if (skills.length === 0) {
|
|
553
|
-
console.log(
|
|
812
|
+
console.log(chalk4.dim(" (none)"));
|
|
554
813
|
return;
|
|
555
814
|
}
|
|
556
815
|
for (const s of skills) {
|
|
557
|
-
const label = `${
|
|
816
|
+
const label = `${chalk4.cyan(getDisplayName(s))}${chalk4.dim(" (skillset)")}${missingSkillMdLabel(s)}`;
|
|
558
817
|
console.log(` ${statusIcon(s)} ${label}`);
|
|
559
818
|
const summary = summarizeDeps(s.record);
|
|
560
|
-
if (summary) console.log(
|
|
819
|
+
if (summary) console.log(chalk4.dim(` ${summary}`));
|
|
561
820
|
if (options.verbose) {
|
|
562
821
|
const deps = (s.record?.installedDependencies || []).slice().sort((a, b) => a.name.localeCompare(b.name));
|
|
563
822
|
if (deps.length) {
|
|
564
|
-
console.log(
|
|
823
|
+
console.log(chalk4.dim(` includes (${deps.length}):`));
|
|
565
824
|
for (const dep of deps) {
|
|
566
825
|
const depName = formatDepName(dep, nameToDisplay);
|
|
567
|
-
console.log(
|
|
826
|
+
console.log(chalk4.dim(` - ${depName} [${dep.sourceType}]`));
|
|
568
827
|
}
|
|
569
828
|
}
|
|
570
829
|
}
|
|
571
|
-
if (options.paths || !s.hasSkillMd) console.log(
|
|
830
|
+
if (options.paths || !s.hasSkillMd) console.log(chalk4.dim(` path: ${s.installDir}`));
|
|
572
831
|
}
|
|
573
832
|
}
|
|
574
833
|
function printSkillsSection(skills, options) {
|
|
575
|
-
console.log(
|
|
834
|
+
console.log(chalk4.bold(` Skills (${skills.length})`));
|
|
576
835
|
if (skills.length === 0) {
|
|
577
|
-
console.log(
|
|
836
|
+
console.log(chalk4.dim(" (none)"));
|
|
578
837
|
return;
|
|
579
838
|
}
|
|
580
839
|
for (const s of skills) {
|
|
581
|
-
const label = `${
|
|
840
|
+
const label = `${chalk4.cyan(getDisplayName(s))}${missingSkillMdLabel(s)}`;
|
|
582
841
|
console.log(` ${statusIcon(s)} ${label}`);
|
|
583
|
-
if (options.paths || !s.hasSkillMd) console.log(
|
|
842
|
+
if (options.paths || !s.hasSkillMd) console.log(chalk4.dim(` path: ${s.installDir}`));
|
|
584
843
|
}
|
|
585
844
|
}
|
|
586
845
|
function printDependenciesSection(skills, nameToDisplay, options) {
|
|
587
|
-
console.log(
|
|
846
|
+
console.log(chalk4.bold(` Dependencies (${skills.length})`));
|
|
588
847
|
if (skills.length === 0) {
|
|
589
|
-
console.log(
|
|
848
|
+
console.log(chalk4.dim(" (none)"));
|
|
590
849
|
return;
|
|
591
850
|
}
|
|
592
851
|
for (const s of skills) {
|
|
593
852
|
const dependedBy = (s.record?.dependedBy || []).map((name) => nameToDisplay.get(name) || name).sort((a, b) => a.localeCompare(b));
|
|
594
|
-
const requiredBy = dependedBy.length ?
|
|
595
|
-
const label = `${
|
|
853
|
+
const requiredBy = dependedBy.length ? chalk4.dim(` \u2190 required by: ${dependedBy.join(", ")}`) : "";
|
|
854
|
+
const label = `${chalk4.cyan(getDisplayName(s))}${missingSkillMdLabel(s)}${requiredBy}`;
|
|
596
855
|
console.log(` ${statusIcon(s)} ${label}`);
|
|
597
|
-
if (options.paths || !s.hasSkillMd) console.log(
|
|
856
|
+
if (options.paths || !s.hasSkillMd) console.log(chalk4.dim(` path: ${s.installDir}`));
|
|
598
857
|
}
|
|
599
858
|
}
|
|
600
859
|
function printPlatform(skills, platform, scope, options) {
|
|
601
|
-
console.log(
|
|
860
|
+
console.log(chalk4.bold(`
|
|
602
861
|
\u{1F4E6} Installed Skills \u2014 ${platform} (${scope})
|
|
603
862
|
`));
|
|
604
863
|
if (skills.length === 0) {
|
|
605
|
-
console.log(
|
|
606
|
-
console.log(
|
|
864
|
+
console.log(chalk4.dim(" No skills installed."));
|
|
865
|
+
console.log(chalk4.dim(` Use ${chalk4.cyan("skild install <source>")} to install a skill.`));
|
|
607
866
|
return;
|
|
608
867
|
}
|
|
609
868
|
const nameToDisplay = buildNameToDisplay(skills);
|
|
@@ -637,11 +896,11 @@ async function list(options = {}) {
|
|
|
637
896
|
return;
|
|
638
897
|
}
|
|
639
898
|
if (allSkills.length === 0) {
|
|
640
|
-
console.log(
|
|
641
|
-
console.log(
|
|
899
|
+
console.log(chalk4.dim("No skills installed."));
|
|
900
|
+
console.log(chalk4.dim(`Use ${chalk4.cyan("skild install <source>")} to install a skill.`));
|
|
642
901
|
return;
|
|
643
902
|
}
|
|
644
|
-
for (const p of
|
|
903
|
+
for (const p of PLATFORMS3) {
|
|
645
904
|
const platformSkills = allSkills.filter((s) => s.platform === p).sort((a, b) => a.name.localeCompare(b.name));
|
|
646
905
|
if (platformSkills.length === 0) continue;
|
|
647
906
|
printPlatform(platformSkills, p, scope, { paths, verbose });
|
|
@@ -649,7 +908,7 @@ async function list(options = {}) {
|
|
|
649
908
|
}
|
|
650
909
|
|
|
651
910
|
// src/commands/info.ts
|
|
652
|
-
import
|
|
911
|
+
import chalk5 from "chalk";
|
|
653
912
|
import { canonicalNameToInstallDirName, getSkillInfo, SkildError as SkildError2 } from "@skild/core";
|
|
654
913
|
async function info(skill, options = {}) {
|
|
655
914
|
const platform = options.target || "claude";
|
|
@@ -662,22 +921,22 @@ async function info(skill, options = {}) {
|
|
|
662
921
|
return;
|
|
663
922
|
}
|
|
664
923
|
const displayName = record.canonicalName || record.name;
|
|
665
|
-
console.log(
|
|
666
|
-
${
|
|
924
|
+
console.log(chalk5.bold(`
|
|
925
|
+
${chalk5.cyan(displayName)}
|
|
667
926
|
`));
|
|
668
|
-
console.log(` ${
|
|
669
|
-
console.log(` ${
|
|
670
|
-
console.log(` ${
|
|
671
|
-
console.log(` ${
|
|
672
|
-
if (record.updatedAt) console.log(` ${
|
|
673
|
-
console.log(` ${
|
|
674
|
-
console.log(` ${
|
|
927
|
+
console.log(` ${chalk5.dim("Path:")} ${record.installDir}`);
|
|
928
|
+
console.log(` ${chalk5.dim("Source:")} ${record.source}`);
|
|
929
|
+
console.log(` ${chalk5.dim("Target:")} ${record.platform} (${record.scope})`);
|
|
930
|
+
console.log(` ${chalk5.dim("Installed:")} ${record.installedAt}`);
|
|
931
|
+
if (record.updatedAt) console.log(` ${chalk5.dim("Updated:")} ${record.updatedAt}`);
|
|
932
|
+
console.log(` ${chalk5.dim("Hash:")} ${record.contentHash}`);
|
|
933
|
+
console.log(` ${chalk5.dim("SKILL.md:")} ${record.hasSkillMd ? chalk5.green("yes") : chalk5.yellow("no")}`);
|
|
675
934
|
const validation = record.skill?.validation;
|
|
676
935
|
if (validation) {
|
|
677
|
-
console.log(` ${
|
|
936
|
+
console.log(` ${chalk5.dim("Validate:")} ${validation.ok ? chalk5.green("ok") : chalk5.red("failed")}`);
|
|
678
937
|
if (!validation.ok) {
|
|
679
938
|
for (const issue of validation.issues) {
|
|
680
|
-
const color = issue.level === "error" ?
|
|
939
|
+
const color = issue.level === "error" ? chalk5.red : chalk5.yellow;
|
|
681
940
|
console.log(` - ${color(issue.level)}: ${issue.message}`);
|
|
682
941
|
}
|
|
683
942
|
}
|
|
@@ -685,20 +944,20 @@ ${chalk4.cyan(displayName)}
|
|
|
685
944
|
console.log("");
|
|
686
945
|
} catch (error) {
|
|
687
946
|
const message = error instanceof SkildError2 ? error.message : error instanceof Error ? error.message : String(error);
|
|
688
|
-
console.error(
|
|
947
|
+
console.error(chalk5.red(message));
|
|
689
948
|
process.exitCode = 1;
|
|
690
949
|
}
|
|
691
950
|
}
|
|
692
951
|
|
|
693
952
|
// src/commands/uninstall.ts
|
|
694
|
-
import
|
|
953
|
+
import chalk6 from "chalk";
|
|
695
954
|
import { canonicalNameToInstallDirName as canonicalNameToInstallDirName2, uninstallSkill, SkildError as SkildError3 } from "@skild/core";
|
|
696
955
|
async function uninstall(skill, options = {}) {
|
|
697
956
|
const platform = options.target || "claude";
|
|
698
957
|
const scope = options.local ? "project" : "global";
|
|
699
958
|
const canonical = skill.trim();
|
|
700
959
|
const resolvedName = canonical.startsWith("@") && canonical.includes("/") ? canonicalNameToInstallDirName2(canonical) : canonical;
|
|
701
|
-
const spinner = createSpinner(`Uninstalling ${
|
|
960
|
+
const spinner = createSpinner(`Uninstalling ${chalk6.cyan(canonical)} from ${chalk6.dim(platform)} (${scope})...`);
|
|
702
961
|
try {
|
|
703
962
|
uninstallSkill(resolvedName, {
|
|
704
963
|
platform,
|
|
@@ -706,40 +965,40 @@ async function uninstall(skill, options = {}) {
|
|
|
706
965
|
allowMissingMetadata: Boolean(options.force),
|
|
707
966
|
withDeps: Boolean(options.withDeps)
|
|
708
967
|
});
|
|
709
|
-
spinner.succeed(`Uninstalled ${
|
|
968
|
+
spinner.succeed(`Uninstalled ${chalk6.green(canonical)}`);
|
|
710
969
|
} catch (error) {
|
|
711
|
-
spinner.fail(`Failed to uninstall ${
|
|
970
|
+
spinner.fail(`Failed to uninstall ${chalk6.red(canonical)}`);
|
|
712
971
|
const message = error instanceof SkildError3 ? error.message : error instanceof Error ? error.message : String(error);
|
|
713
|
-
console.error(
|
|
972
|
+
console.error(chalk6.red(message));
|
|
714
973
|
process.exitCode = 1;
|
|
715
974
|
}
|
|
716
975
|
}
|
|
717
976
|
|
|
718
977
|
// src/commands/update.ts
|
|
719
|
-
import
|
|
978
|
+
import chalk7 from "chalk";
|
|
720
979
|
import { canonicalNameToInstallDirName as canonicalNameToInstallDirName3, updateSkill, SkildError as SkildError4 } from "@skild/core";
|
|
721
980
|
async function update(skill, options = {}) {
|
|
722
981
|
const platform = options.target || "claude";
|
|
723
982
|
const scope = options.local ? "project" : "global";
|
|
724
983
|
const label = skill ? skill : "all skills";
|
|
725
984
|
const resolvedName = skill && skill.trim().startsWith("@") && skill.includes("/") ? canonicalNameToInstallDirName3(skill.trim()) : skill;
|
|
726
|
-
const spinner = createSpinner(`Updating ${
|
|
985
|
+
const spinner = createSpinner(`Updating ${chalk7.cyan(label)} on ${chalk7.dim(platform)} (${scope})...`);
|
|
727
986
|
try {
|
|
728
987
|
const results = await updateSkill(resolvedName, { platform, scope });
|
|
729
|
-
spinner.succeed(`Updated ${
|
|
988
|
+
spinner.succeed(`Updated ${chalk7.green(results.length.toString())} skill(s).`);
|
|
730
989
|
if (options.json) {
|
|
731
990
|
console.log(JSON.stringify(results, null, 2));
|
|
732
991
|
}
|
|
733
992
|
} catch (error) {
|
|
734
|
-
spinner.fail(`Failed to update ${
|
|
993
|
+
spinner.fail(`Failed to update ${chalk7.red(label)}`);
|
|
735
994
|
const message = error instanceof SkildError4 ? error.message : error instanceof Error ? error.message : String(error);
|
|
736
|
-
console.error(
|
|
995
|
+
console.error(chalk7.red(message));
|
|
737
996
|
process.exitCode = 1;
|
|
738
997
|
}
|
|
739
998
|
}
|
|
740
999
|
|
|
741
1000
|
// src/commands/validate.ts
|
|
742
|
-
import
|
|
1001
|
+
import chalk8 from "chalk";
|
|
743
1002
|
import { canonicalNameToInstallDirName as canonicalNameToInstallDirName4, validateSkill } from "@skild/core";
|
|
744
1003
|
async function validate(target, options = {}) {
|
|
745
1004
|
const platform = options.target || "claude";
|
|
@@ -753,42 +1012,102 @@ async function validate(target, options = {}) {
|
|
|
753
1012
|
return;
|
|
754
1013
|
}
|
|
755
1014
|
if (result.ok) {
|
|
756
|
-
console.log(
|
|
757
|
-
if (result.frontmatter?.name) console.log(
|
|
1015
|
+
console.log(chalk8.green("\u2713"), "Valid skill");
|
|
1016
|
+
if (result.frontmatter?.name) console.log(chalk8.dim(` name: ${result.frontmatter.name}`));
|
|
758
1017
|
return;
|
|
759
1018
|
}
|
|
760
|
-
console.error(
|
|
1019
|
+
console.error(chalk8.red("\u2717"), "Invalid skill");
|
|
761
1020
|
for (const issue of result.issues) {
|
|
762
|
-
const color = issue.level === "error" ?
|
|
1021
|
+
const color = issue.level === "error" ? chalk8.red : chalk8.yellow;
|
|
763
1022
|
console.error(` - ${color(issue.level)}: ${issue.message}`);
|
|
764
1023
|
}
|
|
765
1024
|
process.exitCode = 1;
|
|
766
1025
|
}
|
|
767
1026
|
|
|
768
1027
|
// src/commands/init.ts
|
|
769
|
-
import
|
|
1028
|
+
import chalk9 from "chalk";
|
|
770
1029
|
import { initSkill, SkildError as SkildError5 } from "@skild/core";
|
|
771
1030
|
async function init(name, options = {}) {
|
|
772
|
-
const spinner = createSpinner(`Initializing ${
|
|
1031
|
+
const spinner = createSpinner(`Initializing ${chalk9.cyan(name)}...`);
|
|
773
1032
|
try {
|
|
774
1033
|
const createdDir = initSkill(name, {
|
|
775
1034
|
dir: options.dir,
|
|
776
1035
|
description: options.description,
|
|
777
1036
|
force: Boolean(options.force)
|
|
778
1037
|
});
|
|
779
|
-
spinner.succeed(`Created ${
|
|
780
|
-
console.log(
|
|
1038
|
+
spinner.succeed(`Created ${chalk9.green(name)} at ${chalk9.dim(createdDir)}`);
|
|
1039
|
+
console.log(chalk9.dim(`Next: cd ${createdDir} && skild validate .`));
|
|
781
1040
|
} catch (error) {
|
|
782
|
-
spinner.fail(`Failed to init ${
|
|
1041
|
+
spinner.fail(`Failed to init ${chalk9.red(name)}`);
|
|
783
1042
|
const message = error instanceof SkildError5 ? error.message : error instanceof Error ? error.message : String(error);
|
|
784
|
-
console.error(
|
|
1043
|
+
console.error(chalk9.red(message));
|
|
785
1044
|
process.exitCode = 1;
|
|
786
1045
|
}
|
|
787
1046
|
}
|
|
788
1047
|
|
|
789
1048
|
// src/commands/signup.ts
|
|
790
|
-
import
|
|
1049
|
+
import chalk10 from "chalk";
|
|
791
1050
|
import { fetchWithTimeout as fetchWithTimeout2, resolveRegistryUrl as resolveRegistryUrl2, SkildError as SkildError6 } from "@skild/core";
|
|
1051
|
+
|
|
1052
|
+
// src/utils/prompt.ts
|
|
1053
|
+
import readline2 from "readline";
|
|
1054
|
+
async function promptLine(question, defaultValue) {
|
|
1055
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
1056
|
+
try {
|
|
1057
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
1058
|
+
const answer = await new Promise((resolve) => rl.question(`${question}${suffix}: `, resolve));
|
|
1059
|
+
const trimmed = answer.trim();
|
|
1060
|
+
return trimmed || defaultValue || "";
|
|
1061
|
+
} finally {
|
|
1062
|
+
rl.close();
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
async function promptPassword(question) {
|
|
1066
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1067
|
+
return promptLine(question);
|
|
1068
|
+
}
|
|
1069
|
+
const stdin = process.stdin;
|
|
1070
|
+
const stdout = process.stdout;
|
|
1071
|
+
stdout.write(`${question}: `);
|
|
1072
|
+
const wasRaw = Boolean(stdin.isRaw);
|
|
1073
|
+
stdin.setRawMode(true);
|
|
1074
|
+
stdin.resume();
|
|
1075
|
+
readline2.emitKeypressEvents(stdin);
|
|
1076
|
+
const buf = [];
|
|
1077
|
+
return await new Promise((resolve, reject) => {
|
|
1078
|
+
function cleanup() {
|
|
1079
|
+
stdin.off("keypress", onKeypress);
|
|
1080
|
+
stdin.setRawMode(wasRaw);
|
|
1081
|
+
stdin.pause();
|
|
1082
|
+
}
|
|
1083
|
+
function onKeypress(str, key) {
|
|
1084
|
+
if (key?.ctrl && key?.name === "c") {
|
|
1085
|
+
stdout.write("\n");
|
|
1086
|
+
cleanup();
|
|
1087
|
+
const err = new Error("Prompt cancelled");
|
|
1088
|
+
err.code = "PROMPT_CANCELLED";
|
|
1089
|
+
reject(err);
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
if (key?.name === "return" || key?.name === "enter") {
|
|
1093
|
+
stdout.write("\n");
|
|
1094
|
+
cleanup();
|
|
1095
|
+
resolve(buf.join(""));
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
if (key?.name === "backspace" || key?.name === "delete") {
|
|
1099
|
+
if (buf.length) buf.pop();
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
if (!str) return;
|
|
1103
|
+
if (key?.ctrl || key?.meta) return;
|
|
1104
|
+
buf.push(str);
|
|
1105
|
+
}
|
|
1106
|
+
stdin.on("keypress", onKeypress);
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// src/commands/signup.ts
|
|
792
1111
|
async function signup(options) {
|
|
793
1112
|
const registry = resolveRegistryUrl2(options.registry);
|
|
794
1113
|
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
@@ -796,7 +1115,7 @@ async function signup(options) {
|
|
|
796
1115
|
const handle = options.handle?.trim() || "";
|
|
797
1116
|
const password = options.password || "";
|
|
798
1117
|
if ((!email || !handle || !password) && (!interactive || options.json)) {
|
|
799
|
-
console.error(
|
|
1118
|
+
console.error(chalk10.red("Missing signup fields. Use --email/--handle/--password, or run `skild signup` interactively."));
|
|
800
1119
|
process.exitCode = 1;
|
|
801
1120
|
return;
|
|
802
1121
|
}
|
|
@@ -831,13 +1150,13 @@ async function signup(options) {
|
|
|
831
1150
|
);
|
|
832
1151
|
text = await res.text();
|
|
833
1152
|
if (!res.ok) {
|
|
834
|
-
console.error(
|
|
1153
|
+
console.error(chalk10.red(`Signup failed (${res.status}): ${text}`));
|
|
835
1154
|
process.exitCode = 1;
|
|
836
1155
|
return;
|
|
837
1156
|
}
|
|
838
1157
|
} catch (error) {
|
|
839
1158
|
const message = error instanceof SkildError6 ? error.message : error instanceof Error ? error.message : String(error);
|
|
840
|
-
console.error(
|
|
1159
|
+
console.error(chalk10.red(`Signup failed: ${message}`));
|
|
841
1160
|
process.exitCode = 1;
|
|
842
1161
|
return;
|
|
843
1162
|
}
|
|
@@ -845,29 +1164,29 @@ async function signup(options) {
|
|
|
845
1164
|
console.log(text || JSON.stringify({ ok: true }, null, 2));
|
|
846
1165
|
return;
|
|
847
1166
|
}
|
|
848
|
-
console.log(
|
|
1167
|
+
console.log(chalk10.green("Signup successful."));
|
|
849
1168
|
try {
|
|
850
1169
|
const parsed = JSON.parse(text);
|
|
851
1170
|
if (parsed.verification?.requiredForPublish) {
|
|
852
1171
|
if (parsed.verification.mode === "log") {
|
|
853
|
-
console.log(
|
|
1172
|
+
console.log(chalk10.dim("Dev mode: email sending is disabled. Check the registry dev logs for the verification link."));
|
|
854
1173
|
} else if (parsed.verification.sent) {
|
|
855
|
-
console.log(
|
|
1174
|
+
console.log(chalk10.dim("Verification email sent. Check your inbox (and spam)."));
|
|
856
1175
|
} else {
|
|
857
|
-
console.log(
|
|
1176
|
+
console.log(chalk10.yellow("Verification email was not sent. You may need to resend from the Skild Hub."));
|
|
858
1177
|
}
|
|
859
|
-
console.log(
|
|
1178
|
+
console.log(chalk10.dim(`Verify/resend in Skild Hub: ${(parsed.verification.consoleUrl || "https://hub.skild.sh").replace(/\/+$/, "")}/verify-email/request`));
|
|
860
1179
|
} else if (parsed.verification) {
|
|
861
|
-
console.log(
|
|
862
|
-
console.log(
|
|
1180
|
+
console.log(chalk10.dim("Email verification is recommended. Publishing may be restricted in the future."));
|
|
1181
|
+
console.log(chalk10.dim(`Verify/resend in Skild Hub: ${(parsed.verification.consoleUrl || "https://hub.skild.sh").replace(/\/+$/, "")}/verify-email/request`));
|
|
863
1182
|
}
|
|
864
1183
|
} catch {
|
|
865
1184
|
}
|
|
866
|
-
console.log(
|
|
1185
|
+
console.log(chalk10.dim("Next: run `skild login`"));
|
|
867
1186
|
}
|
|
868
1187
|
|
|
869
1188
|
// src/commands/login.ts
|
|
870
|
-
import
|
|
1189
|
+
import chalk11 from "chalk";
|
|
871
1190
|
import { fetchWithTimeout as fetchWithTimeout3, resolveRegistryUrl as resolveRegistryUrl3, saveRegistryAuth, SkildError as SkildError7 } from "@skild/core";
|
|
872
1191
|
async function login(options) {
|
|
873
1192
|
const registry = resolveRegistryUrl3(options.registry);
|
|
@@ -875,7 +1194,7 @@ async function login(options) {
|
|
|
875
1194
|
const handleOrEmail = options.handleOrEmail?.trim() || "";
|
|
876
1195
|
const password = options.password || "";
|
|
877
1196
|
if ((!handleOrEmail || !password) && (!interactive || options.json)) {
|
|
878
|
-
console.error(
|
|
1197
|
+
console.error(chalk11.red("Missing credentials. Use --handle-or-email and --password, or run `skild login` interactively."));
|
|
879
1198
|
process.exitCode = 1;
|
|
880
1199
|
return;
|
|
881
1200
|
}
|
|
@@ -910,13 +1229,13 @@ async function login(options) {
|
|
|
910
1229
|
);
|
|
911
1230
|
text = await res.text();
|
|
912
1231
|
if (!res.ok) {
|
|
913
|
-
console.error(
|
|
1232
|
+
console.error(chalk11.red(`Login failed (${res.status}): ${text}`));
|
|
914
1233
|
process.exitCode = 1;
|
|
915
1234
|
return;
|
|
916
1235
|
}
|
|
917
1236
|
} catch (error) {
|
|
918
1237
|
const message = error instanceof SkildError7 ? error.message : error instanceof Error ? error.message : String(error);
|
|
919
|
-
console.error(
|
|
1238
|
+
console.error(chalk11.red(`Login failed: ${message}`));
|
|
920
1239
|
process.exitCode = 1;
|
|
921
1240
|
return;
|
|
922
1241
|
}
|
|
@@ -932,28 +1251,28 @@ async function login(options) {
|
|
|
932
1251
|
console.log(JSON.stringify({ ok: true, publisher: json.publisher }, null, 2));
|
|
933
1252
|
return;
|
|
934
1253
|
}
|
|
935
|
-
console.log(
|
|
1254
|
+
console.log(chalk11.green(`Logged in as ${chalk11.cyan(json.publisher.handle)}.`));
|
|
936
1255
|
if (json.publisher.emailVerified === false) {
|
|
937
|
-
console.log(
|
|
938
|
-
console.log(
|
|
1256
|
+
console.log(chalk11.yellow("Email not verified. Publishing may be restricted by server policy."));
|
|
1257
|
+
console.log(chalk11.dim("Open the Skild Hub to verify: https://hub.skild.sh/verify-email/request"));
|
|
939
1258
|
}
|
|
940
1259
|
}
|
|
941
1260
|
|
|
942
1261
|
// src/commands/logout.ts
|
|
943
|
-
import
|
|
1262
|
+
import chalk12 from "chalk";
|
|
944
1263
|
import { clearRegistryAuth } from "@skild/core";
|
|
945
1264
|
async function logout() {
|
|
946
1265
|
clearRegistryAuth();
|
|
947
|
-
console.log(
|
|
1266
|
+
console.log(chalk12.green("Logged out."));
|
|
948
1267
|
}
|
|
949
1268
|
|
|
950
1269
|
// src/commands/whoami.ts
|
|
951
|
-
import
|
|
1270
|
+
import chalk13 from "chalk";
|
|
952
1271
|
import { fetchWithTimeout as fetchWithTimeout4, loadRegistryAuth as loadRegistryAuth2, resolveRegistryUrl as resolveRegistryUrl4, SkildError as SkildError8 } from "@skild/core";
|
|
953
1272
|
async function whoami() {
|
|
954
1273
|
const auth = loadRegistryAuth2();
|
|
955
1274
|
if (!auth) {
|
|
956
|
-
console.error(
|
|
1275
|
+
console.error(chalk13.red("Not logged in. Run `skild login` first."));
|
|
957
1276
|
process.exitCode = 1;
|
|
958
1277
|
return;
|
|
959
1278
|
}
|
|
@@ -966,16 +1285,16 @@ async function whoami() {
|
|
|
966
1285
|
);
|
|
967
1286
|
const text = await res.text();
|
|
968
1287
|
if (!res.ok) {
|
|
969
|
-
console.error(
|
|
1288
|
+
console.error(chalk13.red(`whoami failed (${res.status}): ${text}`));
|
|
970
1289
|
process.exitCode = 1;
|
|
971
1290
|
return;
|
|
972
1291
|
}
|
|
973
1292
|
const json = JSON.parse(text);
|
|
974
|
-
console.log(
|
|
1293
|
+
console.log(chalk13.cyan(json.publisher.handle));
|
|
975
1294
|
} catch (error) {
|
|
976
1295
|
const message = error instanceof SkildError8 ? error.message : error instanceof Error ? error.message : String(error);
|
|
977
|
-
console.error(
|
|
978
|
-
console.error(
|
|
1296
|
+
console.error(chalk13.red(`whoami failed: ${message}`));
|
|
1297
|
+
console.error(chalk13.dim("Tip: if you previously logged into a local registry, run `skild logout` then `skild login`."));
|
|
979
1298
|
process.exitCode = 1;
|
|
980
1299
|
}
|
|
981
1300
|
}
|
|
@@ -986,7 +1305,7 @@ import os from "os";
|
|
|
986
1305
|
import path3 from "path";
|
|
987
1306
|
import crypto from "crypto";
|
|
988
1307
|
import * as tar from "tar";
|
|
989
|
-
import
|
|
1308
|
+
import chalk14 from "chalk";
|
|
990
1309
|
import { assertValidAlias, fetchWithTimeout as fetchWithTimeout5, loadRegistryAuth as loadRegistryAuth3, normalizeAlias, resolveRegistryUrl as resolveRegistryUrl5, SkildError as SkildError9, splitCanonicalName, validateSkillDir } from "@skild/core";
|
|
991
1310
|
function sha256Hex(buf) {
|
|
992
1311
|
const h = crypto.createHash("sha256");
|
|
@@ -1002,15 +1321,15 @@ async function publish(options = {}) {
|
|
|
1002
1321
|
const registry = resolveRegistryUrl5(options.registry || auth?.registryUrl);
|
|
1003
1322
|
const token = auth?.token;
|
|
1004
1323
|
if (!token) {
|
|
1005
|
-
console.error(
|
|
1324
|
+
console.error(chalk14.red("Not logged in. Run `skild login` first."));
|
|
1006
1325
|
process.exitCode = 1;
|
|
1007
1326
|
return;
|
|
1008
1327
|
}
|
|
1009
1328
|
const dir = path3.resolve(options.dir || process.cwd());
|
|
1010
1329
|
const validation = validateSkillDir(dir);
|
|
1011
1330
|
if (!validation.ok) {
|
|
1012
|
-
console.error(
|
|
1013
|
-
for (const issue of validation.issues) console.error(
|
|
1331
|
+
console.error(chalk14.red("Skill validation failed:"));
|
|
1332
|
+
for (const issue of validation.issues) console.error(chalk14.red(`- ${issue.message}`));
|
|
1014
1333
|
process.exitCode = 1;
|
|
1015
1334
|
return;
|
|
1016
1335
|
}
|
|
@@ -1024,14 +1343,14 @@ async function publish(options = {}) {
|
|
|
1024
1343
|
const skillset = fm.skillset === true;
|
|
1025
1344
|
const dependencies = Array.isArray(fm.dependencies) ? fm.dependencies : [];
|
|
1026
1345
|
if (!name) {
|
|
1027
|
-
console.error(
|
|
1346
|
+
console.error(chalk14.red("Missing name. Provide SKILL.md frontmatter.name or --name."));
|
|
1028
1347
|
process.exitCode = 1;
|
|
1029
1348
|
return;
|
|
1030
1349
|
}
|
|
1031
1350
|
if (!name.startsWith("@")) {
|
|
1032
1351
|
const seg = name.trim();
|
|
1033
1352
|
if (!/^[a-z0-9][a-z0-9-]{1,63}$/.test(seg)) {
|
|
1034
|
-
console.error(
|
|
1353
|
+
console.error(chalk14.red("Invalid name. Use @publisher/skill or a simple skill name (lowercase letters/digits/dashes)."));
|
|
1035
1354
|
process.exitCode = 1;
|
|
1036
1355
|
return;
|
|
1037
1356
|
}
|
|
@@ -1042,26 +1361,26 @@ async function publish(options = {}) {
|
|
|
1042
1361
|
);
|
|
1043
1362
|
const meText = await meRes.text();
|
|
1044
1363
|
if (!meRes.ok) {
|
|
1045
|
-
console.error(
|
|
1364
|
+
console.error(chalk14.red(`Failed to infer publisher scope (${meRes.status}): ${meText}`));
|
|
1046
1365
|
process.exitCode = 1;
|
|
1047
1366
|
return;
|
|
1048
1367
|
}
|
|
1049
1368
|
const meJson = JSON.parse(meText);
|
|
1050
1369
|
const handle = String(meJson?.publisher?.handle || "").trim().toLowerCase();
|
|
1051
1370
|
if (!handle) {
|
|
1052
|
-
console.error(
|
|
1371
|
+
console.error(chalk14.red("Failed to infer publisher scope from registry response."));
|
|
1053
1372
|
process.exitCode = 1;
|
|
1054
1373
|
return;
|
|
1055
1374
|
}
|
|
1056
1375
|
name = `@${handle}/${seg}`;
|
|
1057
1376
|
}
|
|
1058
1377
|
if (!/^@[a-z0-9][a-z0-9-]{1,31}\/[a-z0-9][a-z0-9-]{1,63}$/.test(name)) {
|
|
1059
|
-
console.error(
|
|
1378
|
+
console.error(chalk14.red("Invalid publish name. Expected @publisher/skill (lowercase letters/digits/dashes)."));
|
|
1060
1379
|
process.exitCode = 1;
|
|
1061
1380
|
return;
|
|
1062
1381
|
}
|
|
1063
1382
|
if (!version2) {
|
|
1064
|
-
console.error(
|
|
1383
|
+
console.error(chalk14.red("Missing version. Provide semver like 1.2.3 via SKILL.md frontmatter or --skill-version."));
|
|
1065
1384
|
process.exitCode = 1;
|
|
1066
1385
|
return;
|
|
1067
1386
|
}
|
|
@@ -1070,12 +1389,12 @@ async function publish(options = {}) {
|
|
|
1070
1389
|
assertValidAlias(alias);
|
|
1071
1390
|
} catch (error) {
|
|
1072
1391
|
const message = error instanceof SkildError9 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1073
|
-
console.error(
|
|
1392
|
+
console.error(chalk14.red(message));
|
|
1074
1393
|
process.exitCode = 1;
|
|
1075
1394
|
return;
|
|
1076
1395
|
}
|
|
1077
1396
|
}
|
|
1078
|
-
const spinner = createSpinner(`Publishing ${
|
|
1397
|
+
const spinner = createSpinner(`Publishing ${chalk14.cyan(`${name}@${version2}`)} to ${chalk14.dim(registry)}...`);
|
|
1079
1398
|
const tempDir = fs3.mkdtempSync(path3.join(os.tmpdir(), "skild-publish-"));
|
|
1080
1399
|
const tarballPath = path3.join(tempDir, "skill.tgz");
|
|
1081
1400
|
try {
|
|
@@ -1112,12 +1431,12 @@ async function publish(options = {}) {
|
|
|
1112
1431
|
const text = await res.text();
|
|
1113
1432
|
if (!res.ok) {
|
|
1114
1433
|
spinner.fail(`Publish failed (${res.status})`);
|
|
1115
|
-
console.error(
|
|
1434
|
+
console.error(chalk14.red(text));
|
|
1116
1435
|
process.exitCode = 1;
|
|
1117
1436
|
return;
|
|
1118
1437
|
}
|
|
1119
1438
|
if (alias) {
|
|
1120
|
-
spinner.text = `Publishing ${
|
|
1439
|
+
spinner.text = `Publishing ${chalk14.cyan(`${name}@${version2}`)} \u2014 setting alias ${chalk14.cyan(alias)}...`;
|
|
1121
1440
|
const aliasRes = await fetchWithTimeout5(
|
|
1122
1441
|
`${registry}/publisher/skills/${encodeURIComponent(scope)}/${encodeURIComponent(skillName)}/alias`,
|
|
1123
1442
|
{
|
|
@@ -1133,7 +1452,7 @@ async function publish(options = {}) {
|
|
|
1133
1452
|
const aliasText = await aliasRes.text();
|
|
1134
1453
|
if (!aliasRes.ok) {
|
|
1135
1454
|
spinner.fail(`Failed to set alias (${aliasRes.status})`);
|
|
1136
|
-
console.error(
|
|
1455
|
+
console.error(chalk14.red(aliasText));
|
|
1137
1456
|
process.exitCode = 1;
|
|
1138
1457
|
return;
|
|
1139
1458
|
}
|
|
@@ -1142,18 +1461,18 @@ async function publish(options = {}) {
|
|
|
1142
1461
|
console.log(text);
|
|
1143
1462
|
return;
|
|
1144
1463
|
}
|
|
1145
|
-
const suffix = alias ? ` (alias: ${
|
|
1146
|
-
spinner.succeed(`Published ${
|
|
1464
|
+
const suffix = alias ? ` (alias: ${chalk14.cyan(alias)})` : "";
|
|
1465
|
+
spinner.succeed(`Published ${chalk14.green(`${name}@${version2}`)}${suffix} (sha256:${integrity.slice(0, 12)}\u2026)`);
|
|
1147
1466
|
try {
|
|
1148
1467
|
const parsed = JSON.parse(text);
|
|
1149
1468
|
const warnings = Array.isArray(parsed.warnings) ? parsed.warnings.map(String).filter(Boolean) : [];
|
|
1150
|
-
for (const w of warnings) console.warn(
|
|
1469
|
+
for (const w of warnings) console.warn(chalk14.yellow(`Warning: ${w}`));
|
|
1151
1470
|
} catch {
|
|
1152
1471
|
}
|
|
1153
1472
|
} catch (error) {
|
|
1154
1473
|
spinner.fail("Publish failed");
|
|
1155
1474
|
const message = error instanceof SkildError9 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1156
|
-
console.error(
|
|
1475
|
+
console.error(chalk14.red(message));
|
|
1157
1476
|
process.exitCode = 1;
|
|
1158
1477
|
} finally {
|
|
1159
1478
|
fs3.rmSync(tempDir, { recursive: true, force: true });
|
|
@@ -1161,7 +1480,7 @@ async function publish(options = {}) {
|
|
|
1161
1480
|
}
|
|
1162
1481
|
|
|
1163
1482
|
// src/commands/search.ts
|
|
1164
|
-
import
|
|
1483
|
+
import chalk15 from "chalk";
|
|
1165
1484
|
import { resolveRegistryUrl as resolveRegistryUrl6, searchRegistrySkills, SkildError as SkildError10 } from "@skild/core";
|
|
1166
1485
|
async function search(query, options = {}) {
|
|
1167
1486
|
const registryUrl = resolveRegistryUrl6(options.registry);
|
|
@@ -1173,39 +1492,39 @@ async function search(query, options = {}) {
|
|
|
1173
1492
|
return;
|
|
1174
1493
|
}
|
|
1175
1494
|
if (!skills.length) {
|
|
1176
|
-
console.log(
|
|
1495
|
+
console.log(chalk15.dim("No results."));
|
|
1177
1496
|
return;
|
|
1178
1497
|
}
|
|
1179
|
-
console.log(
|
|
1180
|
-
\u{1F50E} Results (${skills.length}) \u2014 ${
|
|
1498
|
+
console.log(chalk15.bold(`
|
|
1499
|
+
\u{1F50E} Results (${skills.length}) \u2014 ${chalk15.dim(registryUrl)}
|
|
1181
1500
|
`));
|
|
1182
1501
|
for (const s of skills) {
|
|
1183
1502
|
const name = String(s.name || "").trim();
|
|
1184
1503
|
const desc = String(s.description || "").trim();
|
|
1185
1504
|
if (!name) continue;
|
|
1186
|
-
console.log(` ${
|
|
1505
|
+
console.log(` ${chalk15.cyan(name)}${desc ? chalk15.dim(` \u2014 ${desc}`) : ""}`);
|
|
1187
1506
|
}
|
|
1188
1507
|
} catch (error) {
|
|
1189
1508
|
const message = error instanceof SkildError10 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1190
|
-
console.error(
|
|
1509
|
+
console.error(chalk15.red(message));
|
|
1191
1510
|
process.exitCode = 1;
|
|
1192
1511
|
}
|
|
1193
1512
|
}
|
|
1194
1513
|
|
|
1195
1514
|
// src/index.ts
|
|
1196
|
-
import { PLATFORMS as
|
|
1515
|
+
import { PLATFORMS as PLATFORMS4 } from "@skild/core";
|
|
1197
1516
|
var require2 = createRequire(import.meta.url);
|
|
1198
1517
|
var { version } = require2("../package.json");
|
|
1199
1518
|
var program = new Command();
|
|
1200
1519
|
program.name("skild").description("The npm for Agent Skills \u2014 Discover, install, manage, and publish AI Agent Skills with ease.").version(version);
|
|
1201
|
-
program.command("install <source>").alias("i").description("Install a Skill from a Git URL, degit shorthand, or local directory").option("-t, --target <platform>", `Target platform: ${
|
|
1520
|
+
program.command("install <source>").alias("i").description("Install a Skill from a Git URL, degit shorthand, or local directory").option("-t, --target <platform>", `Target platform: ${PLATFORMS4.join(", ")}`).option("--all", `Install to all platforms: ${PLATFORMS4.join(", ")}`).option("--recursive", "If source is a multi-skill directory/repo, install all discovered skills").option("-y, --yes", "Skip confirmation prompts (assume yes)").option("--depth <n>", "Max directory depth to scan for SKILL.md (default: 6)", "6").option("--max-skills <n>", "Max discovered skills to install (default: 200)", "200").option("-l, --local", "Install to project-level directory instead of global").option("-f, --force", "Overwrite existing installation").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--json", "Output JSON").action(async (source, options) => {
|
|
1202
1521
|
await install(source, options);
|
|
1203
1522
|
});
|
|
1204
|
-
program.command("list").alias("ls").description("List installed Skills").option("-t, --target <platform>", `Target platform: ${
|
|
1205
|
-
program.command("info <skill>").description("Show installed Skill details").option("-t, --target <platform>", `Target platform: ${
|
|
1206
|
-
program.command("uninstall <skill>").alias("rm").description("Uninstall a Skill").option("-t, --target <platform>", `Target platform: ${
|
|
1207
|
-
program.command("update [skill]").alias("up").description("Update one or all installed Skills").option("-t, --target <platform>", `Target platform: ${
|
|
1208
|
-
program.command("validate [target]").alias("v").description("Validate a Skill folder (path) or an installed Skill name").option("-t, --target <platform>", `Target platform: ${
|
|
1523
|
+
program.command("list").alias("ls").description("List installed Skills").option("-t, --target <platform>", `Target platform: ${PLATFORMS4.join(", ")} (optional; omit to list all)`).option("-l, --local", "List project-level directory instead of global").option("--paths", "Show install paths").option("--verbose", "Show skillset dependency details").option("--json", "Output JSON").action(async (options) => list(options));
|
|
1524
|
+
program.command("info <skill>").description("Show installed Skill details").option("-t, --target <platform>", `Target platform: ${PLATFORMS4.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("--json", "Output JSON").action(async (skill, options) => info(skill, options));
|
|
1525
|
+
program.command("uninstall <skill>").alias("rm").description("Uninstall a Skill").option("-t, --target <platform>", `Target platform: ${PLATFORMS4.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("-f, --force", "Uninstall even if metadata is missing").option("--with-deps", "Uninstall dependencies that are only required by this skill").action(async (skill, options) => uninstall(skill, options));
|
|
1526
|
+
program.command("update [skill]").alias("up").description("Update one or all installed Skills").option("-t, --target <platform>", `Target platform: ${PLATFORMS4.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("--json", "Output JSON").action(async (skill, options) => update(skill, options));
|
|
1527
|
+
program.command("validate [target]").alias("v").description("Validate a Skill folder (path) or an installed Skill name").option("-t, --target <platform>", `Target platform: ${PLATFORMS4.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("--json", "Output JSON").action(async (target, options) => validate(target, options));
|
|
1209
1528
|
program.command("init <name>").description("Create a new Skill project").option("--dir <path>", "Target directory (defaults to <name>)").option("--description <text>", "Skill description").option("-f, --force", "Overwrite target directory if it exists").action(async (name, options) => init(name, options));
|
|
1210
1529
|
program.command("signup").description("Create a publisher account in the registry (no GitHub required)").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--email <email>", "Email (optional; will prompt)").option("--handle <handle>", "Publisher handle (owns @handle/* scope) (optional; will prompt)").option("--password <password>", "Password (optional; will prompt)").option("--json", "Output JSON").action(async (options) => signup(options));
|
|
1211
1530
|
program.command("login").description("Login to a registry and store an access token locally").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--handle-or-email <value>", "Handle or email (optional; will prompt)").option("--password <password>", "Password (optional; will prompt)").option("--token-name <name>", "Token label").option("--json", "Output JSON").action(async (options) => login(options));
|
|
@@ -1214,7 +1533,7 @@ program.command("whoami").description("Show current registry identity").action(a
|
|
|
1214
1533
|
program.command("publish").description("Publish a Skill directory to the registry (hosted tarball)").option("--dir <path>", "Skill directory (defaults to cwd)").option("--name <@publisher/skill>", "Override skill name (defaults to SKILL.md frontmatter)").option("--skill-version <semver>", "Override version (defaults to SKILL.md frontmatter)").option("--alias <alias>", "Optional short identifier (global unique) for `skild install <alias>`").option("--description <text>", "Override description (defaults to SKILL.md frontmatter)").option("--targets <list>", "Comma-separated target platforms metadata (optional)").option("--tag <tag>", "Dist-tag (default: latest)", "latest").option("--registry <url>", "Registry base URL (defaults to saved login)").option("--json", "Output JSON").action(async (options) => publish(options));
|
|
1215
1534
|
program.command("search <query>").description("Search Skills in the registry").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--limit <n>", "Max results (default: 50)", "50").option("--json", "Output JSON").action(async (query, options) => search(query, options));
|
|
1216
1535
|
program.action(() => {
|
|
1217
|
-
console.log(
|
|
1536
|
+
console.log(chalk16.bold("\n\u{1F6E1}\uFE0F skild \u2014 Get your agents skilled.\n"));
|
|
1218
1537
|
program.outputHelp();
|
|
1219
1538
|
});
|
|
1220
1539
|
var argv = process.argv.slice();
|