skillspp 0.2.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/background-executor.js +5091 -0
- package/dist/background-executor.js.map +7 -0
- package/dist/background-worker.js +59 -0
- package/dist/background-worker.js.map +7 -0
- package/dist/cli.js +854 -909
- package/dist/cli.js.map +4 -4
- package/package.json +8 -2
- package/src/assets/ascii/logo/skillspp-logo.session.json +14630 -0
- package/src/assets/ascii/logo/skillspp-logo.txt +9 -0
package/dist/cli.js
CHANGED
|
@@ -20,9 +20,7 @@ function parseSource(input) {
|
|
|
20
20
|
if (isLocalPath(input)) {
|
|
21
21
|
return { type: "local", localPath: path.resolve(input) };
|
|
22
22
|
}
|
|
23
|
-
const githubTreeWithPath = input.match(
|
|
24
|
-
/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/
|
|
25
|
-
);
|
|
23
|
+
const githubTreeWithPath = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/);
|
|
26
24
|
if (githubTreeWithPath) {
|
|
27
25
|
const [, owner, repo, ref, subpath] = githubTreeWithPath;
|
|
28
26
|
return {
|
|
@@ -32,9 +30,7 @@ function parseSource(input) {
|
|
|
32
30
|
subpath
|
|
33
31
|
};
|
|
34
32
|
}
|
|
35
|
-
const githubTree = input.match(
|
|
36
|
-
/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/
|
|
37
|
-
);
|
|
33
|
+
const githubTree = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/);
|
|
38
34
|
if (githubTree) {
|
|
39
35
|
const [, owner, repo, ref] = githubTree;
|
|
40
36
|
return {
|
|
@@ -81,13 +77,7 @@ function parseSource(input) {
|
|
|
81
77
|
import fs from "node:fs";
|
|
82
78
|
import path2 from "node:path";
|
|
83
79
|
import matter from "gray-matter";
|
|
84
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
85
|
-
".git",
|
|
86
|
-
"node_modules",
|
|
87
|
-
"dist",
|
|
88
|
-
"build",
|
|
89
|
-
"__pycache__"
|
|
90
|
-
]);
|
|
80
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "__pycache__"]);
|
|
91
81
|
function resolveSourceLabel(parsedSource) {
|
|
92
82
|
switch (parsedSource.type) {
|
|
93
83
|
case "local":
|
|
@@ -136,12 +126,7 @@ function findSkillDirsRecursive(dir, depth, maxDepth, out) {
|
|
|
136
126
|
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) {
|
|
137
127
|
continue;
|
|
138
128
|
}
|
|
139
|
-
findSkillDirsRecursive(
|
|
140
|
-
path2.join(dir, entry.name),
|
|
141
|
-
depth + 1,
|
|
142
|
-
maxDepth,
|
|
143
|
-
out
|
|
144
|
-
);
|
|
129
|
+
findSkillDirsRecursive(path2.join(dir, entry.name), depth + 1, maxDepth, out);
|
|
145
130
|
}
|
|
146
131
|
}
|
|
147
132
|
function discoverSkills(basePath) {
|
|
@@ -200,198 +185,264 @@ var STANDARD_AGENTS = {
|
|
|
200
185
|
displayName: "Universal",
|
|
201
186
|
projectSkillsDir: ".agents/skills",
|
|
202
187
|
globalSkillsDir: ".agents/skills",
|
|
188
|
+
projectPluginsDir: ".agents/plugins/cache",
|
|
189
|
+
globalPluginsDir: ".agents/plugins/cache",
|
|
203
190
|
installMarkers: [".agents"]
|
|
204
191
|
},
|
|
205
192
|
adal: {
|
|
206
193
|
displayName: "AdaL",
|
|
207
194
|
projectSkillsDir: ".adal/skills",
|
|
208
195
|
globalSkillsDir: ".adal/skills",
|
|
196
|
+
projectPluginsDir: ".adal/plugins/cache",
|
|
197
|
+
globalPluginsDir: ".adal/plugins/cache",
|
|
209
198
|
installMarkers: [".adal"]
|
|
210
199
|
},
|
|
211
200
|
antigravity: {
|
|
212
201
|
displayName: "Antigravity",
|
|
213
202
|
projectSkillsDir: ".agent/skills",
|
|
214
203
|
globalSkillsDir: ".gemini/antigravity/skills",
|
|
204
|
+
projectPluginsDir: ".agent/plugins/cache",
|
|
205
|
+
globalPluginsDir: ".gemini/antigravity/plugins/cache",
|
|
215
206
|
installMarkers: [".gemini/antigravity"]
|
|
216
207
|
},
|
|
217
208
|
augment: {
|
|
218
209
|
displayName: "Augment",
|
|
219
210
|
projectSkillsDir: ".augment/skills",
|
|
220
211
|
globalSkillsDir: ".augment/skills",
|
|
212
|
+
projectPluginsDir: ".augment/plugins/cache",
|
|
213
|
+
globalPluginsDir: ".augment/plugins/cache",
|
|
221
214
|
installMarkers: [".augment"]
|
|
222
215
|
},
|
|
223
216
|
"claude-code": {
|
|
224
217
|
displayName: "Claude Code",
|
|
225
218
|
projectSkillsDir: ".claude/skills",
|
|
226
219
|
globalSkillsDir: ".claude/skills",
|
|
220
|
+
projectPluginsDir: ".claude/plugins/cache",
|
|
221
|
+
globalPluginsDir: ".claude/plugins/cache",
|
|
227
222
|
installMarkers: [".claude"]
|
|
228
223
|
},
|
|
229
224
|
"cortex-code": {
|
|
230
225
|
displayName: "Cortex Code",
|
|
231
226
|
projectSkillsDir: ".cortex/skills",
|
|
232
227
|
globalSkillsDir: ".cortex/skills",
|
|
228
|
+
projectPluginsDir: ".cortex/plugins/cache",
|
|
229
|
+
globalPluginsDir: ".cortex/plugins/cache",
|
|
233
230
|
installMarkers: [".cortex"]
|
|
234
231
|
},
|
|
235
232
|
crush: {
|
|
236
233
|
displayName: "Crush",
|
|
237
234
|
projectSkillsDir: ".crush/skills",
|
|
238
235
|
globalSkillsDir: ".crush/skills",
|
|
236
|
+
projectPluginsDir: ".crush/plugins/cache",
|
|
237
|
+
globalPluginsDir: ".crush/plugins/cache",
|
|
239
238
|
installMarkers: [".crush"]
|
|
240
239
|
},
|
|
241
240
|
droid: {
|
|
242
241
|
displayName: "Droid",
|
|
243
242
|
projectSkillsDir: ".factory/skills",
|
|
244
243
|
globalSkillsDir: ".factory/skills",
|
|
244
|
+
projectPluginsDir: ".factory/plugins/cache",
|
|
245
|
+
globalPluginsDir: ".factory/plugins/cache",
|
|
245
246
|
installMarkers: [".factory"]
|
|
246
247
|
},
|
|
247
248
|
goose: {
|
|
248
249
|
displayName: "Goose",
|
|
249
250
|
projectSkillsDir: ".goose/skills",
|
|
250
251
|
globalSkillsDir: ".config/goose/skills",
|
|
252
|
+
projectPluginsDir: ".goose/plugins/cache",
|
|
253
|
+
globalPluginsDir: ".config/goose/plugins/cache",
|
|
251
254
|
installMarkers: [".config/goose"]
|
|
252
255
|
},
|
|
253
256
|
"iflow-cli": {
|
|
254
257
|
displayName: "iFlow CLI",
|
|
255
258
|
projectSkillsDir: ".iflow/skills",
|
|
256
259
|
globalSkillsDir: ".iflow/skills",
|
|
260
|
+
projectPluginsDir: ".iflow/plugins/cache",
|
|
261
|
+
globalPluginsDir: ".iflow/plugins/cache",
|
|
257
262
|
installMarkers: [".iflow"]
|
|
258
263
|
},
|
|
259
264
|
junie: {
|
|
260
265
|
displayName: "Junie",
|
|
261
266
|
projectSkillsDir: ".junie/skills",
|
|
262
267
|
globalSkillsDir: ".junie/skills",
|
|
268
|
+
projectPluginsDir: ".junie/plugins/cache",
|
|
269
|
+
globalPluginsDir: ".junie/plugins/cache",
|
|
263
270
|
installMarkers: [".junie"]
|
|
264
271
|
},
|
|
265
272
|
"kiro-cli": {
|
|
266
273
|
displayName: "Kiro CLI",
|
|
267
274
|
projectSkillsDir: ".kiro/skills",
|
|
268
275
|
globalSkillsDir: ".kiro/skills",
|
|
276
|
+
projectPluginsDir: ".kiro/plugins/cache",
|
|
277
|
+
globalPluginsDir: ".kiro/plugins/cache",
|
|
269
278
|
installMarkers: [".kiro"]
|
|
270
279
|
},
|
|
271
280
|
kode: {
|
|
272
281
|
displayName: "Kode",
|
|
273
282
|
projectSkillsDir: ".kode/skills",
|
|
274
283
|
globalSkillsDir: ".kode/skills",
|
|
284
|
+
projectPluginsDir: ".kode/plugins/cache",
|
|
285
|
+
globalPluginsDir: ".kode/plugins/cache",
|
|
275
286
|
installMarkers: [".kode"]
|
|
276
287
|
},
|
|
277
288
|
openclaw: {
|
|
278
289
|
displayName: "OpenClaw",
|
|
279
290
|
projectSkillsDir: "skills",
|
|
280
291
|
globalSkillsDir: ".openclaw/skills",
|
|
292
|
+
projectPluginsDir: "plugins/cache",
|
|
293
|
+
globalPluginsDir: ".openclaw/plugins/cache",
|
|
281
294
|
installMarkers: [".openclaw"]
|
|
282
295
|
},
|
|
283
296
|
openhands: {
|
|
284
297
|
displayName: "OpenHands",
|
|
285
298
|
projectSkillsDir: ".openhands/skills",
|
|
286
299
|
globalSkillsDir: ".openhands/skills",
|
|
300
|
+
projectPluginsDir: ".openhands/plugins/cache",
|
|
301
|
+
globalPluginsDir: ".openhands/plugins/cache",
|
|
287
302
|
installMarkers: [".openhands"]
|
|
288
303
|
},
|
|
289
304
|
"mistral-vibe": {
|
|
290
305
|
displayName: "Mistral Vibe",
|
|
291
306
|
projectSkillsDir: ".vibe/skills",
|
|
292
307
|
globalSkillsDir: ".vibe/skills",
|
|
308
|
+
projectPluginsDir: ".vibe/plugins/cache",
|
|
309
|
+
globalPluginsDir: ".vibe/plugins/cache",
|
|
293
310
|
installMarkers: [".vibe"]
|
|
294
311
|
},
|
|
295
312
|
neovate: {
|
|
296
313
|
displayName: "Neovate",
|
|
297
314
|
projectSkillsDir: ".neovate/skills",
|
|
298
315
|
globalSkillsDir: ".neovate/skills",
|
|
316
|
+
projectPluginsDir: ".neovate/plugins/cache",
|
|
317
|
+
globalPluginsDir: ".neovate/plugins/cache",
|
|
299
318
|
installMarkers: [".neovate"]
|
|
300
319
|
},
|
|
301
320
|
pochi: {
|
|
302
321
|
displayName: "Pochi",
|
|
303
322
|
projectSkillsDir: ".pochi/skills",
|
|
304
323
|
globalSkillsDir: ".pochi/skills",
|
|
324
|
+
projectPluginsDir: ".pochi/plugins/cache",
|
|
325
|
+
globalPluginsDir: ".pochi/plugins/cache",
|
|
305
326
|
installMarkers: [".pochi"]
|
|
306
327
|
},
|
|
307
328
|
qoder: {
|
|
308
329
|
displayName: "Qoder",
|
|
309
330
|
projectSkillsDir: ".qoder/skills",
|
|
310
331
|
globalSkillsDir: ".qoder/skills",
|
|
332
|
+
projectPluginsDir: ".qoder/plugins/cache",
|
|
333
|
+
globalPluginsDir: ".qoder/plugins/cache",
|
|
311
334
|
installMarkers: [".qoder"]
|
|
312
335
|
},
|
|
313
336
|
"qwen-code": {
|
|
314
337
|
displayName: "Qwen Code",
|
|
315
338
|
projectSkillsDir: ".qwen/skills",
|
|
316
339
|
globalSkillsDir: ".qwen/skills",
|
|
340
|
+
projectPluginsDir: ".qwen/plugins/cache",
|
|
341
|
+
globalPluginsDir: ".qwen/plugins/cache",
|
|
317
342
|
installMarkers: [".qwen"]
|
|
318
343
|
},
|
|
319
344
|
roo: {
|
|
320
345
|
displayName: "Roo Code",
|
|
321
346
|
projectSkillsDir: ".roo/skills",
|
|
322
347
|
globalSkillsDir: ".roo/skills",
|
|
348
|
+
projectPluginsDir: ".roo/plugins/cache",
|
|
349
|
+
globalPluginsDir: ".roo/plugins/cache",
|
|
323
350
|
installMarkers: [".roo"]
|
|
324
351
|
},
|
|
325
352
|
trae: {
|
|
326
353
|
displayName: "Trae",
|
|
327
354
|
projectSkillsDir: ".trae/skills",
|
|
328
355
|
globalSkillsDir: ".trae/skills",
|
|
356
|
+
projectPluginsDir: ".trae/plugins/cache",
|
|
357
|
+
globalPluginsDir: ".trae/plugins/cache",
|
|
329
358
|
installMarkers: [".trae"]
|
|
330
359
|
},
|
|
331
360
|
"trae-cn": {
|
|
332
361
|
displayName: "Trae CN",
|
|
333
362
|
projectSkillsDir: ".trae/skills",
|
|
334
363
|
globalSkillsDir: ".trae/skills",
|
|
364
|
+
projectPluginsDir: ".trae/plugins/cache",
|
|
365
|
+
globalPluginsDir: ".trae/plugins/cache",
|
|
335
366
|
installMarkers: [".trae"]
|
|
336
367
|
},
|
|
337
368
|
windsurf: {
|
|
338
369
|
displayName: "Windsurf",
|
|
339
370
|
projectSkillsDir: ".windsurf/skills",
|
|
340
371
|
globalSkillsDir: ".codeium/windsurf/skills",
|
|
372
|
+
projectPluginsDir: ".windsurf/plugins/cache",
|
|
373
|
+
globalPluginsDir: ".codeium/windsurf/plugins/cache",
|
|
341
374
|
installMarkers: [".windsurf", ".codeium/windsurf"]
|
|
342
375
|
},
|
|
343
376
|
zencoder: {
|
|
344
377
|
displayName: "Zencoder",
|
|
345
378
|
projectSkillsDir: ".zencoder/skills",
|
|
346
379
|
globalSkillsDir: ".zencoder/skills",
|
|
380
|
+
projectPluginsDir: ".zencoder/plugins/cache",
|
|
381
|
+
globalPluginsDir: ".zencoder/plugins/cache",
|
|
347
382
|
installMarkers: [".zencoder"]
|
|
348
383
|
},
|
|
349
384
|
continue: {
|
|
350
385
|
displayName: "Continue",
|
|
351
386
|
projectSkillsDir: ".continue/skills",
|
|
352
387
|
globalSkillsDir: ".continue/skills",
|
|
388
|
+
projectPluginsDir: ".continue/plugins/cache",
|
|
389
|
+
globalPluginsDir: ".continue/plugins/cache",
|
|
353
390
|
installMarkers: [".continue"]
|
|
354
391
|
},
|
|
355
392
|
codebuddy: {
|
|
356
393
|
displayName: "CodeBuddy",
|
|
357
394
|
projectSkillsDir: ".codebuddy/skills",
|
|
358
395
|
globalSkillsDir: ".codebuddy/skills",
|
|
396
|
+
projectPluginsDir: ".codebuddy/plugins/cache",
|
|
397
|
+
globalPluginsDir: ".codebuddy/plugins/cache",
|
|
359
398
|
installMarkers: [".codebuddy"]
|
|
360
399
|
},
|
|
361
400
|
"command-code": {
|
|
362
401
|
displayName: "Command Code",
|
|
363
402
|
projectSkillsDir: ".commandcode/skills",
|
|
364
403
|
globalSkillsDir: ".commandcode/skills",
|
|
404
|
+
projectPluginsDir: ".commandcode/plugins/cache",
|
|
405
|
+
globalPluginsDir: ".commandcode/plugins/cache",
|
|
365
406
|
installMarkers: [".commandcode"]
|
|
366
407
|
},
|
|
367
408
|
kilo: {
|
|
368
409
|
displayName: "Kilo Code",
|
|
369
410
|
projectSkillsDir: ".kilocode/skills",
|
|
370
411
|
globalSkillsDir: ".kilocode/skills",
|
|
412
|
+
projectPluginsDir: ".kilocode/plugins/cache",
|
|
413
|
+
globalPluginsDir: ".kilocode/plugins/cache",
|
|
371
414
|
installMarkers: [".kilocode"]
|
|
372
415
|
},
|
|
373
416
|
mcpjam: {
|
|
374
417
|
displayName: "MCPJam",
|
|
375
418
|
projectSkillsDir: ".mcpjam/skills",
|
|
376
419
|
globalSkillsDir: ".mcpjam/skills",
|
|
420
|
+
projectPluginsDir: ".mcpjam/plugins/cache",
|
|
421
|
+
globalPluginsDir: ".mcpjam/plugins/cache",
|
|
377
422
|
installMarkers: [".mcpjam"]
|
|
378
423
|
},
|
|
379
424
|
mux: {
|
|
380
425
|
displayName: "Mux",
|
|
381
426
|
projectSkillsDir: ".mux/skills",
|
|
382
427
|
globalSkillsDir: ".mux/skills",
|
|
428
|
+
projectPluginsDir: ".mux/plugins/cache",
|
|
429
|
+
globalPluginsDir: ".mux/plugins/cache",
|
|
383
430
|
installMarkers: [".mux"]
|
|
384
431
|
},
|
|
385
432
|
pi: {
|
|
386
433
|
displayName: "Pi",
|
|
387
434
|
projectSkillsDir: ".pi/skills",
|
|
388
435
|
globalSkillsDir: ".pi/agent/skills",
|
|
436
|
+
projectPluginsDir: ".pi/plugins/cache",
|
|
437
|
+
globalPluginsDir: ".pi/agent/plugins/cache",
|
|
389
438
|
installMarkers: [".pi"]
|
|
390
439
|
},
|
|
391
440
|
replit: {
|
|
392
441
|
displayName: "Replit",
|
|
393
442
|
projectSkillsDir: ".agents/skills",
|
|
394
443
|
globalSkillsDir: ".config/agents/skills",
|
|
444
|
+
projectPluginsDir: ".agents/plugins/cache",
|
|
445
|
+
globalPluginsDir: ".config/agents/plugins/cache",
|
|
395
446
|
installMarkers: [".config/agents"]
|
|
396
447
|
}
|
|
397
448
|
};
|
|
@@ -401,48 +452,64 @@ var AGENTS = {
|
|
|
401
452
|
displayName: "Codex",
|
|
402
453
|
projectSkillsDir: ".agents/skills",
|
|
403
454
|
globalSkillsDir: ".codex/skills",
|
|
455
|
+
projectPluginsDir: ".agents/plugins/cache",
|
|
456
|
+
globalPluginsDir: ".codex/plugins/cache",
|
|
404
457
|
installMarkers: [".codex"]
|
|
405
458
|
},
|
|
406
459
|
cursor: {
|
|
407
460
|
displayName: "Cursor",
|
|
408
461
|
projectSkillsDir: ".agents/skills",
|
|
409
462
|
globalSkillsDir: ".cursor/skills",
|
|
463
|
+
projectPluginsDir: ".agents/plugins/cache",
|
|
464
|
+
globalPluginsDir: ".cursor/plugins/cache",
|
|
410
465
|
installMarkers: [".cursor"]
|
|
411
466
|
},
|
|
412
467
|
"gemini-cli": {
|
|
413
468
|
displayName: "Gemini CLI",
|
|
414
469
|
projectSkillsDir: ".agents/skills",
|
|
415
470
|
globalSkillsDir: ".gemini/skills",
|
|
471
|
+
projectPluginsDir: ".agents/plugins/cache",
|
|
472
|
+
globalPluginsDir: ".gemini/plugins/cache",
|
|
416
473
|
installMarkers: [".gemini"]
|
|
417
474
|
},
|
|
418
475
|
"github-copilot": {
|
|
419
476
|
displayName: "GitHub Copilot",
|
|
420
477
|
projectSkillsDir: ".agents/skills",
|
|
421
478
|
globalSkillsDir: ".copilot/skills",
|
|
479
|
+
projectPluginsDir: ".agents/plugins/cache",
|
|
480
|
+
globalPluginsDir: ".copilot/plugins/cache",
|
|
422
481
|
installMarkers: [".copilot"]
|
|
423
482
|
},
|
|
424
483
|
amp: {
|
|
425
484
|
displayName: "Amp",
|
|
426
485
|
projectSkillsDir: ".agents/skills",
|
|
427
486
|
globalSkillsDir: ".config/agents/skills",
|
|
487
|
+
projectPluginsDir: ".agents/plugins/cache",
|
|
488
|
+
globalPluginsDir: ".config/agents/plugins/cache",
|
|
428
489
|
installMarkers: [".config/agents"]
|
|
429
490
|
},
|
|
430
491
|
opencode: {
|
|
431
492
|
displayName: "OpenCode",
|
|
432
493
|
projectSkillsDir: ".agents/skills",
|
|
433
494
|
globalSkillsDir: ".config/opencode/skills",
|
|
495
|
+
projectPluginsDir: ".agents/plugins/cache",
|
|
496
|
+
globalPluginsDir: ".config/opencode/plugins/cache",
|
|
434
497
|
installMarkers: [".config/opencode"]
|
|
435
498
|
},
|
|
436
499
|
windsurf: {
|
|
437
500
|
displayName: "Windsurf",
|
|
438
501
|
projectSkillsDir: ".windsurf/skills",
|
|
439
502
|
globalSkillsDir: ".codeium/windsurf/skills",
|
|
503
|
+
projectPluginsDir: ".windsurf/plugins/cache",
|
|
504
|
+
globalPluginsDir: ".codeium/windsurf/plugins/cache",
|
|
440
505
|
installMarkers: [".windsurf", ".codeium/windsurf"]
|
|
441
506
|
},
|
|
442
507
|
cline: {
|
|
443
508
|
displayName: "Cline",
|
|
444
509
|
projectSkillsDir: ".cline/skills",
|
|
445
510
|
globalSkillsDir: ".cline/skills",
|
|
511
|
+
projectPluginsDir: ".cline/plugins/cache",
|
|
512
|
+
globalPluginsDir: ".cline/plugins/cache",
|
|
446
513
|
installMarkers: [".cline"]
|
|
447
514
|
}
|
|
448
515
|
};
|
|
@@ -498,9 +565,7 @@ function normalizeAgentSelectionInput(values, cwd = process.cwd()) {
|
|
|
498
565
|
if (unknown.length === 0) {
|
|
499
566
|
return values;
|
|
500
567
|
}
|
|
501
|
-
const expandedFromGlob = unknown.every(
|
|
502
|
-
(value) => fs2.existsSync(path3.resolve(cwd, value))
|
|
503
|
-
);
|
|
568
|
+
const expandedFromGlob = unknown.every((value) => fs2.existsSync(path3.resolve(cwd, value)));
|
|
504
569
|
if (!expandedFromGlob) {
|
|
505
570
|
return values;
|
|
506
571
|
}
|
|
@@ -514,6 +579,11 @@ function getAgentSkillsDir(agent, globalInstall, cwd) {
|
|
|
514
579
|
const base = globalInstall ? os.homedir() : cwd;
|
|
515
580
|
return path3.join(base, relative);
|
|
516
581
|
}
|
|
582
|
+
function getAgentPluginsDir(agent, globalInstall, cwd) {
|
|
583
|
+
const relative = globalInstall ? AGENTS[agent].globalPluginsDir : AGENTS[agent].projectPluginsDir;
|
|
584
|
+
const base = globalInstall ? os.homedir() : cwd;
|
|
585
|
+
return path3.join(base, relative);
|
|
586
|
+
}
|
|
517
587
|
function detectInstalledAgents(cwd = process.cwd()) {
|
|
518
588
|
const found = [];
|
|
519
589
|
for (const agent of Object.keys(AGENTS)) {
|
|
@@ -544,22 +614,20 @@ function isAgentInstalled(agent, cwd) {
|
|
|
544
614
|
return false;
|
|
545
615
|
}
|
|
546
616
|
|
|
547
|
-
// ../../packages/
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
return sanitized || "unnamed-skill";
|
|
551
|
-
}
|
|
617
|
+
// ../../packages/cli-shared/src/ui/selection-step.tsx
|
|
618
|
+
import { useMemo, useState as useState2 } from "react";
|
|
619
|
+
import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
|
|
552
620
|
|
|
553
|
-
// src/ui/screens.tsx
|
|
621
|
+
// ../../packages/cli-shared/src/ui/screens.tsx
|
|
554
622
|
import { stripVTControlCharacters } from "node:util";
|
|
555
623
|
import React, { useEffect, useState } from "react";
|
|
556
624
|
import { Box, Text, render, useInput, useStdout } from "ink";
|
|
557
625
|
|
|
558
|
-
// src/ui/format.ts
|
|
626
|
+
// ../../packages/cli-shared/src/ui/format.ts
|
|
559
627
|
import os2 from "node:os";
|
|
560
628
|
import path4 from "node:path";
|
|
561
629
|
|
|
562
|
-
// src/ui/colors.ts
|
|
630
|
+
// ../../packages/cli-shared/src/ui/colors.ts
|
|
563
631
|
var ANSI_ESCAPE = "\x1B[";
|
|
564
632
|
var ANSI_RESET = "\x1B[0m";
|
|
565
633
|
var COLOR_TOKENS = {
|
|
@@ -593,7 +661,7 @@ function dim(text, colorEnabled) {
|
|
|
593
661
|
return ansiStyle(text, "2", colorEnabled);
|
|
594
662
|
}
|
|
595
663
|
|
|
596
|
-
// src/ui/format.ts
|
|
664
|
+
// ../../packages/cli-shared/src/ui/format.ts
|
|
597
665
|
function shortenHomePath(value, homeDir = os2.homedir()) {
|
|
598
666
|
if (value === homeDir || value.startsWith(`${homeDir}${path4.sep}`)) {
|
|
599
667
|
return `~${value.slice(homeDir.length)}`;
|
|
@@ -620,25 +688,31 @@ function formatDriftChips(options) {
|
|
|
620
688
|
)}`;
|
|
621
689
|
}
|
|
622
690
|
|
|
623
|
-
// src/ui/logo.ts
|
|
691
|
+
// ../../packages/cli-shared/src/ui/logo.ts
|
|
624
692
|
import fs3 from "node:fs";
|
|
625
|
-
import path5 from "node:path";
|
|
626
|
-
import { fileURLToPath } from "node:url";
|
|
627
693
|
var DEFAULT_LOGO_FPS = 12;
|
|
628
694
|
var EMPTY_TEXT_ONLY_LOGO = [];
|
|
629
695
|
var animatedCache;
|
|
630
696
|
var staticCache;
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
697
|
+
var configuredSessionPath = null;
|
|
698
|
+
var configuredStaticPath = null;
|
|
699
|
+
function normalizeConfiguredPath(filePath) {
|
|
700
|
+
if (typeof filePath !== "string") {
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
const trimmed = filePath.trim();
|
|
704
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
705
|
+
}
|
|
706
|
+
function configureLogoAssetPaths(paths) {
|
|
707
|
+
configuredSessionPath = normalizeConfiguredPath(paths.sessionPath);
|
|
708
|
+
configuredStaticPath = normalizeConfiguredPath(paths.textPath);
|
|
709
|
+
resetLogoCache();
|
|
634
710
|
}
|
|
635
711
|
function resolveSessionPath() {
|
|
636
|
-
|
|
637
|
-
return customPath || path5.join(resolveLogoDir(), "skillspp-logo.session.json");
|
|
712
|
+
return normalizeConfiguredPath(process.env.SKILLSPP_LOGO_SESSION_PATH) ?? configuredSessionPath;
|
|
638
713
|
}
|
|
639
714
|
function resolveStaticPath() {
|
|
640
|
-
|
|
641
|
-
return customPath || path5.join(resolveLogoDir(), "skillspp-logo.txt");
|
|
715
|
+
return normalizeConfiguredPath(process.env.SKILLSPP_LOGO_TEXT_PATH) ?? configuredStaticPath;
|
|
642
716
|
}
|
|
643
717
|
function trimOuterEmptyRows(lines) {
|
|
644
718
|
let start = 0;
|
|
@@ -747,6 +821,10 @@ function readFileSafe(filePath) {
|
|
|
747
821
|
return null;
|
|
748
822
|
}
|
|
749
823
|
}
|
|
824
|
+
function resetLogoCache() {
|
|
825
|
+
animatedCache = void 0;
|
|
826
|
+
staticCache = void 0;
|
|
827
|
+
}
|
|
750
828
|
function getAnimatedLogoFrames() {
|
|
751
829
|
if (animatedCache !== void 0) {
|
|
752
830
|
return animatedCache;
|
|
@@ -783,7 +861,7 @@ function getBannerLogoLines() {
|
|
|
783
861
|
return getStaticLogoLines() ?? EMPTY_TEXT_ONLY_LOGO;
|
|
784
862
|
}
|
|
785
863
|
|
|
786
|
-
// src/ui/screens.tsx
|
|
864
|
+
// ../../packages/cli-shared/src/ui/screens.tsx
|
|
787
865
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
788
866
|
var DEFAULT_BANNER_WIDTH = 78;
|
|
789
867
|
var DEFAULT_PANEL_MIN_WIDTH = 58;
|
|
@@ -906,10 +984,7 @@ function resolveTerminalColumns() {
|
|
|
906
984
|
return Math.max(MIN_TERMINAL_WIDTH, process.stdout.columns || 80);
|
|
907
985
|
}
|
|
908
986
|
function resolveMaxInnerWidth(indent = " ") {
|
|
909
|
-
return Math.max(
|
|
910
|
-
MIN_INNER_WIDTH,
|
|
911
|
-
resolveTerminalColumns() - indent.length - FRAME_OVERHEAD
|
|
912
|
-
);
|
|
987
|
+
return Math.max(MIN_INNER_WIDTH, resolveTerminalColumns() - indent.length - FRAME_OVERHEAD);
|
|
913
988
|
}
|
|
914
989
|
function clampToTerminalInnerWidth(idealWidth, minWidth, indent = " ") {
|
|
915
990
|
const maxInnerWidth = resolveMaxInnerWidth(indent);
|
|
@@ -917,9 +992,7 @@ function clampToTerminalInnerWidth(idealWidth, minWidth, indent = " ") {
|
|
|
917
992
|
return Math.max(lowerBound, Math.min(idealWidth, maxInnerWidth));
|
|
918
993
|
}
|
|
919
994
|
function resolvePanelWidth(options) {
|
|
920
|
-
const lineLengths = options.lines.map(
|
|
921
|
-
(line) => visibleLength(singleLine(line))
|
|
922
|
-
);
|
|
995
|
+
const lineLengths = options.lines.map((line) => visibleLength(singleLine(line)));
|
|
923
996
|
return Math.max(
|
|
924
997
|
options.minWidth ?? DEFAULT_PANEL_MIN_WIDTH,
|
|
925
998
|
singleLine(options.title).length + 4,
|
|
@@ -928,10 +1001,7 @@ function resolvePanelWidth(options) {
|
|
|
928
1001
|
}
|
|
929
1002
|
function resolveSelectionColumnWidths(innerWidth, targetLabelWidth, targetDescWidth) {
|
|
930
1003
|
const availableColumns = Math.max(8, innerWidth - SELECTION_ROW_OVERHEAD);
|
|
931
|
-
const targetTotal = Math.max(
|
|
932
|
-
8,
|
|
933
|
-
targetLabelWidth + Math.max(0, targetDescWidth)
|
|
934
|
-
);
|
|
1004
|
+
const targetTotal = Math.max(8, targetLabelWidth + Math.max(0, targetDescWidth));
|
|
935
1005
|
if (availableColumns >= targetTotal) {
|
|
936
1006
|
return {
|
|
937
1007
|
labelWidth: targetLabelWidth,
|
|
@@ -957,25 +1027,13 @@ function renderFramedPanel(options) {
|
|
|
957
1027
|
const bottomLeft = options.style === "rounded" ? "\u2570" : "\u2514";
|
|
958
1028
|
const bottomRight = options.style === "rounded" ? "\u256F" : "\u2518";
|
|
959
1029
|
const out = [];
|
|
960
|
-
out.push(
|
|
961
|
-
`${options.indent}${dim(
|
|
962
|
-
`${topLeft}\u2500 ${title} ${"\u2500".repeat(topTail)}${topRight}`
|
|
963
|
-
)}`
|
|
964
|
-
);
|
|
1030
|
+
out.push(`${options.indent}${dim(`${topLeft}\u2500 ${title} ${"\u2500".repeat(topTail)}${topRight}`)}`);
|
|
965
1031
|
for (const line of options.lines) {
|
|
966
1032
|
for (const wrappedLine of wrapVisibleLine(line, width)) {
|
|
967
|
-
out.push(
|
|
968
|
-
`${options.indent}${dim("\u2502")} ${padRight(wrappedLine, width)} ${dim(
|
|
969
|
-
"\u2502"
|
|
970
|
-
)}`
|
|
971
|
-
);
|
|
1033
|
+
out.push(`${options.indent}${dim("\u2502")} ${padRight(wrappedLine, width)} ${dim("\u2502")}`);
|
|
972
1034
|
}
|
|
973
1035
|
}
|
|
974
|
-
out.push(
|
|
975
|
-
`${options.indent}${dim(
|
|
976
|
-
`${bottomLeft}${"\u2500".repeat(bottomWidth)}${bottomRight}`
|
|
977
|
-
)}`
|
|
978
|
-
);
|
|
1036
|
+
out.push(`${options.indent}${dim(`${bottomLeft}${"\u2500".repeat(bottomWidth)}${bottomRight}`)}`);
|
|
979
1037
|
return out.join("\n");
|
|
980
1038
|
}
|
|
981
1039
|
function toSelectedRows(rows, selectedIds) {
|
|
@@ -1022,11 +1080,7 @@ function resolveSelectionPanelLayout(options) {
|
|
|
1022
1080
|
options.minWidth ?? DEFAULT_PANEL_MIN_WIDTH,
|
|
1023
1081
|
indent
|
|
1024
1082
|
);
|
|
1025
|
-
const columns = resolveSelectionColumnWidths(
|
|
1026
|
-
width,
|
|
1027
|
-
options.labelWidth,
|
|
1028
|
-
options.descWidth
|
|
1029
|
-
);
|
|
1083
|
+
const columns = resolveSelectionColumnWidths(width, options.labelWidth, options.descWidth);
|
|
1030
1084
|
return {
|
|
1031
1085
|
width,
|
|
1032
1086
|
labelWidth: columns.labelWidth,
|
|
@@ -1045,10 +1099,7 @@ function renderSectionToText(section) {
|
|
|
1045
1099
|
switch (section.type) {
|
|
1046
1100
|
case "banner": {
|
|
1047
1101
|
const logoLines = getBannerLogoLines();
|
|
1048
|
-
const widestLogoLine = logoLines.reduce(
|
|
1049
|
-
(max, line) => Math.max(max, visibleLength(line)),
|
|
1050
|
-
0
|
|
1051
|
-
);
|
|
1102
|
+
const widestLogoLine = logoLines.reduce((max, line) => Math.max(max, visibleLength(line)), 0);
|
|
1052
1103
|
const resolvedWidth = clampToTerminalInnerWidth(
|
|
1053
1104
|
Math.max(
|
|
1054
1105
|
section.width ?? DEFAULT_BANNER_WIDTH,
|
|
@@ -1075,7 +1126,7 @@ function renderSectionToText(section) {
|
|
|
1075
1126
|
return renderPanelText(section);
|
|
1076
1127
|
}
|
|
1077
1128
|
case "source":
|
|
1078
|
-
return finalizeUiBlock(`
|
|
1129
|
+
return finalizeUiBlock(` ${section.source}`);
|
|
1079
1130
|
case "lines":
|
|
1080
1131
|
return finalizeUiBlock(section.lines.join("\n"));
|
|
1081
1132
|
case "text":
|
|
@@ -1096,10 +1147,7 @@ function HistoryText({ text }) {
|
|
|
1096
1147
|
return /* @__PURE__ */ jsx(Text, { children: text.replace(/\n$/, "") });
|
|
1097
1148
|
}
|
|
1098
1149
|
function renderPinnedLogoHeaderText(lines) {
|
|
1099
|
-
const widestLogoLine = lines.reduce(
|
|
1100
|
-
(max, line) => Math.max(max, visibleLength(line)),
|
|
1101
|
-
0
|
|
1102
|
-
);
|
|
1150
|
+
const widestLogoLine = lines.reduce((max, line) => Math.max(max, visibleLength(line)), 0);
|
|
1103
1151
|
const resolvedWidth = clampToTerminalInnerWidth(
|
|
1104
1152
|
Math.max(PINNED_LOGO_HEADER_WIDTH, widestLogoLine),
|
|
1105
1153
|
Math.max(1, widestLogoLine)
|
|
@@ -1271,10 +1319,7 @@ function renderSectionToTextWithLogoLines(section, logoLinesOverride) {
|
|
|
1271
1319
|
return renderSectionToText(section);
|
|
1272
1320
|
}
|
|
1273
1321
|
const logoLines = logoLinesOverride ?? getBannerLogoLines();
|
|
1274
|
-
const widestLogoLine = logoLines.reduce(
|
|
1275
|
-
(max, line) => Math.max(max, visibleLength(line)),
|
|
1276
|
-
0
|
|
1277
|
-
);
|
|
1322
|
+
const widestLogoLine = logoLines.reduce((max, line) => Math.max(max, visibleLength(line)), 0);
|
|
1278
1323
|
const resolvedWidth = clampToTerminalInnerWidth(
|
|
1279
1324
|
Math.max(
|
|
1280
1325
|
section.width ?? DEFAULT_BANNER_WIDTH,
|
|
@@ -1290,11 +1335,7 @@ function renderSectionToTextWithLogoLines(section, logoLinesOverride) {
|
|
|
1290
1335
|
center(section.title, resolvedWidth)
|
|
1291
1336
|
];
|
|
1292
1337
|
return finalizeUiBlock(
|
|
1293
|
-
[
|
|
1294
|
-
`\u256D${border}\u256E`,
|
|
1295
|
-
...bodyLines.map((line) => `\u2502${line}\u2502`),
|
|
1296
|
-
`\u2570${border}\u256F`
|
|
1297
|
-
].join("\n")
|
|
1338
|
+
[`\u256D${border}\u256E`, ...bodyLines.map((line) => `\u2502${line}\u2502`), `\u2570${border}\u256F`].join("\n")
|
|
1298
1339
|
);
|
|
1299
1340
|
}
|
|
1300
1341
|
function freezeBannerSections(sections, logoLines) {
|
|
@@ -1350,17 +1391,13 @@ function statusStepsSection(steps) {
|
|
|
1350
1391
|
);
|
|
1351
1392
|
}
|
|
1352
1393
|
function completedStepsSection(steps) {
|
|
1353
|
-
return statusStepsSection(
|
|
1354
|
-
steps.map((step) => ({ status: "completed", label: step }))
|
|
1355
|
-
);
|
|
1394
|
+
return statusStepsSection(steps.map((step) => ({ status: "completed", label: step })));
|
|
1356
1395
|
}
|
|
1357
1396
|
function failedStepsSection(steps) {
|
|
1358
|
-
return statusStepsSection(
|
|
1359
|
-
steps.map((step) => ({ status: "failed", label: step }))
|
|
1360
|
-
);
|
|
1397
|
+
return statusStepsSection(steps.map((step) => ({ status: "failed", label: step })));
|
|
1361
1398
|
}
|
|
1362
|
-
function sourceSection(source) {
|
|
1363
|
-
return { type: "source", source };
|
|
1399
|
+
function sourceSection(source, label = "Skills source") {
|
|
1400
|
+
return { type: "source", source: `${label}: ${source}` };
|
|
1364
1401
|
}
|
|
1365
1402
|
function completionSummarySection(options) {
|
|
1366
1403
|
const skillLabel = options.skillCount === 1 ? "skill" : "skills";
|
|
@@ -1383,10 +1420,7 @@ function installationSummarySection(options) {
|
|
|
1383
1420
|
)} ${bold("Agents:")} ${colorToken(
|
|
1384
1421
|
options.agentCount.toString(),
|
|
1385
1422
|
"primary"
|
|
1386
|
-
)} ${bold("Targets:")} ${colorToken(
|
|
1387
|
-
options.targetCount.toString(),
|
|
1388
|
-
"primary"
|
|
1389
|
-
)}`,
|
|
1423
|
+
)} ${bold("Targets:")} ${colorToken(options.targetCount.toString(), "primary")}`,
|
|
1390
1424
|
"",
|
|
1391
1425
|
bold("Targets:")
|
|
1392
1426
|
];
|
|
@@ -1411,13 +1445,14 @@ function installationSummarySection(options) {
|
|
|
1411
1445
|
});
|
|
1412
1446
|
}
|
|
1413
1447
|
function uninstallSummarySection(options) {
|
|
1448
|
+
const itemLabel = options.itemLabel || "Skills";
|
|
1414
1449
|
return panelSection({
|
|
1415
1450
|
title: "Uninstall Summary",
|
|
1416
1451
|
lines: [
|
|
1417
1452
|
`Scope: ${options.globalInstall ? "global" : "current project"}`,
|
|
1418
1453
|
"",
|
|
1419
|
-
|
|
1420
|
-
...options.
|
|
1454
|
+
`${itemLabel} (${options.itemNames.length}):`,
|
|
1455
|
+
...options.itemNames.map((name) => ` - ${name}`),
|
|
1421
1456
|
"",
|
|
1422
1457
|
`Agents (${options.agentDisplayNames.length}): ${compactAgentDisplayNames(
|
|
1423
1458
|
[...options.agentDisplayNames].sort((a, b) => a.localeCompare(b)),
|
|
@@ -1534,9 +1569,7 @@ function Spinner({ label }) {
|
|
|
1534
1569
|
return /* @__PURE__ */ jsx(Text, { children: ` ${colorToken(frames[frameIndex], "primary")} ${label}` });
|
|
1535
1570
|
}
|
|
1536
1571
|
|
|
1537
|
-
// src/ui/selection-step.tsx
|
|
1538
|
-
import { useMemo, useState as useState2 } from "react";
|
|
1539
|
-
import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
|
|
1572
|
+
// ../../packages/cli-shared/src/ui/selection-step.tsx
|
|
1540
1573
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1541
1574
|
var DEFAULT_REQUIRED_MESSAGE = "At least one choice must be selected";
|
|
1542
1575
|
function assertPromptAllowed(shouldPrompt, interactive) {
|
|
@@ -1550,9 +1583,7 @@ function filterSelectionRowIndexes(rows, searchTerm) {
|
|
|
1550
1583
|
return rows.map((_, index) => index);
|
|
1551
1584
|
}
|
|
1552
1585
|
return rows.reduce((acc, row, index) => {
|
|
1553
|
-
const haystack = `${row.label} ${row.description || ""}`.toLocaleLowerCase(
|
|
1554
|
-
"en-US"
|
|
1555
|
-
);
|
|
1586
|
+
const haystack = `${row.label} ${row.description || ""}`.toLocaleLowerCase("en-US");
|
|
1556
1587
|
if (haystack.includes(normalized)) {
|
|
1557
1588
|
acc.push(index);
|
|
1558
1589
|
}
|
|
@@ -1611,9 +1642,7 @@ function buildRenderModel(options) {
|
|
|
1611
1642
|
label: row.label,
|
|
1612
1643
|
description: row.description
|
|
1613
1644
|
})),
|
|
1614
|
-
visibleRowIds: options.visibleIndexes.map(
|
|
1615
|
-
(index) => options.rows[index].id
|
|
1616
|
-
),
|
|
1645
|
+
visibleRowIds: options.visibleIndexes.map((index) => options.rows[index].id),
|
|
1617
1646
|
activeVisibleIndex: options.activeVisibleIndex,
|
|
1618
1647
|
selectedIds: options.selectedIds ?? selectedRowIds(options.rows),
|
|
1619
1648
|
searchable: Boolean(options.request.searchable),
|
|
@@ -1689,14 +1718,8 @@ function renderManySelectionOpenPanel(config, model) {
|
|
|
1689
1718
|
}
|
|
1690
1719
|
function renderSingleSelectionOpenPanel(config, model) {
|
|
1691
1720
|
const visibleRows = toVisibleRows(model);
|
|
1692
|
-
const maxLabelWidth = visibleRows.reduce(
|
|
1693
|
-
|
|
1694
|
-
8
|
|
1695
|
-
);
|
|
1696
|
-
const maxDescWidth = visibleRows.reduce(
|
|
1697
|
-
(max, row) => Math.max(max, row.description.length),
|
|
1698
|
-
0
|
|
1699
|
-
);
|
|
1721
|
+
const maxLabelWidth = visibleRows.reduce((max, row) => Math.max(max, row.label.length), 8);
|
|
1722
|
+
const maxDescWidth = visibleRows.reduce((max, row) => Math.max(max, row.description.length), 0);
|
|
1700
1723
|
const layout = resolveSelectionPanelLayout({
|
|
1701
1724
|
title: config.title,
|
|
1702
1725
|
staticLines: [config.instructionLine, "\u2191\u2193 navigate enter confirm"],
|
|
@@ -1731,9 +1754,7 @@ function SelectionRenderer({ content }) {
|
|
|
1731
1754
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: /* @__PURE__ */ jsx2(Text2, { children: content.replace(/\n$/, "") }) });
|
|
1732
1755
|
}
|
|
1733
1756
|
function MultiSelectPrompt(props) {
|
|
1734
|
-
const initialSelectedIds = new Set(
|
|
1735
|
-
props.options.initialSelectedIds || props.selectedIds || []
|
|
1736
|
-
);
|
|
1757
|
+
const initialSelectedIds = new Set(props.options.initialSelectedIds || props.selectedIds || []);
|
|
1737
1758
|
const [rows, setRows] = useState2(
|
|
1738
1759
|
props.rows.map((row) => ({
|
|
1739
1760
|
...row,
|
|
@@ -1744,16 +1765,10 @@ function MultiSelectPrompt(props) {
|
|
|
1744
1765
|
const [activeVisibleIndex, setActiveVisibleIndex] = useState2(0);
|
|
1745
1766
|
const [errorMessage, setErrorMessage] = useState2();
|
|
1746
1767
|
const visibleIndexes = useMemo(
|
|
1747
|
-
() => filterSelectionRowIndexes(
|
|
1748
|
-
rows,
|
|
1749
|
-
props.options.searchable !== false ? searchTerm : ""
|
|
1750
|
-
),
|
|
1768
|
+
() => filterSelectionRowIndexes(rows, props.options.searchable !== false ? searchTerm : ""),
|
|
1751
1769
|
[rows, searchTerm, props.options.searchable]
|
|
1752
1770
|
);
|
|
1753
|
-
const clampedIndex = clampActiveVisibleIndex(
|
|
1754
|
-
activeVisibleIndex,
|
|
1755
|
-
visibleIndexes.length
|
|
1756
|
-
);
|
|
1771
|
+
const clampedIndex = clampActiveVisibleIndex(activeVisibleIndex, visibleIndexes.length);
|
|
1757
1772
|
useInput2((input, key) => {
|
|
1758
1773
|
if (key.ctrl && input === "c") {
|
|
1759
1774
|
props.onCancel(new PromptCancelledError());
|
|
@@ -1765,9 +1780,7 @@ function MultiSelectPrompt(props) {
|
|
|
1765
1780
|
}
|
|
1766
1781
|
if (key.upArrow) {
|
|
1767
1782
|
if (visibleIndexes.length > 0) {
|
|
1768
|
-
setActiveVisibleIndex(
|
|
1769
|
-
(clampedIndex - 1 + visibleIndexes.length) % visibleIndexes.length
|
|
1770
|
-
);
|
|
1783
|
+
setActiveVisibleIndex((clampedIndex - 1 + visibleIndexes.length) % visibleIndexes.length);
|
|
1771
1784
|
}
|
|
1772
1785
|
setErrorMessage(void 0);
|
|
1773
1786
|
return;
|
|
@@ -1780,9 +1793,7 @@ function MultiSelectPrompt(props) {
|
|
|
1780
1793
|
return;
|
|
1781
1794
|
}
|
|
1782
1795
|
if (input === " ") {
|
|
1783
|
-
setRows(
|
|
1784
|
-
(prev) => toggleSelectionAtVisibleIndex(prev, visibleIndexes, clampedIndex)
|
|
1785
|
-
);
|
|
1796
|
+
setRows((prev) => toggleSelectionAtVisibleIndex(prev, visibleIndexes, clampedIndex));
|
|
1786
1797
|
setErrorMessage(void 0);
|
|
1787
1798
|
return;
|
|
1788
1799
|
}
|
|
@@ -1810,9 +1821,7 @@ function MultiSelectPrompt(props) {
|
|
|
1810
1821
|
if (key.return) {
|
|
1811
1822
|
const selectedIds = selectedRowIds(rows);
|
|
1812
1823
|
if ((props.options.required ?? true) && selectedIds.length === 0) {
|
|
1813
|
-
setErrorMessage(
|
|
1814
|
-
props.options.requiredMessage || DEFAULT_REQUIRED_MESSAGE
|
|
1815
|
-
);
|
|
1824
|
+
setErrorMessage(props.options.requiredMessage || DEFAULT_REQUIRED_MESSAGE);
|
|
1816
1825
|
return;
|
|
1817
1826
|
}
|
|
1818
1827
|
props.onSubmit(selectedIds);
|
|
@@ -1875,9 +1884,7 @@ function SingleSelectPrompt(props) {
|
|
|
1875
1884
|
if (key.return) {
|
|
1876
1885
|
const selectedId2 = props.rows[activeVisibleIndex]?.id || "";
|
|
1877
1886
|
if ((props.options.required ?? true) && !selectedId2) {
|
|
1878
|
-
setErrorMessage(
|
|
1879
|
-
props.options.requiredMessage || DEFAULT_REQUIRED_MESSAGE
|
|
1880
|
-
);
|
|
1887
|
+
setErrorMessage(props.options.requiredMessage || DEFAULT_REQUIRED_MESSAGE);
|
|
1881
1888
|
return;
|
|
1882
1889
|
}
|
|
1883
1890
|
props.onSubmit(selectedId2);
|
|
@@ -1959,7 +1966,148 @@ async function runOneSelectionStep(options) {
|
|
|
1959
1966
|
return selectedId;
|
|
1960
1967
|
}
|
|
1961
1968
|
|
|
1962
|
-
// src/
|
|
1969
|
+
// ../../packages/cli-shared/src/add-command.ts
|
|
1970
|
+
function parseAddLockFormatValue(value) {
|
|
1971
|
+
if (!value) {
|
|
1972
|
+
return void 0;
|
|
1973
|
+
}
|
|
1974
|
+
if (value !== "json" && value !== "yaml") {
|
|
1975
|
+
throw new Error(`Invalid --lock-format value: ${value}`);
|
|
1976
|
+
}
|
|
1977
|
+
return value;
|
|
1978
|
+
}
|
|
1979
|
+
function buildBaseAddOptions(input, presence = {}) {
|
|
1980
|
+
const maxDownloadBytes = input.maxDownloadBytes ? Number(input.maxDownloadBytes) : void 0;
|
|
1981
|
+
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
1982
|
+
throw new Error(`Invalid --max-download-bytes value: ${input.maxDownloadBytes}`);
|
|
1983
|
+
}
|
|
1984
|
+
const parsed = {
|
|
1985
|
+
global: Boolean(input.global),
|
|
1986
|
+
symlink: Boolean(input.symlink),
|
|
1987
|
+
yaml: Boolean(input.yaml),
|
|
1988
|
+
list: Boolean(input.list),
|
|
1989
|
+
all: Boolean(input.all),
|
|
1990
|
+
nonInteractive: Boolean(input.nonInteractive),
|
|
1991
|
+
trustWellKnown: Boolean(input.trustWellKnown),
|
|
1992
|
+
agent: normalizeAgentSelectionInput(input.agent),
|
|
1993
|
+
skill: input.selectedNames,
|
|
1994
|
+
allowHost: input.allowHost?.map((item) => item.toLowerCase()),
|
|
1995
|
+
denyHost: input.denyHost?.map((item) => item.toLowerCase()),
|
|
1996
|
+
maxDownloadBytes,
|
|
1997
|
+
policyMode: input.policyMode,
|
|
1998
|
+
lockFormat: parseAddLockFormatValue(input.lockFormat),
|
|
1999
|
+
experimental: input.experimental ?? false
|
|
2000
|
+
};
|
|
2001
|
+
if (parsed.agent && parsed.agent.length > 0) {
|
|
2002
|
+
parsed.agentFlagProvided = true;
|
|
2003
|
+
}
|
|
2004
|
+
if (presence.agentProvided) {
|
|
2005
|
+
parsed.agentFlagProvided = true;
|
|
2006
|
+
}
|
|
2007
|
+
if (presence.globalProvided) {
|
|
2008
|
+
parsed.globalFlagProvided = true;
|
|
2009
|
+
}
|
|
2010
|
+
if (presence.symlinkProvided) {
|
|
2011
|
+
parsed.symlinkFlagProvided = true;
|
|
2012
|
+
}
|
|
2013
|
+
if (parsed.all) {
|
|
2014
|
+
parsed.skill = ["*"];
|
|
2015
|
+
parsed.agent = ["*"];
|
|
2016
|
+
}
|
|
2017
|
+
return parsed;
|
|
2018
|
+
}
|
|
2019
|
+
function buildNamedAddSelectionRows(items) {
|
|
2020
|
+
return items.map((item) => ({
|
|
2021
|
+
id: item.name,
|
|
2022
|
+
label: item.name,
|
|
2023
|
+
description: item.description
|
|
2024
|
+
}));
|
|
2025
|
+
}
|
|
2026
|
+
function filterNamedAddSelection(items, requested) {
|
|
2027
|
+
if (!requested || requested.length === 0) {
|
|
2028
|
+
return [...items];
|
|
2029
|
+
}
|
|
2030
|
+
if (requested.includes("*")) {
|
|
2031
|
+
return [...items];
|
|
2032
|
+
}
|
|
2033
|
+
const wanted = new Set(requested.map((item) => item.toLowerCase()));
|
|
2034
|
+
return items.filter((item) => wanted.has(item.name.toLowerCase()));
|
|
2035
|
+
}
|
|
2036
|
+
async function resolveNamedAddSelection(options) {
|
|
2037
|
+
const {
|
|
2038
|
+
available,
|
|
2039
|
+
interactive,
|
|
2040
|
+
listMode,
|
|
2041
|
+
requested,
|
|
2042
|
+
rows,
|
|
2043
|
+
keyHints,
|
|
2044
|
+
view,
|
|
2045
|
+
promptTitle,
|
|
2046
|
+
requiredMessage,
|
|
2047
|
+
emptyMessage,
|
|
2048
|
+
multipleInNonInteractiveMessage,
|
|
2049
|
+
renderClosed
|
|
2050
|
+
} = options;
|
|
2051
|
+
if (listMode) {
|
|
2052
|
+
return filterNamedAddSelection(available, requested);
|
|
2053
|
+
}
|
|
2054
|
+
const prompt = {
|
|
2055
|
+
title: promptTitle,
|
|
2056
|
+
required: true,
|
|
2057
|
+
requiredMessage,
|
|
2058
|
+
searchable: true,
|
|
2059
|
+
keyHints: [...keyHints],
|
|
2060
|
+
view
|
|
2061
|
+
};
|
|
2062
|
+
if (requested) {
|
|
2063
|
+
const selected = filterNamedAddSelection(available, requested);
|
|
2064
|
+
if (selected.length > 0) {
|
|
2065
|
+
await runManySelectionStep({
|
|
2066
|
+
interactive,
|
|
2067
|
+
rows: [...rows],
|
|
2068
|
+
selectedIds: selected.map((item) => item.name),
|
|
2069
|
+
shouldPrompt: false,
|
|
2070
|
+
prompt,
|
|
2071
|
+
renderClosed
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
return selected;
|
|
2075
|
+
}
|
|
2076
|
+
if (available.length === 0) {
|
|
2077
|
+
throw new Error(emptyMessage);
|
|
2078
|
+
}
|
|
2079
|
+
if (available.length === 1) {
|
|
2080
|
+
await runManySelectionStep({
|
|
2081
|
+
interactive,
|
|
2082
|
+
rows: [...rows],
|
|
2083
|
+
selectedIds: [available[0].name],
|
|
2084
|
+
shouldPrompt: false,
|
|
2085
|
+
prompt,
|
|
2086
|
+
renderClosed
|
|
2087
|
+
});
|
|
2088
|
+
return [...available];
|
|
2089
|
+
}
|
|
2090
|
+
if (!interactive) {
|
|
2091
|
+
throw new Error(multipleInNonInteractiveMessage);
|
|
2092
|
+
}
|
|
2093
|
+
const selectedNames = await runManySelectionStep({
|
|
2094
|
+
interactive,
|
|
2095
|
+
rows: [...rows],
|
|
2096
|
+
shouldPrompt: true,
|
|
2097
|
+
prompt,
|
|
2098
|
+
renderClosed
|
|
2099
|
+
});
|
|
2100
|
+
const wanted = new Set(selectedNames);
|
|
2101
|
+
return available.filter((item) => wanted.has(item.name));
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// ../../packages/core/src/runtime/installer.ts
|
|
2105
|
+
function sanitizeSkillName(name) {
|
|
2106
|
+
const sanitized = name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[.-]+|[.-]+$/g, "");
|
|
2107
|
+
return sanitized || "unnamed-skill";
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// ../../packages/cli-shared/src/interactive.ts
|
|
1963
2111
|
function isPromptCancelledError(error) {
|
|
1964
2112
|
return error instanceof PromptCancelledError;
|
|
1965
2113
|
}
|
|
@@ -1997,7 +2145,7 @@ function parsePolicyMode(value) {
|
|
|
1997
2145
|
throw new Error(`Invalid --policy-mode value: ${value}`);
|
|
1998
2146
|
}
|
|
1999
2147
|
|
|
2000
|
-
// src/command-builder.ts
|
|
2148
|
+
// ../../packages/cli-shared/src/command-builder.ts
|
|
2001
2149
|
import { CommanderError } from "commander";
|
|
2002
2150
|
|
|
2003
2151
|
// ../../packages/core/src/runtime/telemetry.ts
|
|
@@ -2034,8 +2182,8 @@ function emitLifecycleEvent(emitter, event) {
|
|
|
2034
2182
|
emitter.publish(row);
|
|
2035
2183
|
}
|
|
2036
2184
|
|
|
2037
|
-
// src/command-builder.ts
|
|
2038
|
-
function createCliCommandContext(emitter, options) {
|
|
2185
|
+
// ../../packages/cli-shared/src/command-builder.ts
|
|
2186
|
+
function createCliCommandContext(emitter, options = {}) {
|
|
2039
2187
|
const emitCommandEvent = (command, event) => {
|
|
2040
2188
|
emitLifecycleEvent(emitter, {
|
|
2041
2189
|
eventType: event.eventType,
|
|
@@ -2048,7 +2196,7 @@ function createCliCommandContext(emitter, options) {
|
|
|
2048
2196
|
});
|
|
2049
2197
|
};
|
|
2050
2198
|
return {
|
|
2051
|
-
experimental: options.experimental,
|
|
2199
|
+
experimental: Boolean(options.experimental),
|
|
2052
2200
|
emitCommandEvent,
|
|
2053
2201
|
wrapAction: (command, action) => async (...args) => {
|
|
2054
2202
|
emitCommandEvent(command, {
|
|
@@ -2075,12 +2223,52 @@ function createCliCommandContext(emitter, options) {
|
|
|
2075
2223
|
}
|
|
2076
2224
|
};
|
|
2077
2225
|
}
|
|
2226
|
+
function applyExitOverride(command) {
|
|
2227
|
+
command.exitOverride((error) => {
|
|
2228
|
+
throw error;
|
|
2229
|
+
});
|
|
2230
|
+
for (const subcommand of command.commands) {
|
|
2231
|
+
applyExitOverride(subcommand);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
function isGracefulCommanderExit(error) {
|
|
2235
|
+
return error instanceof CommanderError && (error.code === "commander.helpDisplayed" || error.code === "commander.version");
|
|
2236
|
+
}
|
|
2237
|
+
function inferCommandSource(argv, options = {}) {
|
|
2238
|
+
const valueFlags = new Set(options.valueFlags ?? []);
|
|
2239
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
2240
|
+
const arg = argv[i];
|
|
2241
|
+
if (valueFlags.has(arg)) {
|
|
2242
|
+
i += 1;
|
|
2243
|
+
continue;
|
|
2244
|
+
}
|
|
2245
|
+
if (arg.startsWith("-")) {
|
|
2246
|
+
continue;
|
|
2247
|
+
}
|
|
2248
|
+
return arg;
|
|
2249
|
+
}
|
|
2250
|
+
return options.fallbackSource ?? "cli";
|
|
2251
|
+
}
|
|
2252
|
+
function emitCommanderParseErrorTelemetry(emitter, argv, error, options = {}) {
|
|
2253
|
+
const source = inferCommandSource(argv, options);
|
|
2254
|
+
emitLifecycleEvent(emitter, {
|
|
2255
|
+
eventType: `${source}_failed`,
|
|
2256
|
+
source,
|
|
2257
|
+
reason: "commander_parse_error",
|
|
2258
|
+
command: source,
|
|
2259
|
+
status: "error",
|
|
2260
|
+
error: error.message,
|
|
2261
|
+
metadata: {
|
|
2262
|
+
commanderCode: error.code
|
|
2263
|
+
}
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2078
2266
|
|
|
2079
2267
|
// ../../packages/platform-node/src/background-runner.ts
|
|
2080
2268
|
import fs4 from "node:fs";
|
|
2081
|
-
import
|
|
2269
|
+
import path5 from "node:path";
|
|
2082
2270
|
import { spawn } from "node:child_process";
|
|
2083
|
-
import { fileURLToPath
|
|
2271
|
+
import { fileURLToPath } from "node:url";
|
|
2084
2272
|
var activeChildren = /* @__PURE__ */ new Set();
|
|
2085
2273
|
var cleanupHandlersInstalled = false;
|
|
2086
2274
|
function cleanupActiveChildren() {
|
|
@@ -2110,12 +2298,12 @@ function installCleanupHandlers() {
|
|
|
2110
2298
|
}
|
|
2111
2299
|
}
|
|
2112
2300
|
function resolveWorkerEntry() {
|
|
2113
|
-
const dir =
|
|
2114
|
-
const tsPath =
|
|
2301
|
+
const dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
2302
|
+
const tsPath = path5.join(dir, "background-worker.ts");
|
|
2115
2303
|
if (fs4.existsSync(tsPath)) {
|
|
2116
2304
|
return tsPath;
|
|
2117
2305
|
}
|
|
2118
|
-
return
|
|
2306
|
+
return path5.join(dir, "background-worker.js");
|
|
2119
2307
|
}
|
|
2120
2308
|
function appendOutputChunk(chunks, chunk) {
|
|
2121
2309
|
const next = chunk.toString();
|
|
@@ -2131,18 +2319,14 @@ async function runBackgroundTask(request, options) {
|
|
|
2131
2319
|
installCleanupHandlers();
|
|
2132
2320
|
return new Promise((resolve, reject) => {
|
|
2133
2321
|
const childCwd = typeof request === "object" && request !== null && "payload" in request && typeof request.payload?.cwd === "string" ? request.payload.cwd : process.cwd();
|
|
2134
|
-
const child = spawn(
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
},
|
|
2143
|
-
stdio: ["ignore", "pipe", "pipe", "ipc"]
|
|
2144
|
-
}
|
|
2145
|
-
);
|
|
2322
|
+
const child = spawn(process.execPath, [...process.execArgv, resolveWorkerEntry()], {
|
|
2323
|
+
cwd: childCwd,
|
|
2324
|
+
env: {
|
|
2325
|
+
...process.env,
|
|
2326
|
+
SKILLSPP_BG_EXECUTOR: options.executorModule
|
|
2327
|
+
},
|
|
2328
|
+
stdio: ["ignore", "pipe", "pipe", "ipc"]
|
|
2329
|
+
});
|
|
2146
2330
|
activeChildren.add(child);
|
|
2147
2331
|
const stdoutChunks = [];
|
|
2148
2332
|
const stderrChunks = [];
|
|
@@ -2204,13 +2388,34 @@ async function runBackgroundTask(request, options) {
|
|
|
2204
2388
|
});
|
|
2205
2389
|
}
|
|
2206
2390
|
|
|
2207
|
-
// src/runtime/background-runner.ts
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2391
|
+
// ../../packages/cli-shared/src/runtime/background-runner.ts
|
|
2392
|
+
import fs5 from "node:fs";
|
|
2393
|
+
import path6 from "node:path";
|
|
2394
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
2395
|
+
function resolveExecutorModule(importMetaUrl) {
|
|
2396
|
+
const runtimeDir = path6.dirname(fileURLToPath2(importMetaUrl));
|
|
2397
|
+
const localCandidates = [
|
|
2398
|
+
path6.join(runtimeDir, "background-executor.js"),
|
|
2399
|
+
path6.join(runtimeDir, "background-executor.ts")
|
|
2400
|
+
];
|
|
2401
|
+
for (const candidate of localCandidates) {
|
|
2402
|
+
if (fs5.existsSync(candidate)) {
|
|
2403
|
+
return pathToFileURL(candidate).href;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
return "@skillspp/core/runtime/background-tasks";
|
|
2213
2407
|
}
|
|
2408
|
+
function createBackgroundTaskRunner(importMetaUrl) {
|
|
2409
|
+
return async function runBackgroundTask3(request, options = {}) {
|
|
2410
|
+
return runBackgroundTask(request, {
|
|
2411
|
+
onProgress: options.onProgress,
|
|
2412
|
+
executorModule: resolveExecutorModule(importMetaUrl)
|
|
2413
|
+
});
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
// src/runtime/background-runner.ts
|
|
2418
|
+
var runBackgroundTask2 = createBackgroundTaskRunner(import.meta.url);
|
|
2214
2419
|
|
|
2215
2420
|
// src/commands/add.ts
|
|
2216
2421
|
var SKILL_NAME_WIDTH = 32;
|
|
@@ -2267,17 +2472,10 @@ var ADD_SCOPE_SELECTION_ROWS = [
|
|
|
2267
2472
|
description: "Install into home-directory skills directories"
|
|
2268
2473
|
}
|
|
2269
2474
|
];
|
|
2270
|
-
function buildAddSkillSelectionRows(skills) {
|
|
2271
|
-
return skills.map((skill) => ({
|
|
2272
|
-
id: skill.name,
|
|
2273
|
-
label: skill.name,
|
|
2274
|
-
description: skill.description
|
|
2275
|
-
}));
|
|
2276
|
-
}
|
|
2277
2475
|
function renderAddSkillsSection(options) {
|
|
2278
2476
|
return manySelectionClosedSection(
|
|
2279
2477
|
ADD_SKILLS_SELECTION_VIEW,
|
|
2280
|
-
|
|
2478
|
+
buildNamedAddSelectionRows(options.skills),
|
|
2281
2479
|
options.selectedNames
|
|
2282
2480
|
);
|
|
2283
2481
|
}
|
|
@@ -2294,11 +2492,7 @@ function buildInstallationSummaryRows(options) {
|
|
|
2294
2492
|
if (!canonicalAgent) {
|
|
2295
2493
|
return rows;
|
|
2296
2494
|
}
|
|
2297
|
-
const canonicalBase = getAgentSkillsDir(
|
|
2298
|
-
canonicalAgent,
|
|
2299
|
-
options.globalInstall,
|
|
2300
|
-
options.cwd
|
|
2301
|
-
);
|
|
2495
|
+
const canonicalBase = getAgentSkillsDir(canonicalAgent, options.globalInstall, options.cwd);
|
|
2302
2496
|
for (const rawSkillName of options.skillNames) {
|
|
2303
2497
|
const skillName = sanitizeSkillName(rawSkillName);
|
|
2304
2498
|
const canonicalDir = path7.join(canonicalBase, skillName);
|
|
@@ -2354,95 +2548,33 @@ async function printAddListScreen(options) {
|
|
|
2354
2548
|
]);
|
|
2355
2549
|
}
|
|
2356
2550
|
async function resolveAddSkills(available, merged, interactive) {
|
|
2357
|
-
const
|
|
2358
|
-
if (!requested || requested.length === 0) {
|
|
2359
|
-
return items;
|
|
2360
|
-
}
|
|
2361
|
-
if (requested.includes("*")) {
|
|
2362
|
-
return items;
|
|
2363
|
-
}
|
|
2364
|
-
const wanted2 = new Set(requested.map((item) => item.toLowerCase()));
|
|
2365
|
-
return items.filter((item) => wanted2.has(item.name.toLowerCase()));
|
|
2366
|
-
};
|
|
2367
|
-
if (merged.list) {
|
|
2368
|
-
return filterByRequestedName(available, merged.skill);
|
|
2369
|
-
}
|
|
2370
|
-
const skillRows = buildAddSkillSelectionRows(
|
|
2551
|
+
const skillRows = buildNamedAddSelectionRows(
|
|
2371
2552
|
available.map((item) => ({
|
|
2372
2553
|
name: item.name,
|
|
2373
2554
|
description: item.description
|
|
2374
2555
|
}))
|
|
2375
2556
|
);
|
|
2376
|
-
const renderClosed = (
|
|
2557
|
+
const renderClosed = (selectedNames) => renderAddSkillsSection({
|
|
2377
2558
|
skills: available.map((item) => ({
|
|
2378
2559
|
name: item.name,
|
|
2379
2560
|
description: item.description
|
|
2380
2561
|
})),
|
|
2381
|
-
selectedNames
|
|
2562
|
+
selectedNames
|
|
2382
2563
|
});
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
if (selected.length > 0) {
|
|
2386
|
-
await runManySelectionStep({
|
|
2387
|
-
interactive,
|
|
2388
|
-
rows: skillRows,
|
|
2389
|
-
selectedIds: selected.map((item) => item.name),
|
|
2390
|
-
shouldPrompt: false,
|
|
2391
|
-
prompt: {
|
|
2392
|
-
title: "Choose Skills",
|
|
2393
|
-
required: true,
|
|
2394
|
-
requiredMessage: "At least one skill must be selected",
|
|
2395
|
-
searchable: true,
|
|
2396
|
-
keyHints: ADD_SKILLS_KEY_HINTS,
|
|
2397
|
-
view: ADD_SKILLS_SELECTION_VIEW
|
|
2398
|
-
},
|
|
2399
|
-
renderClosed
|
|
2400
|
-
});
|
|
2401
|
-
}
|
|
2402
|
-
return selected;
|
|
2403
|
-
}
|
|
2404
|
-
if (available.length === 0) {
|
|
2405
|
-
throw new Error("No skills available");
|
|
2406
|
-
}
|
|
2407
|
-
if (available.length === 1) {
|
|
2408
|
-
await runManySelectionStep({
|
|
2409
|
-
interactive,
|
|
2410
|
-
rows: skillRows,
|
|
2411
|
-
selectedIds: [available[0].name],
|
|
2412
|
-
shouldPrompt: false,
|
|
2413
|
-
prompt: {
|
|
2414
|
-
title: "Choose Skills",
|
|
2415
|
-
required: true,
|
|
2416
|
-
requiredMessage: "At least one skill must be selected",
|
|
2417
|
-
searchable: true,
|
|
2418
|
-
keyHints: ADD_SKILLS_KEY_HINTS,
|
|
2419
|
-
view: ADD_SKILLS_SELECTION_VIEW
|
|
2420
|
-
},
|
|
2421
|
-
renderClosed
|
|
2422
|
-
});
|
|
2423
|
-
return available;
|
|
2424
|
-
}
|
|
2425
|
-
if (!interactive) {
|
|
2426
|
-
throw new Error(
|
|
2427
|
-
"Multiple skills found. Use --skill <name> or run in TTY without --non-interactive."
|
|
2428
|
-
);
|
|
2429
|
-
}
|
|
2430
|
-
const selectedNames = await runManySelectionStep({
|
|
2564
|
+
return resolveNamedAddSelection({
|
|
2565
|
+
available,
|
|
2431
2566
|
interactive,
|
|
2567
|
+
listMode: merged.list,
|
|
2568
|
+
requested: merged.skill,
|
|
2432
2569
|
rows: skillRows,
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
keyHints: ADD_SKILLS_KEY_HINTS,
|
|
2440
|
-
view: ADD_SKILLS_SELECTION_VIEW
|
|
2441
|
-
},
|
|
2570
|
+
keyHints: ADD_SKILLS_KEY_HINTS,
|
|
2571
|
+
view: ADD_SKILLS_SELECTION_VIEW,
|
|
2572
|
+
promptTitle: "Choose Skills",
|
|
2573
|
+
requiredMessage: "At least one skill must be selected",
|
|
2574
|
+
emptyMessage: "No skills available",
|
|
2575
|
+
multipleInNonInteractiveMessage: "Multiple skills found. Use --skill <name> or run in TTY without --non-interactive.",
|
|
2442
2576
|
renderClosed
|
|
2443
2577
|
});
|
|
2444
|
-
const wanted = new Set(selectedNames);
|
|
2445
|
-
return available.filter((item) => wanted.has(item.name));
|
|
2446
2578
|
}
|
|
2447
2579
|
async function resolveAddAgents(merged, globalInstall, interactive) {
|
|
2448
2580
|
const rows = resolveAddAgentSelectionRows(globalInstall ? "global" : "local");
|
|
@@ -2490,9 +2622,7 @@ async function resolveAddAgents(merged, globalInstall, interactive) {
|
|
|
2490
2622
|
return selectedAgents;
|
|
2491
2623
|
}
|
|
2492
2624
|
if (!interactive) {
|
|
2493
|
-
throw new Error(
|
|
2494
|
-
"Missing --agent in non-interactive mode. Provide at least one agent."
|
|
2495
|
-
);
|
|
2625
|
+
throw new Error("Missing --agent in non-interactive mode. Provide at least one agent.");
|
|
2496
2626
|
}
|
|
2497
2627
|
if (allForScope.length === 1) {
|
|
2498
2628
|
await runManySelectionStep({
|
|
@@ -2563,56 +2693,27 @@ function resolveAddInstallMode(merged) {
|
|
|
2563
2693
|
}
|
|
2564
2694
|
return "copy";
|
|
2565
2695
|
}
|
|
2566
|
-
function parseLockFormatValue(value) {
|
|
2567
|
-
if (!value) {
|
|
2568
|
-
return void 0;
|
|
2569
|
-
}
|
|
2570
|
-
if (value !== "json" && value !== "yaml") {
|
|
2571
|
-
throw new Error(`Invalid --lock-format value: ${value}`);
|
|
2572
|
-
}
|
|
2573
|
-
return value;
|
|
2574
|
-
}
|
|
2575
2696
|
function toAddOptions(options, presence = {}) {
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
lockFormat: parseLockFormatValue(options.lockFormat),
|
|
2597
|
-
experimental: false
|
|
2598
|
-
};
|
|
2599
|
-
if (parsed.agent && parsed.agent.length > 0) {
|
|
2600
|
-
parsed.agentFlagProvided = true;
|
|
2601
|
-
}
|
|
2602
|
-
if (presence.agentProvided) {
|
|
2603
|
-
parsed.agentFlagProvided = true;
|
|
2604
|
-
}
|
|
2605
|
-
if (presence.globalProvided) {
|
|
2606
|
-
parsed.globalFlagProvided = true;
|
|
2607
|
-
}
|
|
2608
|
-
if (presence.symlinkProvided) {
|
|
2609
|
-
parsed.symlinkFlagProvided = true;
|
|
2610
|
-
}
|
|
2611
|
-
if (parsed.all) {
|
|
2612
|
-
parsed.skill = ["*"];
|
|
2613
|
-
parsed.agent = ["*"];
|
|
2614
|
-
}
|
|
2615
|
-
return parsed;
|
|
2697
|
+
return buildBaseAddOptions(
|
|
2698
|
+
{
|
|
2699
|
+
global: options.global,
|
|
2700
|
+
symlink: options.symlink,
|
|
2701
|
+
yaml: options.yaml,
|
|
2702
|
+
list: options.list,
|
|
2703
|
+
all: options.all,
|
|
2704
|
+
nonInteractive: options.nonInteractive,
|
|
2705
|
+
trustWellKnown: options.trustWellKnown,
|
|
2706
|
+
agent: options.agent,
|
|
2707
|
+
selectedNames: options.skill,
|
|
2708
|
+
allowHost: options.allowHost,
|
|
2709
|
+
denyHost: options.denyHost,
|
|
2710
|
+
maxDownloadBytes: options.maxDownloadBytes,
|
|
2711
|
+
policyMode: parsePolicyMode(options.policyMode),
|
|
2712
|
+
lockFormat: options.lockFormat,
|
|
2713
|
+
experimental: false
|
|
2714
|
+
},
|
|
2715
|
+
presence
|
|
2716
|
+
);
|
|
2616
2717
|
}
|
|
2617
2718
|
async function executeAdd(sourceInput, merged) {
|
|
2618
2719
|
const interactive = canUseInteractive(merged.nonInteractive);
|
|
@@ -2625,9 +2726,7 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2625
2726
|
sourceLabel = resolveSourceLabel(parsedSource);
|
|
2626
2727
|
} catch (error) {
|
|
2627
2728
|
hideLoader();
|
|
2628
|
-
await renderStaticScreen([
|
|
2629
|
-
failedStepsSection(["failed to parse source"])
|
|
2630
|
-
]);
|
|
2729
|
+
await renderStaticScreen([failedStepsSection(["failed to parse source"])]);
|
|
2631
2730
|
throw error;
|
|
2632
2731
|
}
|
|
2633
2732
|
hideLoader();
|
|
@@ -2653,26 +2752,17 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2653
2752
|
);
|
|
2654
2753
|
} catch (error) {
|
|
2655
2754
|
hideLoader();
|
|
2656
|
-
await renderStaticScreen([
|
|
2657
|
-
failedStepsSection(["failed to fetch skill index"])
|
|
2658
|
-
]);
|
|
2755
|
+
await renderStaticScreen([failedStepsSection(["failed to fetch skill index"])]);
|
|
2659
2756
|
throw error;
|
|
2660
2757
|
}
|
|
2661
2758
|
hideLoader();
|
|
2662
2759
|
await renderStaticScreen([
|
|
2663
|
-
completedStepsSection([
|
|
2664
|
-
"skill index fetched",
|
|
2665
|
-
"interactive session ready"
|
|
2666
|
-
])
|
|
2760
|
+
completedStepsSection(["skill index fetched", "interactive session ready"])
|
|
2667
2761
|
]);
|
|
2668
2762
|
if (!merged.list) {
|
|
2669
2763
|
await renderStaticScreen([sourceSection(shortenHomePath(sourceLabel))]);
|
|
2670
2764
|
}
|
|
2671
|
-
const selected = await resolveAddSkills(
|
|
2672
|
-
discovered.skills,
|
|
2673
|
-
merged,
|
|
2674
|
-
interactive
|
|
2675
|
-
);
|
|
2765
|
+
const selected = await resolveAddSkills(discovered.skills, merged, interactive);
|
|
2676
2766
|
if (selected.length === 0) {
|
|
2677
2767
|
if (parsedSource.type === "well-known" || parsedSource.type === "catalog") {
|
|
2678
2768
|
throw new Error("No matching well-known skills found in source");
|
|
@@ -2695,11 +2785,7 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2695
2785
|
global: globalInstall,
|
|
2696
2786
|
globalFlagProvided: true
|
|
2697
2787
|
};
|
|
2698
|
-
const agents = await resolveAddAgents(
|
|
2699
|
-
installOptions,
|
|
2700
|
-
globalInstall,
|
|
2701
|
-
interactive
|
|
2702
|
-
);
|
|
2788
|
+
const agents = await resolveAddAgents(installOptions, globalInstall, interactive);
|
|
2703
2789
|
const mode = resolveAddInstallMode(merged);
|
|
2704
2790
|
await printInstallationSummary({
|
|
2705
2791
|
skillNames: selected.map((item) => item.name),
|
|
@@ -2731,9 +2817,7 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2731
2817
|
);
|
|
2732
2818
|
} catch (error) {
|
|
2733
2819
|
hideLoader();
|
|
2734
|
-
await renderStaticScreen([
|
|
2735
|
-
failedStepsSection(["failed to install skills"])
|
|
2736
|
-
]);
|
|
2820
|
+
await renderStaticScreen([failedStepsSection(["failed to install skills"])]);
|
|
2737
2821
|
throw error;
|
|
2738
2822
|
}
|
|
2739
2823
|
hideLoader();
|
|
@@ -2754,13 +2838,7 @@ async function executeAdd(sourceInput, merged) {
|
|
|
2754
2838
|
}
|
|
2755
2839
|
}
|
|
2756
2840
|
function configureAddCommand(command, action) {
|
|
2757
|
-
return command.description("Install skills from local path or git source").argument("<source>", "Source path or URL").option("-a, --agent <agents...>", "Target agent(s) for installation").option("-s, --skill <skills...>", "Install only selected skill(s)").option("-l, --list", "List skills from source without installing").option("--symlink", "Install by symlinking files to all agents").option(
|
|
2758
|
-
"--yaml",
|
|
2759
|
-
"Create skill-installer.yaml when scaffolding missing installer config"
|
|
2760
|
-
).option("-g, --global", "Install globally").option("--trust-well-known", "Allow hook commands for well-known source").option("--allow-host <hosts...>", "Restrict well-known hosts to allowlist").option("--deny-host <hosts...>", "Block specific well-known hosts").option("--max-download-bytes <n>", "Set well-known download budget").option("--policy-mode <mode>", "Policy mode (enforce|warn)").option("--lock-format <format>", "Lockfile format output (json|yaml)").option(
|
|
2761
|
-
"--non-interactive",
|
|
2762
|
-
"Disable prompts and require explicit selection"
|
|
2763
|
-
).option("--all", "Install all skills and known agents").action(action);
|
|
2841
|
+
return command.description("Install skills from local path or git source").argument("<source>", "Source path or URL").option("-a, --agent <agents...>", "Target agent(s) for installation").option("-s, --skill <skills...>", "Install only selected skill(s)").option("-l, --list", "List skills from source without installing").option("--symlink", "Install by symlinking files to all agents").option("--yaml", "Create skill-installer.yaml when scaffolding missing installer config").option("-g, --global", "Install globally").option("--trust-well-known", "Allow hook commands for well-known source").option("--allow-host <hosts...>", "Restrict well-known hosts to allowlist").option("--deny-host <hosts...>", "Block specific well-known hosts").option("--max-download-bytes <n>", "Set well-known download budget").option("--policy-mode <mode>", "Policy mode (enforce|warn)").option("--lock-format <format>", "Lockfile format output (json|yaml)").option("--non-interactive", "Disable prompts and require explicit selection").option("--all", "Install all skills and known agents").action(action);
|
|
2764
2842
|
}
|
|
2765
2843
|
function registerAddCommand(program, ctx) {
|
|
2766
2844
|
configureAddCommand(
|
|
@@ -2786,7 +2864,7 @@ function registerAddCommand(program, ctx) {
|
|
|
2786
2864
|
import { Command as Command3 } from "commander";
|
|
2787
2865
|
|
|
2788
2866
|
// ../../packages/core/src/sources/git.ts
|
|
2789
|
-
import
|
|
2867
|
+
import fs6 from "node:fs";
|
|
2790
2868
|
import os3 from "node:os";
|
|
2791
2869
|
import path8 from "node:path";
|
|
2792
2870
|
import { spawn as spawn2, spawnSync } from "node:child_process";
|
|
@@ -2812,12 +2890,12 @@ function applyCheckoutRefSync(repoDir, ref) {
|
|
|
2812
2890
|
}
|
|
2813
2891
|
function prepareSourceDir(parsed) {
|
|
2814
2892
|
if (parsed.type === "local") {
|
|
2815
|
-
if (!
|
|
2893
|
+
if (!fs6.existsSync(parsed.localPath)) {
|
|
2816
2894
|
throw new Error(`Local source not found: ${parsed.localPath}`);
|
|
2817
2895
|
}
|
|
2818
2896
|
return { basePath: parsed.localPath };
|
|
2819
2897
|
}
|
|
2820
|
-
const tmp =
|
|
2898
|
+
const tmp = fs6.mkdtempSync(path8.join(os3.tmpdir(), "skillspp-cli-"));
|
|
2821
2899
|
runGit(["clone", "--depth", "1", parsed.repoUrl, tmp]);
|
|
2822
2900
|
const ref = parsed.type === "github" ? parsed.ref : void 0;
|
|
2823
2901
|
applyCheckoutRefSync(tmp, ref);
|
|
@@ -2825,7 +2903,7 @@ function prepareSourceDir(parsed) {
|
|
|
2825
2903
|
return {
|
|
2826
2904
|
basePath,
|
|
2827
2905
|
cleanup: () => {
|
|
2828
|
-
|
|
2906
|
+
fs6.rmSync(tmp, { recursive: true, force: true });
|
|
2829
2907
|
}
|
|
2830
2908
|
};
|
|
2831
2909
|
}
|
|
@@ -2872,11 +2950,54 @@ var DEFAULT_OPTIONS = {
|
|
|
2872
2950
|
maxFilesPerSkill: 128,
|
|
2873
2951
|
maxSkillFileBytes: 512 * 1024
|
|
2874
2952
|
};
|
|
2875
|
-
var EXCLUDED_HOSTS = /* @__PURE__ */ new Set([
|
|
2876
|
-
|
|
2877
|
-
"
|
|
2878
|
-
"
|
|
2879
|
-
|
|
2953
|
+
var EXCLUDED_HOSTS = /* @__PURE__ */ new Set(["github.com", "gitlab.com", "raw.githubusercontent.com"]);
|
|
2954
|
+
var SKILL_CONFIG = {
|
|
2955
|
+
kind: "skills",
|
|
2956
|
+
displayLabel: "well-known skills",
|
|
2957
|
+
indexPath: "/.well-known/skills/index.json",
|
|
2958
|
+
entryLabel: "skill",
|
|
2959
|
+
requireDescription: true,
|
|
2960
|
+
missingManifestMessage: (name) => `Well-known skill '${name}' is missing SKILL.md`,
|
|
2961
|
+
validateName(name) {
|
|
2962
|
+
if (!/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/.test(name)) {
|
|
2963
|
+
throw new Error(`Invalid well-known skill name: ${name}`);
|
|
2964
|
+
}
|
|
2965
|
+
},
|
|
2966
|
+
hasRequiredManifest(filePath) {
|
|
2967
|
+
return filePath.toLowerCase() === "skill.md";
|
|
2968
|
+
},
|
|
2969
|
+
buildRemoteResult({ entry, files, sourceUrl }) {
|
|
2970
|
+
return {
|
|
2971
|
+
name: entry.name,
|
|
2972
|
+
description: entry.description || "",
|
|
2973
|
+
installName: entry.name,
|
|
2974
|
+
sourceUrl,
|
|
2975
|
+
sourceType: "well-known",
|
|
2976
|
+
files
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
};
|
|
2980
|
+
var PLUGIN_CONFIG = {
|
|
2981
|
+
kind: "plugins",
|
|
2982
|
+
displayLabel: "well-known plugins",
|
|
2983
|
+
indexPath: "/.well-known/plugins/index.json",
|
|
2984
|
+
entryLabel: "plugin",
|
|
2985
|
+
requireDescription: false,
|
|
2986
|
+
missingManifestMessage: (name) => `Well-known plugin '${name}' is missing plugin.json`,
|
|
2987
|
+
hasRequiredManifest(filePath) {
|
|
2988
|
+
return filePath.split("/").at(-1)?.toLowerCase() === "plugin.json";
|
|
2989
|
+
},
|
|
2990
|
+
buildRemoteResult({ entry, files, sourceUrl }) {
|
|
2991
|
+
return {
|
|
2992
|
+
name: entry.name,
|
|
2993
|
+
description: entry.description || "",
|
|
2994
|
+
installName: entry.name,
|
|
2995
|
+
sourceUrl,
|
|
2996
|
+
sourceType: "well-known",
|
|
2997
|
+
files
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
};
|
|
2880
3001
|
var SecureWellKnownProvider = class {
|
|
2881
3002
|
id = "well-known";
|
|
2882
3003
|
displayName = "Secure Well-Known Skills";
|
|
@@ -2896,10 +3017,16 @@ var SecureWellKnownProvider = class {
|
|
|
2896
3017
|
}
|
|
2897
3018
|
getSourceIdentifier(url) {
|
|
2898
3019
|
const parsed = new URL(url);
|
|
2899
|
-
const
|
|
2900
|
-
return
|
|
3020
|
+
const pathname = parsed.pathname.replace(/\/$/, "");
|
|
3021
|
+
return pathname && pathname !== "/" ? `wellknown/${parsed.hostname}${pathname}` : `wellknown/${parsed.hostname}`;
|
|
2901
3022
|
}
|
|
2902
3023
|
async fetchAllSkills(url, options = {}) {
|
|
3024
|
+
return this.fetchAllResources(url, options, SKILL_CONFIG);
|
|
3025
|
+
}
|
|
3026
|
+
async fetchAllPlugins(url, options = {}) {
|
|
3027
|
+
return this.fetchAllResources(url, options, PLUGIN_CONFIG);
|
|
3028
|
+
}
|
|
3029
|
+
async fetchAllResources(url, options, config) {
|
|
2903
3030
|
const normalized = this.normalizeOptions(options);
|
|
2904
3031
|
const budget = { remaining: normalized.maxDownloadBytes };
|
|
2905
3032
|
const parsed = new URL(url);
|
|
@@ -2907,29 +3034,19 @@ var SecureWellKnownProvider = class {
|
|
|
2907
3034
|
throw new Error("Well-known provider requires HTTPS URLs");
|
|
2908
3035
|
}
|
|
2909
3036
|
await this.assertHostAllowed(parsed.hostname, normalized);
|
|
2910
|
-
const { index, resolvedBase } = await this.fetchIndex(
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
const skills = [];
|
|
2916
|
-
for (const entry of index.skills) {
|
|
2917
|
-
const skill = await this.fetchSkillByEntry(
|
|
2918
|
-
resolvedBase,
|
|
2919
|
-
entry,
|
|
2920
|
-
normalized,
|
|
2921
|
-
budget
|
|
3037
|
+
const { index, resolvedBase } = await this.fetchIndex(parsed, normalized, budget, config);
|
|
3038
|
+
const resources = [];
|
|
3039
|
+
for (const entry of index) {
|
|
3040
|
+
resources.push(
|
|
3041
|
+
await this.fetchResourceByEntry(resolvedBase, entry, normalized, budget, config)
|
|
2922
3042
|
);
|
|
2923
|
-
if (skill) {
|
|
2924
|
-
skills.push(skill);
|
|
2925
|
-
}
|
|
2926
3043
|
}
|
|
2927
|
-
return
|
|
3044
|
+
return resources;
|
|
2928
3045
|
}
|
|
2929
3046
|
normalizeOptions(options) {
|
|
2930
3047
|
return {
|
|
2931
|
-
allowHosts: (options.allowHosts || []).map((
|
|
2932
|
-
denyHosts: (options.denyHosts || []).map((
|
|
3048
|
+
allowHosts: (options.allowHosts || []).map((value) => value.trim().toLowerCase()).filter(Boolean),
|
|
3049
|
+
denyHosts: (options.denyHosts || []).map((value) => value.trim().toLowerCase()).filter(Boolean),
|
|
2933
3050
|
maxDownloadBytes: options.maxDownloadBytes ?? DEFAULT_OPTIONS.maxDownloadBytes,
|
|
2934
3051
|
timeoutMs: options.timeoutMs ?? DEFAULT_OPTIONS.timeoutMs,
|
|
2935
3052
|
maxRedirects: options.maxRedirects ?? DEFAULT_OPTIONS.maxRedirects,
|
|
@@ -2937,10 +3054,10 @@ var SecureWellKnownProvider = class {
|
|
|
2937
3054
|
maxSkillFileBytes: options.maxSkillFileBytes ?? DEFAULT_OPTIONS.maxSkillFileBytes
|
|
2938
3055
|
};
|
|
2939
3056
|
}
|
|
2940
|
-
async fetchIndex(parsedUrl, options, budget) {
|
|
2941
|
-
const candidates = this.buildBaseCandidates(parsedUrl);
|
|
3057
|
+
async fetchIndex(parsedUrl, options, budget, config) {
|
|
3058
|
+
const candidates = this.buildBaseCandidates(parsedUrl, config.indexPath);
|
|
2942
3059
|
for (const base of candidates) {
|
|
2943
|
-
const indexUrl = `${base}
|
|
3060
|
+
const indexUrl = `${base}${config.indexPath}`;
|
|
2944
3061
|
try {
|
|
2945
3062
|
const jsonText = await this.fetchTextWithLimit(
|
|
2946
3063
|
indexUrl,
|
|
@@ -2949,20 +3066,18 @@ var SecureWellKnownProvider = class {
|
|
|
2949
3066
|
budget
|
|
2950
3067
|
);
|
|
2951
3068
|
const parsed = JSON.parse(jsonText);
|
|
2952
|
-
const validated = this.validateIndex(parsed, options.maxFilesPerSkill);
|
|
3069
|
+
const validated = this.validateIndex(parsed, options.maxFilesPerSkill, config);
|
|
2953
3070
|
return { index: validated, resolvedBase: base };
|
|
2954
3071
|
} catch {
|
|
2955
3072
|
continue;
|
|
2956
3073
|
}
|
|
2957
3074
|
}
|
|
2958
|
-
throw new Error(
|
|
2959
|
-
"No valid well-known skills index found at /.well-known/skills/index.json"
|
|
2960
|
-
);
|
|
3075
|
+
throw new Error(`No valid ${config.displayLabel} index found at ${config.indexPath}`);
|
|
2961
3076
|
}
|
|
2962
|
-
buildBaseCandidates(parsed) {
|
|
3077
|
+
buildBaseCandidates(parsed, indexPath) {
|
|
2963
3078
|
const origin = parsed.origin;
|
|
2964
3079
|
const pathname = parsed.pathname.replace(/\/$/, "");
|
|
2965
|
-
const marker = "
|
|
3080
|
+
const marker = indexPath.replace(/\/index\.json$/, "");
|
|
2966
3081
|
const out = [];
|
|
2967
3082
|
if (pathname.includes(marker)) {
|
|
2968
3083
|
const prefix = pathname.slice(0, pathname.indexOf(marker));
|
|
@@ -2977,53 +3092,52 @@ var SecureWellKnownProvider = class {
|
|
|
2977
3092
|
}
|
|
2978
3093
|
}
|
|
2979
3094
|
return [
|
|
2980
|
-
...new Set(out.map((
|
|
3095
|
+
...new Set(out.map((value) => value.endsWith("/") ? value.slice(0, -1) : value))
|
|
2981
3096
|
].filter(Boolean);
|
|
2982
3097
|
}
|
|
2983
|
-
validateIndex(raw, maxFilesPerSkill) {
|
|
3098
|
+
validateIndex(raw, maxFilesPerSkill, config) {
|
|
2984
3099
|
if (!raw || typeof raw !== "object") {
|
|
2985
3100
|
throw new Error("Invalid well-known index: expected object");
|
|
2986
3101
|
}
|
|
2987
3102
|
const data = raw;
|
|
2988
|
-
|
|
2989
|
-
|
|
3103
|
+
const rows = data[config.kind];
|
|
3104
|
+
if (!Array.isArray(rows)) {
|
|
3105
|
+
throw new Error(`Invalid well-known index: '${config.kind}' must be an array`);
|
|
2990
3106
|
}
|
|
2991
|
-
|
|
3107
|
+
return rows.map((item, idx) => {
|
|
2992
3108
|
if (!item || typeof item !== "object") {
|
|
2993
3109
|
throw new Error(`Invalid well-known index entry[${idx}]`);
|
|
2994
3110
|
}
|
|
2995
3111
|
const row = item;
|
|
2996
3112
|
const name = String(row.name || "").trim();
|
|
2997
|
-
const description =
|
|
2998
|
-
const files = Array.isArray(row.files) ? row.files.map((
|
|
2999
|
-
if (!name ||
|
|
3000
|
-
throw new Error(
|
|
3001
|
-
`Invalid well-known index entry[${idx}]: missing required fields`
|
|
3002
|
-
);
|
|
3113
|
+
const description = typeof row.description === "string" ? row.description.trim() : void 0;
|
|
3114
|
+
const files = Array.isArray(row.files) ? row.files.map((value) => String(value)) : [];
|
|
3115
|
+
if (!name || files.length === 0) {
|
|
3116
|
+
throw new Error(`Invalid well-known index entry[${idx}]: missing required fields`);
|
|
3003
3117
|
}
|
|
3004
|
-
if (
|
|
3005
|
-
throw new Error(`Invalid well-known
|
|
3118
|
+
if (config.requireDescription && !description) {
|
|
3119
|
+
throw new Error(`Invalid well-known index entry[${idx}]: missing required fields`);
|
|
3006
3120
|
}
|
|
3121
|
+
config.validateName?.(name);
|
|
3007
3122
|
if (files.length > maxFilesPerSkill) {
|
|
3008
|
-
throw new Error(`Too many files in well-known
|
|
3123
|
+
throw new Error(`Too many files in well-known ${config.entryLabel} '${name}'`);
|
|
3009
3124
|
}
|
|
3010
|
-
if (!files.some((
|
|
3011
|
-
throw new Error(
|
|
3125
|
+
if (!files.some((filePath) => config.hasRequiredManifest(filePath))) {
|
|
3126
|
+
throw new Error(config.missingManifestMessage(name));
|
|
3012
3127
|
}
|
|
3013
3128
|
for (const file of files) {
|
|
3014
3129
|
this.assertSafeRelativePath(file);
|
|
3015
3130
|
}
|
|
3016
3131
|
return { name, description, files };
|
|
3017
3132
|
});
|
|
3018
|
-
return { skills };
|
|
3019
3133
|
}
|
|
3020
3134
|
assertSafeRelativePath(filePath) {
|
|
3021
3135
|
if (!filePath || filePath.startsWith("/") || filePath.startsWith("\\") || filePath.includes("..") || filePath.includes("\\")) {
|
|
3022
3136
|
throw new Error(`Unsafe well-known file path: ${filePath}`);
|
|
3023
3137
|
}
|
|
3024
3138
|
}
|
|
3025
|
-
async
|
|
3026
|
-
const baseUrl = `${resolvedBase}/.well-known
|
|
3139
|
+
async fetchResourceByEntry(resolvedBase, entry, options, budget, config) {
|
|
3140
|
+
const baseUrl = `${resolvedBase}/.well-known/${config.kind}/${entry.name}`;
|
|
3027
3141
|
const files = /* @__PURE__ */ new Map();
|
|
3028
3142
|
for (const filePath of entry.files) {
|
|
3029
3143
|
this.assertSafeRelativePath(filePath);
|
|
@@ -3035,24 +3149,31 @@ var SecureWellKnownProvider = class {
|
|
|
3035
3149
|
budget
|
|
3036
3150
|
);
|
|
3037
3151
|
if (text.includes("\0")) {
|
|
3038
|
-
throw new Error(
|
|
3039
|
-
`Binary content is not allowed in well-known file: ${filePath}`
|
|
3040
|
-
);
|
|
3152
|
+
throw new Error(`Binary content is not allowed in well-known file: ${filePath}`);
|
|
3041
3153
|
}
|
|
3042
3154
|
files.set(filePath, text);
|
|
3043
3155
|
}
|
|
3044
|
-
const
|
|
3045
|
-
|
|
3046
|
-
|
|
3156
|
+
const manifestPath = this.pickPrimaryManifestPath(entry.files, config);
|
|
3157
|
+
return config.buildRemoteResult({
|
|
3158
|
+
entry,
|
|
3159
|
+
files,
|
|
3160
|
+
sourceUrl: `${baseUrl}/${manifestPath}`
|
|
3161
|
+
});
|
|
3162
|
+
}
|
|
3163
|
+
pickPrimaryManifestPath(filePaths, config) {
|
|
3164
|
+
const manifests = filePaths.filter((filePath) => config.hasRequiredManifest(filePath)).sort((left, right) => {
|
|
3165
|
+
const leftDepth = left.split("/").length;
|
|
3166
|
+
const rightDepth = right.split("/").length;
|
|
3167
|
+
if (leftDepth !== rightDepth) {
|
|
3168
|
+
return leftDepth - rightDepth;
|
|
3169
|
+
}
|
|
3170
|
+
return left.localeCompare(right);
|
|
3171
|
+
});
|
|
3172
|
+
const manifestPath = manifests[0];
|
|
3173
|
+
if (!manifestPath) {
|
|
3174
|
+
throw new Error("Missing required manifest in well-known index entry");
|
|
3047
3175
|
}
|
|
3048
|
-
return
|
|
3049
|
-
name: entry.name,
|
|
3050
|
-
description: entry.description,
|
|
3051
|
-
installName: entry.name,
|
|
3052
|
-
sourceUrl: `${baseUrl}/SKILL.md`,
|
|
3053
|
-
sourceType: "well-known",
|
|
3054
|
-
files
|
|
3055
|
-
};
|
|
3176
|
+
return manifestPath;
|
|
3056
3177
|
}
|
|
3057
3178
|
async fetchTextWithLimit(url, maxPerRequestBytes, options, budget) {
|
|
3058
3179
|
let currentUrl = url;
|
|
@@ -3087,111 +3208,61 @@ var SecureWellKnownProvider = class {
|
|
|
3087
3208
|
continue;
|
|
3088
3209
|
}
|
|
3089
3210
|
if (!response.ok) {
|
|
3090
|
-
throw new Error(
|
|
3091
|
-
`Fetch failed (${response.status} ${response.statusText}) for ${currentUrl}`
|
|
3092
|
-
);
|
|
3093
|
-
}
|
|
3094
|
-
const contentLengthHeader = response.headers.get("content-length");
|
|
3095
|
-
if (contentLengthHeader) {
|
|
3096
|
-
const declared = Number(contentLengthHeader);
|
|
3097
|
-
if (Number.isFinite(declared) && declared > maxPerRequestBytes) {
|
|
3098
|
-
throw new Error(
|
|
3099
|
-
`Response exceeds per-file size limit for ${currentUrl}`
|
|
3100
|
-
);
|
|
3101
|
-
}
|
|
3102
|
-
if (Number.isFinite(declared) && declared > budget.remaining) {
|
|
3103
|
-
throw new Error(
|
|
3104
|
-
`Response exceeds remaining download budget for ${currentUrl}`
|
|
3105
|
-
);
|
|
3106
|
-
}
|
|
3107
|
-
}
|
|
3108
|
-
const reader = response.body?.getReader();
|
|
3109
|
-
if (!reader) {
|
|
3110
|
-
return "";
|
|
3211
|
+
throw new Error(`Failed to fetch ${currentUrl}: ${response.status} ${response.statusText}`);
|
|
3111
3212
|
}
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
const result = await reader.read();
|
|
3116
|
-
if (result.done) {
|
|
3117
|
-
break;
|
|
3118
|
-
}
|
|
3119
|
-
const chunk = result.value;
|
|
3120
|
-
received += chunk.byteLength;
|
|
3121
|
-
if (received > maxPerRequestBytes) {
|
|
3122
|
-
throw new Error(
|
|
3123
|
-
`Response exceeded per-file size limit for ${currentUrl}`
|
|
3124
|
-
);
|
|
3125
|
-
}
|
|
3126
|
-
if (received > budget.remaining) {
|
|
3127
|
-
throw new Error(
|
|
3128
|
-
`Response exceeded remaining download budget for ${currentUrl}`
|
|
3129
|
-
);
|
|
3130
|
-
}
|
|
3131
|
-
chunks.push(chunk);
|
|
3213
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
3214
|
+
if (bytes.byteLength > maxPerRequestBytes) {
|
|
3215
|
+
throw new Error(`Exceeded per-file download limit for ${currentUrl}`);
|
|
3132
3216
|
}
|
|
3133
|
-
budget.remaining -=
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
for (const chunk of chunks) {
|
|
3137
|
-
total.set(chunk, offset);
|
|
3138
|
-
offset += chunk.byteLength;
|
|
3217
|
+
budget.remaining -= bytes.byteLength;
|
|
3218
|
+
if (budget.remaining < 0) {
|
|
3219
|
+
throw new Error(`Exceeded total download budget while fetching ${url}`);
|
|
3139
3220
|
}
|
|
3140
|
-
return new TextDecoder("
|
|
3221
|
+
return new TextDecoder("utf8").decode(bytes);
|
|
3141
3222
|
}
|
|
3142
3223
|
}
|
|
3143
3224
|
async assertHostAllowed(hostname, options) {
|
|
3144
|
-
const
|
|
3145
|
-
if (options.denyHosts.includes(
|
|
3146
|
-
throw new Error(`
|
|
3225
|
+
const lowerHost = hostname.toLowerCase();
|
|
3226
|
+
if (options.denyHosts.includes(lowerHost)) {
|
|
3227
|
+
throw new Error(`Host '${hostname}' is explicitly denied`);
|
|
3147
3228
|
}
|
|
3148
|
-
if (options.allowHosts.length > 0 && !options.allowHosts.includes(
|
|
3149
|
-
throw new Error(`
|
|
3229
|
+
if (options.allowHosts.length > 0 && !options.allowHosts.includes(lowerHost)) {
|
|
3230
|
+
throw new Error(`Host '${hostname}' is not in allowed host list`);
|
|
3150
3231
|
}
|
|
3151
|
-
if (
|
|
3152
|
-
throw new Error(`
|
|
3232
|
+
if (isLocalHostname(lowerHost)) {
|
|
3233
|
+
throw new Error(`Local or loopback host '${hostname}' is not allowed`);
|
|
3153
3234
|
}
|
|
3154
|
-
const
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
throw new Error(
|
|
3158
|
-
`Well-known host resolves to private/local address: ${hostname}`
|
|
3159
|
-
);
|
|
3160
|
-
}
|
|
3235
|
+
const ips = await resolveHostIps(hostname);
|
|
3236
|
+
if (ips.some((ip) => isPrivateOrLocalIp(ip))) {
|
|
3237
|
+
throw new Error(`Host '${hostname}' resolves to a private/local address`);
|
|
3161
3238
|
}
|
|
3162
3239
|
}
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
} catch {
|
|
3240
|
+
};
|
|
3241
|
+
function isLocalHostname(hostname) {
|
|
3242
|
+
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname.endsWith(".local");
|
|
3243
|
+
}
|
|
3244
|
+
async function resolveHostIps(hostname) {
|
|
3245
|
+
const out = /* @__PURE__ */ new Set();
|
|
3246
|
+
try {
|
|
3247
|
+
const entries = await dns.lookup(hostname, { all: true });
|
|
3248
|
+
for (const entry of entries) {
|
|
3249
|
+
out.add(entry.address);
|
|
3174
3250
|
}
|
|
3175
|
-
|
|
3251
|
+
} catch {
|
|
3176
3252
|
}
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
const [a, b] = parts;
|
|
3184
|
-
if (a === 10 || a === 127 || a === 0) return true;
|
|
3185
|
-
if (a === 169 && b === 254) return true;
|
|
3186
|
-
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
3187
|
-
if (a === 192 && b === 168) return true;
|
|
3188
|
-
if (a >= 224) return true;
|
|
3189
|
-
return false;
|
|
3190
|
-
}
|
|
3191
|
-
const value = ip.toLowerCase();
|
|
3192
|
-
return value === "::1" || value === "::" || value.startsWith("fc") || value.startsWith("fd") || value.startsWith("fe80:");
|
|
3253
|
+
return [...out];
|
|
3254
|
+
}
|
|
3255
|
+
function isPrivateOrLocalIp(ip) {
|
|
3256
|
+
const family = net.isIP(ip);
|
|
3257
|
+
if (family === 4) {
|
|
3258
|
+
return ip.startsWith("10.") || ip.startsWith("127.") || ip.startsWith("169.254.") || ip.startsWith("192.168.") || /^172\.(1[6-9]|2\d|3[0-1])\./.test(ip);
|
|
3193
3259
|
}
|
|
3194
|
-
|
|
3260
|
+
if (family === 6) {
|
|
3261
|
+
const normalized = ip.toLowerCase();
|
|
3262
|
+
return normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe80:");
|
|
3263
|
+
}
|
|
3264
|
+
return false;
|
|
3265
|
+
}
|
|
3195
3266
|
var wellKnownProvider = new SecureWellKnownProvider();
|
|
3196
3267
|
|
|
3197
3268
|
// ../../packages/core/src/providers/catalog.ts
|
|
@@ -3220,6 +3291,60 @@ var HttpCatalogProvider = class {
|
|
|
3220
3291
|
return `catalog/${parsed.host}${parsed.pathname.replace(/\/+$/, "")}`;
|
|
3221
3292
|
}
|
|
3222
3293
|
async fetchAllSkills(url, options = {}) {
|
|
3294
|
+
return this.fetchAllResources(url, options, {
|
|
3295
|
+
kind: "skills",
|
|
3296
|
+
indexLabel: "catalog skills",
|
|
3297
|
+
resolveIndexUrl(parsed) {
|
|
3298
|
+
return parsed.pathname.endsWith(".json") ? parsed.toString() : new URL(
|
|
3299
|
+
"index.json",
|
|
3300
|
+
parsed.toString().endsWith("/") ? parsed.toString() : `${parsed.toString()}/`
|
|
3301
|
+
).toString();
|
|
3302
|
+
},
|
|
3303
|
+
requireDescription: true,
|
|
3304
|
+
missingManifestMessage: (name) => `Catalog skill '${name}' is missing SKILL.md`,
|
|
3305
|
+
hasRequiredManifest(filePath) {
|
|
3306
|
+
return filePath.toLowerCase() === "skill.md";
|
|
3307
|
+
},
|
|
3308
|
+
buildRemoteResult({ entry, files, sourceUrl }) {
|
|
3309
|
+
return {
|
|
3310
|
+
name: entry.name,
|
|
3311
|
+
description: entry.description || "",
|
|
3312
|
+
installName: entry.name,
|
|
3313
|
+
sourceUrl,
|
|
3314
|
+
sourceType: "catalog",
|
|
3315
|
+
files
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
});
|
|
3319
|
+
}
|
|
3320
|
+
async fetchAllPlugins(url, options = {}) {
|
|
3321
|
+
return this.fetchAllResources(url, options, {
|
|
3322
|
+
kind: "plugins",
|
|
3323
|
+
indexLabel: "catalog plugins",
|
|
3324
|
+
resolveIndexUrl(parsed) {
|
|
3325
|
+
return parsed.pathname.endsWith(".json") ? parsed.toString() : new URL(
|
|
3326
|
+
"plugins/index.json",
|
|
3327
|
+
parsed.toString().endsWith("/") ? parsed.toString() : `${parsed.toString()}/`
|
|
3328
|
+
).toString();
|
|
3329
|
+
},
|
|
3330
|
+
requireDescription: false,
|
|
3331
|
+
missingManifestMessage: (name) => `Catalog plugin '${name}' is missing plugin.json`,
|
|
3332
|
+
hasRequiredManifest(filePath) {
|
|
3333
|
+
return filePath.split("/").at(-1)?.toLowerCase() === "plugin.json";
|
|
3334
|
+
},
|
|
3335
|
+
buildRemoteResult({ entry, files, sourceUrl }) {
|
|
3336
|
+
return {
|
|
3337
|
+
name: entry.name,
|
|
3338
|
+
description: entry.description || "",
|
|
3339
|
+
installName: entry.name,
|
|
3340
|
+
sourceUrl,
|
|
3341
|
+
sourceType: "catalog",
|
|
3342
|
+
files
|
|
3343
|
+
};
|
|
3344
|
+
}
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
async fetchAllResources(url, options, config) {
|
|
3223
3348
|
const parsed = new URL(url);
|
|
3224
3349
|
if (parsed.protocol !== "https:") {
|
|
3225
3350
|
throw new Error("Catalog provider requires HTTPS URLs");
|
|
@@ -3228,23 +3353,17 @@ var HttpCatalogProvider = class {
|
|
|
3228
3353
|
const timeoutMs = options.timeoutMs ?? DEFAULT_OPTIONS2.timeoutMs;
|
|
3229
3354
|
const maxFilesPerSkill = options.maxFilesPerSkill ?? DEFAULT_OPTIONS2.maxFilesPerSkill;
|
|
3230
3355
|
const maxSkillFileBytes = options.maxSkillFileBytes ?? DEFAULT_OPTIONS2.maxSkillFileBytes;
|
|
3231
|
-
const indexUrl =
|
|
3232
|
-
"index.json",
|
|
3233
|
-
parsed.toString().endsWith("/") ? parsed.toString() : `${parsed.toString()}/`
|
|
3234
|
-
).toString();
|
|
3356
|
+
const indexUrl = config.resolveIndexUrl(parsed);
|
|
3235
3357
|
const indexText = await this.fetchTextWithLimit(
|
|
3236
3358
|
indexUrl,
|
|
3237
3359
|
Math.min(maxDownloadBytes, maxSkillFileBytes),
|
|
3238
3360
|
timeoutMs
|
|
3239
3361
|
);
|
|
3240
|
-
const index = this.validateIndex(
|
|
3241
|
-
JSON.parse(indexText),
|
|
3242
|
-
maxFilesPerSkill
|
|
3243
|
-
);
|
|
3362
|
+
const index = this.validateIndex(JSON.parse(indexText), maxFilesPerSkill, config);
|
|
3244
3363
|
const out = [];
|
|
3245
3364
|
let remaining = maxDownloadBytes - indexText.length;
|
|
3246
3365
|
const indexBase = indexUrl.slice(0, indexUrl.lastIndexOf("/") + 1);
|
|
3247
|
-
for (const row of index
|
|
3366
|
+
for (const row of index) {
|
|
3248
3367
|
const files = /* @__PURE__ */ new Map();
|
|
3249
3368
|
for (const rel of row.files) {
|
|
3250
3369
|
this.assertSafeRelativePath(rel);
|
|
@@ -3260,14 +3379,16 @@ var HttpCatalogProvider = class {
|
|
|
3260
3379
|
remaining -= text.length;
|
|
3261
3380
|
files.set(rel, text);
|
|
3262
3381
|
}
|
|
3263
|
-
out.push(
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3382
|
+
out.push(
|
|
3383
|
+
config.buildRemoteResult({
|
|
3384
|
+
entry: row,
|
|
3385
|
+
files,
|
|
3386
|
+
sourceUrl: new URL(
|
|
3387
|
+
`${row.name}/${this.pickPrimaryManifestPath(row.files, config)}`,
|
|
3388
|
+
indexBase
|
|
3389
|
+
).toString()
|
|
3390
|
+
})
|
|
3391
|
+
);
|
|
3271
3392
|
}
|
|
3272
3393
|
return out;
|
|
3273
3394
|
}
|
|
@@ -3290,39 +3411,55 @@ var HttpCatalogProvider = class {
|
|
|
3290
3411
|
clearTimeout(timeout);
|
|
3291
3412
|
}
|
|
3292
3413
|
}
|
|
3293
|
-
validateIndex(raw, maxFilesPerSkill) {
|
|
3414
|
+
validateIndex(raw, maxFilesPerSkill, config) {
|
|
3294
3415
|
if (!raw || typeof raw !== "object") {
|
|
3295
3416
|
throw new Error("Invalid catalog index: expected object");
|
|
3296
3417
|
}
|
|
3297
3418
|
const data = raw;
|
|
3298
|
-
|
|
3299
|
-
|
|
3419
|
+
const rows = data[config.kind];
|
|
3420
|
+
if (!Array.isArray(rows)) {
|
|
3421
|
+
throw new Error(`Invalid catalog index: '${config.kind}' must be an array`);
|
|
3300
3422
|
}
|
|
3301
|
-
|
|
3423
|
+
return rows.map((item, idx) => {
|
|
3302
3424
|
if (!item || typeof item !== "object") {
|
|
3303
3425
|
throw new Error(`Invalid catalog index entry[${idx}]`);
|
|
3304
3426
|
}
|
|
3305
3427
|
const row = item;
|
|
3306
3428
|
const name = String(row.name || "").trim();
|
|
3307
|
-
const description =
|
|
3429
|
+
const description = typeof row.description === "string" ? row.description.trim() : void 0;
|
|
3308
3430
|
const files = Array.isArray(row.files) ? row.files.map((x) => String(x)) : [];
|
|
3309
|
-
if (!name ||
|
|
3310
|
-
throw new Error(
|
|
3311
|
-
|
|
3312
|
-
|
|
3431
|
+
if (!name || files.length === 0) {
|
|
3432
|
+
throw new Error(`Invalid catalog index entry[${idx}]: missing required fields`);
|
|
3433
|
+
}
|
|
3434
|
+
if (config.requireDescription && !description) {
|
|
3435
|
+
throw new Error(`Invalid catalog index entry[${idx}]: missing required fields`);
|
|
3313
3436
|
}
|
|
3314
3437
|
if (files.length > maxFilesPerSkill) {
|
|
3315
|
-
throw new Error(`Too many files in catalog
|
|
3438
|
+
throw new Error(`Too many files in catalog ${config.kind.slice(0, -1)} '${name}'`);
|
|
3316
3439
|
}
|
|
3317
|
-
if (!files.some((
|
|
3318
|
-
throw new Error(
|
|
3440
|
+
if (!files.some((filePath) => config.hasRequiredManifest(filePath))) {
|
|
3441
|
+
throw new Error(config.missingManifestMessage(name));
|
|
3319
3442
|
}
|
|
3320
3443
|
for (const file of files) {
|
|
3321
3444
|
this.assertSafeRelativePath(file);
|
|
3322
3445
|
}
|
|
3323
3446
|
return { name, description, files };
|
|
3324
3447
|
});
|
|
3325
|
-
|
|
3448
|
+
}
|
|
3449
|
+
pickPrimaryManifestPath(filePaths, config) {
|
|
3450
|
+
const manifests = filePaths.filter((filePath) => config.hasRequiredManifest(filePath)).sort((left, right) => {
|
|
3451
|
+
const leftDepth = left.split("/").length;
|
|
3452
|
+
const rightDepth = right.split("/").length;
|
|
3453
|
+
if (leftDepth !== rightDepth) {
|
|
3454
|
+
return leftDepth - rightDepth;
|
|
3455
|
+
}
|
|
3456
|
+
return left.localeCompare(right);
|
|
3457
|
+
});
|
|
3458
|
+
const manifestPath = manifests[0];
|
|
3459
|
+
if (!manifestPath) {
|
|
3460
|
+
throw new Error("Catalog entry is missing required manifest");
|
|
3461
|
+
}
|
|
3462
|
+
return manifestPath;
|
|
3326
3463
|
}
|
|
3327
3464
|
assertSafeRelativePath(filePath) {
|
|
3328
3465
|
if (!filePath || filePath.startsWith("/") || filePath.startsWith("\\") || filePath.includes("..") || filePath.includes("\\")) {
|
|
@@ -3349,9 +3486,7 @@ function assertExperimentalFeatureEnabled(feature, enabled) {
|
|
|
3349
3486
|
return;
|
|
3350
3487
|
}
|
|
3351
3488
|
if (feature === "catalog") {
|
|
3352
|
-
throw new Error(
|
|
3353
|
-
"Catalog source is experimental and requires explicit experimental mode."
|
|
3354
|
-
);
|
|
3489
|
+
throw new Error("Catalog source is experimental and requires explicit experimental mode.");
|
|
3355
3490
|
}
|
|
3356
3491
|
}
|
|
3357
3492
|
|
|
@@ -3385,7 +3520,7 @@ async function resolveCatalogSkills(sourceUrl, options) {
|
|
|
3385
3520
|
}
|
|
3386
3521
|
|
|
3387
3522
|
// ../../packages/core/src/runtime/lockfile.ts
|
|
3388
|
-
import
|
|
3523
|
+
import fs7 from "node:fs";
|
|
3389
3524
|
import path9 from "node:path";
|
|
3390
3525
|
import YAML from "yaml";
|
|
3391
3526
|
function perSkillLockfilePath(canonicalDir, format = "json") {
|
|
@@ -3404,7 +3539,7 @@ function parseLockPayload(text, format) {
|
|
|
3404
3539
|
function readPerSkillLockfile(canonicalDir) {
|
|
3405
3540
|
const jsonPath = perSkillLockfilePath(canonicalDir, "json");
|
|
3406
3541
|
const yamlPath = perSkillLockfilePath(canonicalDir, "yaml");
|
|
3407
|
-
const raw =
|
|
3542
|
+
const raw = fs7.existsSync(jsonPath) ? parseLockPayload(fs7.readFileSync(jsonPath, "utf8"), "json") : fs7.existsSync(yamlPath) ? parseLockPayload(fs7.readFileSync(yamlPath, "utf8"), "yaml") : null;
|
|
3408
3543
|
if (!raw) {
|
|
3409
3544
|
return null;
|
|
3410
3545
|
}
|
|
@@ -3419,18 +3554,18 @@ function readPerSkillLockfile(canonicalDir) {
|
|
|
3419
3554
|
function isSkillDirEntry(entry) {
|
|
3420
3555
|
return entry.isDirectory() || entry.isSymbolicLink();
|
|
3421
3556
|
}
|
|
3422
|
-
function
|
|
3557
|
+
function listInstalledResourceDirs(kind, globalInstall, cwd) {
|
|
3423
3558
|
const out = /* @__PURE__ */ new Set();
|
|
3424
3559
|
for (const agent of Object.keys(AGENTS)) {
|
|
3425
|
-
const
|
|
3426
|
-
if (!
|
|
3560
|
+
const resourceRoot = kind === "plugin" ? getAgentPluginsDir(agent, globalInstall, cwd) : getAgentSkillsDir(agent, globalInstall, cwd);
|
|
3561
|
+
if (!fs7.existsSync(resourceRoot) || !fs7.statSync(resourceRoot).isDirectory()) {
|
|
3427
3562
|
continue;
|
|
3428
3563
|
}
|
|
3429
|
-
for (const entry of
|
|
3564
|
+
for (const entry of fs7.readdirSync(resourceRoot, { withFileTypes: true })) {
|
|
3430
3565
|
if (!isSkillDirEntry(entry)) {
|
|
3431
3566
|
continue;
|
|
3432
3567
|
}
|
|
3433
|
-
out.add(path9.join(
|
|
3568
|
+
out.add(path9.join(resourceRoot, entry.name));
|
|
3434
3569
|
}
|
|
3435
3570
|
}
|
|
3436
3571
|
return [...out];
|
|
@@ -3440,8 +3575,11 @@ function lockEntrySortTime(entry) {
|
|
|
3440
3575
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
3441
3576
|
}
|
|
3442
3577
|
function readLockfile(globalInstall, cwd) {
|
|
3578
|
+
return readResourceLockfile("skill", globalInstall, cwd);
|
|
3579
|
+
}
|
|
3580
|
+
function readResourceLockfile(kind, globalInstall, cwd) {
|
|
3443
3581
|
const entriesBySkill = /* @__PURE__ */ new Map();
|
|
3444
|
-
for (const skillDir of
|
|
3582
|
+
for (const skillDir of listInstalledResourceDirs(kind, globalInstall, cwd)) {
|
|
3445
3583
|
const entry = readPerSkillLockfile(skillDir);
|
|
3446
3584
|
if (!entry || typeof entry.skillName !== "string") {
|
|
3447
3585
|
continue;
|
|
@@ -3453,9 +3591,7 @@ function readLockfile(globalInstall, cwd) {
|
|
|
3453
3591
|
}
|
|
3454
3592
|
return {
|
|
3455
3593
|
version: 1,
|
|
3456
|
-
entries: [...entriesBySkill.values()].sort(
|
|
3457
|
-
(a, b) => a.skillName.localeCompare(b.skillName)
|
|
3458
|
-
)
|
|
3594
|
+
entries: [...entriesBySkill.values()].sort((a, b) => a.skillName.localeCompare(b.skillName))
|
|
3459
3595
|
};
|
|
3460
3596
|
}
|
|
3461
3597
|
|
|
@@ -3492,9 +3628,7 @@ function buildCheckDriftSummaryLines(options) {
|
|
|
3492
3628
|
function toCheckOptions(options) {
|
|
3493
3629
|
const maxDownloadBytes = options.maxDownloadBytes ? Number(options.maxDownloadBytes) : void 0;
|
|
3494
3630
|
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
3495
|
-
throw new Error(
|
|
3496
|
-
`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`
|
|
3497
|
-
);
|
|
3631
|
+
throw new Error(`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`);
|
|
3498
3632
|
}
|
|
3499
3633
|
return {
|
|
3500
3634
|
global: Boolean(options.global),
|
|
@@ -3599,9 +3733,7 @@ async function executeCheck(options) {
|
|
|
3599
3733
|
detailLines.push("");
|
|
3600
3734
|
}
|
|
3601
3735
|
detailLines.push(` ${kind}`);
|
|
3602
|
-
for (const row of [...rows].sort(
|
|
3603
|
-
(a, b) => a.skillName.localeCompare(b.skillName)
|
|
3604
|
-
)) {
|
|
3736
|
+
for (const row of [...rows].sort((a, b) => a.skillName.localeCompare(b.skillName))) {
|
|
3605
3737
|
detailLines.push(` ${row.skillName}: ${row.detail}`);
|
|
3606
3738
|
}
|
|
3607
3739
|
}
|
|
@@ -3619,9 +3751,7 @@ async function executeCheck(options) {
|
|
|
3619
3751
|
if (conflicts.length > 0) {
|
|
3620
3752
|
conflictLines.push("Local/global conflicts (local preferred):");
|
|
3621
3753
|
for (const conflict of conflicts) {
|
|
3622
|
-
conflictLines.push(
|
|
3623
|
-
` ${conflict.skillName}: winner=${conflict.winner}`
|
|
3624
|
-
);
|
|
3754
|
+
conflictLines.push(` ${conflict.skillName}: winner=${conflict.winner}`);
|
|
3625
3755
|
}
|
|
3626
3756
|
}
|
|
3627
3757
|
if (transitiveConflicts.length > 0) {
|
|
@@ -3651,9 +3781,7 @@ async function executeCheck(options) {
|
|
|
3651
3781
|
}
|
|
3652
3782
|
const updateSkillNames = [
|
|
3653
3783
|
...new Set(
|
|
3654
|
-
drift.filter(
|
|
3655
|
-
(item) => item.kind === "changed-source" || item.kind === "local-modified"
|
|
3656
|
-
).map((item) => item.skillName)
|
|
3784
|
+
drift.filter((item) => item.kind === "changed-source" || item.kind === "local-modified").map((item) => item.skillName)
|
|
3657
3785
|
)
|
|
3658
3786
|
].sort((a, b) => a.localeCompare(b));
|
|
3659
3787
|
const migrateSkillNames = [
|
|
@@ -3666,9 +3794,7 @@ async function executeCheck(options) {
|
|
|
3666
3794
|
if (migrateSkillNames.length > 0) {
|
|
3667
3795
|
lines.push("Migration required:");
|
|
3668
3796
|
for (const skillName of migrateSkillNames) {
|
|
3669
|
-
lines.push(
|
|
3670
|
-
`skillspp update ${skillName} --migrate <new-skill-source>`
|
|
3671
|
-
);
|
|
3797
|
+
lines.push(`skillspp update ${skillName} --migrate <new-skill-source>`);
|
|
3672
3798
|
}
|
|
3673
3799
|
}
|
|
3674
3800
|
if (updateSkillNames.length > 0) {
|
|
@@ -3717,9 +3843,7 @@ import { Command as Command4 } from "commander";
|
|
|
3717
3843
|
function toFindOptions(options) {
|
|
3718
3844
|
const maxDownloadBytes = options.maxDownloadBytes ? Number(options.maxDownloadBytes) : void 0;
|
|
3719
3845
|
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
3720
|
-
throw new Error(
|
|
3721
|
-
`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`
|
|
3722
|
-
);
|
|
3846
|
+
throw new Error(`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`);
|
|
3723
3847
|
}
|
|
3724
3848
|
return {
|
|
3725
3849
|
allowHost: options.allowHost?.map((item) => item.toLowerCase()),
|
|
@@ -3793,18 +3917,12 @@ async function executeFind(source, query, options) {
|
|
|
3793
3917
|
filtered = inventory.skills.filter((item) => matchesQuery(item.name, item.description, query)).sort((a, b) => a.name.localeCompare(b.name));
|
|
3794
3918
|
} catch (error) {
|
|
3795
3919
|
hideLoader();
|
|
3796
|
-
await renderStaticScreen([
|
|
3797
|
-
failedStepsSection(["failed to apply query filter"])
|
|
3798
|
-
]);
|
|
3920
|
+
await renderStaticScreen([failedStepsSection(["failed to apply query filter"])]);
|
|
3799
3921
|
throw error;
|
|
3800
3922
|
}
|
|
3801
3923
|
hideLoader();
|
|
3802
3924
|
const flowSections = [
|
|
3803
|
-
completedStepsSection([
|
|
3804
|
-
"source parsed",
|
|
3805
|
-
"skill inventory fetched",
|
|
3806
|
-
"query filter applied"
|
|
3807
|
-
]),
|
|
3925
|
+
completedStepsSection(["source parsed", "skill inventory fetched", "query filter applied"]),
|
|
3808
3926
|
sourceSection(shortenHomePath(inventory.sourceLabel))
|
|
3809
3927
|
];
|
|
3810
3928
|
const queryTrimmed = query && query.trim().length > 0 ? query : "";
|
|
@@ -3905,7 +4023,7 @@ function registerFindCommand(program, ctx) {
|
|
|
3905
4023
|
}
|
|
3906
4024
|
|
|
3907
4025
|
// src/commands/init.ts
|
|
3908
|
-
import
|
|
4026
|
+
import fs9 from "node:fs";
|
|
3909
4027
|
import path11 from "node:path";
|
|
3910
4028
|
import { Command as Command5 } from "commander";
|
|
3911
4029
|
|
|
@@ -3961,7 +4079,7 @@ function buildAgentConfigScaffoldPlan(agents, input) {
|
|
|
3961
4079
|
}
|
|
3962
4080
|
|
|
3963
4081
|
// ../../packages/core/src/runtime/installer-scaffold.ts
|
|
3964
|
-
import
|
|
4082
|
+
import fs8 from "node:fs";
|
|
3965
4083
|
import path10 from "node:path";
|
|
3966
4084
|
import YAML3 from "yaml";
|
|
3967
4085
|
function installerConfigSkeleton() {
|
|
@@ -3973,7 +4091,7 @@ function installerConfigSkeleton() {
|
|
|
3973
4091
|
};
|
|
3974
4092
|
}
|
|
3975
4093
|
function isFile(filePath) {
|
|
3976
|
-
return
|
|
4094
|
+
return fs8.existsSync(filePath) && fs8.statSync(filePath).isFile();
|
|
3977
4095
|
}
|
|
3978
4096
|
function getInstallerConfigState(skillDir) {
|
|
3979
4097
|
const yamlPath = path10.join(skillDir, "skill-installer.yaml");
|
|
@@ -4000,7 +4118,7 @@ function scaffoldInstallerConfigFile(skillDir, format) {
|
|
|
4000
4118
|
}
|
|
4001
4119
|
const content = format === "yaml" ? YAML3.stringify(installerConfigSkeleton()) : JSON.stringify(installerConfigSkeleton(), null, 2);
|
|
4002
4120
|
const destinationPath = format === "yaml" ? state.yamlPath : state.jsonPath;
|
|
4003
|
-
|
|
4121
|
+
fs8.writeFileSync(destinationPath, `${content}
|
|
4004
4122
|
`, "utf8");
|
|
4005
4123
|
return { created: true, filePath: destinationPath };
|
|
4006
4124
|
}
|
|
@@ -4068,9 +4186,7 @@ async function chooseInitOne(options) {
|
|
|
4068
4186
|
description: choice.description
|
|
4069
4187
|
}))
|
|
4070
4188
|
);
|
|
4071
|
-
const labelByValue = new Map(
|
|
4072
|
-
options.choices.map((choice) => [choice.value, choice.label])
|
|
4073
|
-
);
|
|
4189
|
+
const labelByValue = new Map(options.choices.map((choice) => [choice.value, choice.label]));
|
|
4074
4190
|
const selected = await runOneSelectionStep({
|
|
4075
4191
|
interactive: true,
|
|
4076
4192
|
rows,
|
|
@@ -4160,11 +4276,7 @@ async function collectAnswers(options) {
|
|
|
4160
4276
|
const answers = { ...defaults };
|
|
4161
4277
|
for (const question of initQuestions) {
|
|
4162
4278
|
if (!question.when(interactive)) {
|
|
4163
|
-
setAnswer(
|
|
4164
|
-
answers,
|
|
4165
|
-
question.id,
|
|
4166
|
-
question.normalize(answers[question.id], defaults)
|
|
4167
|
-
);
|
|
4279
|
+
setAnswer(answers, question.id, question.normalize(answers[question.id], defaults));
|
|
4168
4280
|
continue;
|
|
4169
4281
|
}
|
|
4170
4282
|
const value = await question.ask(defaults);
|
|
@@ -4198,9 +4310,7 @@ async function resolveInitAgents(options) {
|
|
|
4198
4310
|
return selectedIds;
|
|
4199
4311
|
}
|
|
4200
4312
|
if (!interactive) {
|
|
4201
|
-
throw new Error(
|
|
4202
|
-
"Missing --agent in non-interactive mode. Provide at least one agent."
|
|
4203
|
-
);
|
|
4313
|
+
throw new Error("Missing --agent in non-interactive mode. Provide at least one agent.");
|
|
4204
4314
|
}
|
|
4205
4315
|
const selected = await runManySelectionStep({
|
|
4206
4316
|
interactive,
|
|
@@ -4302,9 +4412,7 @@ async function executeInit(options, hooks) {
|
|
|
4302
4412
|
];
|
|
4303
4413
|
if (agentConfigPlan.unmapped.length > 0) {
|
|
4304
4414
|
summaryLines.push(
|
|
4305
|
-
`Unmapped agents (skipped): ${agentConfigPlan.unmapped.join(
|
|
4306
|
-
", "
|
|
4307
|
-
)} (no scaffold mapping)`
|
|
4415
|
+
`Unmapped agents (skipped): ${agentConfigPlan.unmapped.join(", ")} (no scaffold mapping)`
|
|
4308
4416
|
);
|
|
4309
4417
|
}
|
|
4310
4418
|
summaryLines.push("");
|
|
@@ -4333,16 +4441,16 @@ async function executeInit(options, hooks) {
|
|
|
4333
4441
|
const completedSteps = [];
|
|
4334
4442
|
let failedLabel = "failed to create directory";
|
|
4335
4443
|
try {
|
|
4336
|
-
const createDirectory = !
|
|
4337
|
-
|
|
4444
|
+
const createDirectory = !fs9.existsSync(skillDir);
|
|
4445
|
+
fs9.mkdirSync(skillDir, { recursive: true });
|
|
4338
4446
|
if (createDirectory) {
|
|
4339
4447
|
completedSteps.push("directory created");
|
|
4340
4448
|
}
|
|
4341
4449
|
failedLabel = "failed to write SKILL.md";
|
|
4342
|
-
if (
|
|
4450
|
+
if (fs9.existsSync(skillFile)) {
|
|
4343
4451
|
throw new Error(`SKILL.md already exists at: ${skillFile}`);
|
|
4344
4452
|
}
|
|
4345
|
-
|
|
4453
|
+
fs9.writeFileSync(skillFile, `${renderSkillContent(answers)}
|
|
4346
4454
|
`, "utf8");
|
|
4347
4455
|
completedSteps.push("SKILL.md written");
|
|
4348
4456
|
failedLabel = "failed to scaffold installer config";
|
|
@@ -4351,11 +4459,11 @@ async function executeInit(options, hooks) {
|
|
|
4351
4459
|
failedLabel = "failed to scaffold agent config";
|
|
4352
4460
|
for (const row of agentConfigPlan.mapped) {
|
|
4353
4461
|
const destination = ensureInsideSkillDir(skillDir, row.path);
|
|
4354
|
-
if (
|
|
4462
|
+
if (fs9.existsSync(destination)) {
|
|
4355
4463
|
throw new Error(`Agent config already exists at: ${destination}`);
|
|
4356
4464
|
}
|
|
4357
|
-
|
|
4358
|
-
|
|
4465
|
+
fs9.mkdirSync(path11.dirname(destination), { recursive: true });
|
|
4466
|
+
fs9.writeFileSync(destination, row.content, "utf8");
|
|
4359
4467
|
}
|
|
4360
4468
|
if (agentConfigPlan.mapped.length > 0) {
|
|
4361
4469
|
completedSteps.push("agent configs scaffolded");
|
|
@@ -4372,27 +4480,24 @@ async function executeInit(options, hooks) {
|
|
|
4372
4480
|
await renderStaticScreen(sections);
|
|
4373
4481
|
}
|
|
4374
4482
|
function configureInitCommand(command, action) {
|
|
4375
|
-
return command.description("Create a new SKILL.md template").argument("[name]", "Optional skill directory/name").option("-a, --agent <agents...>", "Target agent(s) for config scaffolding").option(
|
|
4376
|
-
"--yaml",
|
|
4377
|
-
"Create skill-installer.yaml when scaffolding installer config"
|
|
4378
|
-
).option("--non-interactive", "Disable prompts").action(action);
|
|
4483
|
+
return command.description("Create a new SKILL.md template").argument("[name]", "Optional skill directory/name").option("-a, --agent <agents...>", "Target agent(s) for config scaffolding").option("--yaml", "Create skill-installer.yaml when scaffolding installer config").option("--non-interactive", "Disable prompts").action(action);
|
|
4379
4484
|
}
|
|
4380
4485
|
function registerInitCommand(program, ctx) {
|
|
4381
4486
|
configureInitCommand(
|
|
4382
4487
|
program.command("init"),
|
|
4383
|
-
ctx.wrapAction(
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
await executeInit({
|
|
4488
|
+
ctx.wrapAction("init", async (name, options) => {
|
|
4489
|
+
await executeInit(
|
|
4490
|
+
{
|
|
4387
4491
|
nameArg: name,
|
|
4388
4492
|
nonInteractive: Boolean(options.nonInteractive),
|
|
4389
4493
|
yaml: Boolean(options.yaml),
|
|
4390
4494
|
agent: options.agent
|
|
4391
|
-
},
|
|
4495
|
+
},
|
|
4496
|
+
{
|
|
4392
4497
|
emitCommandEvent: (event) => ctx.emitCommandEvent("init", event)
|
|
4393
|
-
}
|
|
4394
|
-
|
|
4395
|
-
)
|
|
4498
|
+
}
|
|
4499
|
+
);
|
|
4500
|
+
})
|
|
4396
4501
|
);
|
|
4397
4502
|
}
|
|
4398
4503
|
|
|
@@ -4450,10 +4555,7 @@ function renderListScopePanel(options) {
|
|
|
4450
4555
|
function renderListInventorySummary(skillCount) {
|
|
4451
4556
|
return panelSection({
|
|
4452
4557
|
title: "Inventory Summary",
|
|
4453
|
-
lines: [
|
|
4454
|
-
`${skillCount} unique skills found`,
|
|
4455
|
-
"Grouped by (skill name + resolved path)"
|
|
4456
|
-
],
|
|
4558
|
+
lines: [`${skillCount} unique skills found`, "Grouped by (skill name + resolved path)"],
|
|
4457
4559
|
style: "square",
|
|
4458
4560
|
minWidth: 74
|
|
4459
4561
|
});
|
|
@@ -4467,17 +4569,12 @@ function renderListInstalledSkillsPanel(rows) {
|
|
|
4467
4569
|
return a.resolvedPath.localeCompare(b.resolvedPath);
|
|
4468
4570
|
});
|
|
4469
4571
|
const uniqueSkillCount = new Set(sortedRows.map((row) => row.name)).size;
|
|
4470
|
-
const targetCount = sortedRows.reduce(
|
|
4471
|
-
(count, row) => count + row.agents.length,
|
|
4472
|
-
0
|
|
4473
|
-
);
|
|
4572
|
+
const targetCount = sortedRows.reduce((count, row) => count + row.agents.length, 0);
|
|
4474
4573
|
const lines = [];
|
|
4475
4574
|
lines.push(
|
|
4476
4575
|
`${bold("Skills:")} ${dim(uniqueSkillCount.toString())} ${bold(
|
|
4477
4576
|
"Entries:"
|
|
4478
|
-
)} ${dim(sortedRows.length.toString())} ${bold("Targets:")} ${dim(
|
|
4479
|
-
targetCount.toString()
|
|
4480
|
-
)}`
|
|
4577
|
+
)} ${dim(sortedRows.length.toString())} ${bold("Targets:")} ${dim(targetCount.toString())}`
|
|
4481
4578
|
);
|
|
4482
4579
|
lines.push("");
|
|
4483
4580
|
lines.push("Targets");
|
|
@@ -4490,11 +4587,7 @@ function renderListInstalledSkillsPanel(rows) {
|
|
|
4490
4587
|
}
|
|
4491
4588
|
const agents = [...row.agents].sort((a, b) => a.localeCompare(b));
|
|
4492
4589
|
for (const agent of agents) {
|
|
4493
|
-
lines.push(
|
|
4494
|
-
` - ${agent.padEnd(16, " ")} ${dim(
|
|
4495
|
-
shortenHomePath(row.resolvedPath)
|
|
4496
|
-
)}`
|
|
4497
|
-
);
|
|
4590
|
+
lines.push(` - ${agent.padEnd(16, " ")} ${dim(shortenHomePath(row.resolvedPath))}`);
|
|
4498
4591
|
}
|
|
4499
4592
|
}
|
|
4500
4593
|
return panelSection({
|
|
@@ -4616,7 +4709,7 @@ function registerListCommand(program, ctx) {
|
|
|
4616
4709
|
}
|
|
4617
4710
|
|
|
4618
4711
|
// src/commands/remove.ts
|
|
4619
|
-
import
|
|
4712
|
+
import fs10 from "node:fs";
|
|
4620
4713
|
import path12 from "node:path";
|
|
4621
4714
|
import { Command as Command7 } from "commander";
|
|
4622
4715
|
function toRemoveOptions(options) {
|
|
@@ -4717,7 +4810,12 @@ function renderRemoveConfirmPanel(options) {
|
|
|
4717
4810
|
);
|
|
4718
4811
|
}
|
|
4719
4812
|
function renderRemoveUninstallSummaryBox(options) {
|
|
4720
|
-
return uninstallSummarySection(
|
|
4813
|
+
return uninstallSummarySection({
|
|
4814
|
+
globalInstall: options.globalInstall,
|
|
4815
|
+
itemNames: options.skillNames,
|
|
4816
|
+
itemLabel: "Skills",
|
|
4817
|
+
agentDisplayNames: options.agentDisplayNames
|
|
4818
|
+
});
|
|
4721
4819
|
}
|
|
4722
4820
|
function isSkillEntry(entry) {
|
|
4723
4821
|
return entry.isDirectory() || entry.isSymbolicLink();
|
|
@@ -4728,11 +4826,11 @@ function buildInstallIndex(globalInstall, cwd) {
|
|
|
4728
4826
|
const allAgents = Object.keys(AGENTS);
|
|
4729
4827
|
for (const agent of allAgents) {
|
|
4730
4828
|
const dir = getAgentSkillsDir(agent, globalInstall, cwd);
|
|
4731
|
-
if (!
|
|
4829
|
+
if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
|
|
4732
4830
|
continue;
|
|
4733
4831
|
}
|
|
4734
4832
|
const names = /* @__PURE__ */ new Set();
|
|
4735
|
-
for (const entry of
|
|
4833
|
+
for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
|
|
4736
4834
|
if (!isSkillEntry(entry)) {
|
|
4737
4835
|
continue;
|
|
4738
4836
|
}
|
|
@@ -4763,9 +4861,7 @@ function orderAgentsForDisplay(agents) {
|
|
|
4763
4861
|
}
|
|
4764
4862
|
function resolveCandidateAgentsForSkills(skillNames, index) {
|
|
4765
4863
|
return orderAgentsForDisplay(
|
|
4766
|
-
skillNames.flatMap(
|
|
4767
|
-
(name) => Array.from(index.agentsBySkill.get(name) ?? [])
|
|
4768
|
-
)
|
|
4864
|
+
skillNames.flatMap((name) => Array.from(index.agentsBySkill.get(name) ?? []))
|
|
4769
4865
|
);
|
|
4770
4866
|
}
|
|
4771
4867
|
async function executeRemove(positional, options) {
|
|
@@ -4777,9 +4873,7 @@ async function executeRemove(positional, options) {
|
|
|
4777
4873
|
try {
|
|
4778
4874
|
index = buildInstallIndex(globalInstall, cwd);
|
|
4779
4875
|
} catch (error) {
|
|
4780
|
-
await renderStaticScreen([
|
|
4781
|
-
failedStepsSection(["failed to index installed skills"])
|
|
4782
|
-
]);
|
|
4876
|
+
await renderStaticScreen([failedStepsSection(["failed to index installed skills"])]);
|
|
4783
4877
|
throw error;
|
|
4784
4878
|
}
|
|
4785
4879
|
if (index.agentsBySkill.size === 0) {
|
|
@@ -4816,20 +4910,14 @@ async function executeRemove(positional, options) {
|
|
|
4816
4910
|
try {
|
|
4817
4911
|
if (options.agent && options.agent.length > 0) {
|
|
4818
4912
|
if (options.agent.includes("*")) {
|
|
4819
|
-
candidateAgents = orderAgentsForDisplay(
|
|
4820
|
-
Array.from(index.skillsByAgent.keys())
|
|
4821
|
-
);
|
|
4913
|
+
candidateAgents = orderAgentsForDisplay(Array.from(index.skillsByAgent.keys()));
|
|
4822
4914
|
agents = candidateAgents;
|
|
4823
4915
|
} else {
|
|
4824
4916
|
const resolved = resolveAgents(options.agent);
|
|
4825
|
-
const outOfScopeAgents = resolved.filter(
|
|
4826
|
-
(agent) => !scopeAllowedAgents.has(agent)
|
|
4827
|
-
);
|
|
4917
|
+
const outOfScopeAgents = resolved.filter((agent) => !scopeAllowedAgents.has(agent));
|
|
4828
4918
|
if (outOfScopeAgents.length > 0) {
|
|
4829
4919
|
throw new Error(
|
|
4830
|
-
`Agent(s) not available in ${scope} scope: ${outOfScopeAgents.join(
|
|
4831
|
-
", "
|
|
4832
|
-
)}`
|
|
4920
|
+
`Agent(s) not available in ${scope} scope: ${outOfScopeAgents.join(", ")}`
|
|
4833
4921
|
);
|
|
4834
4922
|
}
|
|
4835
4923
|
candidateAgents = resolved;
|
|
@@ -4848,16 +4936,12 @@ async function executeRemove(positional, options) {
|
|
|
4848
4936
|
);
|
|
4849
4937
|
}
|
|
4850
4938
|
}
|
|
4851
|
-
candidateAgents = candidateAgents.filter(
|
|
4852
|
-
(agent) => scopeAllowedAgents.has(agent)
|
|
4853
|
-
);
|
|
4939
|
+
candidateAgents = candidateAgents.filter((agent) => scopeAllowedAgents.has(agent));
|
|
4854
4940
|
agents = agents.filter((agent) => scopeAllowedAgents.has(agent));
|
|
4855
4941
|
candidateAgents = orderAgentsForDisplay(candidateAgents);
|
|
4856
4942
|
agents = orderAgentsForDisplay(agents);
|
|
4857
4943
|
} catch (error) {
|
|
4858
|
-
await renderStaticScreen([
|
|
4859
|
-
failedStepsSection(["failed to resolve target candidates"])
|
|
4860
|
-
]);
|
|
4944
|
+
await renderStaticScreen([failedStepsSection(["failed to resolve target candidates"])]);
|
|
4861
4945
|
throw error;
|
|
4862
4946
|
}
|
|
4863
4947
|
await renderStaticScreen([
|
|
@@ -4893,9 +4977,7 @@ async function executeRemove(positional, options) {
|
|
|
4893
4977
|
if (!options.agent || options.agent.length === 0) {
|
|
4894
4978
|
if (!options.all) {
|
|
4895
4979
|
candidateAgents = resolveCandidateAgentsForSkills(finalSkills, index);
|
|
4896
|
-
candidateAgents = candidateAgents.filter(
|
|
4897
|
-
(agent) => scopeAllowedAgents.has(agent)
|
|
4898
|
-
);
|
|
4980
|
+
candidateAgents = candidateAgents.filter((agent) => scopeAllowedAgents.has(agent));
|
|
4899
4981
|
candidateAgents = orderAgentsForDisplay(candidateAgents);
|
|
4900
4982
|
}
|
|
4901
4983
|
if (candidateAgents.length === 0) {
|
|
@@ -4908,15 +4990,10 @@ async function executeRemove(positional, options) {
|
|
|
4908
4990
|
}
|
|
4909
4991
|
}
|
|
4910
4992
|
const shouldPromptAgents = interactive && (!options.agent || options.agent.length === 0);
|
|
4911
|
-
const visibleCandidateAgents = candidateAgents.filter(
|
|
4912
|
-
(agent) => scopeAllowedAgents.has(agent)
|
|
4913
|
-
);
|
|
4993
|
+
const visibleCandidateAgents = candidateAgents.filter((agent) => scopeAllowedAgents.has(agent));
|
|
4914
4994
|
const selectedAgentIds = await runManySelectionStep({
|
|
4915
4995
|
interactive,
|
|
4916
|
-
rows: buildRemoveAgentSelectionRows(
|
|
4917
|
-
visibleCandidateAgents,
|
|
4918
|
-
scopedAgentRowsById
|
|
4919
|
-
),
|
|
4996
|
+
rows: buildRemoveAgentSelectionRows(visibleCandidateAgents, scopedAgentRowsById),
|
|
4920
4997
|
selectedIds: agents,
|
|
4921
4998
|
shouldPrompt: shouldPromptAgents,
|
|
4922
4999
|
prompt: {
|
|
@@ -4934,9 +5011,7 @@ async function executeRemove(positional, options) {
|
|
|
4934
5011
|
})
|
|
4935
5012
|
});
|
|
4936
5013
|
const selectedAgentSet = new Set(selectedAgentIds);
|
|
4937
|
-
agents = visibleCandidateAgents.filter(
|
|
4938
|
-
(agent) => selectedAgentSet.has(agent)
|
|
4939
|
-
);
|
|
5014
|
+
agents = visibleCandidateAgents.filter((agent) => selectedAgentSet.has(agent));
|
|
4940
5015
|
if (options.all) {
|
|
4941
5016
|
const all = /* @__PURE__ */ new Set();
|
|
4942
5017
|
for (const agent of agents) {
|
|
@@ -5022,10 +5097,10 @@ async function executeRemove(positional, options) {
|
|
|
5022
5097
|
try {
|
|
5023
5098
|
for (const agent of agents) {
|
|
5024
5099
|
const dir = getAgentSkillsDir(agent, globalInstall, cwd);
|
|
5025
|
-
if (!
|
|
5100
|
+
if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
|
|
5026
5101
|
continue;
|
|
5027
5102
|
}
|
|
5028
|
-
for (const entry of
|
|
5103
|
+
for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
|
|
5029
5104
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
5030
5105
|
continue;
|
|
5031
5106
|
}
|
|
@@ -5034,7 +5109,7 @@ async function executeRemove(positional, options) {
|
|
|
5034
5109
|
}
|
|
5035
5110
|
const fullPath = path12.join(dir, entry.name);
|
|
5036
5111
|
failedLabel = `failed to remove ${entry.name} from ${agent}`;
|
|
5037
|
-
|
|
5112
|
+
fs10.rmSync(fullPath, { recursive: true, force: true });
|
|
5038
5113
|
removedCount += 1;
|
|
5039
5114
|
completedRemovalSteps.push(`removed ${entry.name} from ${agent}`);
|
|
5040
5115
|
}
|
|
@@ -5056,12 +5131,9 @@ function configureRemoveCommand(command, action) {
|
|
|
5056
5131
|
function registerRemoveCommand(program, ctx) {
|
|
5057
5132
|
configureRemoveCommand(
|
|
5058
5133
|
program.command("remove").alias("rm"),
|
|
5059
|
-
ctx.wrapAction(
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
await executeRemove(skills, toRemoveOptions(options));
|
|
5063
|
-
}
|
|
5064
|
-
)
|
|
5134
|
+
ctx.wrapAction("remove", async (skills, options) => {
|
|
5135
|
+
await executeRemove(skills, toRemoveOptions(options));
|
|
5136
|
+
})
|
|
5065
5137
|
);
|
|
5066
5138
|
}
|
|
5067
5139
|
|
|
@@ -5070,9 +5142,7 @@ import { Command as Command8 } from "commander";
|
|
|
5070
5142
|
function toUpdateOptions(options) {
|
|
5071
5143
|
const maxDownloadBytes = options.maxDownloadBytes ? Number(options.maxDownloadBytes) : void 0;
|
|
5072
5144
|
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
5073
|
-
throw new Error(
|
|
5074
|
-
`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`
|
|
5075
|
-
);
|
|
5145
|
+
throw new Error(`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`);
|
|
5076
5146
|
}
|
|
5077
5147
|
const lockFormat = options.lockFormat;
|
|
5078
5148
|
if (lockFormat && lockFormat !== "json" && lockFormat !== "yaml") {
|
|
@@ -5120,11 +5190,7 @@ function buildUpdateRows(assessments) {
|
|
|
5120
5190
|
});
|
|
5121
5191
|
}
|
|
5122
5192
|
function renderUpdateSkillsClosedPanel(rows, selectedIds) {
|
|
5123
|
-
return manySelectionClosedSection(
|
|
5124
|
-
UPDATE_SKILLS_SELECTION_VIEW,
|
|
5125
|
-
rows,
|
|
5126
|
-
selectedIds
|
|
5127
|
-
);
|
|
5193
|
+
return manySelectionClosedSection(UPDATE_SKILLS_SELECTION_VIEW, rows, selectedIds);
|
|
5128
5194
|
}
|
|
5129
5195
|
function buildUpdateDriftSummaryLines(options) {
|
|
5130
5196
|
return [
|
|
@@ -5201,10 +5267,7 @@ async function executeMigrateUpdate(options) {
|
|
|
5201
5267
|
}
|
|
5202
5268
|
hideLoader();
|
|
5203
5269
|
await renderStaticScreen([
|
|
5204
|
-
completedStepsSection([
|
|
5205
|
-
`migrated ${options.skillName}`,
|
|
5206
|
-
"lockfile written"
|
|
5207
|
-
]),
|
|
5270
|
+
completedStepsSection([`migrated ${options.skillName}`, "lockfile written"]),
|
|
5208
5271
|
linesSection(["Migration complete.", `Updated ${options.skillName}.`])
|
|
5209
5272
|
]);
|
|
5210
5273
|
}
|
|
@@ -5216,9 +5279,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5216
5279
|
...options,
|
|
5217
5280
|
skill: mergedSkills
|
|
5218
5281
|
};
|
|
5219
|
-
const requestedSkills = (effectiveOptions.skill || []).filter(
|
|
5220
|
-
(skill) => skill !== "*"
|
|
5221
|
-
);
|
|
5282
|
+
const requestedSkills = (effectiveOptions.skill || []).filter((skill) => skill !== "*");
|
|
5222
5283
|
if (effectiveOptions.migrate) {
|
|
5223
5284
|
if (requestedSkills.length !== 1) {
|
|
5224
5285
|
throw new Error(
|
|
@@ -5264,9 +5325,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5264
5325
|
);
|
|
5265
5326
|
} catch (error) {
|
|
5266
5327
|
hideLoader();
|
|
5267
|
-
await renderStaticScreen([
|
|
5268
|
-
failedStepsSection(["failed to assess drift"])
|
|
5269
|
-
]);
|
|
5328
|
+
await renderStaticScreen([failedStepsSection(["failed to assess drift"])]);
|
|
5270
5329
|
throw error;
|
|
5271
5330
|
}
|
|
5272
5331
|
hideLoader();
|
|
@@ -5279,9 +5338,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5279
5338
|
(item) => item.kind === "changed-source" || item.kind === "local-modified"
|
|
5280
5339
|
);
|
|
5281
5340
|
});
|
|
5282
|
-
const migrateRequired = assessments.filter(
|
|
5283
|
-
(assessment) => assessment.drift.some((item) => item.kind === "migrate-required")
|
|
5284
|
-
).map((assessment) => assessment.entry.skillName).sort((a, b) => a.localeCompare(b));
|
|
5341
|
+
const migrateRequired = assessments.filter((assessment) => assessment.drift.some((item) => item.kind === "migrate-required")).map((assessment) => assessment.entry.skillName).sort((a, b) => a.localeCompare(b));
|
|
5285
5342
|
let changedSourceCount = 0;
|
|
5286
5343
|
let localModifiedCount = 0;
|
|
5287
5344
|
for (const assessment of candidateAssessments) {
|
|
@@ -5375,9 +5432,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5375
5432
|
})
|
|
5376
5433
|
]);
|
|
5377
5434
|
if (effectiveOptions.dryRun) {
|
|
5378
|
-
await renderStaticScreen([
|
|
5379
|
-
linesSection(["Dry-run mode: no changes applied."])
|
|
5380
|
-
]);
|
|
5435
|
+
await renderStaticScreen([linesSection(["Dry-run mode: no changes applied."])]);
|
|
5381
5436
|
return;
|
|
5382
5437
|
}
|
|
5383
5438
|
showLoader("updating selected skills");
|
|
@@ -5391,9 +5446,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5391
5446
|
payload: {
|
|
5392
5447
|
cwd,
|
|
5393
5448
|
options: effectiveOptions,
|
|
5394
|
-
selectedSkillNames: selectedAssessments.map(
|
|
5395
|
-
(assessment) => assessment.entry.skillName
|
|
5396
|
-
),
|
|
5449
|
+
selectedSkillNames: selectedAssessments.map((assessment) => assessment.entry.skillName),
|
|
5397
5450
|
lockFormat
|
|
5398
5451
|
}
|
|
5399
5452
|
},
|
|
@@ -5402,9 +5455,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5402
5455
|
if (label === "writing lockfile") {
|
|
5403
5456
|
failedLabel = "failed to write lockfile";
|
|
5404
5457
|
} else if (label.startsWith("updating ")) {
|
|
5405
|
-
failedLabel = `failed to update ${label.slice(
|
|
5406
|
-
"updating ".length
|
|
5407
|
-
)}`;
|
|
5458
|
+
failedLabel = `failed to update ${label.slice("updating ".length)}`;
|
|
5408
5459
|
} else {
|
|
5409
5460
|
failedLabel = "failed to assess selected skills";
|
|
5410
5461
|
}
|
|
@@ -5424,10 +5475,7 @@ async function executeUpdate(options, positionalSkill) {
|
|
|
5424
5475
|
...applied.updatedSkillNames.map((skillName) => `updated ${skillName}`),
|
|
5425
5476
|
"lockfile written"
|
|
5426
5477
|
]),
|
|
5427
|
-
linesSection([
|
|
5428
|
-
"Update complete.",
|
|
5429
|
-
`Updated ${applied.updatedSkillNames.length} skills.`
|
|
5430
|
-
])
|
|
5478
|
+
linesSection(["Update complete.", `Updated ${applied.updatedSkillNames.length} skills.`])
|
|
5431
5479
|
]);
|
|
5432
5480
|
} finally {
|
|
5433
5481
|
hideLoader();
|
|
@@ -5439,23 +5487,20 @@ function configureUpdateCommand(command, action) {
|
|
|
5439
5487
|
function registerUpdateCommand(program, ctx) {
|
|
5440
5488
|
configureUpdateCommand(
|
|
5441
5489
|
program.command("update"),
|
|
5442
|
-
ctx.wrapAction(
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
);
|
|
5452
|
-
}
|
|
5453
|
-
)
|
|
5490
|
+
ctx.wrapAction("update", async (skill, options) => {
|
|
5491
|
+
await executeUpdate(
|
|
5492
|
+
{
|
|
5493
|
+
...toUpdateOptions(options),
|
|
5494
|
+
experimental: ctx.experimental
|
|
5495
|
+
},
|
|
5496
|
+
skill
|
|
5497
|
+
);
|
|
5498
|
+
})
|
|
5454
5499
|
);
|
|
5455
5500
|
}
|
|
5456
5501
|
|
|
5457
5502
|
// src/commands/validate.ts
|
|
5458
|
-
import
|
|
5503
|
+
import fs13 from "node:fs";
|
|
5459
5504
|
import path15 from "node:path";
|
|
5460
5505
|
import os5 from "node:os";
|
|
5461
5506
|
import { Command as Command9 } from "commander";
|
|
@@ -5463,14 +5508,14 @@ import matter2 from "gray-matter";
|
|
|
5463
5508
|
|
|
5464
5509
|
// ../../packages/core/src/runtime/skill-installer.ts
|
|
5465
5510
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
5466
|
-
import
|
|
5511
|
+
import fs12 from "node:fs";
|
|
5467
5512
|
import os4 from "node:os";
|
|
5468
5513
|
import path14 from "node:path";
|
|
5469
5514
|
import YAML4 from "yaml";
|
|
5470
5515
|
import { z } from "zod";
|
|
5471
5516
|
|
|
5472
5517
|
// ../../packages/core/src/runtime/installer-security.ts
|
|
5473
|
-
import
|
|
5518
|
+
import fs11 from "node:fs";
|
|
5474
5519
|
import path13 from "node:path";
|
|
5475
5520
|
function isInsideRoot(rootDir, candidatePath) {
|
|
5476
5521
|
const relative = path13.relative(rootDir, candidatePath);
|
|
@@ -5504,9 +5549,9 @@ function evaluateInstallerLocalDependency(input, _options = {}) {
|
|
|
5504
5549
|
violation: toEscapeViolation(input.source)
|
|
5505
5550
|
};
|
|
5506
5551
|
}
|
|
5507
|
-
if (
|
|
5508
|
-
const realRoot =
|
|
5509
|
-
const realSource =
|
|
5552
|
+
if (fs11.existsSync(resolvedSourcePath)) {
|
|
5553
|
+
const realRoot = fs11.realpathSync.native ? fs11.realpathSync.native(resolvedRoot) : fs11.realpathSync(resolvedRoot);
|
|
5554
|
+
const realSource = fs11.realpathSync.native ? fs11.realpathSync.native(resolvedSourcePath) : fs11.realpathSync(resolvedSourcePath);
|
|
5510
5555
|
if (!isInsideRoot(realRoot, realSource)) {
|
|
5511
5556
|
return {
|
|
5512
5557
|
ok: false,
|
|
@@ -5539,10 +5584,7 @@ function applyMode(result, mode) {
|
|
|
5539
5584
|
};
|
|
5540
5585
|
}
|
|
5541
5586
|
function evaluateInstallerLocalDependencyPolicy(input, mode) {
|
|
5542
|
-
return applyMode(
|
|
5543
|
-
evaluateInstallerLocalDependency(input, { policyMode: "fixed" }),
|
|
5544
|
-
mode
|
|
5545
|
-
);
|
|
5587
|
+
return applyMode(evaluateInstallerLocalDependency(input, { policyMode: "fixed" }), mode);
|
|
5546
5588
|
}
|
|
5547
5589
|
function evaluateHookTrustPolicy(input) {
|
|
5548
5590
|
if (input.sourceType !== "well-known") {
|
|
@@ -5628,9 +5670,7 @@ function sourceLooksLikeRepoShorthand(source) {
|
|
|
5628
5670
|
}
|
|
5629
5671
|
function parseRepoSource(source) {
|
|
5630
5672
|
const withoutProtocol = source.trim().replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
|
5631
|
-
const match = withoutProtocol.match(
|
|
5632
|
-
/^(github\.com|gitlab\.com)\/([^/]+)\/([^/]+?)(?:\.git)?$/
|
|
5633
|
-
);
|
|
5673
|
+
const match = withoutProtocol.match(/^(github\.com|gitlab\.com)\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
5634
5674
|
if (!match) {
|
|
5635
5675
|
throw new Error(`Unsupported repository dependency source: ${source}`);
|
|
5636
5676
|
}
|
|
@@ -5648,9 +5688,7 @@ function deriveDestinationNameFromSource(source) {
|
|
|
5648
5688
|
const parts = parsed.pathname.split("/").filter(Boolean);
|
|
5649
5689
|
const leaf2 = parts[parts.length - 1];
|
|
5650
5690
|
if (!leaf2) {
|
|
5651
|
-
throw new Error(
|
|
5652
|
-
`Cannot derive dependency name from URL source: ${source}`
|
|
5653
|
-
);
|
|
5691
|
+
throw new Error(`Cannot derive dependency name from URL source: ${source}`);
|
|
5654
5692
|
}
|
|
5655
5693
|
return leaf2;
|
|
5656
5694
|
}
|
|
@@ -5674,9 +5712,7 @@ function classifyDependencySource(source) {
|
|
|
5674
5712
|
}
|
|
5675
5713
|
function parseConfigObject(raw) {
|
|
5676
5714
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
5677
|
-
throw new Error(
|
|
5678
|
-
"Invalid skill-installer config: expected an object with top-level keys"
|
|
5679
|
-
);
|
|
5715
|
+
throw new Error("Invalid skill-installer config: expected an object with top-level keys");
|
|
5680
5716
|
}
|
|
5681
5717
|
const parsed = raw;
|
|
5682
5718
|
if (typeof parsed["skill-installer"] !== "undefined") {
|
|
@@ -5717,10 +5753,10 @@ function parseConfigObject(raw) {
|
|
|
5717
5753
|
}
|
|
5718
5754
|
function assertNoLegacyInstallerBlock(skillDir) {
|
|
5719
5755
|
const openAiYamlPath = path14.join(skillDir, "agents", "openai.yaml");
|
|
5720
|
-
if (!
|
|
5756
|
+
if (!fs12.existsSync(openAiYamlPath) || !fs12.statSync(openAiYamlPath).isFile()) {
|
|
5721
5757
|
return;
|
|
5722
5758
|
}
|
|
5723
|
-
const parsed = YAML4.parse(
|
|
5759
|
+
const parsed = YAML4.parse(fs12.readFileSync(openAiYamlPath, "utf8"));
|
|
5724
5760
|
if (parsed && typeof parsed === "object" && typeof parsed["skill-installer"] !== "undefined") {
|
|
5725
5761
|
throw new Error(
|
|
5726
5762
|
"Legacy skill-installer config detected in agents/openai.yaml. Move it to skill-installer.yaml or skill-installer.json."
|
|
@@ -5731,8 +5767,8 @@ function loadInstallerConfig(skillDir) {
|
|
|
5731
5767
|
assertNoLegacyInstallerBlock(skillDir);
|
|
5732
5768
|
const yamlPath = path14.join(skillDir, "skill-installer.yaml");
|
|
5733
5769
|
const jsonPath = path14.join(skillDir, "skill-installer.json");
|
|
5734
|
-
const hasYaml =
|
|
5735
|
-
const hasJson =
|
|
5770
|
+
const hasYaml = fs12.existsSync(yamlPath) && fs12.statSync(yamlPath).isFile();
|
|
5771
|
+
const hasJson = fs12.existsSync(jsonPath) && fs12.statSync(jsonPath).isFile();
|
|
5736
5772
|
if (hasYaml && hasJson) {
|
|
5737
5773
|
throw new Error(
|
|
5738
5774
|
"Both skill-installer.yaml and skill-installer.json exist. Keep only one installer config file."
|
|
@@ -5747,10 +5783,10 @@ function loadInstallerConfig(skillDir) {
|
|
|
5747
5783
|
};
|
|
5748
5784
|
}
|
|
5749
5785
|
if (hasYaml) {
|
|
5750
|
-
const parsed = YAML4.parse(
|
|
5786
|
+
const parsed = YAML4.parse(fs12.readFileSync(yamlPath, "utf8"));
|
|
5751
5787
|
return parseConfigObject(parsed);
|
|
5752
5788
|
}
|
|
5753
|
-
const raw = JSON.parse(
|
|
5789
|
+
const raw = JSON.parse(fs12.readFileSync(jsonPath, "utf8"));
|
|
5754
5790
|
return parseConfigObject(raw);
|
|
5755
5791
|
}
|
|
5756
5792
|
function resolveDependencySourceAndTarget(dep) {
|
|
@@ -5766,19 +5802,13 @@ function resolveDependencySourceAndTarget(dep) {
|
|
|
5766
5802
|
};
|
|
5767
5803
|
}
|
|
5768
5804
|
function runGitClone(repoUrl, targetDir) {
|
|
5769
|
-
const result = spawnSync2(
|
|
5770
|
-
"
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
encoding: "utf8",
|
|
5774
|
-
stdio: "pipe"
|
|
5775
|
-
}
|
|
5776
|
-
);
|
|
5805
|
+
const result = spawnSync2("git", ["clone", "--depth", "1", repoUrl, targetDir], {
|
|
5806
|
+
encoding: "utf8",
|
|
5807
|
+
stdio: "pipe"
|
|
5808
|
+
});
|
|
5777
5809
|
if (result.status !== 0) {
|
|
5778
5810
|
const message = (result.stderr || result.stdout || "git clone failed").trim().split(/\r?\n/)[0] || "git clone failed";
|
|
5779
|
-
throw new Error(
|
|
5780
|
-
`Repository dependency clone failed: ${repoUrl} (${message})`
|
|
5781
|
-
);
|
|
5811
|
+
throw new Error(`Repository dependency clone failed: ${repoUrl} (${message})`);
|
|
5782
5812
|
}
|
|
5783
5813
|
}
|
|
5784
5814
|
async function prepareDependency(source, targetPath, index, sourceCwd, stagingDir, policyMode, events) {
|
|
@@ -5794,9 +5824,7 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
|
|
|
5794
5824
|
if (!evaluation.ok) {
|
|
5795
5825
|
const violation = "violation" in evaluation ? evaluation.violation : void 0;
|
|
5796
5826
|
if (!violation) {
|
|
5797
|
-
throw new Error(
|
|
5798
|
-
`Dependency[${index}] policy evaluation failed for source: ${source}`
|
|
5799
|
-
);
|
|
5827
|
+
throw new Error(`Dependency[${index}] policy evaluation failed for source: ${source}`);
|
|
5800
5828
|
}
|
|
5801
5829
|
if (violation.blocking) {
|
|
5802
5830
|
throw new InstallerSecurityError(violation);
|
|
@@ -5807,12 +5835,10 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
|
|
|
5807
5835
|
});
|
|
5808
5836
|
}
|
|
5809
5837
|
const sourcePath = evaluation.ok ? evaluation.resolvedPath : path14.resolve(sourceCwd, source);
|
|
5810
|
-
if (!
|
|
5811
|
-
throw new Error(
|
|
5812
|
-
`Dependency[${index}] (local) source not found: ${source}`
|
|
5813
|
-
);
|
|
5838
|
+
if (!fs12.existsSync(sourcePath)) {
|
|
5839
|
+
throw new Error(`Dependency[${index}] (local) source not found: ${source}`);
|
|
5814
5840
|
}
|
|
5815
|
-
|
|
5841
|
+
fs12.accessSync(sourcePath, fs12.constants.R_OK);
|
|
5816
5842
|
events.push({
|
|
5817
5843
|
level: "info",
|
|
5818
5844
|
message: `[skills] dependency[${index}] preflight-validated: ${source}`
|
|
@@ -5846,9 +5872,7 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
|
|
|
5846
5872
|
}
|
|
5847
5873
|
const target = new URL(source);
|
|
5848
5874
|
if (target.protocol !== "http:" && target.protocol !== "https:") {
|
|
5849
|
-
throw new Error(
|
|
5850
|
-
`Dependency[${index}] (remote) unsupported protocol: ${target.protocol}`
|
|
5851
|
-
);
|
|
5875
|
+
throw new Error(`Dependency[${index}] (remote) unsupported protocol: ${target.protocol}`);
|
|
5852
5876
|
}
|
|
5853
5877
|
const response = await fetch(target.toString());
|
|
5854
5878
|
if (!response.ok) {
|
|
@@ -5860,8 +5884,8 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
|
|
|
5860
5884
|
stagingDir,
|
|
5861
5885
|
`remote-${index}-${path14.basename(targetPath)}`
|
|
5862
5886
|
);
|
|
5863
|
-
|
|
5864
|
-
|
|
5887
|
+
fs12.mkdirSync(path14.dirname(stagedFilePath), { recursive: true });
|
|
5888
|
+
fs12.writeFileSync(stagedFilePath, Buffer.from(await response.arrayBuffer()));
|
|
5865
5889
|
events.push({
|
|
5866
5890
|
level: "info",
|
|
5867
5891
|
message: `[skills] dependency[${index}] staged: ${source}`
|
|
@@ -5910,9 +5934,7 @@ async function prepareInstallerArtifacts(skillDir, sourceCwd, options = {}) {
|
|
|
5910
5934
|
events
|
|
5911
5935
|
};
|
|
5912
5936
|
}
|
|
5913
|
-
const stagingDir =
|
|
5914
|
-
path14.join(os4.tmpdir(), "skillspp-installer-stage-")
|
|
5915
|
-
);
|
|
5937
|
+
const stagingDir = fs12.mkdtempSync(path14.join(os4.tmpdir(), "skillspp-installer-stage-"));
|
|
5916
5938
|
const preparedDependencies = [];
|
|
5917
5939
|
const seenTargetPaths = /* @__PURE__ */ new Set();
|
|
5918
5940
|
try {
|
|
@@ -5921,13 +5943,11 @@ async function prepareInstallerArtifacts(skillDir, sourceCwd, options = {}) {
|
|
|
5921
5943
|
const { source, targetPath } = resolveDependencySourceAndTarget(dep);
|
|
5922
5944
|
const normalizedTarget = targetPath.replace(/\\/g, "/");
|
|
5923
5945
|
if (seenTargetPaths.has(normalizedTarget)) {
|
|
5924
|
-
throw new Error(
|
|
5925
|
-
`Dependency[${i}] duplicate target path: ${targetPath}`
|
|
5926
|
-
);
|
|
5946
|
+
throw new Error(`Dependency[${i}] duplicate target path: ${targetPath}`);
|
|
5927
5947
|
}
|
|
5928
5948
|
seenTargetPaths.add(normalizedTarget);
|
|
5929
5949
|
const destinationInSkill = ensureInsideRoot(skillDir, targetPath);
|
|
5930
|
-
if (
|
|
5950
|
+
if (fs12.existsSync(destinationInSkill)) {
|
|
5931
5951
|
throw new Error(
|
|
5932
5952
|
`Dependency[${i}] destination already exists: ${path14.relative(
|
|
5933
5953
|
skillDir,
|
|
@@ -5954,13 +5974,13 @@ async function prepareInstallerArtifacts(skillDir, sourceCwd, options = {}) {
|
|
|
5954
5974
|
events
|
|
5955
5975
|
};
|
|
5956
5976
|
} catch (error) {
|
|
5957
|
-
|
|
5977
|
+
fs12.rmSync(stagingDir, { recursive: true, force: true });
|
|
5958
5978
|
throw error;
|
|
5959
5979
|
}
|
|
5960
5980
|
}
|
|
5961
5981
|
function cleanupPreparedInstallerArtifacts(prepared) {
|
|
5962
5982
|
if (prepared.stagingDir) {
|
|
5963
|
-
|
|
5983
|
+
fs12.rmSync(prepared.stagingDir, { recursive: true, force: true });
|
|
5964
5984
|
}
|
|
5965
5985
|
}
|
|
5966
5986
|
|
|
@@ -5974,15 +5994,11 @@ function toValidateOptions(source, options) {
|
|
|
5974
5994
|
}
|
|
5975
5995
|
const maxDescriptionChars = options.maxDescriptionChars ? Number(options.maxDescriptionChars) : void 0;
|
|
5976
5996
|
if (typeof maxDescriptionChars === "number" && (!Number.isFinite(maxDescriptionChars) || maxDescriptionChars <= 0)) {
|
|
5977
|
-
throw new Error(
|
|
5978
|
-
`Invalid --max-description-chars value: ${options.maxDescriptionChars}`
|
|
5979
|
-
);
|
|
5997
|
+
throw new Error(`Invalid --max-description-chars value: ${options.maxDescriptionChars}`);
|
|
5980
5998
|
}
|
|
5981
5999
|
const maxDownloadBytes = options.maxDownloadBytes ? Number(options.maxDownloadBytes) : void 0;
|
|
5982
6000
|
if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
|
|
5983
|
-
throw new Error(
|
|
5984
|
-
`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`
|
|
5985
|
-
);
|
|
6001
|
+
throw new Error(`Invalid --max-download-bytes value: ${options.maxDownloadBytes}`);
|
|
5986
6002
|
}
|
|
5987
6003
|
return {
|
|
5988
6004
|
source,
|
|
@@ -6001,10 +6017,10 @@ function toValidateOptions(source, options) {
|
|
|
6001
6017
|
}
|
|
6002
6018
|
function loadRepoValidateConfig(cwd) {
|
|
6003
6019
|
const configPath = path15.join(cwd, ".skillspp-cli.json");
|
|
6004
|
-
if (!
|
|
6020
|
+
if (!fs13.existsSync(configPath) || !fs13.statSync(configPath).isFile()) {
|
|
6005
6021
|
return {};
|
|
6006
6022
|
}
|
|
6007
|
-
const parsed = JSON.parse(
|
|
6023
|
+
const parsed = JSON.parse(fs13.readFileSync(configPath, "utf8"));
|
|
6008
6024
|
return parsed.validate || {};
|
|
6009
6025
|
}
|
|
6010
6026
|
function resolveThresholds(options, cwd) {
|
|
@@ -6036,7 +6052,7 @@ var typeRules = [
|
|
|
6036
6052
|
when: (frontmatter) => frontmatter.type === "framework",
|
|
6037
6053
|
validate: ({ skillDir, skillName, diagnostics }) => {
|
|
6038
6054
|
const referencesDir = path15.join(skillDir, "references");
|
|
6039
|
-
if (!
|
|
6055
|
+
if (!fs13.existsSync(referencesDir) || !fs13.statSync(referencesDir).isDirectory()) {
|
|
6040
6056
|
addDiagnostic(diagnostics, {
|
|
6041
6057
|
severity: "error",
|
|
6042
6058
|
skill: skillName,
|
|
@@ -6049,9 +6065,7 @@ var typeRules = [
|
|
|
6049
6065
|
}
|
|
6050
6066
|
];
|
|
6051
6067
|
async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics, skillName) {
|
|
6052
|
-
const installerConfigPath =
|
|
6053
|
-
path15.join(skillDir, "skill-installer.yaml")
|
|
6054
|
-
) ? path15.join(skillDir, "skill-installer.yaml") : path15.join(skillDir, "skill-installer.json");
|
|
6068
|
+
const installerConfigPath = fs13.existsSync(path15.join(skillDir, "skill-installer.yaml")) ? path15.join(skillDir, "skill-installer.yaml") : path15.join(skillDir, "skill-installer.json");
|
|
6055
6069
|
try {
|
|
6056
6070
|
const installer = loadInstallerConfig(skillDir);
|
|
6057
6071
|
const fallbackRoots = Array.from(
|
|
@@ -6095,24 +6109,18 @@ async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics,
|
|
|
6095
6109
|
if (hasSecurityViolation) {
|
|
6096
6110
|
return;
|
|
6097
6111
|
}
|
|
6098
|
-
const tempSkillDir =
|
|
6099
|
-
path15.join(os5.tmpdir(), "skillspp-validate-installer-")
|
|
6100
|
-
);
|
|
6112
|
+
const tempSkillDir = fs13.mkdtempSync(path15.join(os5.tmpdir(), "skillspp-validate-installer-"));
|
|
6101
6113
|
try {
|
|
6102
|
-
const filesToCopy = [
|
|
6103
|
-
"SKILL.md",
|
|
6104
|
-
"skill-installer.yaml",
|
|
6105
|
-
"skill-installer.json"
|
|
6106
|
-
];
|
|
6114
|
+
const filesToCopy = ["SKILL.md", "skill-installer.yaml", "skill-installer.json"];
|
|
6107
6115
|
for (const fileName of filesToCopy) {
|
|
6108
6116
|
const src = path15.join(skillDir, fileName);
|
|
6109
|
-
if (
|
|
6110
|
-
|
|
6117
|
+
if (fs13.existsSync(src) && fs13.statSync(src).isFile()) {
|
|
6118
|
+
fs13.copyFileSync(src, path15.join(tempSkillDir, fileName));
|
|
6111
6119
|
}
|
|
6112
6120
|
}
|
|
6113
6121
|
const agentsDir = path15.join(skillDir, "agents");
|
|
6114
|
-
if (
|
|
6115
|
-
|
|
6122
|
+
if (fs13.existsSync(agentsDir) && fs13.statSync(agentsDir).isDirectory()) {
|
|
6123
|
+
fs13.cpSync(agentsDir, path15.join(tempSkillDir, "agents"), {
|
|
6116
6124
|
recursive: true,
|
|
6117
6125
|
force: true
|
|
6118
6126
|
});
|
|
@@ -6154,7 +6162,7 @@ async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics,
|
|
|
6154
6162
|
throw lastMissingSourceError;
|
|
6155
6163
|
}
|
|
6156
6164
|
} finally {
|
|
6157
|
-
|
|
6165
|
+
fs13.rmSync(tempSkillDir, { recursive: true, force: true });
|
|
6158
6166
|
}
|
|
6159
6167
|
} catch (error) {
|
|
6160
6168
|
if (isInstallerSecurityError(error)) {
|
|
@@ -6189,7 +6197,7 @@ async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics,
|
|
|
6189
6197
|
async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thresholds) {
|
|
6190
6198
|
const skillMd = path15.join(skillDir, "SKILL.md");
|
|
6191
6199
|
const skillName = path15.basename(skillDir);
|
|
6192
|
-
if (!
|
|
6200
|
+
if (!fs13.existsSync(skillMd)) {
|
|
6193
6201
|
addDiagnostic(diagnostics, {
|
|
6194
6202
|
severity: "error",
|
|
6195
6203
|
skill: skillName,
|
|
@@ -6199,7 +6207,7 @@ async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thres
|
|
|
6199
6207
|
});
|
|
6200
6208
|
return;
|
|
6201
6209
|
}
|
|
6202
|
-
const content =
|
|
6210
|
+
const content = fs13.readFileSync(skillMd, "utf8");
|
|
6203
6211
|
const lines = content.split(/\r?\n/);
|
|
6204
6212
|
if (lines.length > thresholds.maxLines) {
|
|
6205
6213
|
addDiagnostic(diagnostics, {
|
|
@@ -6270,7 +6278,7 @@ async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thres
|
|
|
6270
6278
|
if (!rel || rel.startsWith("..") || path15.isAbsolute(rel)) {
|
|
6271
6279
|
continue;
|
|
6272
6280
|
}
|
|
6273
|
-
if (!
|
|
6281
|
+
if (!fs13.existsSync(resolved)) {
|
|
6274
6282
|
addDiagnostic(diagnostics, {
|
|
6275
6283
|
severity: "error",
|
|
6276
6284
|
skill: skillName,
|
|
@@ -6285,18 +6293,13 @@ async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thres
|
|
|
6285
6293
|
rule.validate({ skillDir, skillName, diagnostics });
|
|
6286
6294
|
}
|
|
6287
6295
|
}
|
|
6288
|
-
await validateInstallerDependencies(
|
|
6289
|
-
skillDir,
|
|
6290
|
-
sourceRoot,
|
|
6291
|
-
diagnostics,
|
|
6292
|
-
skillName
|
|
6293
|
-
);
|
|
6296
|
+
await validateInstallerDependencies(skillDir, sourceRoot, diagnostics, skillName);
|
|
6294
6297
|
}
|
|
6295
6298
|
function discoverFallbackRoots(cwd) {
|
|
6296
6299
|
const candidates = [cwd, path15.join(cwd, "skills")];
|
|
6297
6300
|
const packagesDir = path15.join(cwd, "packages");
|
|
6298
|
-
if (
|
|
6299
|
-
for (const entry of
|
|
6301
|
+
if (fs13.existsSync(packagesDir) && fs13.statSync(packagesDir).isDirectory()) {
|
|
6302
|
+
for (const entry of fs13.readdirSync(packagesDir, { withFileTypes: true })) {
|
|
6300
6303
|
if (!entry.isDirectory()) {
|
|
6301
6304
|
continue;
|
|
6302
6305
|
}
|
|
@@ -6306,7 +6309,7 @@ function discoverFallbackRoots(cwd) {
|
|
|
6306
6309
|
const seen = /* @__PURE__ */ new Set();
|
|
6307
6310
|
const out = [];
|
|
6308
6311
|
for (const item of candidates) {
|
|
6309
|
-
if (!
|
|
6312
|
+
if (!fs13.existsSync(item)) {
|
|
6310
6313
|
continue;
|
|
6311
6314
|
}
|
|
6312
6315
|
const resolved = path15.resolve(item);
|
|
@@ -6321,7 +6324,7 @@ function discoverFallbackRoots(cwd) {
|
|
|
6321
6324
|
function discoverCiRoots(cwd) {
|
|
6322
6325
|
const config = loadRepoValidateConfig(cwd);
|
|
6323
6326
|
if (config.ciRoots && config.ciRoots.length > 0) {
|
|
6324
|
-
const roots = config.ciRoots.map((item) => path15.resolve(cwd, item)).filter((item) =>
|
|
6327
|
+
const roots = config.ciRoots.map((item) => path15.resolve(cwd, item)).filter((item) => fs13.existsSync(item) && fs13.statSync(item).isDirectory());
|
|
6325
6328
|
if (roots.length > 0) {
|
|
6326
6329
|
return roots;
|
|
6327
6330
|
}
|
|
@@ -6330,7 +6333,7 @@ function discoverCiRoots(cwd) {
|
|
|
6330
6333
|
}
|
|
6331
6334
|
async function stageAndValidateLocalRoot(rootPath, dependencyRoot, seenSkillPaths, diagnostics, strict, thresholds, emitProgress) {
|
|
6332
6335
|
const resolvedRoot = path15.resolve(rootPath);
|
|
6333
|
-
if (!
|
|
6336
|
+
if (!fs13.existsSync(resolvedRoot) || !fs13.statSync(resolvedRoot).isDirectory()) {
|
|
6334
6337
|
addDiagnostic(diagnostics, {
|
|
6335
6338
|
severity: "error",
|
|
6336
6339
|
skill: path15.basename(resolvedRoot),
|
|
@@ -6359,13 +6362,7 @@ async function stageAndValidateLocalRoot(rootPath, dependencyRoot, seenSkillPath
|
|
|
6359
6362
|
}
|
|
6360
6363
|
seenSkillPaths.add(resolvedSkillPath);
|
|
6361
6364
|
await emitProgress?.(`validating ${skill.name}`);
|
|
6362
|
-
await validateSkillDir(
|
|
6363
|
-
skill.path,
|
|
6364
|
-
dependencyRoot,
|
|
6365
|
-
diagnostics,
|
|
6366
|
-
strict,
|
|
6367
|
-
thresholds
|
|
6368
|
-
);
|
|
6365
|
+
await validateSkillDir(skill.path, dependencyRoot, diagnostics, strict, thresholds);
|
|
6369
6366
|
}
|
|
6370
6367
|
}
|
|
6371
6368
|
async function stageAndValidateSource(options, diagnostics, thresholds, emitProgress) {
|
|
@@ -6400,27 +6397,19 @@ async function stageAndValidateSource(options, diagnostics, thresholds, emitProg
|
|
|
6400
6397
|
const tempRoots = [];
|
|
6401
6398
|
try {
|
|
6402
6399
|
for (const remoteSkill of remote) {
|
|
6403
|
-
const tmp =
|
|
6404
|
-
path15.join(os5.tmpdir(), "skillspp-validate-")
|
|
6405
|
-
);
|
|
6400
|
+
const tmp = fs13.mkdtempSync(path15.join(os5.tmpdir(), "skillspp-validate-"));
|
|
6406
6401
|
tempRoots.push(tmp);
|
|
6407
6402
|
for (const [relativePath, content] of remoteSkill.files.entries()) {
|
|
6408
6403
|
const target = path15.join(tmp, relativePath);
|
|
6409
|
-
|
|
6410
|
-
|
|
6404
|
+
fs13.mkdirSync(path15.dirname(target), { recursive: true });
|
|
6405
|
+
fs13.writeFileSync(target, content, "utf8");
|
|
6411
6406
|
}
|
|
6412
6407
|
await emitProgress?.(`validating ${remoteSkill.installName}`);
|
|
6413
|
-
await validateSkillDir(
|
|
6414
|
-
tmp,
|
|
6415
|
-
tmp,
|
|
6416
|
-
diagnostics,
|
|
6417
|
-
Boolean(options.strict),
|
|
6418
|
-
thresholds
|
|
6419
|
-
);
|
|
6408
|
+
await validateSkillDir(tmp, tmp, diagnostics, Boolean(options.strict), thresholds);
|
|
6420
6409
|
}
|
|
6421
6410
|
} finally {
|
|
6422
6411
|
for (const tmp of tempRoots) {
|
|
6423
|
-
|
|
6412
|
+
fs13.rmSync(tmp, { recursive: true, force: true });
|
|
6424
6413
|
}
|
|
6425
6414
|
}
|
|
6426
6415
|
return;
|
|
@@ -6466,12 +6455,7 @@ async function runValidateAnalysis(options, emitProgress) {
|
|
|
6466
6455
|
);
|
|
6467
6456
|
}
|
|
6468
6457
|
} else {
|
|
6469
|
-
await stageAndValidateSource(
|
|
6470
|
-
options,
|
|
6471
|
-
diagnostics,
|
|
6472
|
-
thresholds,
|
|
6473
|
-
emitProgress
|
|
6474
|
-
);
|
|
6458
|
+
await stageAndValidateSource(options, diagnostics, thresholds, emitProgress);
|
|
6475
6459
|
}
|
|
6476
6460
|
await emitProgress?.("collecting diagnostics");
|
|
6477
6461
|
return { diagnostics };
|
|
@@ -6499,9 +6483,7 @@ function buildDiagnosticsPanelLines(diagnostics) {
|
|
|
6499
6483
|
return [colorToken("No diagnostics found.", "success")];
|
|
6500
6484
|
}
|
|
6501
6485
|
const sorted = [...diagnostics].sort(
|
|
6502
|
-
(a, b) => `${a.severity}:${a.skill}:${a.rule}`.localeCompare(
|
|
6503
|
-
`${b.severity}:${b.skill}:${b.rule}`
|
|
6504
|
-
)
|
|
6486
|
+
(a, b) => `${a.severity}:${a.skill}:${a.rule}`.localeCompare(`${b.severity}:${b.skill}:${b.rule}`)
|
|
6505
6487
|
);
|
|
6506
6488
|
return sorted.map((row) => {
|
|
6507
6489
|
const marker = row.severity === "error" ? colorToken("[error]", "danger") : colorToken("[warning]", "warning");
|
|
@@ -6572,38 +6554,23 @@ async function executeValidate(options) {
|
|
|
6572
6554
|
const errors = diagnostics.filter((item) => item.severity === "error");
|
|
6573
6555
|
const warnings = diagnostics.filter((item) => item.severity === "warning");
|
|
6574
6556
|
const validatingSteps = [...runtimeSteps].filter((step) => step.startsWith("validating ")).sort((a, b) => a.localeCompare(b));
|
|
6575
|
-
const runStepLines = [
|
|
6576
|
-
"staging source",
|
|
6577
|
-
...validatingSteps,
|
|
6578
|
-
"collecting diagnostics"
|
|
6579
|
-
];
|
|
6557
|
+
const runStepLines = ["staging source", ...validatingSteps, "collecting diagnostics"];
|
|
6580
6558
|
await renderStaticScreen([
|
|
6581
6559
|
completedStepsSection([
|
|
6582
6560
|
"source parsed",
|
|
6583
6561
|
"candidate skills discovered",
|
|
6584
6562
|
"validation session ready"
|
|
6585
6563
|
]),
|
|
6586
|
-
linesSection([
|
|
6587
|
-
` Validation target: ${resolveValidateTargetLabel(options)}`
|
|
6588
|
-
]),
|
|
6564
|
+
linesSection([` Validation target: ${resolveValidateTargetLabel(options)}`]),
|
|
6589
6565
|
panelSection({
|
|
6590
6566
|
title: "Checks Included",
|
|
6591
6567
|
lines: [
|
|
6592
|
-
` ${colorToken(
|
|
6593
|
-
"\u25CF",
|
|
6594
|
-
"primary"
|
|
6595
|
-
)} SKILL.md exists + frontmatter parseability`,
|
|
6568
|
+
` ${colorToken("\u25CF", "primary")} SKILL.md exists + frontmatter parseability`,
|
|
6596
6569
|
` ${colorToken("\u25CF", "primary")} required fields: name, description`,
|
|
6597
6570
|
` ${colorToken("\u25CF", "primary")} name \u2194 directory consistency`,
|
|
6598
6571
|
` ${colorToken("\u25CF", "primary")} markdown reference existence`,
|
|
6599
|
-
` ${colorToken(
|
|
6600
|
-
|
|
6601
|
-
"primary"
|
|
6602
|
-
)} type-specific rules (framework references dir)`,
|
|
6603
|
-
` ${colorToken(
|
|
6604
|
-
"\u25CF",
|
|
6605
|
-
"primary"
|
|
6606
|
-
)} installer dependency security + preflight`,
|
|
6572
|
+
` ${colorToken("\u25CF", "primary")} type-specific rules (framework references dir)`,
|
|
6573
|
+
` ${colorToken("\u25CF", "primary")} installer dependency security + preflight`,
|
|
6607
6574
|
` ${colorToken("\u25CF", "primary")} line/description budget thresholds`
|
|
6608
6575
|
],
|
|
6609
6576
|
style: "square",
|
|
@@ -6637,10 +6604,7 @@ async function executeValidate(options) {
|
|
|
6637
6604
|
}
|
|
6638
6605
|
}
|
|
6639
6606
|
function configureValidateCommand(command, action) {
|
|
6640
|
-
return command.description("Validate skill source structure and references").argument("[source]", "Source path or URL").option("--ci", "Validate multiple local roots for CI").option("--root <paths...>", "Root paths for CI mode").option("--strict", "Escalate warnings to errors").option("--json", "Emit JSON output").option("--max-lines <n>", "SKILL.md line budget threshold").option(
|
|
6641
|
-
"--max-description-chars <n>",
|
|
6642
|
-
"Description length budget threshold"
|
|
6643
|
-
).option("--allow-host <hosts...>", "Restrict well-known hosts to allowlist").option("--deny-host <hosts...>", "Block specific well-known hosts").option("--max-download-bytes <n>", "Set well-known download budget").option("--policy-mode <mode>", "Policy mode (enforce|warn)").action(action);
|
|
6607
|
+
return command.description("Validate skill source structure and references").argument("[source]", "Source path or URL").option("--ci", "Validate multiple local roots for CI").option("--root <paths...>", "Root paths for CI mode").option("--strict", "Escalate warnings to errors").option("--json", "Emit JSON output").option("--max-lines <n>", "SKILL.md line budget threshold").option("--max-description-chars <n>", "Description length budget threshold").option("--allow-host <hosts...>", "Restrict well-known hosts to allowlist").option("--deny-host <hosts...>", "Block specific well-known hosts").option("--max-download-bytes <n>", "Set well-known download budget").option("--policy-mode <mode>", "Policy mode (enforce|warn)").action(action);
|
|
6644
6608
|
}
|
|
6645
6609
|
function registerValidateCommand(program, ctx) {
|
|
6646
6610
|
configureValidateCommand(
|
|
@@ -6680,9 +6644,22 @@ function createCliTelemetryEmitter(sink) {
|
|
|
6680
6644
|
|
|
6681
6645
|
// src/cli.ts
|
|
6682
6646
|
import picocolors from "picocolors";
|
|
6647
|
+
import path16 from "node:path";
|
|
6648
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6683
6649
|
var require2 = createRequire(import.meta.url);
|
|
6684
6650
|
var packageJson = require2("../package.json");
|
|
6685
6651
|
var CLI_VERSION = typeof packageJson.version === "string" ? packageJson.version : "0.1.0";
|
|
6652
|
+
function resolveSkillsppLogoDir() {
|
|
6653
|
+
const moduleDir = path16.dirname(fileURLToPath3(import.meta.url));
|
|
6654
|
+
return path16.resolve(moduleDir, "../src/assets/ascii/logo");
|
|
6655
|
+
}
|
|
6656
|
+
function configureSkillsppLogoAssetPaths() {
|
|
6657
|
+
const logoDir = resolveSkillsppLogoDir();
|
|
6658
|
+
configureLogoAssetPaths({
|
|
6659
|
+
sessionPath: path16.join(logoDir, "skillspp-logo.session.json"),
|
|
6660
|
+
textPath: path16.join(logoDir, "skillspp-logo.txt")
|
|
6661
|
+
});
|
|
6662
|
+
}
|
|
6686
6663
|
function formatCliError(error) {
|
|
6687
6664
|
if (error && typeof error === "object" && "code" in error) {
|
|
6688
6665
|
const code = String(error.code);
|
|
@@ -6706,14 +6683,6 @@ function createProgram(emitter, experimental) {
|
|
|
6706
6683
|
applyExitOverride(program);
|
|
6707
6684
|
return program;
|
|
6708
6685
|
}
|
|
6709
|
-
function applyExitOverride(command) {
|
|
6710
|
-
command.exitOverride((error) => {
|
|
6711
|
-
throw error;
|
|
6712
|
-
});
|
|
6713
|
-
for (const subcommand of command.commands) {
|
|
6714
|
-
applyExitOverride(subcommand);
|
|
6715
|
-
}
|
|
6716
|
-
}
|
|
6717
6686
|
function parseTelemetryFromArgv(argv) {
|
|
6718
6687
|
for (let i = 0; i < argv.length; i += 1) {
|
|
6719
6688
|
if (argv[i] !== "--telemetry") {
|
|
@@ -6723,35 +6692,8 @@ function parseTelemetryFromArgv(argv) {
|
|
|
6723
6692
|
}
|
|
6724
6693
|
return void 0;
|
|
6725
6694
|
}
|
|
6726
|
-
function inferCommandSource(argv) {
|
|
6727
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
6728
|
-
const arg = argv[i];
|
|
6729
|
-
if (arg === "--telemetry") {
|
|
6730
|
-
i += 1;
|
|
6731
|
-
continue;
|
|
6732
|
-
}
|
|
6733
|
-
if (arg.startsWith("-")) {
|
|
6734
|
-
continue;
|
|
6735
|
-
}
|
|
6736
|
-
return arg;
|
|
6737
|
-
}
|
|
6738
|
-
return "cli";
|
|
6739
|
-
}
|
|
6740
|
-
function emitCommanderParseErrorTelemetry(emitter, argv, error) {
|
|
6741
|
-
const source = inferCommandSource(argv);
|
|
6742
|
-
emitLifecycleEvent(emitter, {
|
|
6743
|
-
eventType: `${source}_failed`,
|
|
6744
|
-
source,
|
|
6745
|
-
reason: "commander_parse_error",
|
|
6746
|
-
command: source,
|
|
6747
|
-
status: "error",
|
|
6748
|
-
error: error.message,
|
|
6749
|
-
metadata: {
|
|
6750
|
-
commanderCode: error.code
|
|
6751
|
-
}
|
|
6752
|
-
});
|
|
6753
|
-
}
|
|
6754
6695
|
async function runCli(argv) {
|
|
6696
|
+
configureSkillsppLogoAssetPaths();
|
|
6755
6697
|
const telemetrySink = parseTelemetrySink(parseTelemetryFromArgv(argv));
|
|
6756
6698
|
const emitter = createCliTelemetryEmitter(telemetrySink);
|
|
6757
6699
|
const experimental = argv.includes("--experimental");
|
|
@@ -6764,11 +6706,13 @@ async function runCli(argv) {
|
|
|
6764
6706
|
await program.parseAsync(argv, { from: "user" });
|
|
6765
6707
|
return 0;
|
|
6766
6708
|
} catch (error) {
|
|
6767
|
-
if (
|
|
6709
|
+
if (isGracefulCommanderExit(error)) {
|
|
6768
6710
|
return 0;
|
|
6769
6711
|
}
|
|
6770
6712
|
if (error instanceof CommanderError2) {
|
|
6771
|
-
emitCommanderParseErrorTelemetry(emitter, argv, error
|
|
6713
|
+
emitCommanderParseErrorTelemetry(emitter, argv, error, {
|
|
6714
|
+
valueFlags: ["--telemetry"]
|
|
6715
|
+
});
|
|
6772
6716
|
throw new Error(error.message);
|
|
6773
6717
|
}
|
|
6774
6718
|
throw error;
|
|
@@ -6797,6 +6741,7 @@ runCli(process.argv.slice(2)).then(
|
|
|
6797
6741
|
}
|
|
6798
6742
|
);
|
|
6799
6743
|
export {
|
|
6744
|
+
configureSkillsppLogoAssetPaths,
|
|
6800
6745
|
runCli
|
|
6801
6746
|
};
|
|
6802
6747
|
//# sourceMappingURL=cli.js.map
|