ctx7 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import figlet from "figlet";
9
9
  import pc7 from "picocolors";
10
10
  import ora3 from "ora";
11
11
  import { readdir, rm as rm2 } from "fs/promises";
12
- import { join as join6 } from "path";
12
+ import { join as join7 } from "path";
13
13
 
14
14
  // src/utils/parse-input.ts
15
15
  function parseSkillInput(input2) {
@@ -115,6 +115,15 @@ async function downloadSkillFromGitHub(skill) {
115
115
  }
116
116
  }
117
117
 
118
+ // src/constants.ts
119
+ import { readFileSync } from "fs";
120
+ import { fileURLToPath } from "url";
121
+ import { dirname, join } from "path";
122
+ var __dirname = dirname(fileURLToPath(import.meta.url));
123
+ var pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
124
+ var VERSION = pkg.version;
125
+ var NAME = pkg.name;
126
+
118
127
  // src/utils/api.ts
119
128
  var baseUrl = "https://context7.com";
120
129
  function getBaseUrl() {
@@ -290,7 +299,12 @@ async function handleGenerateResponse(response, libraryName, onEvent) {
290
299
  return { content, libraryName: finalLibraryName, error };
291
300
  }
292
301
  function getAuthHeaders(accessToken) {
293
- const headers = {};
302
+ const headers = {
303
+ "X-Context7-Source": "cli",
304
+ "X-Context7-Client-IDE": "ctx7-cli",
305
+ "X-Context7-Client-Version": VERSION,
306
+ "X-Context7-Transport": "cli"
307
+ };
294
308
  const apiKey = process.env.CONTEXT7_API_KEY;
295
309
  if (apiKey) {
296
310
  headers["Authorization"] = `Bearer ${apiKey}`;
@@ -367,7 +381,7 @@ var log = {
367
381
  import pc3 from "picocolors";
368
382
  import { select, confirm } from "@inquirer/prompts";
369
383
  import { access } from "fs/promises";
370
- import { join, dirname } from "path";
384
+ import { join as join2, dirname as dirname2 } from "path";
371
385
  import { homedir } from "os";
372
386
 
373
387
  // src/utils/prompts.ts
@@ -378,14 +392,39 @@ function terminalLink(text, url, color) {
378
392
  const colorFn = color ?? ((s) => s);
379
393
  return `\x1B]8;;${url}\x07${colorFn(text)}\x1B]8;;\x07 ${pc2.white("\u2197")}`;
380
394
  }
381
- function formatInstallCount(count, placeholder = "") {
382
- if (count === void 0 || count === 0) return placeholder;
383
- return pc2.yellow(String(count));
384
- }
385
- function formatTrustScore(score) {
395
+ function formatPopularity(count) {
396
+ const filled = "\u2605";
397
+ const empty = "\u2606";
398
+ const max = 4;
399
+ let stars;
400
+ if (count === void 0 || count === 0) stars = 0;
401
+ else if (count < 100) stars = 1;
402
+ else if (count < 500) stars = 2;
403
+ else if (count < 1e3) stars = 3;
404
+ else stars = 4;
405
+ const filledPart = filled.repeat(stars);
406
+ const emptyPart = empty.repeat(max - stars);
407
+ if (stars === 0) return pc2.dim(emptyPart);
408
+ return pc2.yellow(filledPart) + pc2.dim(emptyPart);
409
+ }
410
+ function formatInstallRange(count) {
411
+ if (count === void 0 || count === 0) return "Unknown";
412
+ if (count < 100) return "<100";
413
+ if (count < 500) return "<500";
414
+ if (count < 1e3) return "<1,000";
415
+ return "1,000+";
416
+ }
417
+ function formatTrust(score) {
386
418
  if (score === void 0 || score < 0) return pc2.dim("-");
387
- if (score < 3) return pc2.red(score.toFixed(1));
388
- return pc2.yellow(score.toFixed(1));
419
+ if (score >= 7) return pc2.green("High");
420
+ if (score >= 4) return pc2.yellow("Medium");
421
+ return pc2.red("Low");
422
+ }
423
+ function getTrustLabel(score) {
424
+ if (score === void 0 || score < 0) return "-";
425
+ if (score >= 7) return "High";
426
+ if (score >= 4) return "Medium";
427
+ return "Low";
389
428
  }
390
429
  async function checkboxWithHover(config, options) {
391
430
  const choices = config.choices.filter(
@@ -409,6 +448,7 @@ async function checkboxWithHover(config, options) {
409
448
  theme: {
410
449
  ...config.theme,
411
450
  style: {
451
+ answer: (text) => pc2.green(text),
412
452
  ...config.theme?.style,
413
453
  highlight: (text) => pc2.green(text),
414
454
  renderSelectedChoices: (selected, _allChoices) => {
@@ -476,9 +516,9 @@ async function detectVendorSpecificAgents(scope) {
476
516
  const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
477
517
  const detected = [];
478
518
  for (const ide of VENDOR_SPECIFIC_AGENTS) {
479
- const parentDir = dirname(pathMap[ide]);
519
+ const parentDir = dirname2(pathMap[ide]);
480
520
  try {
481
- await access(join(baseDir, parentDir));
521
+ await access(join2(baseDir, parentDir));
482
522
  detected.push(ide);
483
523
  } catch {
484
524
  }
@@ -487,11 +527,11 @@ async function detectVendorSpecificAgents(scope) {
487
527
  }
488
528
  function getUniversalDir(scope) {
489
529
  if (scope === "global") {
490
- return join(homedir(), UNIVERSAL_SKILLS_GLOBAL_PATH);
530
+ return join2(homedir(), UNIVERSAL_SKILLS_GLOBAL_PATH);
491
531
  }
492
- return join(process.cwd(), UNIVERSAL_SKILLS_PATH);
532
+ return join2(process.cwd(), UNIVERSAL_SKILLS_PATH);
493
533
  }
494
- async function promptForInstallTargets(options) {
534
+ async function promptForInstallTargets(options, forceUniversal = true) {
495
535
  if (hasExplicitIdeOption(options)) {
496
536
  const ides2 = getSelectedIdes(options);
497
537
  const scope2 = options.global ? "global" : "project";
@@ -507,7 +547,7 @@ async function promptForInstallTargets(options) {
507
547
  const detectedVendor = await detectVendorSpecificAgents(scope);
508
548
  let hasUniversalDir = false;
509
549
  try {
510
- await access(join(baseDir, dirname(universalPath)));
550
+ await access(join2(baseDir, dirname2(universalPath)));
511
551
  hasUniversalDir = true;
512
552
  } catch {
513
553
  }
@@ -518,21 +558,25 @@ async function promptForInstallTargets(options) {
518
558
  if (detectedIdes.length > 0) {
519
559
  const pathLines = [];
520
560
  if (hasUniversalDir) {
521
- pathLines.push(join(baseDir, universalPath));
561
+ pathLines.push(join2(baseDir, universalPath));
522
562
  }
523
563
  for (const ide of detectedVendor) {
524
- pathLines.push(join(baseDir, pathMap[ide]));
564
+ pathLines.push(join2(baseDir, pathMap[ide]));
525
565
  }
526
566
  log.blank();
527
567
  let confirmed;
528
- try {
529
- confirmed = await confirm({
530
- message: `Install to detected location(s)?
568
+ if (options.yes) {
569
+ confirmed = true;
570
+ } else {
571
+ try {
572
+ confirmed = await confirm({
573
+ message: `Install to detected location(s)?
531
574
  ${pc3.dim(pathLines.join("\n"))}`,
532
- default: true
533
- });
534
- } catch {
535
- return null;
575
+ default: true
576
+ });
577
+ } catch {
578
+ return null;
579
+ }
536
580
  }
537
581
  if (!confirmed) {
538
582
  log.warn("Installation cancelled");
@@ -542,9 +586,14 @@ ${pc3.dim(pathLines.join("\n"))}`,
542
586
  }
543
587
  const universalLabel = `Universal \u2014 ${UNIVERSAL_AGENTS_LABEL} ${pc3.dim(`(${universalPath})`)}`;
544
588
  const choices = [
545
- { name: universalLabel, value: "universal", checked: true }
589
+ {
590
+ name: `${IDE_NAMES["claude"]} ${pc3.dim(`(${pathMap["claude"]})`)}`,
591
+ value: "claude",
592
+ checked: false
593
+ },
594
+ { name: universalLabel, value: "universal", checked: false }
546
595
  ];
547
- for (const ide of VENDOR_SPECIFIC_AGENTS) {
596
+ for (const ide of VENDOR_SPECIFIC_AGENTS.filter((ide2) => ide2 !== "claude")) {
548
597
  choices.push({
549
598
  name: `${IDE_NAMES[ide]} ${pc3.dim(`(${pathMap[ide]})`)}`,
550
599
  value: ide,
@@ -564,7 +613,7 @@ ${pc3.dim(` ${baseDir}`)}`,
564
613
  style: {
565
614
  highlight: (text) => pc3.green(text),
566
615
  message: (text, status) => {
567
- if (status === "done") return pc3.dim(text.split("\n")[0]);
616
+ if (status === "done") return text.split("\n")[0];
568
617
  return pc3.bold(text);
569
618
  }
570
619
  }
@@ -575,7 +624,7 @@ ${pc3.dim(` ${baseDir}`)}`,
575
624
  } catch {
576
625
  return null;
577
626
  }
578
- const ides = ["universal", ...selectedIdes.filter((ide) => ide !== "universal")];
627
+ const ides = forceUniversal ? ["universal", ...selectedIdes.filter((ide) => ide !== "universal")] : selectedIdes;
579
628
  return { ides, scopes: [scope] };
580
629
  }
581
630
  async function promptForSingleTarget(options) {
@@ -587,8 +636,11 @@ async function promptForSingleTarget(options) {
587
636
  }
588
637
  log.blank();
589
638
  const universalLabel = `Universal ${pc3.dim(`(${UNIVERSAL_SKILLS_PATH})`)}`;
590
- const choices = [{ name: universalLabel, value: "universal" }];
591
- for (const ide of VENDOR_SPECIFIC_AGENTS) {
639
+ const choices = [
640
+ { name: `${IDE_NAMES["claude"]} ${pc3.dim(`(${IDE_PATHS["claude"]})`)}`, value: "claude" },
641
+ { name: universalLabel, value: "universal" }
642
+ ];
643
+ for (const ide of VENDOR_SPECIFIC_AGENTS.filter((ide2) => ide2 !== "claude")) {
592
644
  choices.push({
593
645
  name: `${IDE_NAMES[ide]} ${pc3.dim(`(${IDE_PATHS[ide]})`)}`,
594
646
  value: ide
@@ -640,12 +692,12 @@ function getTargetDirs(targets) {
640
692
  const baseDir = scope === "global" ? homedir() : process.cwd();
641
693
  if (hasUniversal) {
642
694
  const uniPath = scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH;
643
- dirs.push(join(baseDir, uniPath));
695
+ dirs.push(join2(baseDir, uniPath));
644
696
  }
645
697
  for (const ide of targets.ides) {
646
698
  if (ide === "universal") continue;
647
699
  const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
648
- dirs.push(join(baseDir, pathMap[ide]));
700
+ dirs.push(join2(baseDir, pathMap[ide]));
649
701
  }
650
702
  }
651
703
  return dirs;
@@ -655,25 +707,25 @@ function getTargetDirFromSelection(ide, scope) {
655
707
  return getUniversalDir(scope);
656
708
  }
657
709
  if (scope === "global") {
658
- return join(homedir(), IDE_GLOBAL_PATHS[ide]);
710
+ return join2(homedir(), IDE_GLOBAL_PATHS[ide]);
659
711
  }
660
- return join(process.cwd(), IDE_PATHS[ide]);
712
+ return join2(process.cwd(), IDE_PATHS[ide]);
661
713
  }
662
714
 
663
715
  // src/utils/installer.ts
664
716
  import { mkdir, writeFile, rm, symlink, lstat } from "fs/promises";
665
- import { join as join2 } from "path";
717
+ import { join as join3 } from "path";
666
718
  async function installSkillFiles(skillName, files, targetDir) {
667
- const skillDir = join2(targetDir, skillName);
719
+ const skillDir = join3(targetDir, skillName);
668
720
  for (const file of files) {
669
- const filePath = join2(skillDir, file.path);
670
- const fileDir = join2(filePath, "..");
721
+ const filePath = join3(skillDir, file.path);
722
+ const fileDir = join3(filePath, "..");
671
723
  await mkdir(fileDir, { recursive: true });
672
724
  await writeFile(filePath, file.content);
673
725
  }
674
726
  }
675
727
  async function symlinkSkill(skillName, sourcePath, targetDir) {
676
- const targetPath = join2(targetDir, skillName);
728
+ const targetPath = join3(targetDir, skillName);
677
729
  try {
678
730
  const stats = await lstat(targetPath);
679
731
  if (stats.isSymbolicLink() || stats.isDirectory()) {
@@ -700,7 +752,7 @@ function trackEvent(event, data) {
700
752
  import pc6 from "picocolors";
701
753
  import ora2 from "ora";
702
754
  import { mkdir as mkdir2, writeFile as writeFile2, readFile, unlink } from "fs/promises";
703
- import { join as join4 } from "path";
755
+ import { join as join5 } from "path";
704
756
  import { homedir as homedir3 } from "os";
705
757
  import { spawn } from "child_process";
706
758
  import { input, select as select2 } from "@inquirer/prompts";
@@ -1234,6 +1286,12 @@ async function generateCommand(options) {
1234
1286
  }
1235
1287
  searchSpinner.succeed(pc6.green(`Found ${searchResult.results.length} relevant sources`));
1236
1288
  log.blank();
1289
+ if (searchResult.searchFilterApplied) {
1290
+ log.warn(
1291
+ "Your results only include libraries matching your access settings. To search across all public libraries, update your settings at https://context7.com/dashboard?tab=libraries"
1292
+ );
1293
+ log.blank();
1294
+ }
1237
1295
  let selectedLibraries;
1238
1296
  try {
1239
1297
  const formatProjectId = (id) => {
@@ -1447,9 +1505,9 @@ async function generateCommand(options) {
1447
1505
  log.blank();
1448
1506
  };
1449
1507
  const openInEditor = async () => {
1450
- const previewDir = join4(homedir3(), ".context7", "previews");
1508
+ const previewDir = join5(homedir3(), ".context7", "previews");
1451
1509
  await mkdir2(previewDir, { recursive: true });
1452
- previewFile = join4(previewDir, `${skillName}.md`);
1510
+ previewFile = join5(previewDir, `${skillName}.md`);
1453
1511
  if (!previewFileWritten) {
1454
1512
  await writeFile2(previewFile, generatedContent, "utf-8");
1455
1513
  previewFileWritten = true;
@@ -1526,8 +1584,8 @@ async function generateCommand(options) {
1526
1584
  if (options.output && !targetDir.includes("/.config/") && !targetDir.startsWith(homedir3())) {
1527
1585
  finalDir = targetDir.replace(process.cwd(), options.output);
1528
1586
  }
1529
- const skillDir = join4(finalDir, skillName);
1530
- const skillPath = join4(skillDir, "SKILL.md");
1587
+ const skillDir = join5(finalDir, skillName);
1588
+ const skillPath = join5(skillDir, "SKILL.md");
1531
1589
  try {
1532
1590
  await mkdir2(skillDir, { recursive: true });
1533
1591
  await writeFile2(skillPath, generatedContent, "utf-8");
@@ -1546,7 +1604,7 @@ async function generateCommand(options) {
1546
1604
  log.blank();
1547
1605
  console.log(pc6.yellow("Fix permissions with:"));
1548
1606
  for (const dir of failedDirs) {
1549
- const parentDir = join4(dir, "..");
1607
+ const parentDir = join5(dir, "..");
1550
1608
  console.log(pc6.dim(` sudo chown -R $(whoami) "${parentDir}"`));
1551
1609
  }
1552
1610
  log.blank();
@@ -1568,7 +1626,7 @@ import { homedir as homedir4 } from "os";
1568
1626
 
1569
1627
  // src/utils/deps.ts
1570
1628
  import { readFile as readFile2 } from "fs/promises";
1571
- import { join as join5 } from "path";
1629
+ import { join as join6 } from "path";
1572
1630
  async function readFileOrNull(path2) {
1573
1631
  try {
1574
1632
  return await readFile2(path2, "utf-8");
@@ -1580,7 +1638,7 @@ function isSkippedLocally(name) {
1580
1638
  return name.startsWith("@types/");
1581
1639
  }
1582
1640
  async function parsePackageJson(cwd) {
1583
- const content = await readFileOrNull(join5(cwd, "package.json"));
1641
+ const content = await readFileOrNull(join6(cwd, "package.json"));
1584
1642
  if (!content) return [];
1585
1643
  try {
1586
1644
  const pkg2 = JSON.parse(content);
@@ -1597,7 +1655,7 @@ async function parsePackageJson(cwd) {
1597
1655
  }
1598
1656
  }
1599
1657
  async function parseRequirementsTxt(cwd) {
1600
- const content = await readFileOrNull(join5(cwd, "requirements.txt"));
1658
+ const content = await readFileOrNull(join6(cwd, "requirements.txt"));
1601
1659
  if (!content) return [];
1602
1660
  const deps = [];
1603
1661
  for (const line of content.split("\n")) {
@@ -1611,7 +1669,7 @@ async function parseRequirementsTxt(cwd) {
1611
1669
  return deps;
1612
1670
  }
1613
1671
  async function parsePyprojectToml(cwd) {
1614
- const content = await readFileOrNull(join5(cwd, "pyproject.toml"));
1672
+ const content = await readFileOrNull(join6(cwd, "pyproject.toml"));
1615
1673
  if (!content) return [];
1616
1674
  const deps = [];
1617
1675
  const seen = /* @__PURE__ */ new Set();
@@ -1766,13 +1824,12 @@ async function installCommand(input2, skillName, options) {
1766
1824
  } else {
1767
1825
  const indexWidth = data.skills.length.toString().length;
1768
1826
  const maxNameLen = Math.max(...data.skills.map((s) => s.name.length));
1769
- const installsColWidth = 10;
1827
+ const popularityColWidth = 13;
1770
1828
  const choices = skillsWithRepo.map((s, index) => {
1771
1829
  const indexStr = pc7.dim(`${(index + 1).toString().padStart(indexWidth)}.`);
1772
1830
  const paddedName = s.name.padEnd(maxNameLen);
1773
- const installsRaw = s.installCount ? String(s.installCount) : "-";
1774
- const paddedInstalls = formatInstallCount(s.installCount, pc7.dim("-")) + " ".repeat(installsColWidth - installsRaw.length);
1775
- const trust = formatTrustScore(s.trustScore);
1831
+ const popularity = formatPopularity(s.installCount) + " ".repeat(popularityColWidth - 4);
1832
+ const trust = formatTrust(s.trustScore);
1776
1833
  const skillUrl = `https://context7.com/skills${s.project}/${s.name}`;
1777
1834
  const skillLink = terminalLink(s.name, skillUrl, pc7.white);
1778
1835
  const repoLink = terminalLink(s.project, `https://github.com${s.project}`, pc7.white);
@@ -1781,11 +1838,13 @@ async function installCommand(input2, skillName, options) {
1781
1838
  "",
1782
1839
  `${pc7.yellow("Skill:")} ${skillLink}`,
1783
1840
  `${pc7.yellow("Repo:")} ${repoLink}`,
1841
+ `${pc7.yellow("Installs:")} ${pc7.white(formatInstallRange(s.installCount))}`,
1842
+ `${pc7.yellow("Trust:")} ${s.trustScore !== void 0 && s.trustScore >= 0 ? pc7.white(s.trustScore.toFixed(1)) : pc7.dim("-")}`,
1784
1843
  `${pc7.yellow("Description:")}`,
1785
1844
  pc7.white(s.description || "No description")
1786
1845
  ];
1787
1846
  return {
1788
- name: `${indexStr} ${paddedName} ${paddedInstalls}${trust}`,
1847
+ name: `${indexStr} ${paddedName} ${popularity}${trust}`,
1789
1848
  value: s,
1790
1849
  description: metadataLines.join("\n")
1791
1850
  };
@@ -1793,7 +1852,7 @@ async function installCommand(input2, skillName, options) {
1793
1852
  log.blank();
1794
1853
  const checkboxPrefixWidth = 3;
1795
1854
  const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
1796
- const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)");
1855
+ const headerLine = headerPad + pc7.dim("Popularity".padEnd(popularityColWidth)) + pc7.dim("Trust");
1797
1856
  try {
1798
1857
  selectedSkills = await checkboxWithHover({
1799
1858
  message: `Select skills to install:
@@ -1850,7 +1909,7 @@ ${headerLine}`,
1850
1909
  }
1851
1910
  throw dirErr;
1852
1911
  }
1853
- const primarySkillDir = join6(primaryDir, skill.name);
1912
+ const primarySkillDir = join7(primaryDir, skill.name);
1854
1913
  for (const targetDir of symlinkDirs) {
1855
1914
  try {
1856
1915
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -1878,7 +1937,7 @@ ${headerLine}`,
1878
1937
  log.blank();
1879
1938
  log.warn("Fix permissions with:");
1880
1939
  for (const dir of failedDirs) {
1881
- const parentDir = join6(dir, "..");
1940
+ const parentDir = join7(dir, "..");
1882
1941
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
1883
1942
  }
1884
1943
  log.blank();
@@ -1912,14 +1971,16 @@ async function searchCommand(query) {
1912
1971
  trackEvent("search_query", { query, resultCount: data.results.length });
1913
1972
  log.blank();
1914
1973
  const indexWidth = data.results.length.toString().length;
1915
- const maxNameLen = Math.max(...data.results.map((s) => s.name.length));
1916
- const installsColWidth = 10;
1974
+ const nameWithRepo = (s) => `${s.name} ${pc7.dim(`(${s.project})`)}`;
1975
+ const nameWithRepoLen = (s) => `${s.name} (${s.project})`.length;
1976
+ const maxNameLen = Math.max(...data.results.map(nameWithRepoLen));
1977
+ const popularityColWidth = 13;
1917
1978
  const choices = data.results.map((s, index) => {
1918
1979
  const indexStr = pc7.dim(`${(index + 1).toString().padStart(indexWidth)}.`);
1919
- const paddedName = s.name.padEnd(maxNameLen);
1920
- const installsRaw = s.installCount ? String(s.installCount) : "-";
1921
- const paddedInstalls = formatInstallCount(s.installCount, pc7.dim("-")) + " ".repeat(installsColWidth - installsRaw.length);
1922
- const trust = formatTrustScore(s.trustScore);
1980
+ const rawLen = nameWithRepoLen(s);
1981
+ const displayName = nameWithRepo(s) + " ".repeat(maxNameLen - rawLen);
1982
+ const popularity = formatPopularity(s.installCount) + " ".repeat(popularityColWidth - 4);
1983
+ const trust = formatTrust(s.trustScore);
1923
1984
  const skillLink = terminalLink(
1924
1985
  s.name,
1925
1986
  `https://context7.com/skills${s.project}/${s.name}`,
@@ -1931,18 +1992,20 @@ async function searchCommand(query) {
1931
1992
  "",
1932
1993
  `${pc7.yellow("Skill:")} ${skillLink}`,
1933
1994
  `${pc7.yellow("Repo:")} ${repoLink}`,
1995
+ `${pc7.yellow("Installs:")} ${pc7.white(formatInstallRange(s.installCount))}`,
1996
+ `${pc7.yellow("Trust:")} ${s.trustScore !== void 0 && s.trustScore >= 0 ? pc7.white(s.trustScore.toFixed(1)) : pc7.dim("-")}`,
1934
1997
  `${pc7.yellow("Description:")}`,
1935
1998
  pc7.white(s.description || "No description")
1936
1999
  ];
1937
2000
  return {
1938
- name: `${indexStr} ${paddedName} ${paddedInstalls}${trust}`,
2001
+ name: `${indexStr} ${displayName} ${popularity}${trust}`,
1939
2002
  value: s,
1940
2003
  description: metadataLines.join("\n")
1941
2004
  };
1942
2005
  });
1943
2006
  const checkboxPrefixWidth = 3;
1944
2007
  const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
1945
- const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)");
2008
+ const headerLine = headerPad + pc7.dim("Popularity".padEnd(popularityColWidth)) + pc7.dim("Trust");
1946
2009
  let selectedSkills;
1947
2010
  try {
1948
2011
  selectedSkills = await checkboxWithHover({
@@ -1999,7 +2062,7 @@ ${headerLine}`,
1999
2062
  }
2000
2063
  throw dirErr;
2001
2064
  }
2002
- const primarySkillDir = join6(primaryDir, skill.name);
2065
+ const primarySkillDir = join7(primaryDir, skill.name);
2003
2066
  for (const targetDir of symlinkDirs) {
2004
2067
  try {
2005
2068
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -2027,7 +2090,7 @@ ${headerLine}`,
2027
2090
  log.blank();
2028
2091
  log.warn("Fix permissions with:");
2029
2092
  for (const dir of failedDirs) {
2030
- const parentDir = join6(dir, "..");
2093
+ const parentDir = join7(dir, "..");
2031
2094
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
2032
2095
  }
2033
2096
  log.blank();
@@ -2054,7 +2117,7 @@ async function listCommand(options) {
2054
2117
  if (hasExplicitIdeOption(options)) {
2055
2118
  const ides = getSelectedIdes(options);
2056
2119
  for (const ide of ides) {
2057
- 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]);
2120
+ const dir = ide === "universal" ? join7(baseDir, scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH) : join7(baseDir, (scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS)[ide]);
2058
2121
  const label = ide === "universal" ? UNIVERSAL_AGENTS_LABEL : IDE_NAMES[ide];
2059
2122
  const skills = await scanDir(dir);
2060
2123
  if (skills.length > 0) {
@@ -2063,14 +2126,14 @@ async function listCommand(options) {
2063
2126
  }
2064
2127
  } else {
2065
2128
  const universalPath = scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH;
2066
- const universalDir = join6(baseDir, universalPath);
2129
+ const universalDir = join7(baseDir, universalPath);
2067
2130
  const universalSkills = await scanDir(universalDir);
2068
2131
  if (universalSkills.length > 0) {
2069
2132
  results.push({ label: UNIVERSAL_AGENTS_LABEL, path: universalPath, skills: universalSkills });
2070
2133
  }
2071
2134
  for (const ide of VENDOR_SPECIFIC_AGENTS) {
2072
2135
  const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
2073
- const dir = join6(baseDir, pathMap[ide]);
2136
+ const dir = join7(baseDir, pathMap[ide]);
2074
2137
  const skills = await scanDir(dir);
2075
2138
  if (skills.length > 0) {
2076
2139
  results.push({ label: IDE_NAMES[ide], path: pathMap[ide], skills });
@@ -2098,7 +2161,7 @@ async function removeCommand(name, options) {
2098
2161
  return;
2099
2162
  }
2100
2163
  const skillsDir = getTargetDirFromSelection(target.ide, target.scope);
2101
- const skillPath = join6(skillsDir, name);
2164
+ const skillPath = join7(skillsDir, name);
2102
2165
  try {
2103
2166
  await rm2(skillPath, { recursive: true });
2104
2167
  log.success(`Removed skill: ${name}`);
@@ -2183,18 +2246,20 @@ async function suggestCommand(options) {
2183
2246
  searchSpinner.succeed(`Found ${skills.length} relevant skill(s)`);
2184
2247
  trackEvent("suggest_results", { depCount: deps.length, skillCount: skills.length });
2185
2248
  log.blank();
2186
- const maxNameLen = Math.max(...skills.map((s) => s.name.length));
2187
- const installsColWidth = 10;
2188
- const trustColWidth = 12;
2249
+ const nameWithRepo = (s) => `${s.name} ${pc7.dim(`(${s.project})`)}`;
2250
+ const nameWithRepoLen = (s) => `${s.name} (${s.project})`.length;
2251
+ const maxNameLen = Math.max(...skills.map(nameWithRepoLen));
2252
+ const popularityColWidth = 13;
2253
+ const trustColWidth = 8;
2189
2254
  const maxMatchedLen = Math.max(...skills.map((s) => s.matchedDep.length));
2190
2255
  const indexWidth = skills.length.toString().length;
2191
2256
  const choices = skills.map((s, index) => {
2192
2257
  const indexStr = pc7.dim(`${(index + 1).toString().padStart(indexWidth)}.`);
2193
- const paddedName = s.name.padEnd(maxNameLen);
2194
- const installsRaw = s.installCount ? String(s.installCount) : "-";
2195
- const paddedInstalls = formatInstallCount(s.installCount, pc7.dim("-")) + " ".repeat(installsColWidth - installsRaw.length);
2196
- const trustRaw = s.trustScore !== void 0 && s.trustScore >= 0 ? s.trustScore.toFixed(1) : "-";
2197
- const trust = formatTrustScore(s.trustScore) + " ".repeat(trustColWidth - trustRaw.length);
2258
+ const rawLen = nameWithRepoLen(s);
2259
+ const displayName = nameWithRepo(s) + " ".repeat(maxNameLen - rawLen);
2260
+ const popularity = formatPopularity(s.installCount) + " ".repeat(popularityColWidth - 4);
2261
+ const trustLabel = getTrustLabel(s.trustScore);
2262
+ const trust = formatTrust(s.trustScore) + " ".repeat(trustColWidth - trustLabel.length);
2198
2263
  const matched = pc7.yellow(s.matchedDep.padEnd(maxMatchedLen));
2199
2264
  const skillLink = terminalLink(
2200
2265
  s.name,
@@ -2207,19 +2272,21 @@ async function suggestCommand(options) {
2207
2272
  "",
2208
2273
  `${pc7.yellow("Skill:")} ${skillLink}`,
2209
2274
  `${pc7.yellow("Repo:")} ${repoLink}`,
2275
+ `${pc7.yellow("Installs:")} ${pc7.white(formatInstallRange(s.installCount))}`,
2276
+ `${pc7.yellow("Trust:")} ${s.trustScore !== void 0 && s.trustScore >= 0 ? pc7.white(s.trustScore.toFixed(1)) : pc7.dim("-")}`,
2210
2277
  `${pc7.yellow("Relevant:")} ${pc7.white(s.matchedDep)}`,
2211
2278
  `${pc7.yellow("Description:")}`,
2212
2279
  pc7.white(s.description || "No description")
2213
2280
  ];
2214
2281
  return {
2215
- name: `${indexStr} ${paddedName} ${paddedInstalls}${trust}${matched}`,
2282
+ name: `${indexStr} ${displayName} ${popularity}${trust}${matched}`,
2216
2283
  value: s,
2217
2284
  description: metadataLines.join("\n")
2218
2285
  };
2219
2286
  });
2220
2287
  const checkboxPrefixWidth = 3;
2221
2288
  const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
2222
- const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)".padEnd(trustColWidth)) + pc7.dim("Relevant");
2289
+ const headerLine = headerPad + pc7.dim("Popularity".padEnd(popularityColWidth)) + pc7.dim("Trust".padEnd(trustColWidth)) + pc7.dim("Relevant");
2223
2290
  let selectedSkills;
2224
2291
  try {
2225
2292
  selectedSkills = await checkboxWithHover({
@@ -2275,7 +2342,7 @@ ${headerLine}`,
2275
2342
  }
2276
2343
  throw dirErr;
2277
2344
  }
2278
- const primarySkillDir = join6(primaryDir, skill.name);
2345
+ const primarySkillDir = join7(primaryDir, skill.name);
2279
2346
  for (const targetDir of symlinkDirs) {
2280
2347
  try {
2281
2348
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -2303,7 +2370,7 @@ ${headerLine}`,
2303
2370
  log.blank();
2304
2371
  log.warn("Fix permissions with:");
2305
2372
  for (const dir of failedDirs) {
2306
- const parentDir = join6(dir, "..");
2373
+ const parentDir = join7(dir, "..");
2307
2374
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
2308
2375
  }
2309
2376
  log.blank();
@@ -2318,13 +2385,14 @@ ${headerLine}`,
2318
2385
  // src/commands/setup.ts
2319
2386
  import pc8 from "picocolors";
2320
2387
  import ora4 from "ora";
2388
+ import { select as select3 } from "@inquirer/prompts";
2321
2389
  import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
2322
- import { dirname as dirname3, join as join8 } from "path";
2390
+ import { dirname as dirname4, join as join9 } from "path";
2323
2391
  import { randomBytes as randomBytes2 } from "crypto";
2324
2392
 
2325
2393
  // src/setup/agents.ts
2326
2394
  import { access as access2 } from "fs/promises";
2327
- import { join as join7 } from "path";
2395
+ import { join as join8 } from "path";
2328
2396
  import { homedir as homedir5 } from "os";
2329
2397
  var SETUP_AGENT_NAMES = {
2330
2398
  claude: "Claude Code",
@@ -2351,43 +2419,43 @@ var agents = {
2351
2419
  displayName: "Claude Code",
2352
2420
  mcp: {
2353
2421
  projectPath: ".mcp.json",
2354
- globalPath: join7(homedir5(), ".claude.json"),
2422
+ globalPath: join8(homedir5(), ".claude.json"),
2355
2423
  configKey: "mcpServers",
2356
2424
  buildEntry: (auth) => withHeaders({ type: "http", url: mcpUrl(auth) }, auth)
2357
2425
  },
2358
2426
  rule: {
2359
- dir: (scope) => scope === "global" ? join7(homedir5(), ".claude", "rules") : join7(".claude", "rules"),
2427
+ dir: (scope) => scope === "global" ? join8(homedir5(), ".claude", "rules") : join8(".claude", "rules"),
2360
2428
  filename: "context7.md"
2361
2429
  },
2362
2430
  skill: {
2363
- name: "documentation-lookup",
2364
- dir: (scope) => scope === "global" ? join7(homedir5(), ".claude", "skills") : join7(".claude", "skills")
2431
+ name: "context7-mcp",
2432
+ dir: (scope) => scope === "global" ? join8(homedir5(), ".claude", "skills") : join8(".claude", "skills")
2365
2433
  },
2366
2434
  detect: {
2367
2435
  projectPaths: [".mcp.json", ".claude"],
2368
- globalPaths: [join7(homedir5(), ".claude")]
2436
+ globalPaths: [join8(homedir5(), ".claude")]
2369
2437
  }
2370
2438
  },
2371
2439
  cursor: {
2372
2440
  name: "cursor",
2373
2441
  displayName: "Cursor",
2374
2442
  mcp: {
2375
- projectPath: join7(".cursor", "mcp.json"),
2376
- globalPath: join7(homedir5(), ".cursor", "mcp.json"),
2443
+ projectPath: join8(".cursor", "mcp.json"),
2444
+ globalPath: join8(homedir5(), ".cursor", "mcp.json"),
2377
2445
  configKey: "mcpServers",
2378
2446
  buildEntry: (auth) => withHeaders({ url: mcpUrl(auth) }, auth)
2379
2447
  },
2380
2448
  rule: {
2381
- dir: (scope) => scope === "global" ? join7(homedir5(), ".cursor", "rules") : join7(".cursor", "rules"),
2449
+ dir: (scope) => scope === "global" ? join8(homedir5(), ".cursor", "rules") : join8(".cursor", "rules"),
2382
2450
  filename: "context7.mdc"
2383
2451
  },
2384
2452
  skill: {
2385
- name: "documentation-lookup",
2386
- dir: (scope) => scope === "global" ? join7(homedir5(), ".cursor", "skills") : join7(".cursor", "skills")
2453
+ name: "context7-mcp",
2454
+ dir: (scope) => scope === "global" ? join8(homedir5(), ".cursor", "skills") : join8(".cursor", "skills")
2387
2455
  },
2388
2456
  detect: {
2389
2457
  projectPaths: [".cursor"],
2390
- globalPaths: [join7(homedir5(), ".cursor")]
2458
+ globalPaths: [join8(homedir5(), ".cursor")]
2391
2459
  }
2392
2460
  },
2393
2461
  opencode: {
@@ -2395,22 +2463,22 @@ var agents = {
2395
2463
  displayName: "OpenCode",
2396
2464
  mcp: {
2397
2465
  projectPath: ".opencode.json",
2398
- globalPath: join7(homedir5(), ".config", "opencode", "opencode.json"),
2466
+ globalPath: join8(homedir5(), ".config", "opencode", "opencode.json"),
2399
2467
  configKey: "mcp",
2400
2468
  buildEntry: (auth) => withHeaders({ type: "remote", url: mcpUrl(auth), enabled: true }, auth)
2401
2469
  },
2402
2470
  rule: {
2403
- dir: (scope) => scope === "global" ? join7(homedir5(), ".config", "opencode", "rules") : join7(".opencode", "rules"),
2471
+ dir: (scope) => scope === "global" ? join8(homedir5(), ".config", "opencode", "rules") : join8(".opencode", "rules"),
2404
2472
  filename: "context7.md",
2405
- instructionsGlob: (scope) => scope === "global" ? join7(homedir5(), ".config", "opencode", "rules", "*.md") : ".opencode/rules/*.md"
2473
+ instructionsGlob: (scope) => scope === "global" ? join8(homedir5(), ".config", "opencode", "rules", "*.md") : ".opencode/rules/*.md"
2406
2474
  },
2407
2475
  skill: {
2408
- name: "documentation-lookup",
2409
- dir: (scope) => scope === "global" ? join7(homedir5(), ".agents", "skills") : join7(".agents", "skills")
2476
+ name: "context7-mcp",
2477
+ dir: (scope) => scope === "global" ? join8(homedir5(), ".agents", "skills") : join8(".agents", "skills")
2410
2478
  },
2411
2479
  detect: {
2412
2480
  projectPaths: [".opencode.json"],
2413
- globalPaths: [join7(homedir5(), ".config", "opencode")]
2481
+ globalPaths: [join8(homedir5(), ".config", "opencode")]
2414
2482
  }
2415
2483
  }
2416
2484
  };
@@ -2431,7 +2499,7 @@ async function detectAgents(scope) {
2431
2499
  for (const agent of Object.values(agents)) {
2432
2500
  const paths = scope === "global" ? agent.detect.globalPaths : agent.detect.projectPaths;
2433
2501
  for (const p of paths) {
2434
- const fullPath = scope === "global" ? p : join7(process.cwd(), p);
2502
+ const fullPath = scope === "global" ? p : join8(process.cwd(), p);
2435
2503
  if (await pathExists(fullPath)) {
2436
2504
  detected.push(agent.name);
2437
2505
  break;
@@ -2442,60 +2510,6 @@ async function detectAgents(scope) {
2442
2510
  }
2443
2511
 
2444
2512
  // src/setup/templates.ts
2445
- var SKILL_CONTENT = `---
2446
- name: documentation-lookup
2447
- description: This skill should be used when the user asks about libraries, frameworks, API references, or needs code examples. Activates for setup questions, code generation involving libraries, or mentions of specific frameworks like React, Vue, Next.js, Prisma, Supabase, etc.
2448
- ---
2449
-
2450
- When the user asks about libraries, frameworks, or needs code examples, use Context7 to fetch current documentation instead of relying on training data.
2451
-
2452
- ## When to Use This Skill
2453
-
2454
- Activate this skill when the user:
2455
-
2456
- - Asks setup or configuration questions ("How do I configure Next.js middleware?")
2457
- - Requests code involving libraries ("Write a Prisma query for...")
2458
- - Needs API references ("What are the Supabase auth methods?")
2459
- - Mentions specific frameworks (React, Vue, Svelte, Express, Tailwind, etc.)
2460
-
2461
- ## How to Fetch Documentation
2462
-
2463
- ### Step 1: Resolve the Library ID
2464
-
2465
- Call \`resolve-library-id\` with:
2466
-
2467
- - \`libraryName\`: The library name extracted from the user's question
2468
- - \`query\`: The user's full question (improves relevance ranking)
2469
-
2470
- ### Step 2: Select the Best Match
2471
-
2472
- From the resolution results, choose based on:
2473
-
2474
- - Exact or closest name match to what the user asked for
2475
- - Higher benchmark scores indicate better documentation quality
2476
- - If the user mentioned a version (e.g., "React 19"), prefer version-specific IDs
2477
-
2478
- ### Step 3: Fetch the Documentation
2479
-
2480
- Call \`query-docs\` with:
2481
-
2482
- - \`libraryId\`: The selected Context7 library ID (e.g., \`/vercel/next.js\`)
2483
- - \`query\`: The user's specific question
2484
-
2485
- ### Step 4: Use the Documentation
2486
-
2487
- Incorporate the fetched documentation into your response:
2488
-
2489
- - Answer the user's question using current, accurate information
2490
- - Include relevant code examples from the docs
2491
- - Cite the library version when relevant
2492
-
2493
- ## Guidelines
2494
-
2495
- - **Be specific**: Pass the user's full question as the query for better results
2496
- - **Version awareness**: When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step
2497
- - **Prefer official sources**: When multiple matches exist, prefer official/primary packages over community forks
2498
- `;
2499
2513
  var RULE_CONTENT = `---
2500
2514
  alwaysApply: true
2501
2515
  ---
@@ -2512,7 +2526,7 @@ When working with libraries, frameworks, or APIs \u2014 use Context7 MCP to fetc
2512
2526
 
2513
2527
  // src/setup/mcp-writer.ts
2514
2528
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2515
- import { dirname as dirname2 } from "path";
2529
+ import { dirname as dirname3 } from "path";
2516
2530
  async function readJsonConfig(filePath) {
2517
2531
  let raw;
2518
2532
  try {
@@ -2546,7 +2560,7 @@ function mergeInstructions(config, glob) {
2546
2560
  return { ...config, instructions: [...instructions, glob] };
2547
2561
  }
2548
2562
  async function writeJsonConfig(filePath, config) {
2549
- await mkdir3(dirname2(filePath), { recursive: true });
2563
+ await mkdir3(dirname3(filePath), { recursive: true });
2550
2564
  await writeFile3(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
2551
2565
  }
2552
2566
 
@@ -2565,7 +2579,7 @@ function getSelectedAgents(options) {
2565
2579
  return agents2;
2566
2580
  }
2567
2581
  function registerSetupCommand(program2) {
2568
- program2.command("setup").description("Set up Context7 MCP and rule for your AI coding agent").option("--claude", "Set up for Claude Code").option("--cursor", "Set up for Cursor").option("--opencode", "Set up for OpenCode").option("-p, --project", "Configure for current project instead of globally").option("-y, --yes", "Skip confirmation prompts").option("--api-key <key>", "Use API key authentication").option("--oauth", "Use OAuth endpoint (IDE handles auth flow)").action(async (options) => {
2582
+ program2.command("setup").description("Set up Context7 for your AI coding agent").option("--claude", "Set up for Claude Code").option("--cursor", "Set up for Cursor").option("--universal", "Set up for Universal (.agents/skills)").option("--antigravity", "Set up for Antigravity (.agent/skills)").option("--opencode", "Set up for OpenCode").option("--mcp", "Set up MCP server mode").option("--cli", "Set up CLI + Skills mode (no MCP server)").option("-p, --project", "Configure for current project instead of globally").option("-y, --yes", "Skip confirmation prompts").option("--api-key <key>", "Use API key authentication").option("--oauth", "Use OAuth endpoint (IDE handles auth flow)").action(async (options) => {
2569
2583
  await setupCommand(options);
2570
2584
  });
2571
2585
  }
@@ -2605,9 +2619,49 @@ async function resolveAuth(options) {
2605
2619
  if (!apiKey) return null;
2606
2620
  return { mode: "api-key", apiKey };
2607
2621
  }
2622
+ async function resolveMode(options) {
2623
+ if (options.cli) return "cli";
2624
+ if (options.mcp || options.yes || options.oauth || options.apiKey) return "mcp";
2625
+ return select3({
2626
+ message: "How should your agent access Context7?",
2627
+ choices: [
2628
+ {
2629
+ name: `CLI + Skills
2630
+ ${pc8.dim("Installs a find-docs skill that guides your agent to fetch up-to-date library docs using ")}${pc8.dim(pc8.bold("ctx7"))}${pc8.dim(" CLI commands")}`,
2631
+ value: "cli"
2632
+ },
2633
+ {
2634
+ name: `MCP server
2635
+ ${pc8.dim("Agent calls Context7 tools via MCP protocol to retrieve up-to-date library docs")}`,
2636
+ value: "mcp"
2637
+ }
2638
+ ],
2639
+ theme: {
2640
+ style: {
2641
+ highlight: (text) => pc8.green(text),
2642
+ answer: (text) => pc8.green(text.split("\n")[0].trim())
2643
+ }
2644
+ }
2645
+ });
2646
+ }
2647
+ async function resolveCliAuth(apiKey) {
2648
+ if (apiKey) {
2649
+ saveTokens({ access_token: apiKey, token_type: "bearer" });
2650
+ log.blank();
2651
+ log.plain(`${pc8.green("\u2714")} Authenticated`);
2652
+ return;
2653
+ }
2654
+ const existingTokens = loadTokens();
2655
+ if (existingTokens && !isTokenExpired(existingTokens)) {
2656
+ log.blank();
2657
+ log.plain(`${pc8.green("\u2714")} Authenticated`);
2658
+ return;
2659
+ }
2660
+ await performLogin();
2661
+ }
2608
2662
  async function isAlreadyConfigured(agentName, scope) {
2609
2663
  const agent = getAgent(agentName);
2610
- const mcpPath = scope === "global" ? agent.mcp.globalPath : join8(process.cwd(), agent.mcp.projectPath);
2664
+ const mcpPath = scope === "global" ? agent.mcp.globalPath : join9(process.cwd(), agent.mcp.projectPath);
2611
2665
  try {
2612
2666
  const existing = await readJsonConfig(mcpPath);
2613
2667
  const section = existing[agent.mcp.configKey] ?? {};
@@ -2616,10 +2670,10 @@ async function isAlreadyConfigured(agentName, scope) {
2616
2670
  return false;
2617
2671
  }
2618
2672
  }
2619
- async function promptAgents(scope) {
2673
+ async function promptAgents(scope, mode) {
2620
2674
  const choices = await Promise.all(
2621
2675
  ALL_AGENT_NAMES.map(async (name) => {
2622
- const configured = await isAlreadyConfigured(name, scope);
2676
+ const configured = mode === "mcp" ? await isAlreadyConfigured(name, scope) : false;
2623
2677
  return {
2624
2678
  name: SETUP_AGENT_NAMES[name],
2625
2679
  value: name,
@@ -2631,10 +2685,11 @@ async function promptAgents(scope) {
2631
2685
  log.info("Context7 is already configured for all detected agents.");
2632
2686
  return null;
2633
2687
  }
2688
+ const message = mode === "cli" ? "Install find-docs skill for which agents?" : "Which agents do you want to set up?";
2634
2689
  try {
2635
2690
  return await checkboxWithHover(
2636
2691
  {
2637
- message: "Which agents do you want to set up?",
2692
+ message,
2638
2693
  choices,
2639
2694
  loop: false,
2640
2695
  theme: CHECKBOX_THEME
@@ -2645,13 +2700,13 @@ async function promptAgents(scope) {
2645
2700
  return null;
2646
2701
  }
2647
2702
  }
2648
- async function resolveAgents(options, scope) {
2703
+ async function resolveAgents(options, scope, mode = "mcp") {
2649
2704
  const explicit = getSelectedAgents(options);
2650
2705
  if (explicit.length > 0) return explicit;
2651
2706
  const detected = await detectAgents(scope);
2652
2707
  if (detected.length > 0 && options.yes) return detected;
2653
2708
  log.blank();
2654
- const selected = await promptAgents(scope);
2709
+ const selected = await promptAgents(scope, mode);
2655
2710
  if (!selected) {
2656
2711
  log.warn("Setup cancelled");
2657
2712
  return [];
@@ -2660,7 +2715,7 @@ async function resolveAgents(options, scope) {
2660
2715
  }
2661
2716
  async function setupAgent(agentName, auth, scope) {
2662
2717
  const agent = getAgent(agentName);
2663
- const mcpPath = scope === "global" ? agent.mcp.globalPath : join8(process.cwd(), agent.mcp.projectPath);
2718
+ const mcpPath = scope === "global" ? agent.mcp.globalPath : join9(process.cwd(), agent.mcp.projectPath);
2664
2719
  let mcpStatus;
2665
2720
  try {
2666
2721
  const existing = await readJsonConfig(mcpPath);
@@ -2682,21 +2737,24 @@ async function setupAgent(agentName, auth, scope) {
2682
2737
  } catch (err) {
2683
2738
  mcpStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
2684
2739
  }
2685
- const rulePath = scope === "global" ? join8(agent.rule.dir("global"), agent.rule.filename) : join8(process.cwd(), agent.rule.dir("project"), agent.rule.filename);
2740
+ const rulePath = scope === "global" ? join9(agent.rule.dir("global"), agent.rule.filename) : join9(process.cwd(), agent.rule.dir("project"), agent.rule.filename);
2686
2741
  let ruleStatus;
2687
2742
  try {
2688
- await mkdir4(dirname3(rulePath), { recursive: true });
2743
+ await mkdir4(dirname4(rulePath), { recursive: true });
2689
2744
  await writeFile4(rulePath, RULE_CONTENT, "utf-8");
2690
2745
  ruleStatus = "installed";
2691
2746
  } catch (err) {
2692
2747
  ruleStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
2693
2748
  }
2694
- const skillDir = scope === "global" ? agent.skill.dir("global") : join8(process.cwd(), agent.skill.dir("project"));
2695
- const skillPath = join8(skillDir, agent.skill.name, "SKILL.md");
2749
+ const skillDir = scope === "global" ? agent.skill.dir("global") : join9(process.cwd(), agent.skill.dir("project"));
2750
+ const skillPath = join9(skillDir, agent.skill.name, "SKILL.md");
2696
2751
  let skillStatus;
2697
2752
  try {
2698
- await mkdir4(dirname3(skillPath), { recursive: true });
2699
- await writeFile4(skillPath, SKILL_CONTENT, "utf-8");
2753
+ const downloadData = await downloadSkill("/upstash/context7", agent.skill.name);
2754
+ if (downloadData.error || downloadData.files.length === 0) {
2755
+ throw new Error(downloadData.error || "no files");
2756
+ }
2757
+ await installSkillFiles(agent.skill.name, downloadData.files, skillDir);
2700
2758
  skillStatus = "installed";
2701
2759
  } catch (err) {
2702
2760
  skillStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
@@ -2711,11 +2769,7 @@ async function setupAgent(agentName, auth, scope) {
2711
2769
  skillPath
2712
2770
  };
2713
2771
  }
2714
- async function setupCommand(options) {
2715
- trackEvent("command", { name: "setup" });
2716
- const scope = options.project ? "project" : "global";
2717
- const agents2 = await resolveAgents(options, scope);
2718
- if (agents2.length === 0) return;
2772
+ async function setupMcp(agents2, options, scope) {
2719
2773
  const auth = await resolveAuth(options);
2720
2774
  if (!auth) {
2721
2775
  log.warn("Setup cancelled");
@@ -2745,11 +2799,71 @@ async function setupCommand(options) {
2745
2799
  log.blank();
2746
2800
  trackEvent("setup", { agents: agents2, scope, authMode: auth.mode });
2747
2801
  }
2802
+ async function setupCli(options) {
2803
+ await resolveCliAuth(options.apiKey);
2804
+ const targets = await promptForInstallTargets({ ...options, global: !options.project }, false);
2805
+ if (!targets) {
2806
+ log.warn("Setup cancelled");
2807
+ return;
2808
+ }
2809
+ log.blank();
2810
+ const spinner = ora4("Downloading find-docs skill...").start();
2811
+ const downloadData = await downloadSkill("/upstash/context7", "find-docs");
2812
+ if (downloadData.error || downloadData.files.length === 0) {
2813
+ spinner.fail(`Failed to download find-docs skill: ${downloadData.error || "no files"}`);
2814
+ return;
2815
+ }
2816
+ spinner.succeed("Downloaded find-docs skill");
2817
+ const targetDirs = getTargetDirs(targets);
2818
+ const installSpinner = ora4("Installing find-docs skill...").start();
2819
+ for (const dir of targetDirs) {
2820
+ installSpinner.text = `Installing to ${dir}...`;
2821
+ await installSkillFiles("find-docs", downloadData.files, dir);
2822
+ }
2823
+ installSpinner.stop();
2824
+ log.blank();
2825
+ log.plain(`${pc8.green("\u2714")} Context7 CLI setup complete`);
2826
+ log.blank();
2827
+ for (const dir of targetDirs) {
2828
+ log.itemAdd(
2829
+ `find-docs ${pc8.dim("Guides your agent to fetch up-to-date library docs on demand using ctx7 CLI commands")}`
2830
+ );
2831
+ log.plain(` ${pc8.dim(dir)}`);
2832
+ }
2833
+ log.blank();
2834
+ log.plain(` ${pc8.bold("Next steps")}`);
2835
+ log.plain(` Ask your agent: ${pc8.cyan(`"Use ctx7 CLI to look up React hooks"`)}`);
2836
+ log.blank();
2837
+ trackEvent("setup", { mode: "cli" });
2838
+ }
2839
+ async function setupCommand(options) {
2840
+ trackEvent("command", { name: "setup" });
2841
+ try {
2842
+ const mode = await resolveMode(options);
2843
+ if (mode === "mcp") {
2844
+ const scope = options.project ? "project" : "global";
2845
+ const agents2 = await resolveAgents(options, scope, mode);
2846
+ if (agents2.length === 0) return;
2847
+ await setupMcp(agents2, options, scope);
2848
+ } else {
2849
+ await setupCli(options);
2850
+ }
2851
+ } catch (err) {
2852
+ if (err instanceof Error && err.name === "ExitPromptError") process.exit(0);
2853
+ throw err;
2854
+ }
2855
+ }
2748
2856
 
2749
2857
  // src/commands/docs.ts
2750
2858
  import pc9 from "picocolors";
2751
2859
  import ora5 from "ora";
2752
2860
  var isTTY = process.stdout.isTTY;
2861
+ function getReputationLabel(score) {
2862
+ if (score === void 0 || score < 0) return "Unknown";
2863
+ if (score >= 7) return "High";
2864
+ if (score >= 4) return "Medium";
2865
+ return "Low";
2866
+ }
2753
2867
  function getAccessToken() {
2754
2868
  const tokens = loadTokens();
2755
2869
  if (!tokens || isTokenExpired(tokens)) return void 0;
@@ -2757,28 +2871,22 @@ function getAccessToken() {
2757
2871
  }
2758
2872
  function formatLibraryResult(lib, index) {
2759
2873
  const lines = [];
2760
- lines.push(`${pc9.dim(`${index + 1}.`)} ${pc9.bold(lib.title)} ${pc9.cyan(lib.id)}`);
2874
+ lines.push(`${pc9.dim(`${index + 1}.`)} ${pc9.bold(`Title: ${lib.title}`)}`);
2875
+ lines.push(` ${pc9.cyan(`Context7-compatible library ID: ${lib.id}`)}`);
2761
2876
  if (lib.description) {
2762
- lines.push(` ${pc9.dim(lib.description)}`);
2877
+ lines.push(` ${pc9.dim(`Description: ${lib.description}`)}`);
2763
2878
  }
2764
- const meta = [];
2765
2879
  if (lib.totalSnippets) {
2766
- meta.push(`${lib.totalSnippets} snippets`);
2767
- }
2768
- if (lib.stars && lib.stars > 0) {
2769
- meta.push(`${lib.stars.toLocaleString()} stars`);
2880
+ lines.push(` ${pc9.dim(`Code Snippets: ${lib.totalSnippets}`)}`);
2770
2881
  }
2771
2882
  if (lib.trustScore !== void 0) {
2772
- meta.push(`trust: ${lib.trustScore}/10`);
2883
+ lines.push(` ${pc9.dim(`Source Reputation: ${getReputationLabel(lib.trustScore)}`)}`);
2773
2884
  }
2774
2885
  if (lib.benchmarkScore !== void 0 && lib.benchmarkScore > 0) {
2775
- meta.push(`benchmark: ${lib.benchmarkScore}`);
2776
- }
2777
- if (meta.length > 0) {
2778
- lines.push(` ${pc9.dim(meta.join(" \xB7 "))}`);
2886
+ lines.push(` ${pc9.dim(`Benchmark Score: ${lib.benchmarkScore}`)}`);
2779
2887
  }
2780
2888
  if (lib.versions && lib.versions.length > 0) {
2781
- lines.push(` ${pc9.dim(`versions: ${lib.versions.join(", ")}`)}`);
2889
+ lines.push(` ${pc9.dim(`Versions: ${lib.versions.join(", ")}`)}`);
2782
2890
  }
2783
2891
  return lines.join("\n");
2784
2892
  }
@@ -2813,6 +2921,12 @@ async function resolveCommand(library, query, options) {
2813
2921
  return;
2814
2922
  }
2815
2923
  log.blank();
2924
+ if (data.searchFilterApplied) {
2925
+ log.warn(
2926
+ "Your results only include libraries matching your access settings. To search across all public libraries, update your settings at https://context7.com/dashboard?tab=libraries"
2927
+ );
2928
+ log.blank();
2929
+ }
2816
2930
  for (let i = 0; i < results.length; i++) {
2817
2931
  log.plain(formatLibraryResult(results[i], i));
2818
2932
  log.blank();
@@ -2828,9 +2942,9 @@ async function resolveCommand(library, query, options) {
2828
2942
  }
2829
2943
  async function queryCommand(libraryId, query, options) {
2830
2944
  trackEvent("command", { name: "docs" });
2831
- if (!libraryId.startsWith("/")) {
2832
- log.error(`Invalid library ID: ${libraryId}`);
2833
- log.info(`Library IDs start with "/" (e.g., /facebook/react)`);
2945
+ if (!libraryId.startsWith("/") || !/^\/[^/]+\/[^/]/.test(libraryId)) {
2946
+ log.error(`Invalid library ID: "${libraryId}"`);
2947
+ log.info(`Expected format: /owner/repo or /owner/repo/version (e.g., /facebook/react)`);
2834
2948
  log.info(`Run "ctx7 library <name>" to find the correct ID`);
2835
2949
  process.exitCode = 1;
2836
2950
  return;
@@ -2909,15 +3023,6 @@ function registerDocsCommands(program2) {
2909
3023
  });
2910
3024
  }
2911
3025
 
2912
- // src/constants.ts
2913
- import { readFileSync as readFileSync2 } from "fs";
2914
- import { fileURLToPath } from "url";
2915
- import { dirname as dirname4, join as join9 } from "path";
2916
- var __dirname = dirname4(fileURLToPath(import.meta.url));
2917
- var pkg = JSON.parse(readFileSync2(join9(__dirname, "../package.json"), "utf-8"));
2918
- var VERSION = pkg.version;
2919
- var NAME = pkg.name;
2920
-
2921
3026
  // src/index.ts
2922
3027
  var brand = {
2923
3028
  primary: pc10.green,