ctx7 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -3
- package/dist/index.js +274 -205
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -305,113 +305,171 @@ var log = {
|
|
|
305
305
|
};
|
|
306
306
|
|
|
307
307
|
// src/utils/ide.ts
|
|
308
|
-
import
|
|
309
|
-
import { select,
|
|
308
|
+
import pc3 from "picocolors";
|
|
309
|
+
import { select, confirm } from "@inquirer/prompts";
|
|
310
310
|
import { access } from "fs/promises";
|
|
311
|
-
import { join } from "path";
|
|
311
|
+
import { join, dirname } from "path";
|
|
312
312
|
import { homedir } from "os";
|
|
313
313
|
|
|
314
|
+
// src/utils/prompts.ts
|
|
315
|
+
import pc2 from "picocolors";
|
|
316
|
+
import { checkbox } from "@inquirer/prompts";
|
|
317
|
+
import readline from "readline";
|
|
318
|
+
function terminalLink(text, url, color) {
|
|
319
|
+
const colorFn = color ?? ((s) => s);
|
|
320
|
+
return `\x1B]8;;${url}\x07${colorFn(text)}\x1B]8;;\x07 ${pc2.white("\u2197")}`;
|
|
321
|
+
}
|
|
322
|
+
function formatInstallCount(count, placeholder = "") {
|
|
323
|
+
if (count === void 0 || count === 0) return placeholder;
|
|
324
|
+
return pc2.yellow(String(count));
|
|
325
|
+
}
|
|
326
|
+
function formatTrustScore(score) {
|
|
327
|
+
if (score === void 0 || score < 0) return pc2.dim("-");
|
|
328
|
+
if (score < 3) return pc2.red(score.toFixed(1));
|
|
329
|
+
return pc2.yellow(score.toFixed(1));
|
|
330
|
+
}
|
|
331
|
+
async function checkboxWithHover(config, options) {
|
|
332
|
+
const choices = config.choices.filter(
|
|
333
|
+
(c) => typeof c === "object" && c !== null && !("type" in c && c.type === "separator")
|
|
334
|
+
);
|
|
335
|
+
const values = choices.map((c) => c.value);
|
|
336
|
+
const totalItems = values.length;
|
|
337
|
+
let cursorPosition = 0;
|
|
338
|
+
const getName = options?.getName ?? ((v) => v.name);
|
|
339
|
+
const keypressHandler = (_str, key) => {
|
|
340
|
+
if (key.name === "up" && cursorPosition > 0) {
|
|
341
|
+
cursorPosition--;
|
|
342
|
+
} else if (key.name === "down" && cursorPosition < totalItems - 1) {
|
|
343
|
+
cursorPosition++;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
readline.emitKeypressEvents(process.stdin);
|
|
347
|
+
process.stdin.on("keypress", keypressHandler);
|
|
348
|
+
const customConfig = {
|
|
349
|
+
...config,
|
|
350
|
+
theme: {
|
|
351
|
+
...config.theme,
|
|
352
|
+
style: {
|
|
353
|
+
...config.theme?.style,
|
|
354
|
+
highlight: (text) => pc2.green(text),
|
|
355
|
+
renderSelectedChoices: (selected, _allChoices) => {
|
|
356
|
+
if (selected.length === 0) {
|
|
357
|
+
return pc2.dim(getName(values[cursorPosition]));
|
|
358
|
+
}
|
|
359
|
+
return selected.map((c) => getName(c.value)).join(", ");
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
try {
|
|
365
|
+
const selected = await checkbox(customConfig);
|
|
366
|
+
if (selected.length === 0) {
|
|
367
|
+
return [values[cursorPosition]];
|
|
368
|
+
}
|
|
369
|
+
return selected;
|
|
370
|
+
} finally {
|
|
371
|
+
process.stdin.removeListener("keypress", keypressHandler);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
314
375
|
// src/types.ts
|
|
315
376
|
var IDE_PATHS = {
|
|
316
377
|
claude: ".claude/skills",
|
|
317
378
|
cursor: ".cursor/skills",
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
amp: ".agents/skills",
|
|
321
|
-
antigravity: ".agent/skills"
|
|
379
|
+
antigravity: ".agent/skills",
|
|
380
|
+
universal: ".agents/skills"
|
|
322
381
|
};
|
|
323
382
|
var IDE_GLOBAL_PATHS = {
|
|
324
383
|
claude: ".claude/skills",
|
|
325
384
|
cursor: ".cursor/skills",
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
amp: ".config/agents/skills",
|
|
329
|
-
antigravity: ".agent/skills"
|
|
385
|
+
antigravity: ".agent/skills",
|
|
386
|
+
universal: ".config/agents/skills"
|
|
330
387
|
};
|
|
331
388
|
var IDE_NAMES = {
|
|
332
389
|
claude: "Claude Code",
|
|
333
390
|
cursor: "Cursor",
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
amp: "Amp",
|
|
337
|
-
antigravity: "Antigravity"
|
|
391
|
+
antigravity: "Antigravity",
|
|
392
|
+
universal: "Universal"
|
|
338
393
|
};
|
|
394
|
+
var UNIVERSAL_SKILLS_PATH = ".agents/skills";
|
|
395
|
+
var UNIVERSAL_SKILLS_GLOBAL_PATH = ".config/agents/skills";
|
|
396
|
+
var UNIVERSAL_AGENTS_LABEL = "Amp, Codex, Gemini CLI, GitHub Copilot, OpenCode + more";
|
|
397
|
+
var VENDOR_SPECIFIC_AGENTS = ["claude", "cursor", "antigravity"];
|
|
339
398
|
var DEFAULT_CONFIG = {
|
|
340
|
-
defaultIde: "
|
|
399
|
+
defaultIde: "universal",
|
|
341
400
|
defaultScope: "project"
|
|
342
401
|
};
|
|
343
402
|
|
|
344
403
|
// src/utils/ide.ts
|
|
345
|
-
import { dirname } from "path";
|
|
346
404
|
function getSelectedIdes(options) {
|
|
347
405
|
const ides = [];
|
|
348
406
|
if (options.claude) ides.push("claude");
|
|
349
407
|
if (options.cursor) ides.push("cursor");
|
|
350
|
-
if (options.
|
|
351
|
-
if (options.opencode) ides.push("opencode");
|
|
352
|
-
if (options.amp) ides.push("amp");
|
|
408
|
+
if (options.universal) ides.push("universal");
|
|
353
409
|
if (options.antigravity) ides.push("antigravity");
|
|
354
410
|
return ides;
|
|
355
411
|
}
|
|
356
412
|
function hasExplicitIdeOption(options) {
|
|
357
|
-
return !!(options.claude || options.cursor || options.
|
|
358
|
-
}
|
|
359
|
-
async function
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const globalParent = join(homedir(), detectionPath);
|
|
366
|
-
try {
|
|
367
|
-
await access(globalParent);
|
|
368
|
-
globalIdes.push(ide);
|
|
369
|
-
} catch {
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
if (globalIdes.length > 0) {
|
|
373
|
-
return { ides: globalIdes, scope: "global" };
|
|
374
|
-
}
|
|
375
|
-
return null;
|
|
376
|
-
}
|
|
377
|
-
const projectIdes = [];
|
|
378
|
-
for (const ide of allIdes) {
|
|
379
|
-
const detectionPath = dirname(IDE_PATHS[ide]);
|
|
380
|
-
const projectParent = join(process.cwd(), detectionPath);
|
|
413
|
+
return !!(options.claude || options.cursor || options.universal || options.antigravity);
|
|
414
|
+
}
|
|
415
|
+
async function detectVendorSpecificAgents(scope) {
|
|
416
|
+
const baseDir = scope === "global" ? homedir() : process.cwd();
|
|
417
|
+
const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
|
|
418
|
+
const detected = [];
|
|
419
|
+
for (const ide of VENDOR_SPECIFIC_AGENTS) {
|
|
420
|
+
const parentDir = dirname(pathMap[ide]);
|
|
381
421
|
try {
|
|
382
|
-
await access(
|
|
383
|
-
|
|
422
|
+
await access(join(baseDir, parentDir));
|
|
423
|
+
detected.push(ide);
|
|
384
424
|
} catch {
|
|
385
425
|
}
|
|
386
426
|
}
|
|
387
|
-
|
|
388
|
-
|
|
427
|
+
return detected;
|
|
428
|
+
}
|
|
429
|
+
function getUniversalDir(scope) {
|
|
430
|
+
if (scope === "global") {
|
|
431
|
+
return join(homedir(), UNIVERSAL_SKILLS_GLOBAL_PATH);
|
|
389
432
|
}
|
|
390
|
-
return
|
|
433
|
+
return join(process.cwd(), UNIVERSAL_SKILLS_PATH);
|
|
391
434
|
}
|
|
392
435
|
async function promptForInstallTargets(options) {
|
|
393
436
|
if (hasExplicitIdeOption(options)) {
|
|
394
|
-
const
|
|
437
|
+
const ides2 = getSelectedIdes(options);
|
|
395
438
|
const scope2 = options.global ? "global" : "project";
|
|
396
439
|
return {
|
|
397
|
-
ides:
|
|
440
|
+
ides: ides2.length > 0 ? ides2 : [DEFAULT_CONFIG.defaultIde],
|
|
398
441
|
scopes: [scope2]
|
|
399
442
|
};
|
|
400
443
|
}
|
|
401
|
-
const
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
444
|
+
const scope = options.global ? "global" : "project";
|
|
445
|
+
const baseDir = scope === "global" ? homedir() : process.cwd();
|
|
446
|
+
const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
|
|
447
|
+
const universalPath = scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH;
|
|
448
|
+
const detectedVendor = await detectVendorSpecificAgents(scope);
|
|
449
|
+
let hasUniversalDir = false;
|
|
450
|
+
try {
|
|
451
|
+
await access(join(baseDir, dirname(universalPath)));
|
|
452
|
+
hasUniversalDir = true;
|
|
453
|
+
} catch {
|
|
454
|
+
}
|
|
455
|
+
const detectedIdes = [
|
|
456
|
+
...hasUniversalDir ? ["universal"] : [],
|
|
457
|
+
...detectedVendor
|
|
458
|
+
];
|
|
459
|
+
if (detectedIdes.length > 0) {
|
|
460
|
+
const pathLines = [];
|
|
461
|
+
if (hasUniversalDir) {
|
|
462
|
+
pathLines.push(join(baseDir, universalPath));
|
|
463
|
+
}
|
|
464
|
+
for (const ide of detectedVendor) {
|
|
465
|
+
pathLines.push(join(baseDir, pathMap[ide]));
|
|
466
|
+
}
|
|
409
467
|
log.blank();
|
|
410
468
|
let confirmed;
|
|
411
469
|
try {
|
|
412
470
|
confirmed = await confirm({
|
|
413
471
|
message: `Install to detected location(s)?
|
|
414
|
-
${
|
|
472
|
+
${pc3.dim(pathLines.join("\n"))}`,
|
|
415
473
|
default: true
|
|
416
474
|
});
|
|
417
475
|
} catch {
|
|
@@ -421,35 +479,45 @@ ${pc2.dim(pathList)}`,
|
|
|
421
479
|
log.warn("Installation cancelled");
|
|
422
480
|
return null;
|
|
423
481
|
}
|
|
424
|
-
return { ides:
|
|
482
|
+
return { ides: detectedIdes, scopes: [scope] };
|
|
483
|
+
}
|
|
484
|
+
const universalLabel = `Universal \u2014 ${UNIVERSAL_AGENTS_LABEL} ${pc3.dim(`(${universalPath})`)}`;
|
|
485
|
+
const choices = [
|
|
486
|
+
{ name: universalLabel, value: "universal", checked: true }
|
|
487
|
+
];
|
|
488
|
+
for (const ide of VENDOR_SPECIFIC_AGENTS) {
|
|
489
|
+
choices.push({
|
|
490
|
+
name: `${IDE_NAMES[ide]} ${pc3.dim(`(${pathMap[ide]})`)}`,
|
|
491
|
+
value: ide,
|
|
492
|
+
checked: false
|
|
493
|
+
});
|
|
425
494
|
}
|
|
426
495
|
log.blank();
|
|
427
|
-
const scope = options.global ? "global" : "project";
|
|
428
|
-
const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
|
|
429
|
-
const baseDir = scope === "global" ? homedir() : process.cwd();
|
|
430
|
-
const ideChoices = Object.keys(IDE_NAMES).map((ide) => ({
|
|
431
|
-
name: `${IDE_NAMES[ide]} ${pc2.dim(`(${pathMap[ide]})`)}`,
|
|
432
|
-
value: ide,
|
|
433
|
-
checked: ide === DEFAULT_CONFIG.defaultIde
|
|
434
|
-
}));
|
|
435
496
|
let selectedIdes;
|
|
436
497
|
try {
|
|
437
|
-
selectedIdes = await
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
498
|
+
selectedIdes = await checkboxWithHover(
|
|
499
|
+
{
|
|
500
|
+
message: `Which agents do you want to install to?
|
|
501
|
+
${pc3.dim(` ${baseDir}`)}`,
|
|
502
|
+
choices,
|
|
503
|
+
loop: false,
|
|
504
|
+
theme: {
|
|
505
|
+
style: {
|
|
506
|
+
highlight: (text) => pc3.green(text),
|
|
507
|
+
message: (text, status) => {
|
|
508
|
+
if (status === "done") return pc3.dim(text.split("\n")[0]);
|
|
509
|
+
return pc3.bold(text);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
{ getName: (ide) => IDE_NAMES[ide] }
|
|
515
|
+
);
|
|
445
516
|
} catch {
|
|
446
517
|
return null;
|
|
447
518
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
return null;
|
|
451
|
-
}
|
|
452
|
-
return { ides: selectedIdes, scopes: [scope] };
|
|
519
|
+
const ides = ["universal", ...selectedIdes.filter((ide) => ide !== "universal")];
|
|
520
|
+
return { ides, scopes: [scope] };
|
|
453
521
|
}
|
|
454
522
|
async function promptForSingleTarget(options) {
|
|
455
523
|
if (hasExplicitIdeOption(options)) {
|
|
@@ -459,18 +527,22 @@ async function promptForSingleTarget(options) {
|
|
|
459
527
|
return { ide, scope };
|
|
460
528
|
}
|
|
461
529
|
log.blank();
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
530
|
+
const universalLabel = `Universal ${pc3.dim(`(${UNIVERSAL_SKILLS_PATH})`)}`;
|
|
531
|
+
const choices = [{ name: universalLabel, value: "universal" }];
|
|
532
|
+
for (const ide of VENDOR_SPECIFIC_AGENTS) {
|
|
533
|
+
choices.push({
|
|
534
|
+
name: `${IDE_NAMES[ide]} ${pc3.dim(`(${IDE_PATHS[ide]})`)}`,
|
|
535
|
+
value: ide
|
|
536
|
+
});
|
|
537
|
+
}
|
|
466
538
|
let selectedIde;
|
|
467
539
|
try {
|
|
468
540
|
selectedIde = await select({
|
|
469
|
-
message: "Which
|
|
470
|
-
choices
|
|
541
|
+
message: "Which location?",
|
|
542
|
+
choices,
|
|
471
543
|
default: DEFAULT_CONFIG.defaultIde,
|
|
472
544
|
loop: false,
|
|
473
|
-
theme: { style: { highlight: (text) =>
|
|
545
|
+
theme: { style: { highlight: (text) => pc3.green(text) } }
|
|
474
546
|
});
|
|
475
547
|
} catch {
|
|
476
548
|
return null;
|
|
@@ -484,17 +556,17 @@ async function promptForSingleTarget(options) {
|
|
|
484
556
|
message: "Which scope?",
|
|
485
557
|
choices: [
|
|
486
558
|
{
|
|
487
|
-
name: `Project ${
|
|
559
|
+
name: `Project ${pc3.dim("(current directory)")}`,
|
|
488
560
|
value: "project"
|
|
489
561
|
},
|
|
490
562
|
{
|
|
491
|
-
name: `Global ${
|
|
563
|
+
name: `Global ${pc3.dim("(home directory)")}`,
|
|
492
564
|
value: "global"
|
|
493
565
|
}
|
|
494
566
|
],
|
|
495
567
|
default: DEFAULT_CONFIG.defaultScope,
|
|
496
568
|
loop: false,
|
|
497
|
-
theme: { style: { highlight: (text) =>
|
|
569
|
+
theme: { style: { highlight: (text) => pc3.green(text) } }
|
|
498
570
|
});
|
|
499
571
|
} catch {
|
|
500
572
|
return null;
|
|
@@ -503,91 +575,32 @@ async function promptForSingleTarget(options) {
|
|
|
503
575
|
return { ide: selectedIde, scope: selectedScope };
|
|
504
576
|
}
|
|
505
577
|
function getTargetDirs(targets) {
|
|
506
|
-
const
|
|
507
|
-
if (a === "claude") return -1;
|
|
508
|
-
if (b === "claude") return 1;
|
|
509
|
-
return 0;
|
|
510
|
-
});
|
|
578
|
+
const hasUniversal = targets.ides.some((ide) => ide === "universal");
|
|
511
579
|
const dirs = [];
|
|
512
|
-
for (const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
580
|
+
for (const scope of targets.scopes) {
|
|
581
|
+
const baseDir = scope === "global" ? homedir() : process.cwd();
|
|
582
|
+
if (hasUniversal) {
|
|
583
|
+
const uniPath = scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH;
|
|
584
|
+
dirs.push(join(baseDir, uniPath));
|
|
585
|
+
}
|
|
586
|
+
for (const ide of targets.ides) {
|
|
587
|
+
if (ide === "universal") continue;
|
|
588
|
+
const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
|
|
589
|
+
dirs.push(join(baseDir, pathMap[ide]));
|
|
519
590
|
}
|
|
520
591
|
}
|
|
521
592
|
return dirs;
|
|
522
593
|
}
|
|
523
594
|
function getTargetDirFromSelection(ide, scope) {
|
|
595
|
+
if (ide === "universal") {
|
|
596
|
+
return getUniversalDir(scope);
|
|
597
|
+
}
|
|
524
598
|
if (scope === "global") {
|
|
525
599
|
return join(homedir(), IDE_GLOBAL_PATHS[ide]);
|
|
526
600
|
}
|
|
527
601
|
return join(process.cwd(), IDE_PATHS[ide]);
|
|
528
602
|
}
|
|
529
603
|
|
|
530
|
-
// src/utils/prompts.ts
|
|
531
|
-
import pc3 from "picocolors";
|
|
532
|
-
import { checkbox as checkbox2 } from "@inquirer/prompts";
|
|
533
|
-
import readline from "readline";
|
|
534
|
-
function terminalLink(text, url, color) {
|
|
535
|
-
const colorFn = color ?? ((s) => s);
|
|
536
|
-
return `\x1B]8;;${url}\x07${colorFn(text)}\x1B]8;;\x07 ${pc3.white("\u2197")}`;
|
|
537
|
-
}
|
|
538
|
-
function formatInstallCount(count, placeholder = "") {
|
|
539
|
-
if (count === void 0 || count === 0) return placeholder;
|
|
540
|
-
return pc3.yellow(String(count));
|
|
541
|
-
}
|
|
542
|
-
function formatTrustScore(score) {
|
|
543
|
-
if (score === void 0 || score < 0) return pc3.dim("-");
|
|
544
|
-
if (score < 3) return pc3.red(score.toFixed(1));
|
|
545
|
-
return pc3.yellow(score.toFixed(1));
|
|
546
|
-
}
|
|
547
|
-
async function checkboxWithHover(config, options) {
|
|
548
|
-
const choices = config.choices.filter(
|
|
549
|
-
(c) => typeof c === "object" && c !== null && !("type" in c && c.type === "separator")
|
|
550
|
-
);
|
|
551
|
-
const values = choices.map((c) => c.value);
|
|
552
|
-
const totalItems = values.length;
|
|
553
|
-
let cursorPosition = 0;
|
|
554
|
-
const getName = options?.getName ?? ((v) => v.name);
|
|
555
|
-
const keypressHandler = (_str, key) => {
|
|
556
|
-
if (key.name === "up" && cursorPosition > 0) {
|
|
557
|
-
cursorPosition--;
|
|
558
|
-
} else if (key.name === "down" && cursorPosition < totalItems - 1) {
|
|
559
|
-
cursorPosition++;
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
readline.emitKeypressEvents(process.stdin);
|
|
563
|
-
process.stdin.on("keypress", keypressHandler);
|
|
564
|
-
const customConfig = {
|
|
565
|
-
...config,
|
|
566
|
-
theme: {
|
|
567
|
-
...config.theme,
|
|
568
|
-
style: {
|
|
569
|
-
...config.theme?.style,
|
|
570
|
-
highlight: (text) => pc3.green(text),
|
|
571
|
-
renderSelectedChoices: (selected, _allChoices) => {
|
|
572
|
-
if (selected.length === 0) {
|
|
573
|
-
return pc3.dim(getName(values[cursorPosition]));
|
|
574
|
-
}
|
|
575
|
-
return selected.map((c) => getName(c.value)).join(", ");
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
try {
|
|
581
|
-
const selected = await checkbox2(customConfig);
|
|
582
|
-
if (selected.length === 0) {
|
|
583
|
-
return [values[cursorPosition]];
|
|
584
|
-
}
|
|
585
|
-
return selected;
|
|
586
|
-
} finally {
|
|
587
|
-
process.stdin.removeListener("keypress", keypressHandler);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
604
|
// src/utils/installer.ts
|
|
592
605
|
import { mkdir, writeFile, rm, symlink, lstat } from "fs/promises";
|
|
593
606
|
import { join as join2 } from "path";
|
|
@@ -1062,7 +1075,7 @@ var selectOrInput_default = selectOrInput;
|
|
|
1062
1075
|
|
|
1063
1076
|
// src/commands/generate.ts
|
|
1064
1077
|
function registerGenerateCommand(skillCommand) {
|
|
1065
|
-
skillCommand.command("generate").alias("gen").alias("g").option("-o, --output <dir>", "Output directory (default: current directory)").option("--all", "Generate for all detected IDEs").option("--global", "Generate in global skills directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--
|
|
1078
|
+
skillCommand.command("generate").alias("gen").alias("g").option("-o, --output <dir>", "Output directory (default: current directory)").option("--all", "Generate for all detected IDEs").option("--global", "Generate in global skills directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Generate a skill for a library using AI").action(async (options) => {
|
|
1066
1079
|
await generateCommand(options);
|
|
1067
1080
|
});
|
|
1068
1081
|
}
|
|
@@ -1582,48 +1595,56 @@ async function detectProjectDependencies(cwd) {
|
|
|
1582
1595
|
// src/commands/skill.ts
|
|
1583
1596
|
function logInstallSummary(targets, targetDirs, skillNames) {
|
|
1584
1597
|
log.blank();
|
|
1598
|
+
const hasUniversal = targets.ides.some((ide) => ide === "universal");
|
|
1599
|
+
const vendorIdes = targets.ides.filter((ide) => ide !== "universal");
|
|
1585
1600
|
let dirIndex = 0;
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
log.
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1601
|
+
if (hasUniversal && dirIndex < targetDirs.length) {
|
|
1602
|
+
log.plain(`${pc7.bold("Universal")} ${pc7.dim(targetDirs[dirIndex])}`);
|
|
1603
|
+
for (const name of skillNames) {
|
|
1604
|
+
log.itemAdd(name);
|
|
1605
|
+
}
|
|
1606
|
+
dirIndex++;
|
|
1607
|
+
}
|
|
1608
|
+
for (const ide of vendorIdes) {
|
|
1609
|
+
if (dirIndex >= targetDirs.length) break;
|
|
1610
|
+
log.plain(`${pc7.bold(IDE_NAMES[ide])} ${pc7.dim(targetDirs[dirIndex])}`);
|
|
1611
|
+
for (const name of skillNames) {
|
|
1612
|
+
log.itemAdd(name);
|
|
1593
1613
|
}
|
|
1614
|
+
dirIndex++;
|
|
1594
1615
|
}
|
|
1595
1616
|
log.blank();
|
|
1596
1617
|
}
|
|
1597
1618
|
function registerSkillCommands(program2) {
|
|
1598
1619
|
const skill = program2.command("skills").alias("skill").description("Manage AI coding skills");
|
|
1599
1620
|
registerGenerateCommand(skill);
|
|
1600
|
-
skill.command("install").alias("i").alias("add").argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--
|
|
1621
|
+
skill.command("install").alias("i").alias("add").argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Install skills from a repository").action(async (project, skillName, options) => {
|
|
1601
1622
|
await installCommand(project, skillName, options);
|
|
1602
1623
|
});
|
|
1603
1624
|
skill.command("search").alias("s").argument("<keywords...>", "Search keywords").description("Search for skills across all indexed repositories").action(async (keywords) => {
|
|
1604
1625
|
await searchCommand(keywords.join(" "));
|
|
1605
1626
|
});
|
|
1606
|
-
skill.command("list").alias("ls").option("--global", "List global skills").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--
|
|
1627
|
+
skill.command("list").alias("ls").option("--global", "List global skills").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("List installed skills").action(async (options) => {
|
|
1607
1628
|
await listCommand(options);
|
|
1608
1629
|
});
|
|
1609
|
-
skill.command("remove").alias("rm").alias("delete").argument("<name>", "Skill name to remove").option("--global", "Remove from global skills").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--
|
|
1630
|
+
skill.command("remove").alias("rm").alias("delete").argument("<name>", "Skill name to remove").option("--global", "Remove from global skills").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Remove an installed skill").action(async (name, options) => {
|
|
1610
1631
|
await removeCommand(name, options);
|
|
1611
1632
|
});
|
|
1612
1633
|
skill.command("info").argument("<repository>", "GitHub repository (/owner/repo)").description("Show skills in a repository").action(async (project) => {
|
|
1613
1634
|
await infoCommand(project);
|
|
1614
1635
|
});
|
|
1615
|
-
skill.command("suggest").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--
|
|
1636
|
+
skill.command("suggest").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Suggest skills based on your project dependencies").action(async (options) => {
|
|
1616
1637
|
await suggestCommand(options);
|
|
1617
1638
|
});
|
|
1618
1639
|
}
|
|
1619
1640
|
function registerSkillAliases(program2) {
|
|
1620
|
-
program2.command("si", { hidden: true }).argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--
|
|
1641
|
+
program2.command("si", { hidden: true }).argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Install skills (alias for: skills install)").action(async (project, skillName, options) => {
|
|
1621
1642
|
await installCommand(project, skillName, options);
|
|
1622
1643
|
});
|
|
1623
1644
|
program2.command("ss", { hidden: true }).argument("<keywords...>", "Search keywords").description("Search for skills (alias for: skills search)").action(async (keywords) => {
|
|
1624
1645
|
await searchCommand(keywords.join(" "));
|
|
1625
1646
|
});
|
|
1626
|
-
program2.command("ssg", { hidden: true }).option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--
|
|
1647
|
+
program2.command("ssg", { hidden: true }).option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Suggest skills (alias for: skills suggest)").action(async (options) => {
|
|
1627
1648
|
await suggestCommand(options);
|
|
1628
1649
|
});
|
|
1629
1650
|
}
|
|
@@ -1686,10 +1707,13 @@ async function installCommand(input2, skillName, options) {
|
|
|
1686
1707
|
} else {
|
|
1687
1708
|
const indexWidth = data.skills.length.toString().length;
|
|
1688
1709
|
const maxNameLen = Math.max(...data.skills.map((s) => s.name.length));
|
|
1710
|
+
const installsColWidth = 10;
|
|
1689
1711
|
const choices = skillsWithRepo.map((s, index) => {
|
|
1690
1712
|
const indexStr = pc7.dim(`${(index + 1).toString().padStart(indexWidth)}.`);
|
|
1691
1713
|
const paddedName = s.name.padEnd(maxNameLen);
|
|
1692
|
-
const
|
|
1714
|
+
const installsRaw = s.installCount ? String(s.installCount) : "-";
|
|
1715
|
+
const paddedInstalls = formatInstallCount(s.installCount, pc7.dim("-")) + " ".repeat(installsColWidth - installsRaw.length);
|
|
1716
|
+
const trust = formatTrustScore(s.trustScore);
|
|
1693
1717
|
const skillUrl = `https://context7.com/skills${s.project}/${s.name}`;
|
|
1694
1718
|
const skillLink = terminalLink(s.name, skillUrl, pc7.white);
|
|
1695
1719
|
const repoLink = terminalLink(s.project, `https://github.com${s.project}`, pc7.white);
|
|
@@ -1702,20 +1726,30 @@ async function installCommand(input2, skillName, options) {
|
|
|
1702
1726
|
pc7.white(s.description || "No description")
|
|
1703
1727
|
];
|
|
1704
1728
|
return {
|
|
1705
|
-
name:
|
|
1729
|
+
name: `${indexStr} ${paddedName} ${paddedInstalls}${trust}`,
|
|
1706
1730
|
value: s,
|
|
1707
1731
|
description: metadataLines.join("\n")
|
|
1708
1732
|
};
|
|
1709
1733
|
});
|
|
1710
1734
|
log.blank();
|
|
1711
|
-
const
|
|
1712
|
-
const
|
|
1735
|
+
const checkboxPrefixWidth = 3;
|
|
1736
|
+
const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
|
|
1737
|
+
const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)");
|
|
1713
1738
|
try {
|
|
1714
1739
|
selectedSkills = await checkboxWithHover({
|
|
1715
|
-
message
|
|
1740
|
+
message: `Select skills to install:
|
|
1741
|
+
${headerLine}`,
|
|
1716
1742
|
choices,
|
|
1717
1743
|
pageSize: 15,
|
|
1718
|
-
loop: false
|
|
1744
|
+
loop: false,
|
|
1745
|
+
theme: {
|
|
1746
|
+
style: {
|
|
1747
|
+
message: (text, status) => {
|
|
1748
|
+
if (status === "done") return pc7.dim(text.split("\n")[0]);
|
|
1749
|
+
return pc7.bold(text);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1719
1753
|
});
|
|
1720
1754
|
} catch {
|
|
1721
1755
|
log.warn("Installation cancelled");
|
|
@@ -1817,6 +1851,7 @@ async function searchCommand(query) {
|
|
|
1817
1851
|
}
|
|
1818
1852
|
spinner.succeed(`Found ${data.results.length} skill(s)`);
|
|
1819
1853
|
trackEvent("search_query", { query, resultCount: data.results.length });
|
|
1854
|
+
log.blank();
|
|
1820
1855
|
const indexWidth = data.results.length.toString().length;
|
|
1821
1856
|
const maxNameLen = Math.max(...data.results.map((s) => s.name.length));
|
|
1822
1857
|
const installsColWidth = 10;
|
|
@@ -1846,18 +1881,25 @@ async function searchCommand(query) {
|
|
|
1846
1881
|
description: metadataLines.join("\n")
|
|
1847
1882
|
};
|
|
1848
1883
|
});
|
|
1849
|
-
log.blank();
|
|
1850
1884
|
const checkboxPrefixWidth = 3;
|
|
1851
1885
|
const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
|
|
1852
1886
|
const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)");
|
|
1853
|
-
const message = "Select skills to install:\n" + headerLine;
|
|
1854
1887
|
let selectedSkills;
|
|
1855
1888
|
try {
|
|
1856
1889
|
selectedSkills = await checkboxWithHover({
|
|
1857
|
-
message
|
|
1890
|
+
message: `Select skills to install:
|
|
1891
|
+
${headerLine}`,
|
|
1858
1892
|
choices,
|
|
1859
1893
|
pageSize: 15,
|
|
1860
|
-
loop: false
|
|
1894
|
+
loop: false,
|
|
1895
|
+
theme: {
|
|
1896
|
+
style: {
|
|
1897
|
+
message: (text, status) => {
|
|
1898
|
+
if (status === "done") return pc7.dim(text.split("\n")[0]);
|
|
1899
|
+
return pc7.bold(text);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1861
1903
|
});
|
|
1862
1904
|
} catch {
|
|
1863
1905
|
log.warn("Installation cancelled");
|
|
@@ -1940,19 +1982,40 @@ async function searchCommand(query) {
|
|
|
1940
1982
|
async function listCommand(options) {
|
|
1941
1983
|
trackEvent("command", { name: "list" });
|
|
1942
1984
|
const scope = options.global ? "global" : "project";
|
|
1943
|
-
const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
|
|
1944
1985
|
const baseDir = scope === "global" ? homedir4() : process.cwd();
|
|
1945
|
-
const idesToCheck = hasExplicitIdeOption(options) ? getSelectedIdes(options) : Object.keys(IDE_NAMES);
|
|
1946
1986
|
const results = [];
|
|
1947
|
-
|
|
1948
|
-
const skillsDir = join6(baseDir, pathMap[ide]);
|
|
1987
|
+
async function scanDir(dir) {
|
|
1949
1988
|
try {
|
|
1950
|
-
const entries = await readdir(
|
|
1951
|
-
|
|
1952
|
-
if (skillFolders.length > 0) {
|
|
1953
|
-
results.push({ ide, skills: skillFolders });
|
|
1954
|
-
}
|
|
1989
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
1990
|
+
return entries.filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name);
|
|
1955
1991
|
} catch {
|
|
1992
|
+
return [];
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
if (hasExplicitIdeOption(options)) {
|
|
1996
|
+
const ides = getSelectedIdes(options);
|
|
1997
|
+
for (const ide of ides) {
|
|
1998
|
+
const dir = ide === "universal" ? join6(baseDir, scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH) : join6(baseDir, (scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS)[ide]);
|
|
1999
|
+
const label = ide === "universal" ? UNIVERSAL_AGENTS_LABEL : IDE_NAMES[ide];
|
|
2000
|
+
const skills = await scanDir(dir);
|
|
2001
|
+
if (skills.length > 0) {
|
|
2002
|
+
results.push({ label, path: dir, skills });
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
} else {
|
|
2006
|
+
const universalPath = scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH;
|
|
2007
|
+
const universalDir = join6(baseDir, universalPath);
|
|
2008
|
+
const universalSkills = await scanDir(universalDir);
|
|
2009
|
+
if (universalSkills.length > 0) {
|
|
2010
|
+
results.push({ label: UNIVERSAL_AGENTS_LABEL, path: universalPath, skills: universalSkills });
|
|
2011
|
+
}
|
|
2012
|
+
for (const ide of VENDOR_SPECIFIC_AGENTS) {
|
|
2013
|
+
const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
|
|
2014
|
+
const dir = join6(baseDir, pathMap[ide]);
|
|
2015
|
+
const skills = await scanDir(dir);
|
|
2016
|
+
if (skills.length > 0) {
|
|
2017
|
+
results.push({ label: IDE_NAMES[ide], path: pathMap[ide], skills });
|
|
2018
|
+
}
|
|
1956
2019
|
}
|
|
1957
2020
|
}
|
|
1958
2021
|
if (results.length === 0) {
|
|
@@ -1960,10 +2023,8 @@ async function listCommand(options) {
|
|
|
1960
2023
|
return;
|
|
1961
2024
|
}
|
|
1962
2025
|
log.blank();
|
|
1963
|
-
for (const {
|
|
1964
|
-
|
|
1965
|
-
const path2 = pathMap[ide];
|
|
1966
|
-
log.plain(`${pc7.bold(ideName)} ${pc7.dim(path2)}`);
|
|
2026
|
+
for (const { label, path: path2, skills } of results) {
|
|
2027
|
+
log.plain(`${pc7.bold(label)} ${pc7.dim(path2)}`);
|
|
1967
2028
|
for (const skill of skills) {
|
|
1968
2029
|
log.plain(` ${pc7.green(skill)}`);
|
|
1969
2030
|
}
|
|
@@ -2100,14 +2161,22 @@ async function suggestCommand(options) {
|
|
|
2100
2161
|
const checkboxPrefixWidth = 3;
|
|
2101
2162
|
const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
|
|
2102
2163
|
const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)".padEnd(trustColWidth)) + pc7.dim("Relevant");
|
|
2103
|
-
const message = "Select skills to install:\n" + headerLine;
|
|
2104
2164
|
let selectedSkills;
|
|
2105
2165
|
try {
|
|
2106
2166
|
selectedSkills = await checkboxWithHover({
|
|
2107
|
-
message
|
|
2167
|
+
message: `Select skills to install:
|
|
2168
|
+
${headerLine}`,
|
|
2108
2169
|
choices,
|
|
2109
2170
|
pageSize: 15,
|
|
2110
|
-
loop: false
|
|
2171
|
+
loop: false,
|
|
2172
|
+
theme: {
|
|
2173
|
+
style: {
|
|
2174
|
+
message: (text, status) => {
|
|
2175
|
+
if (status === "done") return pc7.dim(text.split("\n")[0]);
|
|
2176
|
+
return pc7.bold(text);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2111
2180
|
});
|
|
2112
2181
|
} catch {
|
|
2113
2182
|
log.warn("Installation cancelled");
|