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/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/core/src/runtime/installer.ts
548
- function sanitizeSkillName(name) {
549
- const sanitized = name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[.-]+|[.-]+$/g, "");
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
- function resolveLogoDir() {
632
- const dirname = path5.dirname(fileURLToPath(import.meta.url));
633
- return path5.resolve(dirname, "../assets/ascii/logo");
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
- const customPath = process.env.SKILLSPP_LOGO_SESSION_PATH;
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
- const customPath = process.env.SKILLSPP_LOGO_TEXT_PATH;
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(` Skills source: ${section.source}`);
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
- `Skills (${options.skillNames.length}):`,
1420
- ...options.skillNames.map((name) => ` - ${name}`),
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
- (max, row) => Math.max(max, row.label.length),
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/interactive.ts
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 path6 from "node:path";
2269
+ import path5 from "node:path";
2082
2270
  import { spawn } from "node:child_process";
2083
- import { fileURLToPath as fileURLToPath2 } from "node:url";
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 = path6.dirname(fileURLToPath2(import.meta.url));
2114
- const tsPath = path6.join(dir, "background-worker.ts");
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 path6.join(dir, "background-worker.js");
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
- process.execPath,
2136
- [...process.execArgv, resolveWorkerEntry()],
2137
- {
2138
- cwd: childCwd,
2139
- env: {
2140
- ...process.env,
2141
- SKILLSPP_BG_EXECUTOR: options.executorModule
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
- async function runBackgroundTask2(request, options = {}) {
2209
- return runBackgroundTask(request, {
2210
- onProgress: options.onProgress,
2211
- executorModule: "@skillspp/core/runtime/background-tasks"
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
- buildAddSkillSelectionRows(options.skills),
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 filterByRequestedName = (items, requested) => {
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 = (selectedNames2) => renderAddSkillsSection({
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: selectedNames2
2562
+ selectedNames
2382
2563
  });
2383
- if (merged.skill) {
2384
- const selected = filterByRequestedName(available, merged.skill);
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
- shouldPrompt: true,
2434
- prompt: {
2435
- title: "Choose Skills",
2436
- required: true,
2437
- requiredMessage: "At least one skill must be selected",
2438
- searchable: true,
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
- const maxDownloadBytes = options.maxDownloadBytes ? Number(options.maxDownloadBytes) : void 0;
2577
- if (typeof maxDownloadBytes === "number" && (!Number.isFinite(maxDownloadBytes) || maxDownloadBytes <= 0)) {
2578
- throw new Error(
2579
- `Invalid --max-download-bytes value: ${options.maxDownloadBytes}`
2580
- );
2581
- }
2582
- const parsed = {
2583
- global: Boolean(options.global),
2584
- symlink: Boolean(options.symlink),
2585
- yaml: Boolean(options.yaml),
2586
- list: Boolean(options.list),
2587
- all: Boolean(options.all),
2588
- nonInteractive: Boolean(options.nonInteractive),
2589
- trustWellKnown: Boolean(options.trustWellKnown),
2590
- agent: normalizeAgentSelectionInput(options.agent),
2591
- skill: options.skill,
2592
- allowHost: options.allowHost?.map((item) => item.toLowerCase()),
2593
- denyHost: options.denyHost?.map((item) => item.toLowerCase()),
2594
- maxDownloadBytes,
2595
- policyMode: parsePolicyMode(options.policyMode),
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 fs5 from "node:fs";
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 (!fs5.existsSync(parsed.localPath)) {
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 = fs5.mkdtempSync(path8.join(os3.tmpdir(), "skillspp-cli-"));
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
- fs5.rmSync(tmp, { recursive: true, force: true });
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
- "github.com",
2877
- "gitlab.com",
2878
- "raw.githubusercontent.com"
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 path16 = parsed.pathname.replace(/\/$/, "");
2900
- return path16 && path16 !== "/" ? `wellknown/${parsed.hostname}${path16}` : `wellknown/${parsed.hostname}`;
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
- parsed,
2912
- normalized,
2913
- budget
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 skills;
3044
+ return resources;
2928
3045
  }
2929
3046
  normalizeOptions(options) {
2930
3047
  return {
2931
- allowHosts: (options.allowHosts || []).map((x) => x.trim().toLowerCase()).filter(Boolean),
2932
- denyHosts: (options.denyHosts || []).map((x) => x.trim().toLowerCase()).filter(Boolean),
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}/.well-known/skills/index.json`;
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 = "/.well-known/skills";
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((x) => x.endsWith("/") ? x.slice(0, -1) : x))
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
- if (!Array.isArray(data.skills)) {
2989
- throw new Error("Invalid well-known index: 'skills' must be an array");
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
- const skills = data.skills.map((item, idx) => {
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 = String(row.description || "").trim();
2998
- const files = Array.isArray(row.files) ? row.files.map((x) => String(x)) : [];
2999
- if (!name || !description || files.length === 0) {
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 (!/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/.test(name)) {
3005
- throw new Error(`Invalid well-known skill name: ${name}`);
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 skill '${name}'`);
3123
+ throw new Error(`Too many files in well-known ${config.entryLabel} '${name}'`);
3009
3124
  }
3010
- if (!files.some((f) => f.toLowerCase() === "skill.md")) {
3011
- throw new Error(`Well-known skill '${name}' is missing SKILL.md`);
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 fetchSkillByEntry(resolvedBase, entry, options, budget) {
3026
- const baseUrl = `${resolvedBase}/.well-known/skills/${entry.name}`;
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 skillContent = files.get("SKILL.md") || files.get("skill.md");
3045
- if (!skillContent) {
3046
- return null;
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
- let received = 0;
3113
- const chunks = [];
3114
- while (true) {
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 -= received;
3134
- const total = new Uint8Array(received);
3135
- let offset = 0;
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("utf-8", { fatal: false }).decode(total);
3221
+ return new TextDecoder("utf8").decode(bytes);
3141
3222
  }
3142
3223
  }
3143
3224
  async assertHostAllowed(hostname, options) {
3144
- const host = hostname.toLowerCase();
3145
- if (options.denyHosts.includes(host)) {
3146
- throw new Error(`Well-known host denied by policy: ${hostname}`);
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(host)) {
3149
- throw new Error(`Well-known host is not in allowlist: ${hostname}`);
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 (this.isLocalHostname(host)) {
3152
- throw new Error(`Well-known host is not allowed: ${hostname}`);
3232
+ if (isLocalHostname(lowerHost)) {
3233
+ throw new Error(`Local or loopback host '${hostname}' is not allowed`);
3153
3234
  }
3154
- const records = await this.resolveHostIps(host);
3155
- for (const ip of records) {
3156
- if (this.isPrivateOrLocalIp(ip)) {
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
- isLocalHostname(host) {
3164
- return host === "localhost" || host.endsWith(".local") || host.endsWith(".internal") || host === "0.0.0.0";
3165
- }
3166
- async resolveHostIps(hostname) {
3167
- const out = /* @__PURE__ */ new Set();
3168
- try {
3169
- const records = await dns.lookup(hostname, { all: true });
3170
- for (const record of records) {
3171
- out.add(record.address);
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
- return [...out];
3251
+ } catch {
3176
3252
  }
3177
- isPrivateOrLocalIp(ip) {
3178
- if (!net.isIP(ip)) {
3179
- return false;
3180
- }
3181
- if (net.isIPv4(ip)) {
3182
- const parts = ip.split(".").map((x) => Number(x));
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 = parsed.pathname.endsWith(".json") ? parsed.toString() : new URL(
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.skills) {
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
- name: row.name,
3265
- description: row.description,
3266
- installName: row.name,
3267
- sourceUrl: new URL(`${row.name}/SKILL.md`, indexBase).toString(),
3268
- sourceType: "catalog",
3269
- files
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
- if (!Array.isArray(data.skills)) {
3299
- throw new Error("Invalid catalog index: 'skills' must be an array");
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
- const skills = data.skills.map((item, idx) => {
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 = String(row.description || "").trim();
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 || !description || files.length === 0) {
3310
- throw new Error(
3311
- `Invalid catalog index entry[${idx}]: missing required fields`
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 skill '${name}'`);
3438
+ throw new Error(`Too many files in catalog ${config.kind.slice(0, -1)} '${name}'`);
3316
3439
  }
3317
- if (!files.some((f) => f.toLowerCase() === "skill.md")) {
3318
- throw new Error(`Catalog skill '${name}' is missing SKILL.md`);
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
- return { skills };
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 fs6 from "node:fs";
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 = fs6.existsSync(jsonPath) ? parseLockPayload(fs6.readFileSync(jsonPath, "utf8"), "json") : fs6.existsSync(yamlPath) ? parseLockPayload(fs6.readFileSync(yamlPath, "utf8"), "yaml") : null;
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 listInstalledSkillDirs(globalInstall, cwd) {
3557
+ function listInstalledResourceDirs(kind, globalInstall, cwd) {
3423
3558
  const out = /* @__PURE__ */ new Set();
3424
3559
  for (const agent of Object.keys(AGENTS)) {
3425
- const skillsRoot = getAgentSkillsDir(agent, globalInstall, cwd);
3426
- if (!fs6.existsSync(skillsRoot) || !fs6.statSync(skillsRoot).isDirectory()) {
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 fs6.readdirSync(skillsRoot, { withFileTypes: true })) {
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(skillsRoot, entry.name));
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 listInstalledSkillDirs(globalInstall, cwd)) {
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 fs8 from "node:fs";
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 fs7 from "node:fs";
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 fs7.existsSync(filePath) && fs7.statSync(filePath).isFile();
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
- fs7.writeFileSync(destinationPath, `${content}
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 = !fs8.existsSync(skillDir);
4337
- fs8.mkdirSync(skillDir, { recursive: true });
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 (fs8.existsSync(skillFile)) {
4450
+ if (fs9.existsSync(skillFile)) {
4343
4451
  throw new Error(`SKILL.md already exists at: ${skillFile}`);
4344
4452
  }
4345
- fs8.writeFileSync(skillFile, `${renderSkillContent(answers)}
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 (fs8.existsSync(destination)) {
4462
+ if (fs9.existsSync(destination)) {
4355
4463
  throw new Error(`Agent config already exists at: ${destination}`);
4356
4464
  }
4357
- fs8.mkdirSync(path11.dirname(destination), { recursive: true });
4358
- fs8.writeFileSync(destination, row.content, "utf8");
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
- "init",
4385
- async (name, options) => {
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 fs9 from "node:fs";
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(options);
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 (!fs9.existsSync(dir) || !fs9.statSync(dir).isDirectory()) {
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 fs9.readdirSync(dir, { withFileTypes: true })) {
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 (!fs9.existsSync(dir) || !fs9.statSync(dir).isDirectory()) {
5100
+ if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
5026
5101
  continue;
5027
5102
  }
5028
- for (const entry of fs9.readdirSync(dir, { withFileTypes: true })) {
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
- fs9.rmSync(fullPath, { recursive: true, force: true });
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
- "remove",
5061
- async (skills, options) => {
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
- "update",
5444
- async (skill, options) => {
5445
- await executeUpdate(
5446
- {
5447
- ...toUpdateOptions(options),
5448
- experimental: ctx.experimental
5449
- },
5450
- skill
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 fs12 from "node:fs";
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 fs11 from "node:fs";
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 fs10 from "node:fs";
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 (fs10.existsSync(resolvedSourcePath)) {
5508
- const realRoot = fs10.realpathSync.native ? fs10.realpathSync.native(resolvedRoot) : fs10.realpathSync(resolvedRoot);
5509
- const realSource = fs10.realpathSync.native ? fs10.realpathSync.native(resolvedSourcePath) : fs10.realpathSync(resolvedSourcePath);
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 (!fs11.existsSync(openAiYamlPath) || !fs11.statSync(openAiYamlPath).isFile()) {
5756
+ if (!fs12.existsSync(openAiYamlPath) || !fs12.statSync(openAiYamlPath).isFile()) {
5721
5757
  return;
5722
5758
  }
5723
- const parsed = YAML4.parse(fs11.readFileSync(openAiYamlPath, "utf8"));
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 = fs11.existsSync(yamlPath) && fs11.statSync(yamlPath).isFile();
5735
- const hasJson = fs11.existsSync(jsonPath) && fs11.statSync(jsonPath).isFile();
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(fs11.readFileSync(yamlPath, "utf8"));
5786
+ const parsed = YAML4.parse(fs12.readFileSync(yamlPath, "utf8"));
5751
5787
  return parseConfigObject(parsed);
5752
5788
  }
5753
- const raw = JSON.parse(fs11.readFileSync(jsonPath, "utf8"));
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
- "git",
5771
- ["clone", "--depth", "1", repoUrl, targetDir],
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 (!fs11.existsSync(sourcePath)) {
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
- fs11.accessSync(sourcePath, fs11.constants.R_OK);
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
- fs11.mkdirSync(path14.dirname(stagedFilePath), { recursive: true });
5864
- fs11.writeFileSync(stagedFilePath, Buffer.from(await response.arrayBuffer()));
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 = fs11.mkdtempSync(
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 (fs11.existsSync(destinationInSkill)) {
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
- fs11.rmSync(stagingDir, { recursive: true, force: true });
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
- fs11.rmSync(prepared.stagingDir, { recursive: true, force: true });
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 (!fs12.existsSync(configPath) || !fs12.statSync(configPath).isFile()) {
6020
+ if (!fs13.existsSync(configPath) || !fs13.statSync(configPath).isFile()) {
6005
6021
  return {};
6006
6022
  }
6007
- const parsed = JSON.parse(fs12.readFileSync(configPath, "utf8"));
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 (!fs12.existsSync(referencesDir) || !fs12.statSync(referencesDir).isDirectory()) {
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 = fs12.existsSync(
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 = fs12.mkdtempSync(
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 (fs12.existsSync(src) && fs12.statSync(src).isFile()) {
6110
- fs12.copyFileSync(src, path15.join(tempSkillDir, fileName));
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 (fs12.existsSync(agentsDir) && fs12.statSync(agentsDir).isDirectory()) {
6115
- fs12.cpSync(agentsDir, path15.join(tempSkillDir, "agents"), {
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
- fs12.rmSync(tempSkillDir, { recursive: true, force: true });
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 (!fs12.existsSync(skillMd)) {
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 = fs12.readFileSync(skillMd, "utf8");
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 (!fs12.existsSync(resolved)) {
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 (fs12.existsSync(packagesDir) && fs12.statSync(packagesDir).isDirectory()) {
6299
- for (const entry of fs12.readdirSync(packagesDir, { withFileTypes: true })) {
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 (!fs12.existsSync(item)) {
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) => fs12.existsSync(item) && fs12.statSync(item).isDirectory());
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 (!fs12.existsSync(resolvedRoot) || !fs12.statSync(resolvedRoot).isDirectory()) {
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 = fs12.mkdtempSync(
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
- fs12.mkdirSync(path15.dirname(target), { recursive: true });
6410
- fs12.writeFileSync(target, content, "utf8");
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
- fs12.rmSync(tmp, { recursive: true, force: true });
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
- "\u25CF",
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 (error instanceof CommanderError2 && (error.code === "commander.helpDisplayed" || error.code === "commander.version")) {
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