ctx7 0.4.2 → 0.4.3

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();
@@ -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,7 +2340,7 @@ 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) {
@@ -2323,14 +2349,14 @@ async function listCommand(options) {
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
2355
  results.push({ label: UNIVERSAL_AGENTS_LABEL, path: universalPath, skills: universalSkills });
2330
2356
  }
2331
2357
  for (const ide of VENDOR_SPECIFIC_AGENTS) {
2332
2358
  const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
2333
- const dir = join7(baseDir, pathMap[ide]);
2359
+ const dir = join6(baseDir, pathMap[ide]);
2334
2360
  const skills = await scanDir(dir);
2335
2361
  if (skills.length > 0) {
2336
2362
  results.push({ label: IDE_NAMES[ide], path: pathMap[ide], skills });
@@ -2358,7 +2384,13 @@ async function removeCommand(name, options) {
2358
2384
  return;
2359
2385
  }
2360
2386
  const skillsDir = getTargetDirFromSelection(target.ide, target.scope);
2361
- const skillPath = join7(skillsDir, name);
2387
+ let skillPath;
2388
+ try {
2389
+ skillPath = assertSkillNameInRoot(skillsDir, name);
2390
+ } catch {
2391
+ log.error(`Invalid skill name: ${name}`);
2392
+ return;
2393
+ }
2362
2394
  try {
2363
2395
  await rm2(skillPath, { recursive: true });
2364
2396
  log.success(`Removed skill: ${name}`);
@@ -2539,7 +2571,7 @@ ${headerLine}`,
2539
2571
  }
2540
2572
  throw dirErr;
2541
2573
  }
2542
- const primarySkillDir = join7(primaryDir, skill.name);
2574
+ const primarySkillDir = join6(primaryDir, skill.name);
2543
2575
  for (const targetDir of symlinkDirs) {
2544
2576
  try {
2545
2577
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -2567,7 +2599,7 @@ ${headerLine}`,
2567
2599
  log.blank();
2568
2600
  log.warn("Fix permissions with:");
2569
2601
  for (const dir of failedDirs) {
2570
- const parentDir = join7(dir, "..");
2602
+ const parentDir = join6(dir, "..");
2571
2603
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
2572
2604
  }
2573
2605
  log.blank();
@@ -2584,12 +2616,12 @@ import pc8 from "picocolors";
2584
2616
  import ora4 from "ora";
2585
2617
  import { select as select3 } from "@inquirer/prompts";
2586
2618
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
2587
- import { dirname as dirname5, join as join9 } from "path";
2619
+ import { dirname as dirname6, join as join8 } from "path";
2588
2620
  import { randomBytes as randomBytes2 } from "crypto";
2589
2621
 
2590
2622
  // src/setup/agents.ts
2591
2623
  import { access as access2 } from "fs/promises";
2592
- import { join as join8 } from "path";
2624
+ import { join as join7 } from "path";
2593
2625
  import { homedir as homedir5 } from "os";
2594
2626
  var SETUP_AGENT_NAMES = {
2595
2627
  claude: "Claude Code",
@@ -2603,14 +2635,25 @@ var AUTH_MODE_LABELS = {
2603
2635
  "api-key": "API Key"
2604
2636
  };
2605
2637
  var MCP_BASE_URL = "https://mcp.context7.com";
2638
+ var STDIO_PACKAGE = "@upstash/context7-mcp";
2639
+ function stdioArgs(auth) {
2640
+ const args = ["-y", STDIO_PACKAGE];
2641
+ if (auth.mode === "api-key" && auth.apiKey) {
2642
+ args.push("--api-key", auth.apiKey);
2643
+ }
2644
+ return args;
2645
+ }
2646
+ function stdioEntry(auth) {
2647
+ return { command: "npx", args: stdioArgs(auth) };
2648
+ }
2606
2649
  function claudeConfigDir() {
2607
- return process.env.CLAUDE_CONFIG_DIR || join8(homedir5(), ".claude");
2650
+ return process.env.CLAUDE_CONFIG_DIR || join7(homedir5(), ".claude");
2608
2651
  }
2609
2652
  function claudeGlobalMcpPath() {
2610
2653
  if (process.env.CLAUDE_CONFIG_DIR) {
2611
- return join8(claudeConfigDir(), ".claude.json");
2654
+ return join7(claudeConfigDir(), ".claude.json");
2612
2655
  }
2613
- return join8(homedir5(), ".claude.json");
2656
+ return join7(homedir5(), ".claude.json");
2614
2657
  }
2615
2658
  function mcpUrl(auth) {
2616
2659
  return auth.mode === "oauth" ? `${MCP_BASE_URL}/mcp/oauth` : `${MCP_BASE_URL}/mcp`;
@@ -2631,16 +2674,16 @@ var agents = {
2631
2674
  return [claudeGlobalMcpPath()];
2632
2675
  },
2633
2676
  configKey: "mcpServers",
2634
- buildEntry: (auth) => withHeaders({ type: "http", url: mcpUrl(auth) }, auth)
2677
+ buildEntry: (auth, transport) => transport === "stdio" ? stdioEntry(auth) : withHeaders({ type: "http", url: mcpUrl(auth) }, auth)
2635
2678
  },
2636
2679
  rule: {
2637
2680
  kind: "file",
2638
- dir: (scope) => scope === "global" ? join8(claudeConfigDir(), "rules") : join8(".claude", "rules"),
2681
+ dir: (scope) => scope === "global" ? join7(claudeConfigDir(), "rules") : join7(".claude", "rules"),
2639
2682
  filename: "context7.md"
2640
2683
  },
2641
2684
  skill: {
2642
2685
  name: "context7-mcp",
2643
- dir: (scope) => scope === "global" ? join8(claudeConfigDir(), "skills") : join8(".claude", "skills")
2686
+ dir: (scope) => scope === "global" ? join7(claudeConfigDir(), "skills") : join7(".claude", "skills")
2644
2687
  },
2645
2688
  detect: {
2646
2689
  projectPaths: [".mcp.json", ".claude"],
@@ -2653,23 +2696,23 @@ var agents = {
2653
2696
  name: "cursor",
2654
2697
  displayName: "Cursor",
2655
2698
  mcp: {
2656
- projectPaths: [join8(".cursor", "mcp.json")],
2657
- globalPaths: [join8(homedir5(), ".cursor", "mcp.json")],
2699
+ projectPaths: [join7(".cursor", "mcp.json")],
2700
+ globalPaths: [join7(homedir5(), ".cursor", "mcp.json")],
2658
2701
  configKey: "mcpServers",
2659
- buildEntry: (auth) => withHeaders({ url: mcpUrl(auth) }, auth)
2702
+ buildEntry: (auth, transport) => transport === "stdio" ? stdioEntry(auth) : withHeaders({ url: mcpUrl(auth) }, auth)
2660
2703
  },
2661
2704
  rule: {
2662
2705
  kind: "file",
2663
- dir: (scope) => scope === "global" ? join8(homedir5(), ".cursor", "rules") : join8(".cursor", "rules"),
2706
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".cursor", "rules") : join7(".cursor", "rules"),
2664
2707
  filename: "context7.mdc"
2665
2708
  },
2666
2709
  skill: {
2667
2710
  name: "context7-mcp",
2668
- dir: (scope) => scope === "global" ? join8(homedir5(), ".cursor", "skills") : join8(".cursor", "skills")
2711
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".cursor", "skills") : join7(".cursor", "skills")
2669
2712
  },
2670
2713
  detect: {
2671
2714
  projectPaths: [".cursor"],
2672
- globalPaths: [join8(homedir5(), ".cursor")]
2715
+ globalPaths: [join7(homedir5(), ".cursor")]
2673
2716
  }
2674
2717
  },
2675
2718
  opencode: {
@@ -2678,78 +2721,72 @@ var agents = {
2678
2721
  mcp: {
2679
2722
  projectPaths: ["opencode.json", "opencode.jsonc", ".opencode.json", ".opencode.jsonc"],
2680
2723
  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")
2724
+ join7(homedir5(), ".config", "opencode", "opencode.json"),
2725
+ join7(homedir5(), ".config", "opencode", "opencode.jsonc"),
2726
+ join7(homedir5(), ".config", "opencode", ".opencode.json"),
2727
+ join7(homedir5(), ".config", "opencode", ".opencode.jsonc")
2685
2728
  ],
2686
2729
  configKey: "mcp",
2687
- buildEntry: (auth) => withHeaders({ type: "remote", url: mcpUrl(auth), enabled: true }, auth)
2730
+ buildEntry: (auth, transport) => transport === "stdio" ? { type: "local", command: ["npx", ...stdioArgs(auth)], enabled: true } : withHeaders({ type: "remote", url: mcpUrl(auth), enabled: true }, auth)
2688
2731
  },
2689
2732
  rule: {
2690
2733
  kind: "append",
2691
- file: (scope) => scope === "global" ? join8(homedir5(), ".config", "opencode", "AGENTS.md") : "AGENTS.md",
2734
+ file: (scope) => scope === "global" ? join7(homedir5(), ".config", "opencode", "AGENTS.md") : "AGENTS.md",
2692
2735
  sectionMarker: "<!-- context7 -->"
2693
2736
  },
2694
2737
  skill: {
2695
2738
  name: "context7-mcp",
2696
- dir: (scope) => scope === "global" ? join8(homedir5(), ".agents", "skills") : join8(".agents", "skills")
2739
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".agents", "skills") : join7(".agents", "skills")
2697
2740
  },
2698
2741
  detect: {
2699
2742
  projectPaths: ["opencode.json", "opencode.jsonc", ".opencode.json", ".opencode.jsonc"],
2700
- globalPaths: [join8(homedir5(), ".config", "opencode")]
2743
+ globalPaths: [join7(homedir5(), ".config", "opencode")]
2701
2744
  }
2702
2745
  },
2703
2746
  codex: {
2704
2747
  name: "codex",
2705
2748
  displayName: "Codex",
2706
2749
  mcp: {
2707
- projectPaths: [join8(".codex", "config.toml")],
2708
- globalPaths: [join8(homedir5(), ".codex", "config.toml")],
2750
+ projectPaths: [join7(".codex", "config.toml")],
2751
+ globalPaths: [join7(homedir5(), ".codex", "config.toml")],
2709
2752
  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
- }
2753
+ buildEntry: (auth, transport) => transport === "stdio" ? stdioEntry(auth) : withHeaders({ type: "http", url: mcpUrl(auth) }, auth)
2717
2754
  },
2718
2755
  rule: {
2719
2756
  kind: "append",
2720
- file: (scope) => scope === "global" ? join8(homedir5(), ".codex", "AGENTS.md") : "AGENTS.md",
2757
+ file: (scope) => scope === "global" ? join7(homedir5(), ".codex", "AGENTS.md") : "AGENTS.md",
2721
2758
  sectionMarker: "<!-- context7 -->"
2722
2759
  },
2723
2760
  skill: {
2724
2761
  name: "context7-mcp",
2725
- dir: (scope) => scope === "global" ? join8(homedir5(), ".agents", "skills") : join8(".agents", "skills")
2762
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".agents", "skills") : join7(".agents", "skills")
2726
2763
  },
2727
2764
  detect: {
2728
2765
  projectPaths: [".codex"],
2729
- globalPaths: [join8(homedir5(), ".codex")]
2766
+ globalPaths: [join7(homedir5(), ".codex")]
2730
2767
  }
2731
2768
  },
2732
2769
  gemini: {
2733
2770
  name: "gemini",
2734
2771
  displayName: "Gemini CLI",
2735
2772
  mcp: {
2736
- projectPaths: [join8(".gemini", "settings.json")],
2737
- globalPaths: [join8(homedir5(), ".gemini", "settings.json")],
2773
+ projectPaths: [join7(".gemini", "settings.json")],
2774
+ globalPaths: [join7(homedir5(), ".gemini", "settings.json")],
2738
2775
  configKey: "mcpServers",
2739
- buildEntry: (auth) => withHeaders({ httpUrl: mcpUrl(auth) }, auth)
2776
+ buildEntry: (auth, transport) => transport === "stdio" ? stdioEntry(auth) : withHeaders({ httpUrl: mcpUrl(auth) }, auth)
2740
2777
  },
2741
2778
  rule: {
2742
2779
  kind: "append",
2743
- file: (scope) => scope === "global" ? join8(homedir5(), ".gemini", "GEMINI.md") : "GEMINI.md",
2780
+ file: (scope) => scope === "global" ? join7(homedir5(), ".gemini", "GEMINI.md") : "GEMINI.md",
2744
2781
  sectionMarker: "<!-- context7 -->"
2745
2782
  },
2746
2783
  skill: {
2747
2784
  name: "context7-mcp",
2748
- dir: (scope) => scope === "global" ? join8(homedir5(), ".gemini", "skills") : join8(".gemini", "skills")
2785
+ dir: (scope) => scope === "global" ? join7(homedir5(), ".gemini", "skills") : join7(".gemini", "skills")
2749
2786
  },
2750
2787
  detect: {
2751
2788
  projectPaths: [".gemini"],
2752
- globalPaths: [join8(homedir5(), ".gemini")]
2789
+ globalPaths: [join7(homedir5(), ".gemini")]
2753
2790
  }
2754
2791
  }
2755
2792
  };
@@ -2770,7 +2807,7 @@ async function detectAgents(scope) {
2770
2807
  for (const agent of Object.values(agents)) {
2771
2808
  const paths = scope === "global" ? agent.detect.globalPaths : agent.detect.projectPaths;
2772
2809
  for (const p of paths) {
2773
- const fullPath = scope === "global" ? p : join8(process.cwd(), p);
2810
+ const fullPath = scope === "global" ? p : join7(process.cwd(), p);
2774
2811
  if (await pathExists(fullPath)) {
2775
2812
  detected.push(agent.name);
2776
2813
  break;
@@ -2869,7 +2906,7 @@ function customizeSkillFilesForAgent(agent, skillName, files) {
2869
2906
 
2870
2907
  // src/setup/mcp-writer.ts
2871
2908
  import { access as access3, readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2872
- import { dirname as dirname4 } from "path";
2909
+ import { dirname as dirname5 } from "path";
2873
2910
  function stripJsonComments(text) {
2874
2911
  let result = "";
2875
2912
  let i = 0;
@@ -2948,7 +2985,7 @@ async function resolveMcpPath(candidates) {
2948
2985
  return candidates[0];
2949
2986
  }
2950
2987
  async function writeJsonConfig(filePath, config) {
2951
- await mkdir3(dirname4(filePath), { recursive: true });
2988
+ await mkdir3(dirname5(filePath), { recursive: true });
2952
2989
  await writeFile3(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
2953
2990
  }
2954
2991
  async function readTomlServerExists(filePath, serverName) {
@@ -2959,6 +2996,70 @@ async function readTomlServerExists(filePath, serverName) {
2959
2996
  return false;
2960
2997
  }
2961
2998
  }
2999
+ async function readTomlServerEntry(filePath, serverName) {
3000
+ let raw;
3001
+ try {
3002
+ raw = await readFile3(filePath, "utf-8");
3003
+ } catch {
3004
+ return void 0;
3005
+ }
3006
+ const sectionHeader = `[mcp_servers.${serverName}]`;
3007
+ const startIdx = raw.indexOf(sectionHeader);
3008
+ if (startIdx === -1) return void 0;
3009
+ const rest = raw.slice(startIdx + sectionHeader.length);
3010
+ const nextHeader = /^\[/m.exec(rest);
3011
+ const block = nextHeader ? rest.slice(0, nextHeader.index) : rest;
3012
+ const entry = {};
3013
+ const lineRe = /^([A-Za-z_][\w-]*)\s*=\s*(.+?)\s*$/gm;
3014
+ let lineMatch;
3015
+ while ((lineMatch = lineRe.exec(block)) !== null) {
3016
+ const [, key, valueText] = lineMatch;
3017
+ try {
3018
+ entry[key] = JSON.parse(valueText);
3019
+ } catch {
3020
+ }
3021
+ }
3022
+ return Object.keys(entry).length > 0 ? entry : void 0;
3023
+ }
3024
+ function isStdioContext7Entry(entry) {
3025
+ if (!entry || typeof entry !== "object") return false;
3026
+ const e = entry;
3027
+ const refs = (s) => typeof s === "string" && s.includes(STDIO_PACKAGE);
3028
+ if (Array.isArray(e.command)) {
3029
+ return e.command.some(refs);
3030
+ }
3031
+ if (typeof e.command === "string" && Array.isArray(e.args)) {
3032
+ return e.args.some(refs);
3033
+ }
3034
+ return false;
3035
+ }
3036
+ function getJsonServerEntry(config, configKey, serverName) {
3037
+ const section = config[configKey];
3038
+ if (!section || typeof section !== "object") return void 0;
3039
+ const entry = section[serverName];
3040
+ return entry && typeof entry === "object" ? entry : void 0;
3041
+ }
3042
+ function stripApiKeyPair(args) {
3043
+ const result = [];
3044
+ for (let i = 0; i < args.length; i++) {
3045
+ if (args[i] === "--api-key") {
3046
+ i++;
3047
+ continue;
3048
+ }
3049
+ result.push(args[i]);
3050
+ }
3051
+ return result;
3052
+ }
3053
+ function patchStdioApiKey(entry, apiKey) {
3054
+ if (Array.isArray(entry.command)) {
3055
+ const cmd = stripApiKeyPair(entry.command);
3056
+ if (apiKey) cmd.push("--api-key", apiKey);
3057
+ return { ...entry, command: cmd };
3058
+ }
3059
+ const args = Array.isArray(entry.args) ? stripApiKeyPair(entry.args) : [];
3060
+ if (apiKey) args.push("--api-key", apiKey);
3061
+ return { ...entry, args };
3062
+ }
2962
3063
  function buildTomlServerBlock(serverName, entry) {
2963
3064
  const lines = [`[mcp_servers.${serverName}]`];
2964
3065
  const headers = entry.headers;
@@ -3004,11 +3105,11 @@ async function appendTomlServer(filePath, serverName, entry) {
3004
3105
  const before = rawBefore.length > 0 ? rawBefore + "\n\n" : "";
3005
3106
  const after = rawAfter.length > 0 ? "\n" + rawAfter : "";
3006
3107
  const content = before + block + after;
3007
- await mkdir3(dirname4(filePath), { recursive: true });
3108
+ await mkdir3(dirname5(filePath), { recursive: true });
3008
3109
  await writeFile3(filePath, content, "utf-8");
3009
3110
  } else {
3010
3111
  const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
3011
- await mkdir3(dirname4(filePath), { recursive: true });
3112
+ await mkdir3(dirname5(filePath), { recursive: true });
3012
3113
  await writeFile3(filePath, existing + separator + block, "utf-8");
3013
3114
  }
3014
3115
  return { alreadyExists };
@@ -3041,13 +3142,16 @@ async function removeTomlServer(filePath, serverName) {
3041
3142
  const rawBefore = existing.slice(0, startIdx).replace(/\n+$/, "");
3042
3143
  const rawAfter = existing.slice(startIdx + sectionHeader.length + endOffset).replace(/^\n+/, "");
3043
3144
  const content = [rawBefore, rawAfter].filter(Boolean).join("\n\n");
3044
- await mkdir3(dirname4(filePath), { recursive: true });
3145
+ await mkdir3(dirname5(filePath), { recursive: true });
3045
3146
  await writeFile3(filePath, content.length > 0 ? `${content}
3046
3147
  ` : "", "utf-8");
3047
3148
  return { removed: true };
3048
3149
  }
3049
3150
 
3050
3151
  // src/commands/setup.ts
3152
+ function resolveTransport(options) {
3153
+ return options.stdio ? "stdio" : "http";
3154
+ }
3051
3155
  var CHECKBOX_THEME = {
3052
3156
  style: {
3053
3157
  highlight: (text) => pc8.green(text),
@@ -3064,7 +3168,7 @@ function getSelectedAgents(options) {
3064
3168
  return agents2;
3065
3169
  }
3066
3170
  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) => {
3171
+ 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
3172
  await setupCommand(options);
3069
3173
  });
3070
3174
  }
@@ -3105,7 +3209,7 @@ async function resolveAuth(options) {
3105
3209
  }
3106
3210
  async function resolveMode(options) {
3107
3211
  if (options.cli) return "cli";
3108
- if (options.mcp || options.yes || options.oauth || options.apiKey) return "mcp";
3212
+ if (options.mcp || options.yes || options.oauth || options.apiKey || options.stdio) return "mcp";
3109
3213
  return select3({
3110
3214
  message: "How should your agent access Context7?",
3111
3215
  choices: [
@@ -3181,13 +3285,13 @@ async function installRule(agentName, mode, scope) {
3181
3285
  const rule = agent.rule;
3182
3286
  const content = await getRuleContent(mode, agentName);
3183
3287
  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 });
3288
+ const ruleDir = scope === "global" ? rule.dir("global") : join8(process.cwd(), rule.dir("project"));
3289
+ const rulePath = join8(ruleDir, rule.filename);
3290
+ await mkdir4(dirname6(rulePath), { recursive: true });
3187
3291
  await writeFile4(rulePath, content, "utf-8");
3188
3292
  return { status: "installed", path: rulePath };
3189
3293
  }
3190
- const filePath = scope === "global" ? rule.file("global") : join9(process.cwd(), rule.file("project"));
3294
+ const filePath = scope === "global" ? rule.file("global") : join8(process.cwd(), rule.file("project"));
3191
3295
  const escapedMarker = rule.sectionMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3192
3296
  const section = `${rule.sectionMarker}
3193
3297
  ${content}${rule.sectionMarker}`;
@@ -3203,30 +3307,37 @@ ${content}${rule.sectionMarker}`;
3203
3307
  return { status: "updated", path: filePath };
3204
3308
  }
3205
3309
  const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
3206
- await mkdir4(dirname5(filePath), { recursive: true });
3310
+ await mkdir4(dirname6(filePath), { recursive: true });
3207
3311
  await writeFile4(filePath, existing + separator + section + "\n", "utf-8");
3208
3312
  return { status: "installed", path: filePath };
3209
3313
  }
3210
- async function setupAgent(agentName, auth, scope) {
3314
+ function resolveEntryToWrite(agent, auth, transport, existingEntry) {
3315
+ if (transport === "stdio" && existingEntry && isStdioContext7Entry(existingEntry)) {
3316
+ const apiKey = auth.mode === "api-key" ? auth.apiKey : void 0;
3317
+ return patchStdioApiKey(existingEntry, apiKey);
3318
+ }
3319
+ return agent.mcp.buildEntry(auth, transport);
3320
+ }
3321
+ async function setupAgent(agentName, auth, transport, scope) {
3211
3322
  const agent = getAgent(agentName);
3212
- const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((p) => join9(process.cwd(), p));
3323
+ const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((p) => join8(process.cwd(), p));
3213
3324
  const mcpPath = await resolveMcpPath(mcpCandidates);
3214
3325
  let mcpStatus;
3215
3326
  try {
3216
3327
  if (mcpPath.endsWith(".toml")) {
3217
- const { alreadyExists } = await appendTomlServer(
3218
- mcpPath,
3219
- "context7",
3220
- agent.mcp.buildEntry(auth)
3221
- );
3328
+ const existingTomlEntry = transport === "stdio" ? await readTomlServerEntry(mcpPath, "context7") : void 0;
3329
+ const entry = resolveEntryToWrite(agent, auth, transport, existingTomlEntry);
3330
+ const { alreadyExists } = await appendTomlServer(mcpPath, "context7", entry);
3222
3331
  mcpStatus = alreadyExists ? `reconfigured with ${AUTH_MODE_LABELS[auth.mode]}` : `configured with ${AUTH_MODE_LABELS[auth.mode]}`;
3223
3332
  } else {
3224
3333
  const existing = await readJsonConfig(mcpPath);
3334
+ const existingJsonEntry = transport === "stdio" ? getJsonServerEntry(existing, agent.mcp.configKey, "context7") : void 0;
3335
+ const entry = resolveEntryToWrite(agent, auth, transport, existingJsonEntry);
3225
3336
  const { config, alreadyExists } = mergeServerEntry(
3226
3337
  existing,
3227
3338
  agent.mcp.configKey,
3228
3339
  "context7",
3229
- agent.mcp.buildEntry(auth)
3340
+ entry
3230
3341
  );
3231
3342
  mcpStatus = alreadyExists ? `reconfigured with ${AUTH_MODE_LABELS[auth.mode]}` : `configured with ${AUTH_MODE_LABELS[auth.mode]}`;
3232
3343
  await writeJsonConfig(mcpPath, config);
@@ -3244,8 +3355,8 @@ async function setupAgent(agentName, auth, scope) {
3244
3355
  ruleStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
3245
3356
  rulePath = "";
3246
3357
  }
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");
3358
+ const skillDir = scope === "global" ? agent.skill.dir("global") : join8(process.cwd(), agent.skill.dir("project"));
3359
+ const skillPath = join8(skillDir, agent.skill.name, "SKILL.md");
3249
3360
  let skillStatus;
3250
3361
  try {
3251
3362
  const downloadData = await downloadSkill("/upstash/context7", agent.skill.name);
@@ -3268,6 +3379,11 @@ async function setupAgent(agentName, auth, scope) {
3268
3379
  };
3269
3380
  }
3270
3381
  async function setupMcp(agents2, options, scope) {
3382
+ const transport = resolveTransport(options);
3383
+ if (transport === "stdio" && options.oauth) {
3384
+ log.error("--stdio is incompatible with --oauth (OAuth uses the hosted HTTP endpoint).");
3385
+ return;
3386
+ }
3271
3387
  const auth = await resolveAuth(options);
3272
3388
  if (!auth) {
3273
3389
  log.warn("Setup cancelled");
@@ -3278,7 +3394,7 @@ async function setupMcp(agents2, options, scope) {
3278
3394
  const results = [];
3279
3395
  for (const agentName of agents2) {
3280
3396
  spinner.text = `Setting up ${getAgent(agentName).displayName}...`;
3281
- results.push(await setupAgent(agentName, auth, scope));
3397
+ results.push(await setupAgent(agentName, auth, transport, scope));
3282
3398
  }
3283
3399
  spinner.succeed("Context7 setup complete");
3284
3400
  log.blank();
@@ -3295,7 +3411,7 @@ async function setupMcp(agents2, options, scope) {
3295
3411
  log.plain(` ${pc8.dim(r.skillPath)}`);
3296
3412
  if (r.skillStatus.includes("EACCES")) {
3297
3413
  log.plain(
3298
- ` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname5(dirname5(r.skillPath))}`)}`
3414
+ ` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname6(dirname6(r.skillPath))}`)}`
3299
3415
  );
3300
3416
  }
3301
3417
  }
@@ -3305,7 +3421,7 @@ async function setupMcp(agents2, options, scope) {
3305
3421
  }
3306
3422
  async function setupCliAgent(agentName, scope, downloadData) {
3307
3423
  const agent = getAgent(agentName);
3308
- const skillDir = scope === "global" ? agent.skill.dir("global") : join9(process.cwd(), agent.skill.dir("project"));
3424
+ const skillDir = scope === "global" ? agent.skill.dir("global") : join8(process.cwd(), agent.skill.dir("project"));
3309
3425
  let skillStatus;
3310
3426
  try {
3311
3427
  const files = customizeSkillFilesForAgent(agentName, "find-docs", downloadData.files);
@@ -3314,7 +3430,7 @@ async function setupCliAgent(agentName, scope, downloadData) {
3314
3430
  } catch (err) {
3315
3431
  skillStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
3316
3432
  }
3317
- const skillPath = join9(skillDir, "find-docs");
3433
+ const skillPath = join8(skillDir, "find-docs");
3318
3434
  let ruleStatus;
3319
3435
  let rulePath;
3320
3436
  try {
@@ -3356,7 +3472,7 @@ async function setupCli(options) {
3356
3472
  log.plain(` ${pc8.dim(r.skillPath)}`);
3357
3473
  if (r.skillStatus.includes("EACCES")) {
3358
3474
  log.plain(
3359
- ` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname5(dirname5(r.skillPath))}`)}`
3475
+ ` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname6(dirname6(r.skillPath))}`)}`
3360
3476
  );
3361
3477
  }
3362
3478
  const ruleIcon = r.ruleStatus === "installed" || r.ruleStatus === "updated" ? pc8.green("+") : pc8.dim("~");
@@ -3388,7 +3504,7 @@ async function setupCommand(options) {
3388
3504
  // src/commands/remove.ts
3389
3505
  import pc9 from "picocolors";
3390
3506
  import ora5 from "ora";
3391
- import { join as join10 } from "path";
3507
+ import { join as join9 } from "path";
3392
3508
  import { access as access4, readFile as readFile5, rm as rm3, writeFile as writeFile5 } from "fs/promises";
3393
3509
  var CHECKBOX_THEME2 = {
3394
3510
  style: {
@@ -3496,7 +3612,7 @@ async function pathExists2(path2) {
3496
3612
  }
3497
3613
  async function hasMcpConfig(agentName, scope) {
3498
3614
  const agent = getAgent(agentName);
3499
- const candidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join10(process.cwd(), path2));
3615
+ const candidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join9(process.cwd(), path2));
3500
3616
  const mcpPath = await resolveMcpPath(candidates);
3501
3617
  if (mcpPath.endsWith(".toml")) {
3502
3618
  return readTomlServerExists(mcpPath, "context7");
@@ -3517,10 +3633,10 @@ async function hasRule(agentName, scope) {
3517
3633
  const agent = getAgent(agentName);
3518
3634
  const rule = agent.rule;
3519
3635
  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));
3636
+ const ruleDir = scope === "global" ? rule.dir("global") : join9(process.cwd(), rule.dir("project"));
3637
+ return pathExists2(join9(ruleDir, rule.filename));
3522
3638
  }
3523
- const filePath = scope === "global" ? rule.file("global") : join10(process.cwd(), rule.file("project"));
3639
+ const filePath = scope === "global" ? rule.file("global") : join9(process.cwd(), rule.file("project"));
3524
3640
  try {
3525
3641
  const existing = await readFile5(filePath, "utf-8");
3526
3642
  return existing.includes(CONTEXT7_SECTION_MARKER);
@@ -3530,8 +3646,8 @@ async function hasRule(agentName, scope) {
3530
3646
  }
3531
3647
  async function hasSkill(agentName, scope, skillName) {
3532
3648
  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));
3649
+ const skillsDir = scope === "global" ? agent.skill.dir("global") : join9(process.cwd(), agent.skill.dir("project"));
3650
+ return pathExists2(join9(skillsDir, skillName));
3535
3651
  }
3536
3652
  async function detectAvailableModes(agents2, scope) {
3537
3653
  let hasMcpArtifacts = false;
@@ -3583,7 +3699,7 @@ async function resolveModes(options, agents2, scope) {
3583
3699
  }
3584
3700
  async function uninstallMcp(agentName, scope) {
3585
3701
  const agent = getAgent(agentName);
3586
- const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join10(process.cwd(), path2));
3702
+ const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join9(process.cwd(), path2));
3587
3703
  const mcpPath = await resolveMcpPath(mcpCandidates);
3588
3704
  try {
3589
3705
  if (mcpPath.endsWith(".toml")) {
@@ -3604,8 +3720,8 @@ async function uninstallRule(agentName, scope) {
3604
3720
  const agent = getAgent(agentName);
3605
3721
  const rule = agent.rule;
3606
3722
  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);
3723
+ const rulePath = scope === "global" ? rule.dir("global") : join9(process.cwd(), rule.dir("project"));
3724
+ const targetPath = join9(rulePath, rule.filename);
3609
3725
  try {
3610
3726
  await rm3(targetPath);
3611
3727
  return { status: "removed", path: targetPath };
@@ -3615,7 +3731,7 @@ async function uninstallRule(agentName, scope) {
3615
3731
  return { status: `failed: ${error.message}`, path: targetPath };
3616
3732
  }
3617
3733
  }
3618
- const filePath = scope === "global" ? rule.file("global") : join10(process.cwd(), rule.file("project"));
3734
+ const filePath = scope === "global" ? rule.file("global") : join9(process.cwd(), rule.file("project"));
3619
3735
  try {
3620
3736
  const existing = await readFile5(filePath, "utf-8");
3621
3737
  if (!existing.includes(CONTEXT7_SECTION_MARKER)) {
@@ -3638,10 +3754,10 @@ async function uninstallRule(agentName, scope) {
3638
3754
  }
3639
3755
  async function uninstallSkills(agentName, scope, skillNames) {
3640
3756
  const agent = getAgent(agentName);
3641
- const skillsDir = scope === "global" ? agent.skill.dir("global") : join10(process.cwd(), agent.skill.dir("project"));
3757
+ const skillsDir = scope === "global" ? agent.skill.dir("global") : join9(process.cwd(), agent.skill.dir("project"));
3642
3758
  const results = [];
3643
3759
  for (const skillName of skillNames) {
3644
- const skillPath = join10(skillsDir, skillName);
3760
+ const skillPath = join9(skillsDir, skillName);
3645
3761
  try {
3646
3762
  await rm3(skillPath, { recursive: true });
3647
3763
  results.push({ name: skillName, status: "removed", path: skillPath });
@@ -3904,10 +4020,10 @@ import pc11 from "picocolors";
3904
4020
 
3905
4021
  // src/utils/update-check.ts
3906
4022
  import { homedir as homedir6 } from "os";
3907
- import { dirname as dirname6, join as join11 } from "path";
4023
+ import { dirname as dirname7, join as join10 } from "path";
3908
4024
  import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
3909
4025
  var DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
3910
- var UPDATE_STATE_FILE = join11(homedir6(), ".context7", "cli-state.json");
4026
+ var UPDATE_STATE_FILE = join10(homedir6(), ".context7", "cli-state.json");
3911
4027
  function getStateFilePath(stateFile) {
3912
4028
  return stateFile ?? UPDATE_STATE_FILE;
3913
4029
  }
@@ -3921,7 +4037,7 @@ async function readUpdateState(stateFile) {
3921
4037
  }
3922
4038
  async function writeUpdateState(state, stateFile) {
3923
4039
  const path2 = getStateFilePath(stateFile);
3924
- await mkdir5(dirname6(path2), { recursive: true });
4040
+ await mkdir5(dirname7(path2), { recursive: true });
3925
4041
  await writeFile6(path2, JSON.stringify(state, null, 2) + "\n", "utf-8");
3926
4042
  }
3927
4043
  function compareVersions(a, b) {
@@ -4098,13 +4214,13 @@ function registerUpgradeCommand(program2) {
4098
4214
  });
4099
4215
  }
4100
4216
  function runCommand(command, args) {
4101
- return new Promise((resolve2, reject) => {
4217
+ return new Promise((resolve3, reject) => {
4102
4218
  const child = spawn2(command, args, {
4103
4219
  stdio: "inherit",
4104
4220
  shell: process.platform === "win32"
4105
4221
  });
4106
4222
  child.on("error", reject);
4107
- child.on("close", (code) => resolve2(code));
4223
+ child.on("close", (code) => resolve3(code));
4108
4224
  });
4109
4225
  }
4110
4226
  async function runUpgradePlan(plan) {