skild 0.4.3 → 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 +488 -405
- 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,63 +71,281 @@ 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++;
|
|
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);
|
|
90
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
|
}
|
|
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;
|
|
348
|
+
}
|
|
131
349
|
|
|
132
350
|
// src/commands/install-discovery.ts
|
|
133
351
|
import fs from "fs";
|
|
@@ -230,220 +448,6 @@ function asDiscoveredSkills(discovered, toSuggestedSource, toMaterializedDir) {
|
|
|
230
448
|
materializedDir: toMaterializedDir ? toMaterializedDir(d) : void 0
|
|
231
449
|
}));
|
|
232
450
|
}
|
|
233
|
-
function buildSkillTree(found) {
|
|
234
|
-
const root = { id: "", name: ".", children: [], leafIndices: [] };
|
|
235
|
-
const byId = /* @__PURE__ */ new Map([["", root]]);
|
|
236
|
-
for (let i = 0; i < found.length; i += 1) {
|
|
237
|
-
const relPath = found[i].relPath;
|
|
238
|
-
const parts = relPath === "." ? ["."] : relPath.split("/").filter(Boolean);
|
|
239
|
-
let currentId = "";
|
|
240
|
-
let current = root;
|
|
241
|
-
current.leafIndices.push(i);
|
|
242
|
-
for (const part of parts) {
|
|
243
|
-
const nextId = currentId ? `${currentId}/${part}` : part;
|
|
244
|
-
let node = byId.get(nextId);
|
|
245
|
-
if (!node) {
|
|
246
|
-
node = { id: nextId, name: part, children: [], leafIndices: [] };
|
|
247
|
-
byId.set(nextId, node);
|
|
248
|
-
current.children.push(node);
|
|
249
|
-
}
|
|
250
|
-
node.leafIndices.push(i);
|
|
251
|
-
current = node;
|
|
252
|
-
currentId = nextId;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return root;
|
|
256
|
-
}
|
|
257
|
-
function getNodeState(node, selected) {
|
|
258
|
-
const total = node.leafIndices.length;
|
|
259
|
-
if (total === 0) return "none";
|
|
260
|
-
let selectedCount = 0;
|
|
261
|
-
for (const idx of node.leafIndices) {
|
|
262
|
-
if (selected.has(idx)) selectedCount += 1;
|
|
263
|
-
}
|
|
264
|
-
if (selectedCount === 0) return "none";
|
|
265
|
-
if (selectedCount === total) return "all";
|
|
266
|
-
return "partial";
|
|
267
|
-
}
|
|
268
|
-
function renderSkillTree(root, selected) {
|
|
269
|
-
const items = [];
|
|
270
|
-
const stack = [];
|
|
271
|
-
for (let i = root.children.length - 1; i >= 0; i -= 1) {
|
|
272
|
-
stack.push({ node: root.children[i], depth: 0 });
|
|
273
|
-
}
|
|
274
|
-
while (stack.length) {
|
|
275
|
-
const current = stack.pop();
|
|
276
|
-
items.push(current);
|
|
277
|
-
const children = current.node.children;
|
|
278
|
-
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
279
|
-
stack.push({ node: children[i], depth: current.depth + 1 });
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return items;
|
|
283
|
-
}
|
|
284
|
-
function parseSelectionInput(input, maxIndex) {
|
|
285
|
-
const trimmed = input.trim().toLowerCase();
|
|
286
|
-
if (!trimmed) return { command: "done" };
|
|
287
|
-
if (["q", "quit", "exit", "cancel"].includes(trimmed)) return { command: "cancel" };
|
|
288
|
-
if (["a", "all"].includes(trimmed)) return { command: "all" };
|
|
289
|
-
if (["n", "none"].includes(trimmed)) return { command: "none" };
|
|
290
|
-
if (["i", "invert"].includes(trimmed)) return { command: "invert" };
|
|
291
|
-
const tokens = trimmed.split(/[,\s]+/).filter(Boolean);
|
|
292
|
-
const indices = [];
|
|
293
|
-
for (const token of tokens) {
|
|
294
|
-
if (/^\d+$/.test(token)) {
|
|
295
|
-
const value = Number(token);
|
|
296
|
-
if (value < 1 || value > maxIndex) return null;
|
|
297
|
-
indices.push(value);
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
if (/^\d+-\d+$/.test(token)) {
|
|
301
|
-
const [startRaw, endRaw] = token.split("-");
|
|
302
|
-
const start = Number(startRaw);
|
|
303
|
-
const end = Number(endRaw);
|
|
304
|
-
if (!Number.isFinite(start) || !Number.isFinite(end) || start < 1 || end < 1 || start > maxIndex || end > maxIndex) {
|
|
305
|
-
return null;
|
|
306
|
-
}
|
|
307
|
-
const from = Math.min(start, end);
|
|
308
|
-
const to = Math.max(start, end);
|
|
309
|
-
for (let i = from; i <= to; i += 1) indices.push(i);
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
return null;
|
|
313
|
-
}
|
|
314
|
-
return { indices };
|
|
315
|
-
}
|
|
316
|
-
async function promptSkillSelection(found, options) {
|
|
317
|
-
const root = buildSkillTree(found);
|
|
318
|
-
const selected = /* @__PURE__ */ new Set();
|
|
319
|
-
if (options.defaultAll) {
|
|
320
|
-
for (let i = 0; i < found.length; i += 1) selected.add(i);
|
|
321
|
-
}
|
|
322
|
-
while (true) {
|
|
323
|
-
console.log(chalk2.cyan("\nSelect skills to install"));
|
|
324
|
-
console.log(chalk2.dim("Toggle by number. Commands: a=all, n=none, i=invert, enter=continue, q=cancel."));
|
|
325
|
-
const display = renderSkillTree(root, selected);
|
|
326
|
-
for (let i = 0; i < display.length; i += 1) {
|
|
327
|
-
const item = display[i];
|
|
328
|
-
const state = getNodeState(item.node, selected);
|
|
329
|
-
const box = state === "all" ? "[x]" : state === "partial" ? "[-]" : "[ ]";
|
|
330
|
-
const indent = " ".repeat(item.depth);
|
|
331
|
-
const idx = String(i + 1).padStart(2, " ");
|
|
332
|
-
console.log(`${idx} ${box} ${indent}${item.node.name}`);
|
|
333
|
-
}
|
|
334
|
-
const answer = await promptLine("Selection");
|
|
335
|
-
const parsed = parseSelectionInput(answer, display.length);
|
|
336
|
-
if (!parsed) {
|
|
337
|
-
console.log(chalk2.yellow('Invalid selection. Use numbers like "1,3-5" or a/n/i.'));
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
if (parsed.command === "done") break;
|
|
341
|
-
if (parsed.command === "cancel") return null;
|
|
342
|
-
if (parsed.command === "all") {
|
|
343
|
-
selected.clear();
|
|
344
|
-
for (let i = 0; i < found.length; i += 1) selected.add(i);
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
if (parsed.command === "none") {
|
|
348
|
-
selected.clear();
|
|
349
|
-
continue;
|
|
350
|
-
}
|
|
351
|
-
if (parsed.command === "invert") {
|
|
352
|
-
const next = /* @__PURE__ */ new Set();
|
|
353
|
-
for (let i = 0; i < found.length; i += 1) {
|
|
354
|
-
if (!selected.has(i)) next.add(i);
|
|
355
|
-
}
|
|
356
|
-
selected.clear();
|
|
357
|
-
for (const idx of next) selected.add(idx);
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
if (parsed.indices) {
|
|
361
|
-
for (const index of parsed.indices) {
|
|
362
|
-
const item = display[index - 1];
|
|
363
|
-
if (!item) continue;
|
|
364
|
-
const state = getNodeState(item.node, selected);
|
|
365
|
-
if (state === "all") {
|
|
366
|
-
for (const leaf of item.node.leafIndices) selected.delete(leaf);
|
|
367
|
-
} else {
|
|
368
|
-
for (const leaf of item.node.leafIndices) selected.add(leaf);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
if (selected.size === 0) return null;
|
|
374
|
-
const selectedFound = [];
|
|
375
|
-
for (let i = 0; i < found.length; i += 1) {
|
|
376
|
-
if (selected.has(i)) selectedFound.push(found[i]);
|
|
377
|
-
}
|
|
378
|
-
return selectedFound;
|
|
379
|
-
}
|
|
380
|
-
async function promptPlatformSelection(defaultAll) {
|
|
381
|
-
const selected = /* @__PURE__ */ new Set();
|
|
382
|
-
if (defaultAll) {
|
|
383
|
-
for (let i = 0; i < PLATFORMS.length; i += 1) selected.add(i);
|
|
384
|
-
}
|
|
385
|
-
while (true) {
|
|
386
|
-
console.log(chalk2.cyan("\nSelect platforms to install to"));
|
|
387
|
-
console.log(chalk2.dim("Toggle by number. Commands: a=all, n=none, i=invert, enter=continue, q=cancel."));
|
|
388
|
-
for (let i = 0; i < PLATFORMS.length; i += 1) {
|
|
389
|
-
const label = PLATFORMS[i];
|
|
390
|
-
const box = selected.has(i) ? "[x]" : "[ ]";
|
|
391
|
-
const idx = String(i + 1).padStart(2, " ");
|
|
392
|
-
console.log(`${idx} ${box} ${label}`);
|
|
393
|
-
}
|
|
394
|
-
const answer = await promptLine("Selection");
|
|
395
|
-
const parsed = parseSelectionInput(answer, PLATFORMS.length);
|
|
396
|
-
if (!parsed) {
|
|
397
|
-
console.log(chalk2.yellow('Invalid selection. Use numbers like "1,3-5" or a/n/i.'));
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
if (parsed.command === "done") break;
|
|
401
|
-
if (parsed.command === "cancel") return null;
|
|
402
|
-
if (parsed.command === "all") {
|
|
403
|
-
selected.clear();
|
|
404
|
-
for (let i = 0; i < PLATFORMS.length; i += 1) selected.add(i);
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
if (parsed.command === "none") {
|
|
408
|
-
selected.clear();
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
if (parsed.command === "invert") {
|
|
412
|
-
const next = /* @__PURE__ */ new Set();
|
|
413
|
-
for (let i = 0; i < PLATFORMS.length; i += 1) {
|
|
414
|
-
if (!selected.has(i)) next.add(i);
|
|
415
|
-
}
|
|
416
|
-
selected.clear();
|
|
417
|
-
for (const idx of next) selected.add(idx);
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
if (parsed.indices) {
|
|
421
|
-
for (const index of parsed.indices) {
|
|
422
|
-
const idx = index - 1;
|
|
423
|
-
if (idx < 0 || idx >= PLATFORMS.length) continue;
|
|
424
|
-
if (selected.has(idx)) selected.delete(idx);
|
|
425
|
-
else selected.add(idx);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
if (selected.size === 0) return null;
|
|
430
|
-
const chosen = [];
|
|
431
|
-
for (let i = 0; i < PLATFORMS.length; i += 1) {
|
|
432
|
-
if (selected.has(i)) chosen.push(PLATFORMS[i]);
|
|
433
|
-
}
|
|
434
|
-
return chosen;
|
|
435
|
-
}
|
|
436
|
-
function printSelectedSkills(found) {
|
|
437
|
-
const names = found.map((skill) => skill.relPath);
|
|
438
|
-
if (names.length === 0) return;
|
|
439
|
-
const preview = names.slice(0, 20).map((name) => ` - ${name}`).join("\n");
|
|
440
|
-
console.log(chalk2.dim(`
|
|
441
|
-
Selected skills (${names.length}):`));
|
|
442
|
-
console.log(preview);
|
|
443
|
-
if (names.length > 20) {
|
|
444
|
-
console.log(chalk2.dim(` ... and ${names.length - 20} more`));
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
451
|
async function install(source, options = {}) {
|
|
448
452
|
const scope = options.local ? "project" : "global";
|
|
449
453
|
const auth = loadRegistryAuth();
|
|
@@ -459,41 +463,37 @@ async function install(source, options = {}) {
|
|
|
459
463
|
if (requestedAll && options.target) {
|
|
460
464
|
const message = "Invalid options: use either --all or --target, not both.";
|
|
461
465
|
if (jsonOnly) printJson({ ok: false, error: message });
|
|
462
|
-
else console.error(
|
|
466
|
+
else console.error(chalk3.red(message));
|
|
463
467
|
process.exitCode = 1;
|
|
464
468
|
return;
|
|
465
469
|
}
|
|
466
470
|
let targets = [];
|
|
471
|
+
let deferPlatformSelection = false;
|
|
467
472
|
if (requestedAll) {
|
|
468
|
-
targets = [...
|
|
473
|
+
targets = [...PLATFORMS2];
|
|
469
474
|
} else if (requestedTarget) {
|
|
470
475
|
targets = [requestedTarget];
|
|
471
476
|
} else if (yes) {
|
|
472
|
-
targets = [...
|
|
477
|
+
targets = [...PLATFORMS2];
|
|
473
478
|
} else if (interactive) {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
console.error(chalk2.red("No platforms selected."));
|
|
477
|
-
process.exitCode = 1;
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
targets = selectedTargets;
|
|
479
|
+
deferPlatformSelection = true;
|
|
480
|
+
targets = [...PLATFORMS2];
|
|
481
481
|
} else {
|
|
482
482
|
targets = ["claude"];
|
|
483
483
|
}
|
|
484
|
-
const allPlatformsSelected = targets.length ===
|
|
484
|
+
const allPlatformsSelected = targets.length === PLATFORMS2.length;
|
|
485
485
|
let resolvedSource = source.trim();
|
|
486
486
|
try {
|
|
487
487
|
if (looksLikeAlias(resolvedSource)) {
|
|
488
488
|
const registryUrl = resolveRegistryUrl(registryUrlForDeps);
|
|
489
489
|
const resolved = await resolveRegistryAlias(registryUrl, resolvedSource);
|
|
490
|
-
if (!jsonOnly) logger.info(`Resolved ${
|
|
490
|
+
if (!jsonOnly) logger.info(`Resolved ${chalk3.cyan(resolvedSource)} \u2192 ${chalk3.cyan(resolved.spec)} (${resolved.type})`);
|
|
491
491
|
resolvedSource = resolved.spec;
|
|
492
492
|
}
|
|
493
493
|
} catch (error) {
|
|
494
494
|
const message = error instanceof SkildError ? error.message : error instanceof Error ? error.message : String(error);
|
|
495
495
|
if (jsonOnly) printJson({ ok: false, error: message });
|
|
496
|
-
else console.error(
|
|
496
|
+
else console.error(chalk3.red(message));
|
|
497
497
|
process.exitCode = 1;
|
|
498
498
|
return;
|
|
499
499
|
}
|
|
@@ -524,7 +524,7 @@ async function install(source, options = {}) {
|
|
|
524
524
|
if (found.length > maxSkills) {
|
|
525
525
|
const message = `Found more than ${maxSkills} skills. Increase --max-skills to proceed.`;
|
|
526
526
|
if (jsonOnly) printJson({ ok: false, error: "TOO_MANY_SKILLS", message, source, resolvedSource, maxSkills });
|
|
527
|
-
else console.error(
|
|
527
|
+
else console.error(chalk3.red(message));
|
|
528
528
|
process.exitCode = 1;
|
|
529
529
|
return null;
|
|
530
530
|
}
|
|
@@ -546,20 +546,29 @@ async function install(source, options = {}) {
|
|
|
546
546
|
const headline = found.length === 1 ? `No SKILL.md found at root. Found 1 skill.
|
|
547
547
|
` : `No SKILL.md found at root. Found ${found.length} skills.
|
|
548
548
|
`;
|
|
549
|
-
console.log(
|
|
549
|
+
console.log(chalk3.yellow(headline));
|
|
550
550
|
if (!interactive && !yes) {
|
|
551
|
-
console.log(
|
|
551
|
+
console.log(chalk3.dim(`Tip: rerun with ${chalk3.cyan("skild install <source> --recursive")} to install all.`));
|
|
552
552
|
process.exitCode = 1;
|
|
553
553
|
return null;
|
|
554
554
|
}
|
|
555
555
|
if (!yes) {
|
|
556
|
-
const selected = await
|
|
556
|
+
const selected = await promptSkillsInteractive(found, { defaultAll: true });
|
|
557
557
|
if (!selected) {
|
|
558
|
-
console.log(
|
|
558
|
+
console.log(chalk3.red("No skills selected."));
|
|
559
559
|
process.exitCode = 1;
|
|
560
560
|
return null;
|
|
561
561
|
}
|
|
562
|
-
|
|
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
|
+
}
|
|
563
572
|
effectiveRecursive = true;
|
|
564
573
|
if (spinner) spinner.start();
|
|
565
574
|
recursiveSkillCount = selected.length;
|
|
@@ -572,12 +581,25 @@ async function install(source, options = {}) {
|
|
|
572
581
|
}
|
|
573
582
|
try {
|
|
574
583
|
const spinner = jsonOnly ? null : createSpinner(
|
|
575
|
-
allPlatformsSelected ? `Installing ${
|
|
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})...`
|
|
576
585
|
);
|
|
577
586
|
let cleanupMaterialized = null;
|
|
578
587
|
let materializedRoot = null;
|
|
579
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
|
+
}
|
|
580
601
|
if (resolvedSource.startsWith("@") && resolvedSource.includes("/")) {
|
|
602
|
+
if (!await ensurePlatformSelection()) return;
|
|
581
603
|
await installOne(resolvedSource);
|
|
582
604
|
} else {
|
|
583
605
|
const maybeLocalRoot = path2.resolve(resolvedSource);
|
|
@@ -585,6 +607,7 @@ async function install(source, options = {}) {
|
|
|
585
607
|
if (isLocal) {
|
|
586
608
|
const hasSkillMd = fs2.existsSync(path2.join(maybeLocalRoot, "SKILL.md"));
|
|
587
609
|
if (hasSkillMd) {
|
|
610
|
+
if (!await ensurePlatformSelection()) return;
|
|
588
611
|
await installOne(resolvedSource);
|
|
589
612
|
} else {
|
|
590
613
|
const discovered = discoverSkillDirsWithHeuristics(maybeLocalRoot, { maxDepth, maxSkills });
|
|
@@ -594,7 +617,7 @@ async function install(source, options = {}) {
|
|
|
594
617
|
printJson({ ok: false, error: "SKILL_MD_NOT_FOUND", message, source, resolvedSource });
|
|
595
618
|
} else {
|
|
596
619
|
if (spinner) spinner.stop();
|
|
597
|
-
console.error(
|
|
620
|
+
console.error(chalk3.red(message));
|
|
598
621
|
}
|
|
599
622
|
process.exitCode = 1;
|
|
600
623
|
return;
|
|
@@ -602,9 +625,9 @@ async function install(source, options = {}) {
|
|
|
602
625
|
const found = asDiscoveredSkills(discovered, (d) => path2.join(maybeLocalRoot, d.relPath));
|
|
603
626
|
const selected = await resolveDiscoveredSelection(found, spinner);
|
|
604
627
|
if (!selected) return;
|
|
605
|
-
if (spinner) spinner.text = `Installing ${
|
|
628
|
+
if (spinner) spinner.text = `Installing ${chalk3.cyan(source)} \u2014 discovered ${selected.length} skills...`;
|
|
606
629
|
for (const skill of selected) {
|
|
607
|
-
if (spinner) spinner.text = `Installing ${
|
|
630
|
+
if (spinner) spinner.text = `Installing ${chalk3.cyan(skill.relPath)} (${scope})...`;
|
|
608
631
|
await installOne(skill.suggestedSource, skill.materializedDir);
|
|
609
632
|
}
|
|
610
633
|
}
|
|
@@ -623,7 +646,7 @@ async function install(source, options = {}) {
|
|
|
623
646
|
printJson({ ok: false, error: "SKILL_MD_NOT_FOUND", message, source, resolvedSource });
|
|
624
647
|
} else {
|
|
625
648
|
if (spinner) spinner.stop();
|
|
626
|
-
console.error(
|
|
649
|
+
console.error(chalk3.red(message));
|
|
627
650
|
}
|
|
628
651
|
process.exitCode = 1;
|
|
629
652
|
return;
|
|
@@ -635,9 +658,9 @@ async function install(source, options = {}) {
|
|
|
635
658
|
);
|
|
636
659
|
const selected = await resolveDiscoveredSelection(found, spinner);
|
|
637
660
|
if (!selected) return;
|
|
638
|
-
if (spinner) spinner.text = `Installing ${
|
|
661
|
+
if (spinner) spinner.text = `Installing ${chalk3.cyan(source)} \u2014 discovered ${selected.length} skills...`;
|
|
639
662
|
for (const skill of selected) {
|
|
640
|
-
if (spinner) spinner.text = `Installing ${
|
|
663
|
+
if (spinner) spinner.text = `Installing ${chalk3.cyan(skill.relPath)} (${scope})...`;
|
|
641
664
|
await installOne(skill.suggestedSource, skill.materializedDir);
|
|
642
665
|
}
|
|
643
666
|
}
|
|
@@ -669,42 +692,42 @@ async function install(source, options = {}) {
|
|
|
669
692
|
if (errors.length === 0) {
|
|
670
693
|
const displayName = results[0]?.canonicalName || results[0]?.name || source;
|
|
671
694
|
spinner.succeed(
|
|
672
|
-
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 || "")}`
|
|
673
696
|
);
|
|
674
697
|
} else {
|
|
675
698
|
const attempted = results.length + errors.length;
|
|
676
699
|
spinner.fail(
|
|
677
|
-
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`
|
|
678
701
|
);
|
|
679
702
|
process.exitCode = 1;
|
|
680
|
-
if (!effectiveRecursive && targets.length === 1 && errors[0]) console.error(
|
|
703
|
+
if (!effectiveRecursive && targets.length === 1 && errors[0]) console.error(chalk3.red(errors[0].error));
|
|
681
704
|
}
|
|
682
705
|
if (!effectiveRecursive && targets.length === 1 && results[0]) {
|
|
683
706
|
const record = results[0];
|
|
684
707
|
if (record.hasSkillMd) logger.installDetail("SKILL.md found \u2713");
|
|
685
708
|
else logger.installDetail("Warning: No SKILL.md found", true);
|
|
686
709
|
if (record.skill?.validation && !record.skill.validation.ok) {
|
|
687
|
-
logger.installDetail(`Validation: ${
|
|
710
|
+
logger.installDetail(`Validation: ${chalk3.yellow("failed")} (${record.skill.validation.issues.length} issues)`, true);
|
|
688
711
|
} else if (record.skill?.validation?.ok) {
|
|
689
|
-
logger.installDetail(`Validation: ${
|
|
712
|
+
logger.installDetail(`Validation: ${chalk3.green("ok")}`);
|
|
690
713
|
}
|
|
691
714
|
} else if (effectiveRecursive || targets.length > 1) {
|
|
692
715
|
for (const r of results.slice(0, 60)) {
|
|
693
716
|
const displayName = r.canonicalName || r.name;
|
|
694
|
-
const suffix = r.hasSkillMd ?
|
|
695
|
-
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)}`);
|
|
696
719
|
}
|
|
697
|
-
if (results.length > 60) console.log(
|
|
720
|
+
if (results.length > 60) console.log(chalk3.dim(` ... and ${results.length - 60} more`));
|
|
698
721
|
if (errors.length) {
|
|
699
|
-
console.log(
|
|
700
|
-
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}`));
|
|
701
724
|
}
|
|
702
725
|
process.exitCode = errors.length ? 1 : 0;
|
|
703
726
|
}
|
|
704
727
|
} catch (error) {
|
|
705
728
|
const message = error instanceof SkildError ? error.message : error instanceof Error ? error.message : String(error);
|
|
706
729
|
if (jsonOnly) printJson({ ok: false, error: message });
|
|
707
|
-
else console.error(
|
|
730
|
+
else console.error(chalk3.red(message));
|
|
708
731
|
process.exitCode = 1;
|
|
709
732
|
}
|
|
710
733
|
}
|
|
@@ -740,8 +763,8 @@ async function reportDownload(record, registryOverride) {
|
|
|
740
763
|
}
|
|
741
764
|
|
|
742
765
|
// src/commands/list.ts
|
|
743
|
-
import
|
|
744
|
-
import { PLATFORMS as
|
|
766
|
+
import chalk4 from "chalk";
|
|
767
|
+
import { PLATFORMS as PLATFORMS3, listAllSkills, listSkills } from "@skild/core";
|
|
745
768
|
function isSkillset(skill) {
|
|
746
769
|
return Boolean(skill.record?.skillset || skill.record?.skill?.frontmatter?.skillset);
|
|
747
770
|
}
|
|
@@ -756,10 +779,10 @@ function buildNameToDisplay(skills) {
|
|
|
756
779
|
return mapping;
|
|
757
780
|
}
|
|
758
781
|
function statusIcon(skill) {
|
|
759
|
-
return skill.hasSkillMd ?
|
|
782
|
+
return skill.hasSkillMd ? chalk4.green("\u2713") : chalk4.yellow("\u26A0");
|
|
760
783
|
}
|
|
761
784
|
function missingSkillMdLabel(skill) {
|
|
762
|
-
return skill.hasSkillMd ? "" :
|
|
785
|
+
return skill.hasSkillMd ? "" : chalk4.yellow(" (missing SKILL.md)");
|
|
763
786
|
}
|
|
764
787
|
function formatDepName(dep, nameToDisplay) {
|
|
765
788
|
return dep.canonicalName || nameToDisplay.get(dep.name) || dep.name;
|
|
@@ -784,62 +807,62 @@ function summarizeDeps(record) {
|
|
|
784
807
|
return `deps: ${byType.total}${parts.length ? ` (${parts.join(", ")})` : ""}`;
|
|
785
808
|
}
|
|
786
809
|
function printSkillsetSection(skills, nameToDisplay, options) {
|
|
787
|
-
console.log(
|
|
810
|
+
console.log(chalk4.bold(` Skillsets (${skills.length})`));
|
|
788
811
|
if (skills.length === 0) {
|
|
789
|
-
console.log(
|
|
812
|
+
console.log(chalk4.dim(" (none)"));
|
|
790
813
|
return;
|
|
791
814
|
}
|
|
792
815
|
for (const s of skills) {
|
|
793
|
-
const label = `${
|
|
816
|
+
const label = `${chalk4.cyan(getDisplayName(s))}${chalk4.dim(" (skillset)")}${missingSkillMdLabel(s)}`;
|
|
794
817
|
console.log(` ${statusIcon(s)} ${label}`);
|
|
795
818
|
const summary = summarizeDeps(s.record);
|
|
796
|
-
if (summary) console.log(
|
|
819
|
+
if (summary) console.log(chalk4.dim(` ${summary}`));
|
|
797
820
|
if (options.verbose) {
|
|
798
821
|
const deps = (s.record?.installedDependencies || []).slice().sort((a, b) => a.name.localeCompare(b.name));
|
|
799
822
|
if (deps.length) {
|
|
800
|
-
console.log(
|
|
823
|
+
console.log(chalk4.dim(` includes (${deps.length}):`));
|
|
801
824
|
for (const dep of deps) {
|
|
802
825
|
const depName = formatDepName(dep, nameToDisplay);
|
|
803
|
-
console.log(
|
|
826
|
+
console.log(chalk4.dim(` - ${depName} [${dep.sourceType}]`));
|
|
804
827
|
}
|
|
805
828
|
}
|
|
806
829
|
}
|
|
807
|
-
if (options.paths || !s.hasSkillMd) console.log(
|
|
830
|
+
if (options.paths || !s.hasSkillMd) console.log(chalk4.dim(` path: ${s.installDir}`));
|
|
808
831
|
}
|
|
809
832
|
}
|
|
810
833
|
function printSkillsSection(skills, options) {
|
|
811
|
-
console.log(
|
|
834
|
+
console.log(chalk4.bold(` Skills (${skills.length})`));
|
|
812
835
|
if (skills.length === 0) {
|
|
813
|
-
console.log(
|
|
836
|
+
console.log(chalk4.dim(" (none)"));
|
|
814
837
|
return;
|
|
815
838
|
}
|
|
816
839
|
for (const s of skills) {
|
|
817
|
-
const label = `${
|
|
840
|
+
const label = `${chalk4.cyan(getDisplayName(s))}${missingSkillMdLabel(s)}`;
|
|
818
841
|
console.log(` ${statusIcon(s)} ${label}`);
|
|
819
|
-
if (options.paths || !s.hasSkillMd) console.log(
|
|
842
|
+
if (options.paths || !s.hasSkillMd) console.log(chalk4.dim(` path: ${s.installDir}`));
|
|
820
843
|
}
|
|
821
844
|
}
|
|
822
845
|
function printDependenciesSection(skills, nameToDisplay, options) {
|
|
823
|
-
console.log(
|
|
846
|
+
console.log(chalk4.bold(` Dependencies (${skills.length})`));
|
|
824
847
|
if (skills.length === 0) {
|
|
825
|
-
console.log(
|
|
848
|
+
console.log(chalk4.dim(" (none)"));
|
|
826
849
|
return;
|
|
827
850
|
}
|
|
828
851
|
for (const s of skills) {
|
|
829
852
|
const dependedBy = (s.record?.dependedBy || []).map((name) => nameToDisplay.get(name) || name).sort((a, b) => a.localeCompare(b));
|
|
830
|
-
const requiredBy = dependedBy.length ?
|
|
831
|
-
const label = `${
|
|
853
|
+
const requiredBy = dependedBy.length ? chalk4.dim(` \u2190 required by: ${dependedBy.join(", ")}`) : "";
|
|
854
|
+
const label = `${chalk4.cyan(getDisplayName(s))}${missingSkillMdLabel(s)}${requiredBy}`;
|
|
832
855
|
console.log(` ${statusIcon(s)} ${label}`);
|
|
833
|
-
if (options.paths || !s.hasSkillMd) console.log(
|
|
856
|
+
if (options.paths || !s.hasSkillMd) console.log(chalk4.dim(` path: ${s.installDir}`));
|
|
834
857
|
}
|
|
835
858
|
}
|
|
836
859
|
function printPlatform(skills, platform, scope, options) {
|
|
837
|
-
console.log(
|
|
860
|
+
console.log(chalk4.bold(`
|
|
838
861
|
\u{1F4E6} Installed Skills \u2014 ${platform} (${scope})
|
|
839
862
|
`));
|
|
840
863
|
if (skills.length === 0) {
|
|
841
|
-
console.log(
|
|
842
|
-
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.`));
|
|
843
866
|
return;
|
|
844
867
|
}
|
|
845
868
|
const nameToDisplay = buildNameToDisplay(skills);
|
|
@@ -873,11 +896,11 @@ async function list(options = {}) {
|
|
|
873
896
|
return;
|
|
874
897
|
}
|
|
875
898
|
if (allSkills.length === 0) {
|
|
876
|
-
console.log(
|
|
877
|
-
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.`));
|
|
878
901
|
return;
|
|
879
902
|
}
|
|
880
|
-
for (const p of
|
|
903
|
+
for (const p of PLATFORMS3) {
|
|
881
904
|
const platformSkills = allSkills.filter((s) => s.platform === p).sort((a, b) => a.name.localeCompare(b.name));
|
|
882
905
|
if (platformSkills.length === 0) continue;
|
|
883
906
|
printPlatform(platformSkills, p, scope, { paths, verbose });
|
|
@@ -885,7 +908,7 @@ async function list(options = {}) {
|
|
|
885
908
|
}
|
|
886
909
|
|
|
887
910
|
// src/commands/info.ts
|
|
888
|
-
import
|
|
911
|
+
import chalk5 from "chalk";
|
|
889
912
|
import { canonicalNameToInstallDirName, getSkillInfo, SkildError as SkildError2 } from "@skild/core";
|
|
890
913
|
async function info(skill, options = {}) {
|
|
891
914
|
const platform = options.target || "claude";
|
|
@@ -898,22 +921,22 @@ async function info(skill, options = {}) {
|
|
|
898
921
|
return;
|
|
899
922
|
}
|
|
900
923
|
const displayName = record.canonicalName || record.name;
|
|
901
|
-
console.log(
|
|
902
|
-
${
|
|
924
|
+
console.log(chalk5.bold(`
|
|
925
|
+
${chalk5.cyan(displayName)}
|
|
903
926
|
`));
|
|
904
|
-
console.log(` ${
|
|
905
|
-
console.log(` ${
|
|
906
|
-
console.log(` ${
|
|
907
|
-
console.log(` ${
|
|
908
|
-
if (record.updatedAt) console.log(` ${
|
|
909
|
-
console.log(` ${
|
|
910
|
-
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")}`);
|
|
911
934
|
const validation = record.skill?.validation;
|
|
912
935
|
if (validation) {
|
|
913
|
-
console.log(` ${
|
|
936
|
+
console.log(` ${chalk5.dim("Validate:")} ${validation.ok ? chalk5.green("ok") : chalk5.red("failed")}`);
|
|
914
937
|
if (!validation.ok) {
|
|
915
938
|
for (const issue of validation.issues) {
|
|
916
|
-
const color = issue.level === "error" ?
|
|
939
|
+
const color = issue.level === "error" ? chalk5.red : chalk5.yellow;
|
|
917
940
|
console.log(` - ${color(issue.level)}: ${issue.message}`);
|
|
918
941
|
}
|
|
919
942
|
}
|
|
@@ -921,20 +944,20 @@ ${chalk4.cyan(displayName)}
|
|
|
921
944
|
console.log("");
|
|
922
945
|
} catch (error) {
|
|
923
946
|
const message = error instanceof SkildError2 ? error.message : error instanceof Error ? error.message : String(error);
|
|
924
|
-
console.error(
|
|
947
|
+
console.error(chalk5.red(message));
|
|
925
948
|
process.exitCode = 1;
|
|
926
949
|
}
|
|
927
950
|
}
|
|
928
951
|
|
|
929
952
|
// src/commands/uninstall.ts
|
|
930
|
-
import
|
|
953
|
+
import chalk6 from "chalk";
|
|
931
954
|
import { canonicalNameToInstallDirName as canonicalNameToInstallDirName2, uninstallSkill, SkildError as SkildError3 } from "@skild/core";
|
|
932
955
|
async function uninstall(skill, options = {}) {
|
|
933
956
|
const platform = options.target || "claude";
|
|
934
957
|
const scope = options.local ? "project" : "global";
|
|
935
958
|
const canonical = skill.trim();
|
|
936
959
|
const resolvedName = canonical.startsWith("@") && canonical.includes("/") ? canonicalNameToInstallDirName2(canonical) : canonical;
|
|
937
|
-
const spinner = createSpinner(`Uninstalling ${
|
|
960
|
+
const spinner = createSpinner(`Uninstalling ${chalk6.cyan(canonical)} from ${chalk6.dim(platform)} (${scope})...`);
|
|
938
961
|
try {
|
|
939
962
|
uninstallSkill(resolvedName, {
|
|
940
963
|
platform,
|
|
@@ -942,40 +965,40 @@ async function uninstall(skill, options = {}) {
|
|
|
942
965
|
allowMissingMetadata: Boolean(options.force),
|
|
943
966
|
withDeps: Boolean(options.withDeps)
|
|
944
967
|
});
|
|
945
|
-
spinner.succeed(`Uninstalled ${
|
|
968
|
+
spinner.succeed(`Uninstalled ${chalk6.green(canonical)}`);
|
|
946
969
|
} catch (error) {
|
|
947
|
-
spinner.fail(`Failed to uninstall ${
|
|
970
|
+
spinner.fail(`Failed to uninstall ${chalk6.red(canonical)}`);
|
|
948
971
|
const message = error instanceof SkildError3 ? error.message : error instanceof Error ? error.message : String(error);
|
|
949
|
-
console.error(
|
|
972
|
+
console.error(chalk6.red(message));
|
|
950
973
|
process.exitCode = 1;
|
|
951
974
|
}
|
|
952
975
|
}
|
|
953
976
|
|
|
954
977
|
// src/commands/update.ts
|
|
955
|
-
import
|
|
978
|
+
import chalk7 from "chalk";
|
|
956
979
|
import { canonicalNameToInstallDirName as canonicalNameToInstallDirName3, updateSkill, SkildError as SkildError4 } from "@skild/core";
|
|
957
980
|
async function update(skill, options = {}) {
|
|
958
981
|
const platform = options.target || "claude";
|
|
959
982
|
const scope = options.local ? "project" : "global";
|
|
960
983
|
const label = skill ? skill : "all skills";
|
|
961
984
|
const resolvedName = skill && skill.trim().startsWith("@") && skill.includes("/") ? canonicalNameToInstallDirName3(skill.trim()) : skill;
|
|
962
|
-
const spinner = createSpinner(`Updating ${
|
|
985
|
+
const spinner = createSpinner(`Updating ${chalk7.cyan(label)} on ${chalk7.dim(platform)} (${scope})...`);
|
|
963
986
|
try {
|
|
964
987
|
const results = await updateSkill(resolvedName, { platform, scope });
|
|
965
|
-
spinner.succeed(`Updated ${
|
|
988
|
+
spinner.succeed(`Updated ${chalk7.green(results.length.toString())} skill(s).`);
|
|
966
989
|
if (options.json) {
|
|
967
990
|
console.log(JSON.stringify(results, null, 2));
|
|
968
991
|
}
|
|
969
992
|
} catch (error) {
|
|
970
|
-
spinner.fail(`Failed to update ${
|
|
993
|
+
spinner.fail(`Failed to update ${chalk7.red(label)}`);
|
|
971
994
|
const message = error instanceof SkildError4 ? error.message : error instanceof Error ? error.message : String(error);
|
|
972
|
-
console.error(
|
|
995
|
+
console.error(chalk7.red(message));
|
|
973
996
|
process.exitCode = 1;
|
|
974
997
|
}
|
|
975
998
|
}
|
|
976
999
|
|
|
977
1000
|
// src/commands/validate.ts
|
|
978
|
-
import
|
|
1001
|
+
import chalk8 from "chalk";
|
|
979
1002
|
import { canonicalNameToInstallDirName as canonicalNameToInstallDirName4, validateSkill } from "@skild/core";
|
|
980
1003
|
async function validate(target, options = {}) {
|
|
981
1004
|
const platform = options.target || "claude";
|
|
@@ -989,42 +1012,102 @@ async function validate(target, options = {}) {
|
|
|
989
1012
|
return;
|
|
990
1013
|
}
|
|
991
1014
|
if (result.ok) {
|
|
992
|
-
console.log(
|
|
993
|
-
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}`));
|
|
994
1017
|
return;
|
|
995
1018
|
}
|
|
996
|
-
console.error(
|
|
1019
|
+
console.error(chalk8.red("\u2717"), "Invalid skill");
|
|
997
1020
|
for (const issue of result.issues) {
|
|
998
|
-
const color = issue.level === "error" ?
|
|
1021
|
+
const color = issue.level === "error" ? chalk8.red : chalk8.yellow;
|
|
999
1022
|
console.error(` - ${color(issue.level)}: ${issue.message}`);
|
|
1000
1023
|
}
|
|
1001
1024
|
process.exitCode = 1;
|
|
1002
1025
|
}
|
|
1003
1026
|
|
|
1004
1027
|
// src/commands/init.ts
|
|
1005
|
-
import
|
|
1028
|
+
import chalk9 from "chalk";
|
|
1006
1029
|
import { initSkill, SkildError as SkildError5 } from "@skild/core";
|
|
1007
1030
|
async function init(name, options = {}) {
|
|
1008
|
-
const spinner = createSpinner(`Initializing ${
|
|
1031
|
+
const spinner = createSpinner(`Initializing ${chalk9.cyan(name)}...`);
|
|
1009
1032
|
try {
|
|
1010
1033
|
const createdDir = initSkill(name, {
|
|
1011
1034
|
dir: options.dir,
|
|
1012
1035
|
description: options.description,
|
|
1013
1036
|
force: Boolean(options.force)
|
|
1014
1037
|
});
|
|
1015
|
-
spinner.succeed(`Created ${
|
|
1016
|
-
console.log(
|
|
1038
|
+
spinner.succeed(`Created ${chalk9.green(name)} at ${chalk9.dim(createdDir)}`);
|
|
1039
|
+
console.log(chalk9.dim(`Next: cd ${createdDir} && skild validate .`));
|
|
1017
1040
|
} catch (error) {
|
|
1018
|
-
spinner.fail(`Failed to init ${
|
|
1041
|
+
spinner.fail(`Failed to init ${chalk9.red(name)}`);
|
|
1019
1042
|
const message = error instanceof SkildError5 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1020
|
-
console.error(
|
|
1043
|
+
console.error(chalk9.red(message));
|
|
1021
1044
|
process.exitCode = 1;
|
|
1022
1045
|
}
|
|
1023
1046
|
}
|
|
1024
1047
|
|
|
1025
1048
|
// src/commands/signup.ts
|
|
1026
|
-
import
|
|
1049
|
+
import chalk10 from "chalk";
|
|
1027
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
|
|
1028
1111
|
async function signup(options) {
|
|
1029
1112
|
const registry = resolveRegistryUrl2(options.registry);
|
|
1030
1113
|
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
@@ -1032,7 +1115,7 @@ async function signup(options) {
|
|
|
1032
1115
|
const handle = options.handle?.trim() || "";
|
|
1033
1116
|
const password = options.password || "";
|
|
1034
1117
|
if ((!email || !handle || !password) && (!interactive || options.json)) {
|
|
1035
|
-
console.error(
|
|
1118
|
+
console.error(chalk10.red("Missing signup fields. Use --email/--handle/--password, or run `skild signup` interactively."));
|
|
1036
1119
|
process.exitCode = 1;
|
|
1037
1120
|
return;
|
|
1038
1121
|
}
|
|
@@ -1067,13 +1150,13 @@ async function signup(options) {
|
|
|
1067
1150
|
);
|
|
1068
1151
|
text = await res.text();
|
|
1069
1152
|
if (!res.ok) {
|
|
1070
|
-
console.error(
|
|
1153
|
+
console.error(chalk10.red(`Signup failed (${res.status}): ${text}`));
|
|
1071
1154
|
process.exitCode = 1;
|
|
1072
1155
|
return;
|
|
1073
1156
|
}
|
|
1074
1157
|
} catch (error) {
|
|
1075
1158
|
const message = error instanceof SkildError6 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1076
|
-
console.error(
|
|
1159
|
+
console.error(chalk10.red(`Signup failed: ${message}`));
|
|
1077
1160
|
process.exitCode = 1;
|
|
1078
1161
|
return;
|
|
1079
1162
|
}
|
|
@@ -1081,29 +1164,29 @@ async function signup(options) {
|
|
|
1081
1164
|
console.log(text || JSON.stringify({ ok: true }, null, 2));
|
|
1082
1165
|
return;
|
|
1083
1166
|
}
|
|
1084
|
-
console.log(
|
|
1167
|
+
console.log(chalk10.green("Signup successful."));
|
|
1085
1168
|
try {
|
|
1086
1169
|
const parsed = JSON.parse(text);
|
|
1087
1170
|
if (parsed.verification?.requiredForPublish) {
|
|
1088
1171
|
if (parsed.verification.mode === "log") {
|
|
1089
|
-
console.log(
|
|
1172
|
+
console.log(chalk10.dim("Dev mode: email sending is disabled. Check the registry dev logs for the verification link."));
|
|
1090
1173
|
} else if (parsed.verification.sent) {
|
|
1091
|
-
console.log(
|
|
1174
|
+
console.log(chalk10.dim("Verification email sent. Check your inbox (and spam)."));
|
|
1092
1175
|
} else {
|
|
1093
|
-
console.log(
|
|
1176
|
+
console.log(chalk10.yellow("Verification email was not sent. You may need to resend from the Skild Hub."));
|
|
1094
1177
|
}
|
|
1095
|
-
console.log(
|
|
1178
|
+
console.log(chalk10.dim(`Verify/resend in Skild Hub: ${(parsed.verification.consoleUrl || "https://hub.skild.sh").replace(/\/+$/, "")}/verify-email/request`));
|
|
1096
1179
|
} else if (parsed.verification) {
|
|
1097
|
-
console.log(
|
|
1098
|
-
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`));
|
|
1099
1182
|
}
|
|
1100
1183
|
} catch {
|
|
1101
1184
|
}
|
|
1102
|
-
console.log(
|
|
1185
|
+
console.log(chalk10.dim("Next: run `skild login`"));
|
|
1103
1186
|
}
|
|
1104
1187
|
|
|
1105
1188
|
// src/commands/login.ts
|
|
1106
|
-
import
|
|
1189
|
+
import chalk11 from "chalk";
|
|
1107
1190
|
import { fetchWithTimeout as fetchWithTimeout3, resolveRegistryUrl as resolveRegistryUrl3, saveRegistryAuth, SkildError as SkildError7 } from "@skild/core";
|
|
1108
1191
|
async function login(options) {
|
|
1109
1192
|
const registry = resolveRegistryUrl3(options.registry);
|
|
@@ -1111,7 +1194,7 @@ async function login(options) {
|
|
|
1111
1194
|
const handleOrEmail = options.handleOrEmail?.trim() || "";
|
|
1112
1195
|
const password = options.password || "";
|
|
1113
1196
|
if ((!handleOrEmail || !password) && (!interactive || options.json)) {
|
|
1114
|
-
console.error(
|
|
1197
|
+
console.error(chalk11.red("Missing credentials. Use --handle-or-email and --password, or run `skild login` interactively."));
|
|
1115
1198
|
process.exitCode = 1;
|
|
1116
1199
|
return;
|
|
1117
1200
|
}
|
|
@@ -1146,13 +1229,13 @@ async function login(options) {
|
|
|
1146
1229
|
);
|
|
1147
1230
|
text = await res.text();
|
|
1148
1231
|
if (!res.ok) {
|
|
1149
|
-
console.error(
|
|
1232
|
+
console.error(chalk11.red(`Login failed (${res.status}): ${text}`));
|
|
1150
1233
|
process.exitCode = 1;
|
|
1151
1234
|
return;
|
|
1152
1235
|
}
|
|
1153
1236
|
} catch (error) {
|
|
1154
1237
|
const message = error instanceof SkildError7 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1155
|
-
console.error(
|
|
1238
|
+
console.error(chalk11.red(`Login failed: ${message}`));
|
|
1156
1239
|
process.exitCode = 1;
|
|
1157
1240
|
return;
|
|
1158
1241
|
}
|
|
@@ -1168,28 +1251,28 @@ async function login(options) {
|
|
|
1168
1251
|
console.log(JSON.stringify({ ok: true, publisher: json.publisher }, null, 2));
|
|
1169
1252
|
return;
|
|
1170
1253
|
}
|
|
1171
|
-
console.log(
|
|
1254
|
+
console.log(chalk11.green(`Logged in as ${chalk11.cyan(json.publisher.handle)}.`));
|
|
1172
1255
|
if (json.publisher.emailVerified === false) {
|
|
1173
|
-
console.log(
|
|
1174
|
-
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"));
|
|
1175
1258
|
}
|
|
1176
1259
|
}
|
|
1177
1260
|
|
|
1178
1261
|
// src/commands/logout.ts
|
|
1179
|
-
import
|
|
1262
|
+
import chalk12 from "chalk";
|
|
1180
1263
|
import { clearRegistryAuth } from "@skild/core";
|
|
1181
1264
|
async function logout() {
|
|
1182
1265
|
clearRegistryAuth();
|
|
1183
|
-
console.log(
|
|
1266
|
+
console.log(chalk12.green("Logged out."));
|
|
1184
1267
|
}
|
|
1185
1268
|
|
|
1186
1269
|
// src/commands/whoami.ts
|
|
1187
|
-
import
|
|
1270
|
+
import chalk13 from "chalk";
|
|
1188
1271
|
import { fetchWithTimeout as fetchWithTimeout4, loadRegistryAuth as loadRegistryAuth2, resolveRegistryUrl as resolveRegistryUrl4, SkildError as SkildError8 } from "@skild/core";
|
|
1189
1272
|
async function whoami() {
|
|
1190
1273
|
const auth = loadRegistryAuth2();
|
|
1191
1274
|
if (!auth) {
|
|
1192
|
-
console.error(
|
|
1275
|
+
console.error(chalk13.red("Not logged in. Run `skild login` first."));
|
|
1193
1276
|
process.exitCode = 1;
|
|
1194
1277
|
return;
|
|
1195
1278
|
}
|
|
@@ -1202,16 +1285,16 @@ async function whoami() {
|
|
|
1202
1285
|
);
|
|
1203
1286
|
const text = await res.text();
|
|
1204
1287
|
if (!res.ok) {
|
|
1205
|
-
console.error(
|
|
1288
|
+
console.error(chalk13.red(`whoami failed (${res.status}): ${text}`));
|
|
1206
1289
|
process.exitCode = 1;
|
|
1207
1290
|
return;
|
|
1208
1291
|
}
|
|
1209
1292
|
const json = JSON.parse(text);
|
|
1210
|
-
console.log(
|
|
1293
|
+
console.log(chalk13.cyan(json.publisher.handle));
|
|
1211
1294
|
} catch (error) {
|
|
1212
1295
|
const message = error instanceof SkildError8 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1213
|
-
console.error(
|
|
1214
|
-
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`."));
|
|
1215
1298
|
process.exitCode = 1;
|
|
1216
1299
|
}
|
|
1217
1300
|
}
|
|
@@ -1222,7 +1305,7 @@ import os from "os";
|
|
|
1222
1305
|
import path3 from "path";
|
|
1223
1306
|
import crypto from "crypto";
|
|
1224
1307
|
import * as tar from "tar";
|
|
1225
|
-
import
|
|
1308
|
+
import chalk14 from "chalk";
|
|
1226
1309
|
import { assertValidAlias, fetchWithTimeout as fetchWithTimeout5, loadRegistryAuth as loadRegistryAuth3, normalizeAlias, resolveRegistryUrl as resolveRegistryUrl5, SkildError as SkildError9, splitCanonicalName, validateSkillDir } from "@skild/core";
|
|
1227
1310
|
function sha256Hex(buf) {
|
|
1228
1311
|
const h = crypto.createHash("sha256");
|
|
@@ -1238,15 +1321,15 @@ async function publish(options = {}) {
|
|
|
1238
1321
|
const registry = resolveRegistryUrl5(options.registry || auth?.registryUrl);
|
|
1239
1322
|
const token = auth?.token;
|
|
1240
1323
|
if (!token) {
|
|
1241
|
-
console.error(
|
|
1324
|
+
console.error(chalk14.red("Not logged in. Run `skild login` first."));
|
|
1242
1325
|
process.exitCode = 1;
|
|
1243
1326
|
return;
|
|
1244
1327
|
}
|
|
1245
1328
|
const dir = path3.resolve(options.dir || process.cwd());
|
|
1246
1329
|
const validation = validateSkillDir(dir);
|
|
1247
1330
|
if (!validation.ok) {
|
|
1248
|
-
console.error(
|
|
1249
|
-
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}`));
|
|
1250
1333
|
process.exitCode = 1;
|
|
1251
1334
|
return;
|
|
1252
1335
|
}
|
|
@@ -1260,14 +1343,14 @@ async function publish(options = {}) {
|
|
|
1260
1343
|
const skillset = fm.skillset === true;
|
|
1261
1344
|
const dependencies = Array.isArray(fm.dependencies) ? fm.dependencies : [];
|
|
1262
1345
|
if (!name) {
|
|
1263
|
-
console.error(
|
|
1346
|
+
console.error(chalk14.red("Missing name. Provide SKILL.md frontmatter.name or --name."));
|
|
1264
1347
|
process.exitCode = 1;
|
|
1265
1348
|
return;
|
|
1266
1349
|
}
|
|
1267
1350
|
if (!name.startsWith("@")) {
|
|
1268
1351
|
const seg = name.trim();
|
|
1269
1352
|
if (!/^[a-z0-9][a-z0-9-]{1,63}$/.test(seg)) {
|
|
1270
|
-
console.error(
|
|
1353
|
+
console.error(chalk14.red("Invalid name. Use @publisher/skill or a simple skill name (lowercase letters/digits/dashes)."));
|
|
1271
1354
|
process.exitCode = 1;
|
|
1272
1355
|
return;
|
|
1273
1356
|
}
|
|
@@ -1278,26 +1361,26 @@ async function publish(options = {}) {
|
|
|
1278
1361
|
);
|
|
1279
1362
|
const meText = await meRes.text();
|
|
1280
1363
|
if (!meRes.ok) {
|
|
1281
|
-
console.error(
|
|
1364
|
+
console.error(chalk14.red(`Failed to infer publisher scope (${meRes.status}): ${meText}`));
|
|
1282
1365
|
process.exitCode = 1;
|
|
1283
1366
|
return;
|
|
1284
1367
|
}
|
|
1285
1368
|
const meJson = JSON.parse(meText);
|
|
1286
1369
|
const handle = String(meJson?.publisher?.handle || "").trim().toLowerCase();
|
|
1287
1370
|
if (!handle) {
|
|
1288
|
-
console.error(
|
|
1371
|
+
console.error(chalk14.red("Failed to infer publisher scope from registry response."));
|
|
1289
1372
|
process.exitCode = 1;
|
|
1290
1373
|
return;
|
|
1291
1374
|
}
|
|
1292
1375
|
name = `@${handle}/${seg}`;
|
|
1293
1376
|
}
|
|
1294
1377
|
if (!/^@[a-z0-9][a-z0-9-]{1,31}\/[a-z0-9][a-z0-9-]{1,63}$/.test(name)) {
|
|
1295
|
-
console.error(
|
|
1378
|
+
console.error(chalk14.red("Invalid publish name. Expected @publisher/skill (lowercase letters/digits/dashes)."));
|
|
1296
1379
|
process.exitCode = 1;
|
|
1297
1380
|
return;
|
|
1298
1381
|
}
|
|
1299
1382
|
if (!version2) {
|
|
1300
|
-
console.error(
|
|
1383
|
+
console.error(chalk14.red("Missing version. Provide semver like 1.2.3 via SKILL.md frontmatter or --skill-version."));
|
|
1301
1384
|
process.exitCode = 1;
|
|
1302
1385
|
return;
|
|
1303
1386
|
}
|
|
@@ -1306,12 +1389,12 @@ async function publish(options = {}) {
|
|
|
1306
1389
|
assertValidAlias(alias);
|
|
1307
1390
|
} catch (error) {
|
|
1308
1391
|
const message = error instanceof SkildError9 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1309
|
-
console.error(
|
|
1392
|
+
console.error(chalk14.red(message));
|
|
1310
1393
|
process.exitCode = 1;
|
|
1311
1394
|
return;
|
|
1312
1395
|
}
|
|
1313
1396
|
}
|
|
1314
|
-
const spinner = createSpinner(`Publishing ${
|
|
1397
|
+
const spinner = createSpinner(`Publishing ${chalk14.cyan(`${name}@${version2}`)} to ${chalk14.dim(registry)}...`);
|
|
1315
1398
|
const tempDir = fs3.mkdtempSync(path3.join(os.tmpdir(), "skild-publish-"));
|
|
1316
1399
|
const tarballPath = path3.join(tempDir, "skill.tgz");
|
|
1317
1400
|
try {
|
|
@@ -1348,12 +1431,12 @@ async function publish(options = {}) {
|
|
|
1348
1431
|
const text = await res.text();
|
|
1349
1432
|
if (!res.ok) {
|
|
1350
1433
|
spinner.fail(`Publish failed (${res.status})`);
|
|
1351
|
-
console.error(
|
|
1434
|
+
console.error(chalk14.red(text));
|
|
1352
1435
|
process.exitCode = 1;
|
|
1353
1436
|
return;
|
|
1354
1437
|
}
|
|
1355
1438
|
if (alias) {
|
|
1356
|
-
spinner.text = `Publishing ${
|
|
1439
|
+
spinner.text = `Publishing ${chalk14.cyan(`${name}@${version2}`)} \u2014 setting alias ${chalk14.cyan(alias)}...`;
|
|
1357
1440
|
const aliasRes = await fetchWithTimeout5(
|
|
1358
1441
|
`${registry}/publisher/skills/${encodeURIComponent(scope)}/${encodeURIComponent(skillName)}/alias`,
|
|
1359
1442
|
{
|
|
@@ -1369,7 +1452,7 @@ async function publish(options = {}) {
|
|
|
1369
1452
|
const aliasText = await aliasRes.text();
|
|
1370
1453
|
if (!aliasRes.ok) {
|
|
1371
1454
|
spinner.fail(`Failed to set alias (${aliasRes.status})`);
|
|
1372
|
-
console.error(
|
|
1455
|
+
console.error(chalk14.red(aliasText));
|
|
1373
1456
|
process.exitCode = 1;
|
|
1374
1457
|
return;
|
|
1375
1458
|
}
|
|
@@ -1378,18 +1461,18 @@ async function publish(options = {}) {
|
|
|
1378
1461
|
console.log(text);
|
|
1379
1462
|
return;
|
|
1380
1463
|
}
|
|
1381
|
-
const suffix = alias ? ` (alias: ${
|
|
1382
|
-
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)`);
|
|
1383
1466
|
try {
|
|
1384
1467
|
const parsed = JSON.parse(text);
|
|
1385
1468
|
const warnings = Array.isArray(parsed.warnings) ? parsed.warnings.map(String).filter(Boolean) : [];
|
|
1386
|
-
for (const w of warnings) console.warn(
|
|
1469
|
+
for (const w of warnings) console.warn(chalk14.yellow(`Warning: ${w}`));
|
|
1387
1470
|
} catch {
|
|
1388
1471
|
}
|
|
1389
1472
|
} catch (error) {
|
|
1390
1473
|
spinner.fail("Publish failed");
|
|
1391
1474
|
const message = error instanceof SkildError9 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1392
|
-
console.error(
|
|
1475
|
+
console.error(chalk14.red(message));
|
|
1393
1476
|
process.exitCode = 1;
|
|
1394
1477
|
} finally {
|
|
1395
1478
|
fs3.rmSync(tempDir, { recursive: true, force: true });
|
|
@@ -1397,7 +1480,7 @@ async function publish(options = {}) {
|
|
|
1397
1480
|
}
|
|
1398
1481
|
|
|
1399
1482
|
// src/commands/search.ts
|
|
1400
|
-
import
|
|
1483
|
+
import chalk15 from "chalk";
|
|
1401
1484
|
import { resolveRegistryUrl as resolveRegistryUrl6, searchRegistrySkills, SkildError as SkildError10 } from "@skild/core";
|
|
1402
1485
|
async function search(query, options = {}) {
|
|
1403
1486
|
const registryUrl = resolveRegistryUrl6(options.registry);
|
|
@@ -1409,39 +1492,39 @@ async function search(query, options = {}) {
|
|
|
1409
1492
|
return;
|
|
1410
1493
|
}
|
|
1411
1494
|
if (!skills.length) {
|
|
1412
|
-
console.log(
|
|
1495
|
+
console.log(chalk15.dim("No results."));
|
|
1413
1496
|
return;
|
|
1414
1497
|
}
|
|
1415
|
-
console.log(
|
|
1416
|
-
\u{1F50E} Results (${skills.length}) \u2014 ${
|
|
1498
|
+
console.log(chalk15.bold(`
|
|
1499
|
+
\u{1F50E} Results (${skills.length}) \u2014 ${chalk15.dim(registryUrl)}
|
|
1417
1500
|
`));
|
|
1418
1501
|
for (const s of skills) {
|
|
1419
1502
|
const name = String(s.name || "").trim();
|
|
1420
1503
|
const desc = String(s.description || "").trim();
|
|
1421
1504
|
if (!name) continue;
|
|
1422
|
-
console.log(` ${
|
|
1505
|
+
console.log(` ${chalk15.cyan(name)}${desc ? chalk15.dim(` \u2014 ${desc}`) : ""}`);
|
|
1423
1506
|
}
|
|
1424
1507
|
} catch (error) {
|
|
1425
1508
|
const message = error instanceof SkildError10 ? error.message : error instanceof Error ? error.message : String(error);
|
|
1426
|
-
console.error(
|
|
1509
|
+
console.error(chalk15.red(message));
|
|
1427
1510
|
process.exitCode = 1;
|
|
1428
1511
|
}
|
|
1429
1512
|
}
|
|
1430
1513
|
|
|
1431
1514
|
// src/index.ts
|
|
1432
|
-
import { PLATFORMS as
|
|
1515
|
+
import { PLATFORMS as PLATFORMS4 } from "@skild/core";
|
|
1433
1516
|
var require2 = createRequire(import.meta.url);
|
|
1434
1517
|
var { version } = require2("../package.json");
|
|
1435
1518
|
var program = new Command();
|
|
1436
1519
|
program.name("skild").description("The npm for Agent Skills \u2014 Discover, install, manage, and publish AI Agent Skills with ease.").version(version);
|
|
1437
|
-
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) => {
|
|
1438
1521
|
await install(source, options);
|
|
1439
1522
|
});
|
|
1440
|
-
program.command("list").alias("ls").description("List installed Skills").option("-t, --target <platform>", `Target platform: ${
|
|
1441
|
-
program.command("info <skill>").description("Show installed Skill details").option("-t, --target <platform>", `Target platform: ${
|
|
1442
|
-
program.command("uninstall <skill>").alias("rm").description("Uninstall a Skill").option("-t, --target <platform>", `Target platform: ${
|
|
1443
|
-
program.command("update [skill]").alias("up").description("Update one or all installed Skills").option("-t, --target <platform>", `Target platform: ${
|
|
1444
|
-
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));
|
|
1445
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));
|
|
1446
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));
|
|
1447
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));
|
|
@@ -1450,7 +1533,7 @@ program.command("whoami").description("Show current registry identity").action(a
|
|
|
1450
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));
|
|
1451
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));
|
|
1452
1535
|
program.action(() => {
|
|
1453
|
-
console.log(
|
|
1536
|
+
console.log(chalk16.bold("\n\u{1F6E1}\uFE0F skild \u2014 Get your agents skilled.\n"));
|
|
1454
1537
|
program.outputHelp();
|
|
1455
1538
|
});
|
|
1456
1539
|
var argv = process.argv.slice();
|