ctx7 0.2.3 → 0.2.4-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,8 +8,8 @@ import figlet from "figlet";
8
8
  // src/commands/skill.ts
9
9
  import pc7 from "picocolors";
10
10
  import ora3 from "ora";
11
- import { readdir, rm as rm2 } from "fs/promises";
12
- import { join as join6 } from "path";
11
+ import { readdir as readdir2, rm as rm3 } from "fs/promises";
12
+ import { join as join7 } from "path";
13
13
 
14
14
  // src/utils/parse-input.ts
15
15
  function parseSkillInput(input2) {
@@ -1495,7 +1495,7 @@ async function generateCommand(options) {
1495
1495
  import { homedir as homedir4 } from "os";
1496
1496
 
1497
1497
  // src/utils/deps.ts
1498
- import { readFile as readFile2 } from "fs/promises";
1498
+ import { readFile as readFile2, readdir } from "fs/promises";
1499
1499
  import { join as join5 } from "path";
1500
1500
  async function readFileOrNull(path2) {
1501
1501
  try {
@@ -1513,11 +1513,16 @@ async function parsePackageJson(cwd) {
1513
1513
  try {
1514
1514
  const pkg2 = JSON.parse(content);
1515
1515
  const names = /* @__PURE__ */ new Set();
1516
- for (const key of Object.keys(pkg2.dependencies || {})) {
1517
- if (!isSkippedLocally(key)) names.add(key);
1518
- }
1519
- for (const key of Object.keys(pkg2.devDependencies || {})) {
1520
- if (!isSkippedLocally(key)) names.add(key);
1516
+ const depTypes = [
1517
+ "dependencies",
1518
+ "devDependencies",
1519
+ "optionalDependencies",
1520
+ "peerDependencies"
1521
+ ];
1522
+ for (const type of depTypes) {
1523
+ for (const key of Object.keys(pkg2[type] || {})) {
1524
+ if (!isSkippedLocally(key)) names.add(key);
1525
+ }
1521
1526
  }
1522
1527
  return [...names];
1523
1528
  } catch {
@@ -1532,9 +1537,7 @@ async function parseRequirementsTxt(cwd) {
1532
1537
  const trimmed = line.trim();
1533
1538
  if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) continue;
1534
1539
  const name = trimmed.split(/[=<>!~;@\s\[]/)[0].trim();
1535
- if (name && !isSkippedLocally(name)) {
1536
- deps.push(name);
1537
- }
1540
+ if (name && !isSkippedLocally(name)) deps.push(name);
1538
1541
  }
1539
1542
  return deps;
1540
1543
  }
@@ -1556,8 +1559,7 @@ async function parsePyprojectToml(cwd) {
1556
1559
  }
1557
1560
  const poetryMatch = content.match(/\[tool\.poetry\.dependencies\]([\s\S]*?)(?:\n\[|$)/);
1558
1561
  if (poetryMatch) {
1559
- const lines = poetryMatch[1].split("\n");
1560
- for (const line of lines) {
1562
+ for (const line of poetryMatch[1].split("\n")) {
1561
1563
  const match = line.match(/^(\S+)\s*=/);
1562
1564
  if (match) {
1563
1565
  const name = match[1].trim();
@@ -1578,6 +1580,109 @@ async function detectProjectDependencies(cwd) {
1578
1580
  ]);
1579
1581
  return [...new Set(results.flat())];
1580
1582
  }
1583
+ async function parseLockfileDeps(cwd) {
1584
+ const content = await readFileOrNull(join5(cwd, "package-lock.json"));
1585
+ if (!content) return null;
1586
+ try {
1587
+ const lock = JSON.parse(content);
1588
+ const rootPkg = lock.packages?.[""];
1589
+ if (rootPkg) {
1590
+ const deps = [
1591
+ ...Object.keys(rootPkg.dependencies || {}),
1592
+ ...Object.keys(rootPkg.devDependencies || {}),
1593
+ ...Object.keys(rootPkg.optionalDependencies || {})
1594
+ ];
1595
+ return deps.filter((d) => !isSkippedLocally(d));
1596
+ }
1597
+ } catch {
1598
+ }
1599
+ return null;
1600
+ }
1601
+ async function detectNewlyInstalledPackages(cwd) {
1602
+ const declaredDeps = await parsePackageJson(cwd);
1603
+ const declaredSet = new Set(declaredDeps);
1604
+ const nodeModulesPath = join5(cwd, "node_modules");
1605
+ try {
1606
+ const entries = await readdir(nodeModulesPath);
1607
+ const isPnpm = entries.includes(".pnpm");
1608
+ if (isPnpm) {
1609
+ const regularPackages = entries.filter((e) => !e.startsWith(".") && !e.startsWith("@"));
1610
+ const scopedPackages = [];
1611
+ for (const scope of entries.filter((e) => e.startsWith("@"))) {
1612
+ try {
1613
+ const packages = await readdir(join5(nodeModulesPath, scope));
1614
+ for (const pkg2 of packages) {
1615
+ if (!pkg2.startsWith(".")) scopedPackages.push(`${scope}/${pkg2}`);
1616
+ }
1617
+ } catch {
1618
+ }
1619
+ }
1620
+ const installed = [...regularPackages, ...scopedPackages];
1621
+ return installed.filter((pkg2) => !declaredSet.has(pkg2) && !isSkippedLocally(pkg2));
1622
+ } else {
1623
+ const lockfileDeps = await parseLockfileDeps(cwd);
1624
+ return lockfileDeps ? lockfileDeps.filter((pkg2) => !declaredSet.has(pkg2)) : [];
1625
+ }
1626
+ } catch {
1627
+ return [];
1628
+ }
1629
+ }
1630
+
1631
+ // src/utils/cache.ts
1632
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, rm as rm2 } from "fs/promises";
1633
+ import { join as join6 } from "path";
1634
+ var CACHE_DIR = join6("node_modules", ".cache", "context7");
1635
+ var PENDING_FILE = "pending.json";
1636
+ var PENDING_TTL_MS = 20 * 60 * 1e3;
1637
+ async function getCachePath(cwd) {
1638
+ const dir = join6(cwd, CACHE_DIR);
1639
+ await mkdir3(dir, { recursive: true });
1640
+ return join6(dir, PENDING_FILE);
1641
+ }
1642
+ async function loadPendingPackages(cwd) {
1643
+ try {
1644
+ const cachePath = await getCachePath(cwd);
1645
+ const content = await readFile3(cachePath, "utf-8");
1646
+ const cache = JSON.parse(content);
1647
+ const now = Date.now();
1648
+ const valid = cache.packages.filter((p) => now - p.detectedAt < PENDING_TTL_MS);
1649
+ return valid.map((p) => p.name);
1650
+ } catch {
1651
+ return [];
1652
+ }
1653
+ }
1654
+ async function savePendingPackages(cwd, packages) {
1655
+ const cachePath = await getCachePath(cwd);
1656
+ const cache = {
1657
+ packages: packages.map((name) => ({
1658
+ name,
1659
+ detectedAt: Date.now()
1660
+ }))
1661
+ };
1662
+ await writeFile3(cachePath, JSON.stringify(cache, null, 2));
1663
+ }
1664
+ async function clearPendingPackages(cwd) {
1665
+ try {
1666
+ const cachePath = await getCachePath(cwd);
1667
+ await rm2(cachePath, { force: true });
1668
+ } catch {
1669
+ }
1670
+ }
1671
+ async function appendPendingPackages(cwd, newPackages) {
1672
+ const existing = await loadPendingPackages(cwd);
1673
+ const merged = [.../* @__PURE__ */ new Set([...existing, ...newPackages])];
1674
+ await savePendingPackages(cwd, merged);
1675
+ }
1676
+
1677
+ // src/utils/ansi.ts
1678
+ var ansi = {
1679
+ reset: "\x1B[0m",
1680
+ bold: "\x1B[1m",
1681
+ dim: "\x1B[2m",
1682
+ cyan: "\x1B[36m",
1683
+ green: "\x1B[32m",
1684
+ yellow: "\x1B[33m"
1685
+ };
1581
1686
 
1582
1687
  // src/commands/skill.ts
1583
1688
  function logInstallSummary(targets, targetDirs, skillNames) {
@@ -1612,9 +1717,12 @@ function registerSkillCommands(program2) {
1612
1717
  skill.command("info").argument("<repository>", "GitHub repository (/owner/repo)").description("Show skills in a repository").action(async (project) => {
1613
1718
  await infoCommand(project);
1614
1719
  });
1615
- skill.command("suggest").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Suggest skills based on your project dependencies").action(async (options) => {
1720
+ skill.command("suggest").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Suggest and install skills based on your project dependencies").action(async (options) => {
1616
1721
  await suggestCommand(options);
1617
1722
  });
1723
+ skill.command("detect").description("Detect newly installed packages (for postinstall hooks)").action(async () => {
1724
+ await detectCommand();
1725
+ });
1618
1726
  }
1619
1727
  function registerSkillAliases(program2) {
1620
1728
  program2.command("si", { hidden: true }).argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Install skills (alias for: skills install)").action(async (project, skillName, options) => {
@@ -1757,7 +1865,7 @@ async function installCommand(input2, skillName, options) {
1757
1865
  }
1758
1866
  throw dirErr;
1759
1867
  }
1760
- const primarySkillDir = join6(primaryDir, skill.name);
1868
+ const primarySkillDir = join7(primaryDir, skill.name);
1761
1869
  for (const targetDir of symlinkDirs) {
1762
1870
  try {
1763
1871
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -1785,7 +1893,7 @@ async function installCommand(input2, skillName, options) {
1785
1893
  log.blank();
1786
1894
  log.warn("Fix permissions with:");
1787
1895
  for (const dir of failedDirs) {
1788
- const parentDir = join6(dir, "..");
1896
+ const parentDir = join7(dir, "..");
1789
1897
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
1790
1898
  }
1791
1899
  log.blank();
@@ -1898,7 +2006,7 @@ async function searchCommand(query) {
1898
2006
  }
1899
2007
  throw dirErr;
1900
2008
  }
1901
- const primarySkillDir = join6(primaryDir, skill.name);
2009
+ const primarySkillDir = join7(primaryDir, skill.name);
1902
2010
  for (const targetDir of symlinkDirs) {
1903
2011
  try {
1904
2012
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -1926,7 +2034,7 @@ async function searchCommand(query) {
1926
2034
  log.blank();
1927
2035
  log.warn("Fix permissions with:");
1928
2036
  for (const dir of failedDirs) {
1929
- const parentDir = join6(dir, "..");
2037
+ const parentDir = join7(dir, "..");
1930
2038
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
1931
2039
  }
1932
2040
  log.blank();
@@ -1945,9 +2053,9 @@ async function listCommand(options) {
1945
2053
  const idesToCheck = hasExplicitIdeOption(options) ? getSelectedIdes(options) : Object.keys(IDE_NAMES);
1946
2054
  const results = [];
1947
2055
  for (const ide of idesToCheck) {
1948
- const skillsDir = join6(baseDir, pathMap[ide]);
2056
+ const skillsDir = join7(baseDir, pathMap[ide]);
1949
2057
  try {
1950
- const entries = await readdir(skillsDir, { withFileTypes: true });
2058
+ const entries = await readdir2(skillsDir, { withFileTypes: true });
1951
2059
  const skillFolders = entries.filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name);
1952
2060
  if (skillFolders.length > 0) {
1953
2061
  results.push({ ide, skills: skillFolders });
@@ -1978,9 +2086,9 @@ async function removeCommand(name, options) {
1978
2086
  return;
1979
2087
  }
1980
2088
  const skillsDir = getTargetDirFromSelection(target.ide, target.scope);
1981
- const skillPath = join6(skillsDir, name);
2089
+ const skillPath = join7(skillsDir, name);
1982
2090
  try {
1983
- await rm2(skillPath, { recursive: true });
2091
+ await rm3(skillPath, { recursive: true });
1984
2092
  log.success(`Removed skill: ${name}`);
1985
2093
  } catch (err) {
1986
2094
  const error = err;
@@ -2030,11 +2138,225 @@ async function infoCommand(input2) {
2030
2138
  `
2031
2139
  );
2032
2140
  }
2141
+ async function detectCommand() {
2142
+ if (process.env.CI || process.env.NODE_ENV === "production") {
2143
+ return;
2144
+ }
2145
+ trackEvent("command", { name: "detect" });
2146
+ const cwd = process.cwd();
2147
+ const isInteractive = process.stdout.isTTY;
2148
+ let deps;
2149
+ if (isInteractive) {
2150
+ const pendingPackages = await loadPendingPackages(cwd);
2151
+ if (pendingPackages.length > 0) {
2152
+ deps = pendingPackages;
2153
+ log.blank();
2154
+ log.info(
2155
+ `Found ${pc7.bold(String(deps.length))} pending package(s) from recent installs: ${pc7.cyan(deps.join(", "))}`
2156
+ );
2157
+ } else {
2158
+ const scanSpinner = ora3("Detecting newly installed packages...").start();
2159
+ deps = await detectNewlyInstalledPackages(cwd);
2160
+ if (deps.length === 0) {
2161
+ scanSpinner.stop();
2162
+ scanSpinner.clear();
2163
+ log.blank();
2164
+ log.warn("No new packages detected");
2165
+ return;
2166
+ }
2167
+ await appendPendingPackages(cwd, deps);
2168
+ scanSpinner.succeed(
2169
+ `Detected ${deps.length} new package(s): ${ansi.yellow}${deps.join(", ")}${ansi.reset}`
2170
+ );
2171
+ }
2172
+ } else {
2173
+ const scanSpinner = ora3("Detecting newly installed packages...").start();
2174
+ deps = await detectNewlyInstalledPackages(cwd);
2175
+ if (deps.length === 0) {
2176
+ scanSpinner.stop();
2177
+ scanSpinner.clear();
2178
+ return;
2179
+ }
2180
+ await appendPendingPackages(cwd, deps);
2181
+ scanSpinner.succeed(
2182
+ `Detected ${deps.length} new package(s): ${ansi.yellow}${deps.join(", ")}${ansi.reset}`
2183
+ );
2184
+ }
2185
+ const searchSpinner = ora3("Finding matching skills...").start();
2186
+ const tokens = loadTokens();
2187
+ const accessToken = tokens && !isTokenExpired(tokens) ? tokens.access_token : void 0;
2188
+ let data;
2189
+ try {
2190
+ data = await suggestSkills(deps, accessToken);
2191
+ } catch {
2192
+ searchSpinner.fail(pc7.red("Failed to connect to Context7"));
2193
+ return;
2194
+ }
2195
+ if (data.error) {
2196
+ searchSpinner.fail(pc7.red(`Error: ${data.message || data.error}`));
2197
+ return;
2198
+ }
2199
+ const skills = data.skills;
2200
+ if (skills.length === 0) {
2201
+ searchSpinner.stop();
2202
+ searchSpinner.clear();
2203
+ return;
2204
+ }
2205
+ searchSpinner.succeed(`Found ${skills.length} relevant skill(s)`);
2206
+ if (!isInteractive) {
2207
+ console.log();
2208
+ console.log(
2209
+ `${ansi.cyan}${ansi.bold}${skills.length}${ansi.reset} skill(s) available for new packages:`
2210
+ );
2211
+ for (const skill of skills.slice(0, 5)) {
2212
+ console.log(
2213
+ `${ansi.dim}\u2502${ansi.reset} ${ansi.green}+${ansi.reset} ${ansi.bold}${ansi.green}${skill.name}${ansi.reset} ${ansi.dim}for${ansi.reset} ${ansi.yellow}${skill.matchedDep}${ansi.reset}`
2214
+ );
2215
+ if (skill.description) {
2216
+ const desc = skill.description.length > 55 ? skill.description.slice(0, 52) + "..." : skill.description;
2217
+ console.log(`${ansi.dim}\u2502 ${desc}${ansi.reset}`);
2218
+ }
2219
+ }
2220
+ if (skills.length > 5) {
2221
+ console.log(`${ansi.dim}\u2502 ... and ${skills.length - 5} more${ansi.reset}`);
2222
+ }
2223
+ console.log(
2224
+ `${ansi.dim}\u2514${ansi.reset} Run ${ansi.cyan}${ansi.bold}ctx7 skills detect${ansi.reset} to install`
2225
+ );
2226
+ console.log();
2227
+ return;
2228
+ }
2229
+ log.blank();
2230
+ const maxNameLen = Math.max(...skills.map((s) => s.name.length));
2231
+ const installsColWidth = 10;
2232
+ const trustColWidth = 12;
2233
+ const maxMatchedLen = Math.max(...skills.map((s) => s.matchedDep.length));
2234
+ const indexWidth = skills.length.toString().length;
2235
+ const choices = skills.map((s, index) => {
2236
+ const indexStr = pc7.dim(`${(index + 1).toString().padStart(indexWidth)}.`);
2237
+ const paddedName = s.name.padEnd(maxNameLen);
2238
+ const installsRaw = s.installCount ? String(s.installCount) : "-";
2239
+ const paddedInstalls = formatInstallCount(s.installCount, pc7.dim("-")) + " ".repeat(installsColWidth - installsRaw.length);
2240
+ const trustRaw = s.trustScore !== void 0 && s.trustScore >= 0 ? s.trustScore.toFixed(1) : "-";
2241
+ const trust = formatTrustScore(s.trustScore) + " ".repeat(trustColWidth - trustRaw.length);
2242
+ const matched = pc7.yellow(s.matchedDep.padEnd(maxMatchedLen));
2243
+ const skillLink = terminalLink(
2244
+ s.name,
2245
+ `https://context7.com/skills${s.project}/${s.name}`,
2246
+ pc7.white
2247
+ );
2248
+ const repoLink = terminalLink(s.project, `https://github.com${s.project}`, pc7.white);
2249
+ const metadataLines = [
2250
+ pc7.dim("\u2500".repeat(50)),
2251
+ "",
2252
+ `${pc7.yellow("Skill:")} ${skillLink}`,
2253
+ `${pc7.yellow("Repo:")} ${repoLink}`,
2254
+ `${pc7.yellow("Relevant:")} ${pc7.white(s.matchedDep)}`,
2255
+ `${pc7.yellow("Description:")}`,
2256
+ pc7.white(s.description || "No description")
2257
+ ];
2258
+ return {
2259
+ name: `${indexStr} ${paddedName} ${paddedInstalls}${trust}${matched}`,
2260
+ value: s,
2261
+ description: metadataLines.join("\n")
2262
+ };
2263
+ });
2264
+ const checkboxPrefixWidth = 3;
2265
+ const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
2266
+ const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)".padEnd(trustColWidth)) + pc7.dim("Relevant");
2267
+ const message = "Select skills to install:\n" + headerLine;
2268
+ let selectedSkills;
2269
+ try {
2270
+ selectedSkills = await checkboxWithHover({
2271
+ message,
2272
+ choices,
2273
+ pageSize: 15,
2274
+ loop: false
2275
+ });
2276
+ } catch {
2277
+ log.warn("Installation cancelled");
2278
+ return;
2279
+ }
2280
+ if (selectedSkills.length === 0) {
2281
+ log.warn("No skills selected");
2282
+ return;
2283
+ }
2284
+ const targets = await promptForInstallTargets({});
2285
+ if (!targets) {
2286
+ log.warn("Installation cancelled");
2287
+ return;
2288
+ }
2289
+ const targetDirs = getTargetDirs(targets);
2290
+ const installSpinner = ora3("Installing skills...").start();
2291
+ let permissionError = false;
2292
+ const failedDirs = /* @__PURE__ */ new Set();
2293
+ const installedSkills = [];
2294
+ for (const skill of selectedSkills) {
2295
+ try {
2296
+ installSpinner.text = `Downloading ${skill.name}...`;
2297
+ const downloadData = await downloadSkill(skill.project, skill.name);
2298
+ if (downloadData.error) {
2299
+ log.warn(`Failed to download ${skill.name}: ${downloadData.error}`);
2300
+ continue;
2301
+ }
2302
+ installSpinner.text = `Installing ${skill.name}...`;
2303
+ const [primaryDir, ...symlinkDirs] = targetDirs;
2304
+ try {
2305
+ await installSkillFiles(skill.name, downloadData.files, primaryDir);
2306
+ } catch (dirErr) {
2307
+ const error = dirErr;
2308
+ if (error.code === "EACCES" || error.code === "EPERM") {
2309
+ permissionError = true;
2310
+ failedDirs.add(primaryDir);
2311
+ }
2312
+ throw dirErr;
2313
+ }
2314
+ const primarySkillDir = join7(primaryDir, skill.name);
2315
+ for (const targetDir of symlinkDirs) {
2316
+ try {
2317
+ await symlinkSkill(skill.name, primarySkillDir, targetDir);
2318
+ } catch (dirErr) {
2319
+ const error = dirErr;
2320
+ if (error.code === "EACCES" || error.code === "EPERM") {
2321
+ permissionError = true;
2322
+ failedDirs.add(targetDir);
2323
+ }
2324
+ throw dirErr;
2325
+ }
2326
+ }
2327
+ installedSkills.push(`${skill.project}/${skill.name}`);
2328
+ } catch (err) {
2329
+ const error = err;
2330
+ if (error.code === "EACCES" || error.code === "EPERM") {
2331
+ continue;
2332
+ }
2333
+ const errMsg = err instanceof Error ? err.message : String(err);
2334
+ log.warn(`Failed to install ${skill.name}: ${errMsg}`);
2335
+ }
2336
+ }
2337
+ if (permissionError) {
2338
+ installSpinner.fail("Permission denied");
2339
+ log.blank();
2340
+ log.warn("Fix permissions with:");
2341
+ for (const dir of failedDirs) {
2342
+ const parentDir = join7(dir, "..");
2343
+ log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
2344
+ }
2345
+ log.blank();
2346
+ return;
2347
+ }
2348
+ installSpinner.succeed(`Installed ${installedSkills.length} skill(s)`);
2349
+ trackEvent("detect_install", { skills: installedSkills, ides: targets.ides });
2350
+ await clearPendingPackages(cwd);
2351
+ const installedNames = selectedSkills.map((s) => s.name);
2352
+ logInstallSummary(targets, targetDirs, installedNames);
2353
+ }
2033
2354
  async function suggestCommand(options) {
2034
2355
  trackEvent("command", { name: "suggest" });
2035
2356
  log.blank();
2357
+ const cwd = process.cwd();
2036
2358
  const scanSpinner = ora3("Scanning project dependencies...").start();
2037
- const deps = await detectProjectDependencies(process.cwd());
2359
+ const deps = await detectProjectDependencies(cwd);
2038
2360
  if (deps.length === 0) {
2039
2361
  scanSpinner.warn(pc7.yellow("No dependencies detected"));
2040
2362
  log.info(`Try ${pc7.cyan("ctx7 skills search <keyword>")} to search manually`);
@@ -2147,7 +2469,7 @@ async function suggestCommand(options) {
2147
2469
  }
2148
2470
  throw dirErr;
2149
2471
  }
2150
- const primarySkillDir = join6(primaryDir, skill.name);
2472
+ const primarySkillDir = join7(primaryDir, skill.name);
2151
2473
  for (const targetDir of symlinkDirs) {
2152
2474
  try {
2153
2475
  await symlinkSkill(skill.name, primarySkillDir, targetDir);
@@ -2175,7 +2497,7 @@ async function suggestCommand(options) {
2175
2497
  log.blank();
2176
2498
  log.warn("Fix permissions with:");
2177
2499
  for (const dir of failedDirs) {
2178
- const parentDir = join6(dir, "..");
2500
+ const parentDir = join7(dir, "..");
2179
2501
  log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
2180
2502
  }
2181
2503
  log.blank();
@@ -2190,9 +2512,9 @@ async function suggestCommand(options) {
2190
2512
  // src/constants.ts
2191
2513
  import { readFileSync as readFileSync2 } from "fs";
2192
2514
  import { fileURLToPath } from "url";
2193
- import { dirname as dirname2, join as join7 } from "path";
2515
+ import { dirname as dirname2, join as join8 } from "path";
2194
2516
  var __dirname = dirname2(fileURLToPath(import.meta.url));
2195
- var pkg = JSON.parse(readFileSync2(join7(__dirname, "../package.json"), "utf-8"));
2517
+ var pkg = JSON.parse(readFileSync2(join8(__dirname, "../package.json"), "utf-8"));
2196
2518
  var VERSION = pkg.version;
2197
2519
  var NAME = pkg.name;
2198
2520