ctx7 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js 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 join7 } from "path";
12
+ import { join as join6 } from "path";
13
13
 
14
14
  // src/utils/parse-input.ts
15
15
  function parseSkillInput(input2) {
@@ -30,6 +30,31 @@ function parseSkillInput(input2) {
30
30
 
31
31
  // src/utils/github.ts
32
32
  import { execSync } from "child_process";
33
+
34
+ // src/utils/skill-name.ts
35
+ import { resolve, dirname, basename } from "path";
36
+ var SAFE_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
37
+ function isSafeSkillName(name) {
38
+ if (typeof name !== "string") return false;
39
+ if (name.length === 0 || name.length > 128) return false;
40
+ if (name === "." || name === "..") return false;
41
+ if (name.includes("\0")) return false;
42
+ if (!SAFE_NAME.test(name)) return false;
43
+ return true;
44
+ }
45
+ function assertSkillNameInRoot(skillsRoot, skillName) {
46
+ if (!isSafeSkillName(skillName)) {
47
+ throw new Error(`Unsafe skill name: ${JSON.stringify(skillName)}`);
48
+ }
49
+ const root = resolve(skillsRoot);
50
+ const target = resolve(root, skillName);
51
+ if (dirname(target) !== root || basename(target) !== skillName) {
52
+ throw new Error(`Skill name "${skillName}" escapes the skills root`);
53
+ }
54
+ return target;
55
+ }
56
+
57
+ // src/utils/github.ts
33
58
  var GITHUB_API = "https://api.github.com";
34
59
  var GITHUB_RAW = "https://raw.githubusercontent.com";
35
60
  function parseGitHubUrl(url) {
@@ -85,7 +110,8 @@ function parseSkillFrontmatter(content) {
85
110
  const frontmatter = frontmatterMatch[1];
86
111
  const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
87
112
  if (!nameMatch) return null;
88
- const name = nameMatch[1].trim().replace(/^["']|["']$/g, "").toLowerCase();
113
+ const name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
114
+ if (!isSafeSkillName(name)) return null;
89
115
  let description = "";
90
116
  const multiLineMatch = frontmatter.match(/^description:\s*([|>])-?\s*$/m);
91
117
  if (multiLineMatch) {
@@ -176,7 +202,7 @@ async function listSkillsFromGitHub(project) {
176
202
  async function getSkillFromGitHub(project, skillName) {
177
203
  const result = await listSkillsFromGitHub(project);
178
204
  if (result.status !== "ok") return result;
179
- const skill = result.skills.find((s) => s.name === skillName.toLowerCase());
205
+ const skill = result.skills.find((s) => s.name.toLowerCase() === skillName.toLowerCase());
180
206
  return { ...result, skill };
181
207
  }
182
208
  async function downloadSkillFromGitHub(skill) {
@@ -226,8 +252,8 @@ async function downloadSkillFromGitHub(skill) {
226
252
  // src/constants.ts
227
253
  import { readFileSync } from "fs";
228
254
  import { fileURLToPath } from "url";
229
- import { dirname, join } from "path";
230
- var __dirname = dirname(fileURLToPath(import.meta.url));
255
+ import { dirname as dirname2, join } from "path";
256
+ var __dirname = dirname2(fileURLToPath(import.meta.url));
231
257
  var pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
232
258
  var VERSION = pkg.version;
233
259
  var NAME = pkg.name;
@@ -518,7 +544,7 @@ var log = {
518
544
  import pc3 from "picocolors";
519
545
  import { select, confirm } from "@inquirer/prompts";
520
546
  import { access } from "fs/promises";
521
- import { join as join2, dirname as dirname2 } from "path";
547
+ import { join as join2, dirname as dirname3 } from "path";
522
548
  import { homedir } from "os";
523
549
 
524
550
  // src/utils/prompts.ts
@@ -661,7 +687,7 @@ async function detectVendorSpecificAgents(scope) {
661
687
  const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
662
688
  const detected = [];
663
689
  for (const ide of VENDOR_SPECIFIC_AGENTS) {
664
- const parentDir = dirname2(pathMap[ide]);
690
+ const parentDir = dirname3(pathMap[ide]);
665
691
  try {
666
692
  await access(join2(baseDir, parentDir));
667
693
  detected.push(ide);
@@ -692,7 +718,7 @@ async function promptForInstallTargets(options, forceUniversal = true) {
692
718
  const detectedVendor = await detectVendorSpecificAgents(scope);
693
719
  let hasUniversalDir = false;
694
720
  try {
695
- await access(join2(baseDir, dirname2(universalPath)));
721
+ await access(join2(baseDir, dirname3(universalPath)));
696
722
  hasUniversalDir = true;
697
723
  } catch {
698
724
  }
@@ -859,21 +885,21 @@ function getTargetDirFromSelection(ide, scope) {
859
885
 
860
886
  // src/utils/installer.ts
861
887
  import { mkdir, writeFile, rm, symlink, lstat } from "fs/promises";
862
- import { join as join3, resolve, dirname as dirname3 } from "path";
863
- async function installSkillFiles(skillName, files, targetDir) {
864
- const skillDir = resolve(targetDir, skillName);
888
+ import { resolve as resolve2, dirname as dirname4 } from "path";
889
+ async function installSkillFiles(skillName, files, skillsRoot) {
890
+ const skillDir = assertSkillNameInRoot(skillsRoot, skillName);
865
891
  for (const file of files) {
866
- const filePath = resolve(skillDir, file.path);
892
+ const filePath = resolve2(skillDir, file.path);
867
893
  if (!filePath.startsWith(skillDir + "/") && !filePath.startsWith(skillDir + "\\") && filePath !== skillDir) {
868
894
  throw new Error(`Skill file path "${file.path}" resolves outside the target directory`);
869
895
  }
870
- const fileDir = dirname3(filePath);
896
+ const fileDir = dirname4(filePath);
871
897
  await mkdir(fileDir, { recursive: true });
872
898
  await writeFile(filePath, file.content);
873
899
  }
874
900
  }
875
- async function symlinkSkill(skillName, sourcePath, targetDir) {
876
- const targetPath = join3(targetDir, skillName);
901
+ async function symlinkSkill(skillName, sourcePath, skillsRoot) {
902
+ const targetPath = assertSkillNameInRoot(skillsRoot, skillName);
877
903
  try {
878
904
  const stats = await lstat(targetPath);
879
905
  if (stats.isSymbolicLink() || stats.isDirectory()) {
@@ -881,7 +907,7 @@ async function symlinkSkill(skillName, sourcePath, targetDir) {
881
907
  }
882
908
  } catch {
883
909
  }
884
- await mkdir(targetDir, { recursive: true });
910
+ await mkdir(skillsRoot, { recursive: true });
885
911
  await symlink(sourcePath, targetPath);
886
912
  }
887
913
 
@@ -900,7 +926,7 @@ function trackEvent(event, data) {
900
926
  import pc6 from "picocolors";
901
927
  import ora2 from "ora";
902
928
  import { mkdir as mkdir2, writeFile as writeFile2, readFile, unlink } from "fs/promises";
903
- import { join as join5 } from "path";
929
+ import { join as join4 } from "path";
904
930
  import { homedir as homedir3 } from "os";
905
931
  import { spawn } from "child_process";
906
932
  import { input, select as select2 } from "@inquirer/prompts";
@@ -997,11 +1023,11 @@ function createCallbackServer(expectedState) {
997
1023
  let resolveResult;
998
1024
  let rejectResult;
999
1025
  let serverInstance = null;
1000
- const portPromise = new Promise((resolve2) => {
1001
- resolvePort = resolve2;
1026
+ const portPromise = new Promise((resolve3) => {
1027
+ resolvePort = resolve3;
1002
1028
  });
1003
- const resultPromise = new Promise((resolve2, reject) => {
1004
- resolveResult = resolve2;
1029
+ const resultPromise = new Promise((resolve3, reject) => {
1030
+ resolveResult = resolve3;
1005
1031
  rejectResult = reject;
1006
1032
  });
1007
1033
  const server = http.createServer((req, res) => {
@@ -1681,19 +1707,19 @@ async function generateCommand(options) {
1681
1707
  log.blank();
1682
1708
  };
1683
1709
  const openInEditor = async () => {
1684
- const previewDir = join5(homedir3(), ".context7", "previews");
1710
+ const previewDir = join4(homedir3(), ".context7", "previews");
1685
1711
  await mkdir2(previewDir, { recursive: true });
1686
- previewFile = join5(previewDir, `${skillName}.md`);
1712
+ previewFile = join4(previewDir, `${skillName}.md`);
1687
1713
  if (!previewFileWritten) {
1688
1714
  await writeFile2(previewFile, generatedContent, "utf-8");
1689
1715
  previewFileWritten = true;
1690
1716
  }
1691
1717
  const editor = process.env.EDITOR || "open";
1692
- await new Promise((resolve2) => {
1718
+ await new Promise((resolve3) => {
1693
1719
  const child = spawn(editor, [previewFile], {
1694
1720
  stdio: "inherit"
1695
1721
  });
1696
- child.on("close", () => resolve2());
1722
+ child.on("close", () => resolve3());
1697
1723
  });
1698
1724
  };
1699
1725
  const syncFromPreviewFile = async () => {
@@ -1702,7 +1728,7 @@ async function generateCommand(options) {
1702
1728
  }
1703
1729
  };
1704
1730
  showPreview();
1705
- await new Promise((resolve2) => setTimeout(resolve2, 100));
1731
+ await new Promise((resolve3) => setTimeout(resolve3, 100));
1706
1732
  try {
1707
1733
  let action;
1708
1734
  while (true) {
@@ -1759,8 +1785,8 @@ async function generateCommand(options) {
1759
1785
  if (options.output && !targetDir.includes("/.config/") && !targetDir.startsWith(homedir3())) {
1760
1786
  finalDir = targetDir.replace(process.cwd(), options.output);
1761
1787
  }
1762
- const skillDir = join5(finalDir, skillName);
1763
- const skillPath = join5(skillDir, "SKILL.md");
1788
+ const skillDir = join4(finalDir, skillName);
1789
+ const skillPath = join4(skillDir, "SKILL.md");
1764
1790
  try {
1765
1791
  await mkdir2(skillDir, { recursive: true });
1766
1792
  await writeFile2(skillPath, generatedContent, "utf-8");
@@ -1779,7 +1805,7 @@ async function generateCommand(options) {
1779
1805
  log.blank();
1780
1806
  console.log(pc6.yellow("Fix permissions with:"));
1781
1807
  for (const dir of failedDirs) {
1782
- const parentDir = join5(dir, "..");
1808
+ const parentDir = join4(dir, "..");
1783
1809
  console.log(pc6.dim(` sudo chown -R $(whoami) "${parentDir}"`));
1784
1810
  }
1785
1811
  log.blank();
@@ -1801,7 +1827,7 @@ import { homedir as homedir4 } from "os";
1801
1827
 
1802
1828
  // src/utils/deps.ts
1803
1829
  import { readFile as readFile2 } from "fs/promises";
1804
- import { join as join6 } from "path";
1830
+ import { join as join5 } from "path";
1805
1831
  async function readFileOrNull(path2) {
1806
1832
  try {
1807
1833
  return await readFile2(path2, "utf-8");
@@ -1813,7 +1839,7 @@ function isSkippedLocally(name) {
1813
1839
  return name.startsWith("@types/");
1814
1840
  }
1815
1841
  async function parsePackageJson(cwd) {
1816
- const content = await readFileOrNull(join6(cwd, "package.json"));
1842
+ const content = await readFileOrNull(join5(cwd, "package.json"));
1817
1843
  if (!content) return [];
1818
1844
  try {
1819
1845
  const pkg2 = JSON.parse(content);
@@ -1830,7 +1856,7 @@ async function parsePackageJson(cwd) {
1830
1856
  }
1831
1857
  }
1832
1858
  async function parseRequirementsTxt(cwd) {
1833
- const content = await readFileOrNull(join6(cwd, "requirements.txt"));
1859
+ const content = await readFileOrNull(join5(cwd, "requirements.txt"));
1834
1860
  if (!content) return [];
1835
1861
  const deps = [];
1836
1862
  for (const line of content.split("\n")) {
@@ -1844,7 +1870,7 @@ async function parseRequirementsTxt(cwd) {
1844
1870
  return deps;
1845
1871
  }
1846
1872
  async function parsePyprojectToml(cwd) {
1847
- const content = await readFileOrNull(join6(cwd, "pyproject.toml"));
1873
+ const content = await readFileOrNull(join5(cwd, "pyproject.toml"));
1848
1874
  if (!content) return [];
1849
1875
  const deps = [];
1850
1876
  const seen = /* @__PURE__ */ new Set();
@@ -1916,7 +1942,7 @@ function registerSkillCommands(program2) {
1916
1942
  skill.command("search").alias("s").argument("<keywords...>", "Search keywords").description("Search for skills across all indexed repositories").action(async (keywords) => {
1917
1943
  await searchCommand(keywords.join(" "));
1918
1944
  });
1919
- 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) => {
1945
+ skill.command("list").alias("ls").option("--json", "Output as JSON").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) => {
1920
1946
  await listCommand(options);
1921
1947
  });
1922
1948
  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) => {
@@ -2106,7 +2132,7 @@ ${headerLine}`,
2106
2132
  }
2107
2133
  throw dirErr;
2108
2134
  }
2109
- const primarySkillDir = join7(primaryDir, skill.name);
2135
+ const primarySkillDir = join6(primaryDir, skill.name);
2110
2136
  for (const targetDir of symlinkDirs) {
2111
2137
  try {
2112
2138
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -2134,7 +2160,7 @@ ${headerLine}`,
2134
2160
  log.blank();
2135
2161
  log.warn("Fix permissions with:");
2136
2162
  for (const dir of failedDirs) {
2137
- const parentDir = join7(dir, "..");
2163
+ const parentDir = join6(dir, "..");
2138
2164
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
2139
2165
  }
2140
2166
  log.blank();
@@ -2259,7 +2285,7 @@ ${headerLine}`,
2259
2285
  }
2260
2286
  throw dirErr;
2261
2287
  }
2262
- const primarySkillDir = join7(primaryDir, skill.name);
2288
+ const primarySkillDir = join6(primaryDir, skill.name);
2263
2289
  for (const targetDir of symlinkDirs) {
2264
2290
  try {
2265
2291
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -2287,7 +2313,7 @@ ${headerLine}`,
2287
2313
  log.blank();
2288
2314
  log.warn("Fix permissions with:");
2289
2315
  for (const dir of failedDirs) {
2290
- const parentDir = join7(dir, "..");
2316
+ const parentDir = join6(dir, "..");
2291
2317
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
2292
2318
  }
2293
2319
  log.blank();
@@ -2314,36 +2340,59 @@ async function listCommand(options) {
2314
2340
  if (hasExplicitIdeOption(options)) {
2315
2341
  const ides = getSelectedIdes(options);
2316
2342
  for (const ide of ides) {
2317
- 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]);
2343
+ 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]);
2318
2344
  const label = ide === "universal" ? UNIVERSAL_AGENTS_LABEL : IDE_NAMES[ide];
2319
2345
  const skills = await scanDir(dir);
2320
2346
  if (skills.length > 0) {
2321
- results.push({ label, path: dir, skills });
2347
+ results.push({ label, displayPath: dir, dir, source: ide, skills });
2322
2348
  }
2323
2349
  }
2324
2350
  } else {
2325
2351
  const universalPath = scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH;
2326
- const universalDir = join7(baseDir, universalPath);
2352
+ const universalDir = join6(baseDir, universalPath);
2327
2353
  const universalSkills = await scanDir(universalDir);
2328
2354
  if (universalSkills.length > 0) {
2329
- results.push({ label: UNIVERSAL_AGENTS_LABEL, path: universalPath, skills: universalSkills });
2355
+ results.push({
2356
+ label: UNIVERSAL_AGENTS_LABEL,
2357
+ displayPath: universalPath,
2358
+ dir: universalDir,
2359
+ source: "universal",
2360
+ skills: universalSkills
2361
+ });
2330
2362
  }
2331
2363
  for (const ide of VENDOR_SPECIFIC_AGENTS) {
2332
2364
  const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
2333
- const dir = join7(baseDir, pathMap[ide]);
2365
+ const dir = join6(baseDir, pathMap[ide]);
2334
2366
  const skills = await scanDir(dir);
2335
2367
  if (skills.length > 0) {
2336
- results.push({ label: IDE_NAMES[ide], path: pathMap[ide], skills });
2368
+ results.push({
2369
+ label: IDE_NAMES[ide],
2370
+ displayPath: pathMap[ide],
2371
+ dir,
2372
+ source: ide,
2373
+ skills
2374
+ });
2337
2375
  }
2338
2376
  }
2339
2377
  }
2378
+ if (options.json) {
2379
+ const skills = results.flatMap(
2380
+ (result) => result.skills.map((name) => ({
2381
+ name,
2382
+ path: join6(result.dir, name),
2383
+ source: result.source
2384
+ }))
2385
+ );
2386
+ console.log(JSON.stringify({ skills }, null, 2));
2387
+ return;
2388
+ }
2340
2389
  if (results.length === 0) {
2341
2390
  log.warn("No skills installed");
2342
2391
  return;
2343
2392
  }
2344
2393
  log.blank();
2345
- for (const { label, path: path2, skills } of results) {
2346
- log.plain(`${pc7.bold(label)} ${pc7.dim(path2)}`);
2394
+ for (const { label, displayPath, skills } of results) {
2395
+ log.plain(`${pc7.bold(label)} ${pc7.dim(displayPath)}`);
2347
2396
  for (const skill of skills) {
2348
2397
  log.plain(` ${pc7.green(skill)}`);
2349
2398
  }
@@ -2358,7 +2407,13 @@ async function removeCommand(name, options) {
2358
2407
  return;
2359
2408
  }
2360
2409
  const skillsDir = getTargetDirFromSelection(target.ide, target.scope);
2361
- const skillPath = join7(skillsDir, name);
2410
+ let skillPath;
2411
+ try {
2412
+ skillPath = assertSkillNameInRoot(skillsDir, name);
2413
+ } catch {
2414
+ log.error(`Invalid skill name: ${name}`);
2415
+ return;
2416
+ }
2362
2417
  try {
2363
2418
  await rm2(skillPath, { recursive: true });
2364
2419
  log.success(`Removed skill: ${name}`);
@@ -2539,7 +2594,7 @@ ${headerLine}`,
2539
2594
  }
2540
2595
  throw dirErr;
2541
2596
  }
2542
- const primarySkillDir = join7(primaryDir, skill.name);
2597
+ const primarySkillDir = join6(primaryDir, skill.name);
2543
2598
  for (const targetDir of symlinkDirs) {
2544
2599
  try {
2545
2600
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -2567,7 +2622,7 @@ ${headerLine}`,
2567
2622
  log.blank();
2568
2623
  log.warn("Fix permissions with:");
2569
2624
  for (const dir of failedDirs) {
2570
- const parentDir = join7(dir, "..");
2625
+ const parentDir = join6(dir, "..");
2571
2626
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
2572
2627
  }
2573
2628
  log.blank();
@@ -2584,12 +2639,12 @@ import pc8 from "picocolors";
2584
2639
  import ora4 from "ora";
2585
2640
  import { select as select3 } from "@inquirer/prompts";
2586
2641
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
2587
- import { dirname as dirname5, join as join9 } from "path";
2642
+ import { dirname as dirname6, join as join8 } from "path";
2588
2643
  import { randomBytes as randomBytes2 } from "crypto";
2589
2644
 
2590
2645
  // src/setup/agents.ts
2591
2646
  import { access as access2 } from "fs/promises";
2592
- import { join as join8 } from "path";
2647
+ import { join as join7 } from "path";
2593
2648
  import { homedir as homedir5 } from "os";
2594
2649
  var SETUP_AGENT_NAMES = {
2595
2650
  claude: "Claude Code",
@@ -2603,14 +2658,25 @@ var AUTH_MODE_LABELS = {
2603
2658
  "api-key": "API Key"
2604
2659
  };
2605
2660
  var MCP_BASE_URL = "https://mcp.context7.com";
2661
+ var STDIO_PACKAGE = "@upstash/context7-mcp";
2662
+ function stdioArgs(auth) {
2663
+ const args = ["-y", STDIO_PACKAGE];
2664
+ if (auth.mode === "api-key" && auth.apiKey) {
2665
+ args.push("--api-key", auth.apiKey);
2666
+ }
2667
+ return args;
2668
+ }
2669
+ function stdioEntry(auth) {
2670
+ return { command: "npx", args: stdioArgs(auth) };
2671
+ }
2606
2672
  function claudeConfigDir() {
2607
- return process.env.CLAUDE_CONFIG_DIR || join8(homedir5(), ".claude");
2673
+ return process.env.CLAUDE_CONFIG_DIR || join7(homedir5(), ".claude");
2608
2674
  }
2609
2675
  function claudeGlobalMcpPath() {
2610
2676
  if (process.env.CLAUDE_CONFIG_DIR) {
2611
- return join8(claudeConfigDir(), ".claude.json");
2677
+ return join7(claudeConfigDir(), ".claude.json");
2612
2678
  }
2613
- return join8(homedir5(), ".claude.json");
2679
+ return join7(homedir5(), ".claude.json");
2614
2680
  }
2615
2681
  function mcpUrl(auth) {
2616
2682
  return auth.mode === "oauth" ? `${MCP_BASE_URL}/mcp/oauth` : `${MCP_BASE_URL}/mcp`;
@@ -2631,16 +2697,16 @@ var agents = {
2631
2697
  return [claudeGlobalMcpPath()];
2632
2698
  },
2633
2699
  configKey: "mcpServers",
2634
- buildEntry: (auth) => withHeaders({ type: "http", url: mcpUrl(auth) }, auth)
2700
+ buildEntry: (auth, transport) => transport === "stdio" ? stdioEntry(auth) : withHeaders({ type: "http", url: mcpUrl(auth) }, auth)
2635
2701
  },
2636
2702
  rule: {
2637
2703
  kind: "file",
2638
- dir: (scope) => scope === "global" ? join8(claudeConfigDir(), "rules") : join8(".claude", "rules"),
2704
+ dir: (scope) => scope === "global" ? join7(claudeConfigDir(), "rules") : join7(".claude", "rules"),
2639
2705
  filename: "context7.md"
2640
2706
  },
2641
2707
  skill: {
2642
2708
  name: "context7-mcp",
2643
- dir: (scope) => scope === "global" ? join8(claudeConfigDir(), "skills") : join8(".claude", "skills")
2709
+ dir: (scope) => scope === "global" ? join7(claudeConfigDir(), "skills") : join7(".claude", "skills")
2644
2710
  },
2645
2711
  detect: {
2646
2712
  projectPaths: [".mcp.json", ".claude"],
@@ -2653,23 +2719,23 @@ var agents = {
2653
2719
  name: "cursor",
2654
2720
  displayName: "Cursor",
2655
2721
  mcp: {
2656
- projectPaths: [join8(".cursor", "mcp.json")],
2657
- globalPaths: [join8(homedir5(), ".cursor", "mcp.json")],
2722
+ projectPaths: [join7(".cursor", "mcp.json")],
2723
+ globalPaths: [join7(homedir5(), ".cursor", "mcp.json")],
2658
2724
  configKey: "mcpServers",
2659
- buildEntry: (auth) => withHeaders({ url: mcpUrl(auth) }, auth)
2725
+ buildEntry: (auth, transport) => transport === "stdio" ? stdioEntry(auth) : withHeaders({ url: mcpUrl(auth) }, auth)
2660
2726
  },
2661
2727
  rule: {
2662
2728
  kind: "file",
2663
- dir: (scope) => scope === "global" ? join8(homedir5(), ".cursor", "rules") : join8(".cursor", "rules"),
2729
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".cursor", "rules") : join7(".cursor", "rules"),
2664
2730
  filename: "context7.mdc"
2665
2731
  },
2666
2732
  skill: {
2667
2733
  name: "context7-mcp",
2668
- dir: (scope) => scope === "global" ? join8(homedir5(), ".cursor", "skills") : join8(".cursor", "skills")
2734
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".cursor", "skills") : join7(".cursor", "skills")
2669
2735
  },
2670
2736
  detect: {
2671
2737
  projectPaths: [".cursor"],
2672
- globalPaths: [join8(homedir5(), ".cursor")]
2738
+ globalPaths: [join7(homedir5(), ".cursor")]
2673
2739
  }
2674
2740
  },
2675
2741
  opencode: {
@@ -2678,78 +2744,72 @@ var agents = {
2678
2744
  mcp: {
2679
2745
  projectPaths: ["opencode.json", "opencode.jsonc", ".opencode.json", ".opencode.jsonc"],
2680
2746
  globalPaths: [
2681
- join8(homedir5(), ".config", "opencode", "opencode.json"),
2682
- join8(homedir5(), ".config", "opencode", "opencode.jsonc"),
2683
- join8(homedir5(), ".config", "opencode", ".opencode.json"),
2684
- join8(homedir5(), ".config", "opencode", ".opencode.jsonc")
2747
+ join7(homedir5(), ".config", "opencode", "opencode.json"),
2748
+ join7(homedir5(), ".config", "opencode", "opencode.jsonc"),
2749
+ join7(homedir5(), ".config", "opencode", ".opencode.json"),
2750
+ join7(homedir5(), ".config", "opencode", ".opencode.jsonc")
2685
2751
  ],
2686
2752
  configKey: "mcp",
2687
- buildEntry: (auth) => withHeaders({ type: "remote", url: mcpUrl(auth), enabled: true }, auth)
2753
+ buildEntry: (auth, transport) => transport === "stdio" ? { type: "local", command: ["npx", ...stdioArgs(auth)], enabled: true } : withHeaders({ type: "remote", url: mcpUrl(auth), enabled: true }, auth)
2688
2754
  },
2689
2755
  rule: {
2690
2756
  kind: "append",
2691
- file: (scope) => scope === "global" ? join8(homedir5(), ".config", "opencode", "AGENTS.md") : "AGENTS.md",
2757
+ file: (scope) => scope === "global" ? join7(homedir5(), ".config", "opencode", "AGENTS.md") : "AGENTS.md",
2692
2758
  sectionMarker: "<!-- context7 -->"
2693
2759
  },
2694
2760
  skill: {
2695
2761
  name: "context7-mcp",
2696
- dir: (scope) => scope === "global" ? join8(homedir5(), ".agents", "skills") : join8(".agents", "skills")
2762
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".agents", "skills") : join7(".agents", "skills")
2697
2763
  },
2698
2764
  detect: {
2699
2765
  projectPaths: ["opencode.json", "opencode.jsonc", ".opencode.json", ".opencode.jsonc"],
2700
- globalPaths: [join8(homedir5(), ".config", "opencode")]
2766
+ globalPaths: [join7(homedir5(), ".config", "opencode")]
2701
2767
  }
2702
2768
  },
2703
2769
  codex: {
2704
2770
  name: "codex",
2705
2771
  displayName: "Codex",
2706
2772
  mcp: {
2707
- projectPaths: [join8(".codex", "config.toml")],
2708
- globalPaths: [join8(homedir5(), ".codex", "config.toml")],
2773
+ projectPaths: [join7(".codex", "config.toml")],
2774
+ globalPaths: [join7(homedir5(), ".codex", "config.toml")],
2709
2775
  configKey: "mcp_servers",
2710
- buildEntry: (auth) => {
2711
- const entry = { type: "http", url: mcpUrl(auth) };
2712
- if (auth.mode === "api-key" && auth.apiKey) {
2713
- entry.headers = { CONTEXT7_API_KEY: auth.apiKey };
2714
- }
2715
- return entry;
2716
- }
2776
+ buildEntry: (auth, transport) => transport === "stdio" ? stdioEntry(auth) : withHeaders({ type: "http", url: mcpUrl(auth) }, auth)
2717
2777
  },
2718
2778
  rule: {
2719
2779
  kind: "append",
2720
- file: (scope) => scope === "global" ? join8(homedir5(), ".codex", "AGENTS.md") : "AGENTS.md",
2780
+ file: (scope) => scope === "global" ? join7(homedir5(), ".codex", "AGENTS.md") : "AGENTS.md",
2721
2781
  sectionMarker: "<!-- context7 -->"
2722
2782
  },
2723
2783
  skill: {
2724
2784
  name: "context7-mcp",
2725
- dir: (scope) => scope === "global" ? join8(homedir5(), ".agents", "skills") : join8(".agents", "skills")
2785
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".agents", "skills") : join7(".agents", "skills")
2726
2786
  },
2727
2787
  detect: {
2728
2788
  projectPaths: [".codex"],
2729
- globalPaths: [join8(homedir5(), ".codex")]
2789
+ globalPaths: [join7(homedir5(), ".codex")]
2730
2790
  }
2731
2791
  },
2732
2792
  gemini: {
2733
2793
  name: "gemini",
2734
2794
  displayName: "Gemini CLI",
2735
2795
  mcp: {
2736
- projectPaths: [join8(".gemini", "settings.json")],
2737
- globalPaths: [join8(homedir5(), ".gemini", "settings.json")],
2796
+ projectPaths: [join7(".gemini", "settings.json")],
2797
+ globalPaths: [join7(homedir5(), ".gemini", "settings.json")],
2738
2798
  configKey: "mcpServers",
2739
- buildEntry: (auth) => withHeaders({ httpUrl: mcpUrl(auth) }, auth)
2799
+ buildEntry: (auth, transport) => transport === "stdio" ? stdioEntry(auth) : withHeaders({ httpUrl: mcpUrl(auth) }, auth)
2740
2800
  },
2741
2801
  rule: {
2742
2802
  kind: "append",
2743
- file: (scope) => scope === "global" ? join8(homedir5(), ".gemini", "GEMINI.md") : "GEMINI.md",
2803
+ file: (scope) => scope === "global" ? join7(homedir5(), ".gemini", "GEMINI.md") : "GEMINI.md",
2744
2804
  sectionMarker: "<!-- context7 -->"
2745
2805
  },
2746
2806
  skill: {
2747
2807
  name: "context7-mcp",
2748
- dir: (scope) => scope === "global" ? join8(homedir5(), ".gemini", "skills") : join8(".gemini", "skills")
2808
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".gemini", "skills") : join7(".gemini", "skills")
2749
2809
  },
2750
2810
  detect: {
2751
2811
  projectPaths: [".gemini"],
2752
- globalPaths: [join8(homedir5(), ".gemini")]
2812
+ globalPaths: [join7(homedir5(), ".gemini")]
2753
2813
  }
2754
2814
  }
2755
2815
  };
@@ -2770,7 +2830,7 @@ async function detectAgents(scope) {
2770
2830
  for (const agent of Object.values(agents)) {
2771
2831
  const paths = scope === "global" ? agent.detect.globalPaths : agent.detect.projectPaths;
2772
2832
  for (const p of paths) {
2773
- const fullPath = scope === "global" ? p : join8(process.cwd(), p);
2833
+ const fullPath = scope === "global" ? p : join7(process.cwd(), p);
2774
2834
  if (await pathExists(fullPath)) {
2775
2835
  detected.push(agent.name);
2776
2836
  break;
@@ -2869,7 +2929,7 @@ function customizeSkillFilesForAgent(agent, skillName, files) {
2869
2929
 
2870
2930
  // src/setup/mcp-writer.ts
2871
2931
  import { access as access3, readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2872
- import { dirname as dirname4 } from "path";
2932
+ import { dirname as dirname5 } from "path";
2873
2933
  function stripJsonComments(text) {
2874
2934
  let result = "";
2875
2935
  let i = 0;
@@ -2948,7 +3008,7 @@ async function resolveMcpPath(candidates) {
2948
3008
  return candidates[0];
2949
3009
  }
2950
3010
  async function writeJsonConfig(filePath, config) {
2951
- await mkdir3(dirname4(filePath), { recursive: true });
3011
+ await mkdir3(dirname5(filePath), { recursive: true });
2952
3012
  await writeFile3(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
2953
3013
  }
2954
3014
  async function readTomlServerExists(filePath, serverName) {
@@ -2959,6 +3019,70 @@ async function readTomlServerExists(filePath, serverName) {
2959
3019
  return false;
2960
3020
  }
2961
3021
  }
3022
+ async function readTomlServerEntry(filePath, serverName) {
3023
+ let raw;
3024
+ try {
3025
+ raw = await readFile3(filePath, "utf-8");
3026
+ } catch {
3027
+ return void 0;
3028
+ }
3029
+ const sectionHeader = `[mcp_servers.${serverName}]`;
3030
+ const startIdx = raw.indexOf(sectionHeader);
3031
+ if (startIdx === -1) return void 0;
3032
+ const rest = raw.slice(startIdx + sectionHeader.length);
3033
+ const nextHeader = /^\[/m.exec(rest);
3034
+ const block = nextHeader ? rest.slice(0, nextHeader.index) : rest;
3035
+ const entry = {};
3036
+ const lineRe = /^([A-Za-z_][\w-]*)\s*=\s*(.+?)\s*$/gm;
3037
+ let lineMatch;
3038
+ while ((lineMatch = lineRe.exec(block)) !== null) {
3039
+ const [, key, valueText] = lineMatch;
3040
+ try {
3041
+ entry[key] = JSON.parse(valueText);
3042
+ } catch {
3043
+ }
3044
+ }
3045
+ return Object.keys(entry).length > 0 ? entry : void 0;
3046
+ }
3047
+ function isStdioContext7Entry(entry) {
3048
+ if (!entry || typeof entry !== "object") return false;
3049
+ const e = entry;
3050
+ const refs = (s) => typeof s === "string" && s.includes(STDIO_PACKAGE);
3051
+ if (Array.isArray(e.command)) {
3052
+ return e.command.some(refs);
3053
+ }
3054
+ if (typeof e.command === "string" && Array.isArray(e.args)) {
3055
+ return e.args.some(refs);
3056
+ }
3057
+ return false;
3058
+ }
3059
+ function getJsonServerEntry(config, configKey, serverName) {
3060
+ const section = config[configKey];
3061
+ if (!section || typeof section !== "object") return void 0;
3062
+ const entry = section[serverName];
3063
+ return entry && typeof entry === "object" ? entry : void 0;
3064
+ }
3065
+ function stripApiKeyPair(args) {
3066
+ const result = [];
3067
+ for (let i = 0; i < args.length; i++) {
3068
+ if (args[i] === "--api-key") {
3069
+ i++;
3070
+ continue;
3071
+ }
3072
+ result.push(args[i]);
3073
+ }
3074
+ return result;
3075
+ }
3076
+ function patchStdioApiKey(entry, apiKey) {
3077
+ if (Array.isArray(entry.command)) {
3078
+ const cmd = stripApiKeyPair(entry.command);
3079
+ if (apiKey) cmd.push("--api-key", apiKey);
3080
+ return { ...entry, command: cmd };
3081
+ }
3082
+ const args = Array.isArray(entry.args) ? stripApiKeyPair(entry.args) : [];
3083
+ if (apiKey) args.push("--api-key", apiKey);
3084
+ return { ...entry, args };
3085
+ }
2962
3086
  function buildTomlServerBlock(serverName, entry) {
2963
3087
  const lines = [`[mcp_servers.${serverName}]`];
2964
3088
  const headers = entry.headers;
@@ -3004,11 +3128,11 @@ async function appendTomlServer(filePath, serverName, entry) {
3004
3128
  const before = rawBefore.length > 0 ? rawBefore + "\n\n" : "";
3005
3129
  const after = rawAfter.length > 0 ? "\n" + rawAfter : "";
3006
3130
  const content = before + block + after;
3007
- await mkdir3(dirname4(filePath), { recursive: true });
3131
+ await mkdir3(dirname5(filePath), { recursive: true });
3008
3132
  await writeFile3(filePath, content, "utf-8");
3009
3133
  } else {
3010
3134
  const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
3011
- await mkdir3(dirname4(filePath), { recursive: true });
3135
+ await mkdir3(dirname5(filePath), { recursive: true });
3012
3136
  await writeFile3(filePath, existing + separator + block, "utf-8");
3013
3137
  }
3014
3138
  return { alreadyExists };
@@ -3041,13 +3165,16 @@ async function removeTomlServer(filePath, serverName) {
3041
3165
  const rawBefore = existing.slice(0, startIdx).replace(/\n+$/, "");
3042
3166
  const rawAfter = existing.slice(startIdx + sectionHeader.length + endOffset).replace(/^\n+/, "");
3043
3167
  const content = [rawBefore, rawAfter].filter(Boolean).join("\n\n");
3044
- await mkdir3(dirname4(filePath), { recursive: true });
3168
+ await mkdir3(dirname5(filePath), { recursive: true });
3045
3169
  await writeFile3(filePath, content.length > 0 ? `${content}
3046
3170
  ` : "", "utf-8");
3047
3171
  return { removed: true };
3048
3172
  }
3049
3173
 
3050
3174
  // src/commands/setup.ts
3175
+ function resolveTransport(options) {
3176
+ return options.stdio ? "stdio" : "http";
3177
+ }
3051
3178
  var CHECKBOX_THEME = {
3052
3179
  style: {
3053
3180
  highlight: (text) => pc8.green(text),
@@ -3064,7 +3191,7 @@ function getSelectedAgents(options) {
3064
3191
  return agents2;
3065
3192
  }
3066
3193
  function registerSetupCommand(program2) {
3067
- 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("--codex", "Set up for Codex").option("--gemini", "Set up for Gemini CLI").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) => {
3194
+ 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("--codex", "Set up for Codex").option("--gemini", "Set up for Gemini CLI").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)").option("--stdio", "Configure the MCP server as a local stdio process (default: HTTP)").action(async (options) => {
3068
3195
  await setupCommand(options);
3069
3196
  });
3070
3197
  }
@@ -3105,7 +3232,7 @@ async function resolveAuth(options) {
3105
3232
  }
3106
3233
  async function resolveMode(options) {
3107
3234
  if (options.cli) return "cli";
3108
- if (options.mcp || options.yes || options.oauth || options.apiKey) return "mcp";
3235
+ if (options.mcp || options.yes || options.oauth || options.apiKey || options.stdio) return "mcp";
3109
3236
  return select3({
3110
3237
  message: "How should your agent access Context7?",
3111
3238
  choices: [
@@ -3181,13 +3308,13 @@ async function installRule(agentName, mode, scope) {
3181
3308
  const rule = agent.rule;
3182
3309
  const content = await getRuleContent(mode, agentName);
3183
3310
  if (rule.kind === "file") {
3184
- const ruleDir = scope === "global" ? rule.dir("global") : join9(process.cwd(), rule.dir("project"));
3185
- const rulePath = join9(ruleDir, rule.filename);
3186
- await mkdir4(dirname5(rulePath), { recursive: true });
3311
+ const ruleDir = scope === "global" ? rule.dir("global") : join8(process.cwd(), rule.dir("project"));
3312
+ const rulePath = join8(ruleDir, rule.filename);
3313
+ await mkdir4(dirname6(rulePath), { recursive: true });
3187
3314
  await writeFile4(rulePath, content, "utf-8");
3188
3315
  return { status: "installed", path: rulePath };
3189
3316
  }
3190
- const filePath = scope === "global" ? rule.file("global") : join9(process.cwd(), rule.file("project"));
3317
+ const filePath = scope === "global" ? rule.file("global") : join8(process.cwd(), rule.file("project"));
3191
3318
  const escapedMarker = rule.sectionMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3192
3319
  const section = `${rule.sectionMarker}
3193
3320
  ${content}${rule.sectionMarker}`;
@@ -3203,30 +3330,37 @@ ${content}${rule.sectionMarker}`;
3203
3330
  return { status: "updated", path: filePath };
3204
3331
  }
3205
3332
  const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
3206
- await mkdir4(dirname5(filePath), { recursive: true });
3333
+ await mkdir4(dirname6(filePath), { recursive: true });
3207
3334
  await writeFile4(filePath, existing + separator + section + "\n", "utf-8");
3208
3335
  return { status: "installed", path: filePath };
3209
3336
  }
3210
- async function setupAgent(agentName, auth, scope) {
3337
+ function resolveEntryToWrite(agent, auth, transport, existingEntry) {
3338
+ if (transport === "stdio" && existingEntry && isStdioContext7Entry(existingEntry)) {
3339
+ const apiKey = auth.mode === "api-key" ? auth.apiKey : void 0;
3340
+ return patchStdioApiKey(existingEntry, apiKey);
3341
+ }
3342
+ return agent.mcp.buildEntry(auth, transport);
3343
+ }
3344
+ async function setupAgent(agentName, auth, transport, scope) {
3211
3345
  const agent = getAgent(agentName);
3212
- const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((p) => join9(process.cwd(), p));
3346
+ const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((p) => join8(process.cwd(), p));
3213
3347
  const mcpPath = await resolveMcpPath(mcpCandidates);
3214
3348
  let mcpStatus;
3215
3349
  try {
3216
3350
  if (mcpPath.endsWith(".toml")) {
3217
- const { alreadyExists } = await appendTomlServer(
3218
- mcpPath,
3219
- "context7",
3220
- agent.mcp.buildEntry(auth)
3221
- );
3351
+ const existingTomlEntry = transport === "stdio" ? await readTomlServerEntry(mcpPath, "context7") : void 0;
3352
+ const entry = resolveEntryToWrite(agent, auth, transport, existingTomlEntry);
3353
+ const { alreadyExists } = await appendTomlServer(mcpPath, "context7", entry);
3222
3354
  mcpStatus = alreadyExists ? `reconfigured with ${AUTH_MODE_LABELS[auth.mode]}` : `configured with ${AUTH_MODE_LABELS[auth.mode]}`;
3223
3355
  } else {
3224
3356
  const existing = await readJsonConfig(mcpPath);
3357
+ const existingJsonEntry = transport === "stdio" ? getJsonServerEntry(existing, agent.mcp.configKey, "context7") : void 0;
3358
+ const entry = resolveEntryToWrite(agent, auth, transport, existingJsonEntry);
3225
3359
  const { config, alreadyExists } = mergeServerEntry(
3226
3360
  existing,
3227
3361
  agent.mcp.configKey,
3228
3362
  "context7",
3229
- agent.mcp.buildEntry(auth)
3363
+ entry
3230
3364
  );
3231
3365
  mcpStatus = alreadyExists ? `reconfigured with ${AUTH_MODE_LABELS[auth.mode]}` : `configured with ${AUTH_MODE_LABELS[auth.mode]}`;
3232
3366
  await writeJsonConfig(mcpPath, config);
@@ -3244,8 +3378,8 @@ async function setupAgent(agentName, auth, scope) {
3244
3378
  ruleStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
3245
3379
  rulePath = "";
3246
3380
  }
3247
- const skillDir = scope === "global" ? agent.skill.dir("global") : join9(process.cwd(), agent.skill.dir("project"));
3248
- const skillPath = join9(skillDir, agent.skill.name, "SKILL.md");
3381
+ const skillDir = scope === "global" ? agent.skill.dir("global") : join8(process.cwd(), agent.skill.dir("project"));
3382
+ const skillPath = join8(skillDir, agent.skill.name, "SKILL.md");
3249
3383
  let skillStatus;
3250
3384
  try {
3251
3385
  const downloadData = await downloadSkill("/upstash/context7", agent.skill.name);
@@ -3268,6 +3402,11 @@ async function setupAgent(agentName, auth, scope) {
3268
3402
  };
3269
3403
  }
3270
3404
  async function setupMcp(agents2, options, scope) {
3405
+ const transport = resolveTransport(options);
3406
+ if (transport === "stdio" && options.oauth) {
3407
+ log.error("--stdio is incompatible with --oauth (OAuth uses the hosted HTTP endpoint).");
3408
+ return;
3409
+ }
3271
3410
  const auth = await resolveAuth(options);
3272
3411
  if (!auth) {
3273
3412
  log.warn("Setup cancelled");
@@ -3278,7 +3417,7 @@ async function setupMcp(agents2, options, scope) {
3278
3417
  const results = [];
3279
3418
  for (const agentName of agents2) {
3280
3419
  spinner.text = `Setting up ${getAgent(agentName).displayName}...`;
3281
- results.push(await setupAgent(agentName, auth, scope));
3420
+ results.push(await setupAgent(agentName, auth, transport, scope));
3282
3421
  }
3283
3422
  spinner.succeed("Context7 setup complete");
3284
3423
  log.blank();
@@ -3295,7 +3434,7 @@ async function setupMcp(agents2, options, scope) {
3295
3434
  log.plain(` ${pc8.dim(r.skillPath)}`);
3296
3435
  if (r.skillStatus.includes("EACCES")) {
3297
3436
  log.plain(
3298
- ` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname5(dirname5(r.skillPath))}`)}`
3437
+ ` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname6(dirname6(r.skillPath))}`)}`
3299
3438
  );
3300
3439
  }
3301
3440
  }
@@ -3305,7 +3444,7 @@ async function setupMcp(agents2, options, scope) {
3305
3444
  }
3306
3445
  async function setupCliAgent(agentName, scope, downloadData) {
3307
3446
  const agent = getAgent(agentName);
3308
- const skillDir = scope === "global" ? agent.skill.dir("global") : join9(process.cwd(), agent.skill.dir("project"));
3447
+ const skillDir = scope === "global" ? agent.skill.dir("global") : join8(process.cwd(), agent.skill.dir("project"));
3309
3448
  let skillStatus;
3310
3449
  try {
3311
3450
  const files = customizeSkillFilesForAgent(agentName, "find-docs", downloadData.files);
@@ -3314,7 +3453,7 @@ async function setupCliAgent(agentName, scope, downloadData) {
3314
3453
  } catch (err) {
3315
3454
  skillStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
3316
3455
  }
3317
- const skillPath = join9(skillDir, "find-docs");
3456
+ const skillPath = join8(skillDir, "find-docs");
3318
3457
  let ruleStatus;
3319
3458
  let rulePath;
3320
3459
  try {
@@ -3356,7 +3495,7 @@ async function setupCli(options) {
3356
3495
  log.plain(` ${pc8.dim(r.skillPath)}`);
3357
3496
  if (r.skillStatus.includes("EACCES")) {
3358
3497
  log.plain(
3359
- ` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname5(dirname5(r.skillPath))}`)}`
3498
+ ` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname6(dirname6(r.skillPath))}`)}`
3360
3499
  );
3361
3500
  }
3362
3501
  const ruleIcon = r.ruleStatus === "installed" || r.ruleStatus === "updated" ? pc8.green("+") : pc8.dim("~");
@@ -3388,7 +3527,7 @@ async function setupCommand(options) {
3388
3527
  // src/commands/remove.ts
3389
3528
  import pc9 from "picocolors";
3390
3529
  import ora5 from "ora";
3391
- import { join as join10 } from "path";
3530
+ import { join as join9 } from "path";
3392
3531
  import { access as access4, readFile as readFile5, rm as rm3, writeFile as writeFile5 } from "fs/promises";
3393
3532
  var CHECKBOX_THEME2 = {
3394
3533
  style: {
@@ -3496,7 +3635,7 @@ async function pathExists2(path2) {
3496
3635
  }
3497
3636
  async function hasMcpConfig(agentName, scope) {
3498
3637
  const agent = getAgent(agentName);
3499
- const candidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join10(process.cwd(), path2));
3638
+ const candidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join9(process.cwd(), path2));
3500
3639
  const mcpPath = await resolveMcpPath(candidates);
3501
3640
  if (mcpPath.endsWith(".toml")) {
3502
3641
  return readTomlServerExists(mcpPath, "context7");
@@ -3517,10 +3656,10 @@ async function hasRule(agentName, scope) {
3517
3656
  const agent = getAgent(agentName);
3518
3657
  const rule = agent.rule;
3519
3658
  if (rule.kind === "file") {
3520
- const ruleDir = scope === "global" ? rule.dir("global") : join10(process.cwd(), rule.dir("project"));
3521
- return pathExists2(join10(ruleDir, rule.filename));
3659
+ const ruleDir = scope === "global" ? rule.dir("global") : join9(process.cwd(), rule.dir("project"));
3660
+ return pathExists2(join9(ruleDir, rule.filename));
3522
3661
  }
3523
- const filePath = scope === "global" ? rule.file("global") : join10(process.cwd(), rule.file("project"));
3662
+ const filePath = scope === "global" ? rule.file("global") : join9(process.cwd(), rule.file("project"));
3524
3663
  try {
3525
3664
  const existing = await readFile5(filePath, "utf-8");
3526
3665
  return existing.includes(CONTEXT7_SECTION_MARKER);
@@ -3530,8 +3669,8 @@ async function hasRule(agentName, scope) {
3530
3669
  }
3531
3670
  async function hasSkill(agentName, scope, skillName) {
3532
3671
  const agent = getAgent(agentName);
3533
- const skillsDir = scope === "global" ? agent.skill.dir("global") : join10(process.cwd(), agent.skill.dir("project"));
3534
- return pathExists2(join10(skillsDir, skillName));
3672
+ const skillsDir = scope === "global" ? agent.skill.dir("global") : join9(process.cwd(), agent.skill.dir("project"));
3673
+ return pathExists2(join9(skillsDir, skillName));
3535
3674
  }
3536
3675
  async function detectAvailableModes(agents2, scope) {
3537
3676
  let hasMcpArtifacts = false;
@@ -3583,7 +3722,7 @@ async function resolveModes(options, agents2, scope) {
3583
3722
  }
3584
3723
  async function uninstallMcp(agentName, scope) {
3585
3724
  const agent = getAgent(agentName);
3586
- const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join10(process.cwd(), path2));
3725
+ const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join9(process.cwd(), path2));
3587
3726
  const mcpPath = await resolveMcpPath(mcpCandidates);
3588
3727
  try {
3589
3728
  if (mcpPath.endsWith(".toml")) {
@@ -3604,8 +3743,8 @@ async function uninstallRule(agentName, scope) {
3604
3743
  const agent = getAgent(agentName);
3605
3744
  const rule = agent.rule;
3606
3745
  if (rule.kind === "file") {
3607
- const rulePath = scope === "global" ? rule.dir("global") : join10(process.cwd(), rule.dir("project"));
3608
- const targetPath = join10(rulePath, rule.filename);
3746
+ const rulePath = scope === "global" ? rule.dir("global") : join9(process.cwd(), rule.dir("project"));
3747
+ const targetPath = join9(rulePath, rule.filename);
3609
3748
  try {
3610
3749
  await rm3(targetPath);
3611
3750
  return { status: "removed", path: targetPath };
@@ -3615,7 +3754,7 @@ async function uninstallRule(agentName, scope) {
3615
3754
  return { status: `failed: ${error.message}`, path: targetPath };
3616
3755
  }
3617
3756
  }
3618
- const filePath = scope === "global" ? rule.file("global") : join10(process.cwd(), rule.file("project"));
3757
+ const filePath = scope === "global" ? rule.file("global") : join9(process.cwd(), rule.file("project"));
3619
3758
  try {
3620
3759
  const existing = await readFile5(filePath, "utf-8");
3621
3760
  if (!existing.includes(CONTEXT7_SECTION_MARKER)) {
@@ -3638,10 +3777,10 @@ async function uninstallRule(agentName, scope) {
3638
3777
  }
3639
3778
  async function uninstallSkills(agentName, scope, skillNames) {
3640
3779
  const agent = getAgent(agentName);
3641
- const skillsDir = scope === "global" ? agent.skill.dir("global") : join10(process.cwd(), agent.skill.dir("project"));
3780
+ const skillsDir = scope === "global" ? agent.skill.dir("global") : join9(process.cwd(), agent.skill.dir("project"));
3642
3781
  const results = [];
3643
3782
  for (const skillName of skillNames) {
3644
- const skillPath = join10(skillsDir, skillName);
3783
+ const skillPath = join9(skillsDir, skillName);
3645
3784
  try {
3646
3785
  await rm3(skillPath, { recursive: true });
3647
3786
  results.push({ name: skillName, status: "removed", path: skillPath });
@@ -3904,10 +4043,10 @@ import pc11 from "picocolors";
3904
4043
 
3905
4044
  // src/utils/update-check.ts
3906
4045
  import { homedir as homedir6 } from "os";
3907
- import { dirname as dirname6, join as join11 } from "path";
4046
+ import { dirname as dirname7, join as join10 } from "path";
3908
4047
  import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
3909
4048
  var DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
3910
- var UPDATE_STATE_FILE = join11(homedir6(), ".context7", "cli-state.json");
4049
+ var UPDATE_STATE_FILE = join10(homedir6(), ".context7", "cli-state.json");
3911
4050
  function getStateFilePath(stateFile) {
3912
4051
  return stateFile ?? UPDATE_STATE_FILE;
3913
4052
  }
@@ -3921,7 +4060,7 @@ async function readUpdateState(stateFile) {
3921
4060
  }
3922
4061
  async function writeUpdateState(state, stateFile) {
3923
4062
  const path2 = getStateFilePath(stateFile);
3924
- await mkdir5(dirname6(path2), { recursive: true });
4063
+ await mkdir5(dirname7(path2), { recursive: true });
3925
4064
  await writeFile6(path2, JSON.stringify(state, null, 2) + "\n", "utf-8");
3926
4065
  }
3927
4066
  function compareVersions(a, b) {
@@ -4098,13 +4237,13 @@ function registerUpgradeCommand(program2) {
4098
4237
  });
4099
4238
  }
4100
4239
  function runCommand(command, args) {
4101
- return new Promise((resolve2, reject) => {
4240
+ return new Promise((resolve3, reject) => {
4102
4241
  const child = spawn2(command, args, {
4103
4242
  stdio: "inherit",
4104
4243
  shell: process.platform === "win32"
4105
4244
  });
4106
4245
  child.on("error", reject);
4107
- child.on("close", (code) => resolve2(code));
4246
+ child.on("close", (code) => resolve3(code));
4108
4247
  });
4109
4248
  }
4110
4249
  async function runUpgradePlan(plan) {