ctx7 0.5.1 → 0.5.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 +213 -65
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -152,15 +152,24 @@ function getGitHubHeaders() {
|
|
|
152
152
|
...ghToken && { Authorization: `token ${ghToken}` }
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
|
+
async function extractGitHubError(response) {
|
|
156
|
+
let detail = `HTTP ${response.status}`;
|
|
157
|
+
try {
|
|
158
|
+
const body = await response.json();
|
|
159
|
+
if (body.message) detail += `: ${body.message}`;
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
return detail;
|
|
163
|
+
}
|
|
155
164
|
async function fetchRepoTree(owner, repo, branch, headers) {
|
|
156
165
|
const treeUrl = `${GITHUB_API}/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`;
|
|
157
166
|
const response = await fetch(treeUrl, { headers });
|
|
158
|
-
if (!response.ok) return
|
|
167
|
+
if (!response.ok) return { error: await extractGitHubError(response) };
|
|
159
168
|
return await response.json();
|
|
160
169
|
}
|
|
161
170
|
async function fetchDefaultBranch(owner, repo, headers) {
|
|
162
171
|
const response = await fetch(`${GITHUB_API}/repos/${owner}/${repo}`, { headers });
|
|
163
|
-
if (!response.ok) return { status: response.status };
|
|
172
|
+
if (!response.ok) return { error: await extractGitHubError(response), status: response.status };
|
|
164
173
|
const data = await response.json();
|
|
165
174
|
return { branch: data.default_branch };
|
|
166
175
|
}
|
|
@@ -171,9 +180,12 @@ async function listSkillsFromGitHub(project) {
|
|
|
171
180
|
const [owner, repo] = parts;
|
|
172
181
|
const headers = getGitHubHeaders();
|
|
173
182
|
const branchResult = await fetchDefaultBranch(owner, repo, headers);
|
|
174
|
-
if ("
|
|
183
|
+
if ("error" in branchResult) {
|
|
184
|
+
if (branchResult.status === 404) return { status: "repo_not_found" };
|
|
185
|
+
return { status: "error", error: branchResult.error };
|
|
186
|
+
}
|
|
175
187
|
const treeData = await fetchRepoTree(owner, repo, branchResult.branch, headers);
|
|
176
|
-
if (
|
|
188
|
+
if ("error" in treeData) return { status: "error", error: treeData.error };
|
|
177
189
|
const skillMdFiles = treeData.tree.filter(
|
|
178
190
|
(item) => item.type === "blob" && item.path.toLowerCase().endsWith("skill.md")
|
|
179
191
|
);
|
|
@@ -214,8 +226,9 @@ async function downloadSkillFromGitHub(skill) {
|
|
|
214
226
|
const { owner, repo, branch, path: skillPath } = parsed;
|
|
215
227
|
const ghHeaders = getGitHubHeaders();
|
|
216
228
|
const treeData = await fetchRepoTree(owner, repo, branch, ghHeaders);
|
|
217
|
-
if (
|
|
218
|
-
|
|
229
|
+
if ("error" in treeData) {
|
|
230
|
+
const hint = !ghHeaders["Authorization"] && /403|429|rate/.test(treeData.error) ? " \u2014 run `gh auth login` or set the GITHUB_TOKEN env var to increase rate limits" : "";
|
|
231
|
+
return { files: [], error: `GitHub API error: ${treeData.error}${hint}` };
|
|
219
232
|
}
|
|
220
233
|
const skillFiles = treeData.tree.filter(
|
|
221
234
|
(item) => item.type === "blob" && item.path.startsWith(skillPath + "/")
|
|
@@ -925,48 +938,153 @@ function trackEvent(event, data) {
|
|
|
925
938
|
// src/commands/generate.ts
|
|
926
939
|
import pc6 from "picocolors";
|
|
927
940
|
import ora2 from "ora";
|
|
928
|
-
import { mkdir as
|
|
941
|
+
import { mkdir as mkdir3, writeFile as writeFile2, readFile, unlink } from "fs/promises";
|
|
929
942
|
import { join as join4 } from "path";
|
|
930
943
|
import { homedir as homedir3 } from "os";
|
|
931
944
|
import { spawn } from "child_process";
|
|
932
945
|
import { input, select as select2 } from "@inquirer/prompts";
|
|
933
946
|
|
|
934
947
|
// src/utils/auth.ts
|
|
948
|
+
import * as fs2 from "fs";
|
|
949
|
+
import * as os2 from "os";
|
|
950
|
+
|
|
951
|
+
// src/utils/storage-paths.ts
|
|
935
952
|
import * as fs from "fs";
|
|
936
|
-
import
|
|
953
|
+
import { access as access2, chmod, mkdir as mkdir2, rename } from "fs/promises";
|
|
937
954
|
import * as os from "os";
|
|
938
|
-
|
|
939
|
-
var
|
|
955
|
+
import * as path from "path";
|
|
956
|
+
var APP_DIR = "context7";
|
|
957
|
+
var LEGACY_DIR = ".context7";
|
|
958
|
+
var CREDENTIALS_FILE_NAME = "credentials.json";
|
|
959
|
+
var UPDATE_STATE_FILE_NAME = "cli-state.json";
|
|
960
|
+
var PREVIEWS_DIR_NAME = "previews";
|
|
961
|
+
function xdgBase(envVar, ...defaultSegments) {
|
|
962
|
+
const value = process.env[envVar];
|
|
963
|
+
const base = value && path.isAbsolute(value) ? value : path.join(os.homedir(), ...defaultSegments);
|
|
964
|
+
return path.join(base, APP_DIR);
|
|
965
|
+
}
|
|
966
|
+
function getConfigDir() {
|
|
967
|
+
return xdgBase("XDG_CONFIG_HOME", ".config");
|
|
968
|
+
}
|
|
969
|
+
function getStateDir() {
|
|
970
|
+
return xdgBase("XDG_STATE_HOME", ".local", "state");
|
|
971
|
+
}
|
|
972
|
+
function getCacheDir() {
|
|
973
|
+
return xdgBase("XDG_CACHE_HOME", ".cache");
|
|
974
|
+
}
|
|
975
|
+
function getCredentialsFilePath() {
|
|
976
|
+
return path.join(getConfigDir(), CREDENTIALS_FILE_NAME);
|
|
977
|
+
}
|
|
978
|
+
function getUpdateStateFilePath() {
|
|
979
|
+
return path.join(getStateDir(), UPDATE_STATE_FILE_NAME);
|
|
980
|
+
}
|
|
981
|
+
function getPreviewsDir() {
|
|
982
|
+
return path.join(getCacheDir(), PREVIEWS_DIR_NAME);
|
|
983
|
+
}
|
|
984
|
+
function getLegacyFilePath(fileName) {
|
|
985
|
+
return path.join(os.homedir(), LEGACY_DIR, fileName);
|
|
986
|
+
}
|
|
987
|
+
function migrateLegacyFileSync(fileName, targetPath, mode) {
|
|
988
|
+
const legacyPath = getLegacyFilePath(fileName);
|
|
989
|
+
if (legacyPath === targetPath || fs.existsSync(targetPath) || !fs.existsSync(legacyPath)) {
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
try {
|
|
993
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true, mode: 448 });
|
|
994
|
+
fs.renameSync(legacyPath, targetPath);
|
|
995
|
+
if (mode !== void 0) {
|
|
996
|
+
fs.chmodSync(targetPath, mode);
|
|
997
|
+
}
|
|
998
|
+
} catch {
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
async function migrateLegacyFile(fileName, targetPath, mode) {
|
|
1002
|
+
const legacyPath = getLegacyFilePath(fileName);
|
|
1003
|
+
if (legacyPath === targetPath || await exists(targetPath) || !await exists(legacyPath)) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
try {
|
|
1007
|
+
await mkdir2(path.dirname(targetPath), { recursive: true, mode: 448 });
|
|
1008
|
+
await rename(legacyPath, targetPath);
|
|
1009
|
+
if (mode !== void 0) {
|
|
1010
|
+
await chmod(targetPath, mode);
|
|
1011
|
+
}
|
|
1012
|
+
} catch {
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function resolveReadPathSync(fileName, targetPath, mode) {
|
|
1016
|
+
migrateLegacyFileSync(fileName, targetPath, mode);
|
|
1017
|
+
if (fs.existsSync(targetPath)) {
|
|
1018
|
+
return targetPath;
|
|
1019
|
+
}
|
|
1020
|
+
const legacyPath = getLegacyFilePath(fileName);
|
|
1021
|
+
return fs.existsSync(legacyPath) ? legacyPath : targetPath;
|
|
1022
|
+
}
|
|
1023
|
+
async function resolveReadPath(fileName, targetPath, mode) {
|
|
1024
|
+
await migrateLegacyFile(fileName, targetPath, mode);
|
|
1025
|
+
if (await exists(targetPath)) {
|
|
1026
|
+
return targetPath;
|
|
1027
|
+
}
|
|
1028
|
+
const legacyPath = getLegacyFilePath(fileName);
|
|
1029
|
+
return await exists(legacyPath) ? legacyPath : targetPath;
|
|
1030
|
+
}
|
|
1031
|
+
async function exists(filePath) {
|
|
1032
|
+
try {
|
|
1033
|
+
await access2(filePath);
|
|
1034
|
+
return true;
|
|
1035
|
+
} catch {
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// src/utils/auth.ts
|
|
940
1041
|
function ensureConfigDir() {
|
|
941
|
-
|
|
942
|
-
|
|
1042
|
+
const configDir = getConfigDir();
|
|
1043
|
+
if (!fs2.existsSync(configDir)) {
|
|
1044
|
+
fs2.mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
943
1045
|
}
|
|
944
1046
|
}
|
|
1047
|
+
var CREDENTIALS_MODE = 384;
|
|
945
1048
|
function saveTokens(tokens) {
|
|
1049
|
+
const credentialsFile = getCredentialsFilePath();
|
|
1050
|
+
migrateLegacyFileSync(CREDENTIALS_FILE_NAME, credentialsFile, CREDENTIALS_MODE);
|
|
946
1051
|
ensureConfigDir();
|
|
947
1052
|
const data = {
|
|
948
1053
|
...tokens,
|
|
949
1054
|
expires_at: tokens.expires_at ?? (tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0)
|
|
950
1055
|
};
|
|
951
|
-
|
|
1056
|
+
fs2.writeFileSync(credentialsFile, JSON.stringify(data, null, 2), { mode: CREDENTIALS_MODE });
|
|
1057
|
+
fs2.chmodSync(credentialsFile, CREDENTIALS_MODE);
|
|
952
1058
|
}
|
|
953
1059
|
function loadTokens() {
|
|
954
|
-
|
|
1060
|
+
const credentialsFile = resolveReadPathSync(
|
|
1061
|
+
CREDENTIALS_FILE_NAME,
|
|
1062
|
+
getCredentialsFilePath(),
|
|
1063
|
+
CREDENTIALS_MODE
|
|
1064
|
+
);
|
|
1065
|
+
if (!fs2.existsSync(credentialsFile)) {
|
|
955
1066
|
return null;
|
|
956
1067
|
}
|
|
957
1068
|
try {
|
|
958
|
-
const data = JSON.parse(
|
|
1069
|
+
const data = JSON.parse(fs2.readFileSync(credentialsFile, "utf-8"));
|
|
959
1070
|
return data;
|
|
960
1071
|
} catch {
|
|
961
1072
|
return null;
|
|
962
1073
|
}
|
|
963
1074
|
}
|
|
964
1075
|
function clearTokens() {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1076
|
+
const credentialsFile = getCredentialsFilePath();
|
|
1077
|
+
let removed = false;
|
|
1078
|
+
if (fs2.existsSync(credentialsFile)) {
|
|
1079
|
+
fs2.unlinkSync(credentialsFile);
|
|
1080
|
+
removed = true;
|
|
968
1081
|
}
|
|
969
|
-
|
|
1082
|
+
const legacyCredentialsFile = getLegacyFilePath(CREDENTIALS_FILE_NAME);
|
|
1083
|
+
if (fs2.existsSync(legacyCredentialsFile)) {
|
|
1084
|
+
fs2.unlinkSync(legacyCredentialsFile);
|
|
1085
|
+
removed = true;
|
|
1086
|
+
}
|
|
1087
|
+
return removed;
|
|
970
1088
|
}
|
|
971
1089
|
function isTokenExpired(tokens) {
|
|
972
1090
|
if (!tokens.expires_at) {
|
|
@@ -1012,7 +1130,7 @@ var DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;
|
|
|
1012
1130
|
async function startDeviceAuthorization(baseUrl3, clientId) {
|
|
1013
1131
|
const params = new URLSearchParams({ client_id: clientId });
|
|
1014
1132
|
try {
|
|
1015
|
-
const hostname2 =
|
|
1133
|
+
const hostname2 = os2.hostname();
|
|
1016
1134
|
if (hostname2) params.set("hostname", hostname2);
|
|
1017
1135
|
} catch {
|
|
1018
1136
|
}
|
|
@@ -1686,8 +1804,8 @@ async function generateCommand(options) {
|
|
|
1686
1804
|
log.blank();
|
|
1687
1805
|
};
|
|
1688
1806
|
const openInEditor = async () => {
|
|
1689
|
-
const previewDir =
|
|
1690
|
-
await
|
|
1807
|
+
const previewDir = getPreviewsDir();
|
|
1808
|
+
await mkdir3(previewDir, { recursive: true });
|
|
1691
1809
|
previewFile = join4(previewDir, `${skillName}.md`);
|
|
1692
1810
|
if (!previewFileWritten) {
|
|
1693
1811
|
await writeFile2(previewFile, generatedContent, "utf-8");
|
|
@@ -1767,7 +1885,7 @@ async function generateCommand(options) {
|
|
|
1767
1885
|
const skillDir = join4(finalDir, skillName);
|
|
1768
1886
|
const skillPath = join4(skillDir, "SKILL.md");
|
|
1769
1887
|
try {
|
|
1770
|
-
await
|
|
1888
|
+
await mkdir3(skillDir, { recursive: true });
|
|
1771
1889
|
await writeFile2(skillPath, generatedContent, "utf-8");
|
|
1772
1890
|
} catch (err) {
|
|
1773
1891
|
const error = err;
|
|
@@ -2619,12 +2737,12 @@ ${headerLine}`,
|
|
|
2619
2737
|
import pc8 from "picocolors";
|
|
2620
2738
|
import ora4 from "ora";
|
|
2621
2739
|
import { select as select3 } from "@inquirer/prompts";
|
|
2622
|
-
import { mkdir as
|
|
2623
|
-
import { dirname as
|
|
2740
|
+
import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
2741
|
+
import { dirname as dirname7, join as join8 } from "path";
|
|
2624
2742
|
import { randomBytes } from "crypto";
|
|
2625
2743
|
|
|
2626
2744
|
// src/setup/agents.ts
|
|
2627
|
-
import { access as
|
|
2745
|
+
import { access as access3 } from "fs/promises";
|
|
2628
2746
|
import { join as join7 } from "path";
|
|
2629
2747
|
import { homedir as homedir5 } from "os";
|
|
2630
2748
|
var SETUP_AGENT_NAMES = {
|
|
@@ -2828,7 +2946,7 @@ function getAgent(name) {
|
|
|
2828
2946
|
var ALL_AGENT_NAMES = Object.keys(agents);
|
|
2829
2947
|
async function pathExists(p) {
|
|
2830
2948
|
try {
|
|
2831
|
-
await
|
|
2949
|
+
await access3(p);
|
|
2832
2950
|
return true;
|
|
2833
2951
|
} catch {
|
|
2834
2952
|
return false;
|
|
@@ -2937,8 +3055,8 @@ function customizeSkillFilesForAgent(agent, skillName, files) {
|
|
|
2937
3055
|
}
|
|
2938
3056
|
|
|
2939
3057
|
// src/setup/mcp-writer.ts
|
|
2940
|
-
import { access as
|
|
2941
|
-
import { dirname as
|
|
3058
|
+
import { access as access4, readFile as readFile3, writeFile as writeFile3, mkdir as mkdir4 } from "fs/promises";
|
|
3059
|
+
import { dirname as dirname6 } from "path";
|
|
2942
3060
|
function stripJsonComments(text) {
|
|
2943
3061
|
let result = "";
|
|
2944
3062
|
let i = 0;
|
|
@@ -3009,7 +3127,7 @@ function removeServerEntry(existing, configKey, serverName) {
|
|
|
3009
3127
|
async function resolveMcpPath(candidates) {
|
|
3010
3128
|
for (const candidate of candidates) {
|
|
3011
3129
|
try {
|
|
3012
|
-
await
|
|
3130
|
+
await access4(candidate);
|
|
3013
3131
|
return candidate;
|
|
3014
3132
|
} catch {
|
|
3015
3133
|
}
|
|
@@ -3017,7 +3135,7 @@ async function resolveMcpPath(candidates) {
|
|
|
3017
3135
|
return candidates[0];
|
|
3018
3136
|
}
|
|
3019
3137
|
async function writeJsonConfig(filePath, config) {
|
|
3020
|
-
await
|
|
3138
|
+
await mkdir4(dirname6(filePath), { recursive: true });
|
|
3021
3139
|
await writeFile3(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
3022
3140
|
}
|
|
3023
3141
|
async function readTomlServerExists(filePath, serverName) {
|
|
@@ -3137,11 +3255,11 @@ async function appendTomlServer(filePath, serverName, entry) {
|
|
|
3137
3255
|
const before = rawBefore.length > 0 ? rawBefore + "\n\n" : "";
|
|
3138
3256
|
const after = rawAfter.length > 0 ? "\n" + rawAfter : "";
|
|
3139
3257
|
const content = before + block + after;
|
|
3140
|
-
await
|
|
3258
|
+
await mkdir4(dirname6(filePath), { recursive: true });
|
|
3141
3259
|
await writeFile3(filePath, content, "utf-8");
|
|
3142
3260
|
} else {
|
|
3143
3261
|
const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
|
|
3144
|
-
await
|
|
3262
|
+
await mkdir4(dirname6(filePath), { recursive: true });
|
|
3145
3263
|
await writeFile3(filePath, existing + separator + block, "utf-8");
|
|
3146
3264
|
}
|
|
3147
3265
|
return { alreadyExists };
|
|
@@ -3174,7 +3292,7 @@ async function removeTomlServer(filePath, serverName) {
|
|
|
3174
3292
|
const rawBefore = existing.slice(0, startIdx).replace(/\n+$/, "");
|
|
3175
3293
|
const rawAfter = existing.slice(startIdx + sectionHeader.length + endOffset).replace(/^\n+/, "");
|
|
3176
3294
|
const content = [rawBefore, rawAfter].filter(Boolean).join("\n\n");
|
|
3177
|
-
await
|
|
3295
|
+
await mkdir4(dirname6(filePath), { recursive: true });
|
|
3178
3296
|
await writeFile3(filePath, content.length > 0 ? `${content}
|
|
3179
3297
|
` : "", "utf-8");
|
|
3180
3298
|
return { removed: true };
|
|
@@ -3320,7 +3438,7 @@ async function installRule(agentName, mode, scope) {
|
|
|
3320
3438
|
if (rule.kind === "file") {
|
|
3321
3439
|
const ruleDir = scope === "global" ? rule.dir("global") : join8(process.cwd(), rule.dir("project"));
|
|
3322
3440
|
const rulePath = join8(ruleDir, rule.filename);
|
|
3323
|
-
await
|
|
3441
|
+
await mkdir5(dirname7(rulePath), { recursive: true });
|
|
3324
3442
|
await writeFile4(rulePath, content, "utf-8");
|
|
3325
3443
|
return { status: "installed", path: rulePath };
|
|
3326
3444
|
}
|
|
@@ -3340,7 +3458,7 @@ ${content}${rule.sectionMarker}`;
|
|
|
3340
3458
|
return { status: "updated", path: filePath };
|
|
3341
3459
|
}
|
|
3342
3460
|
const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
|
|
3343
|
-
await
|
|
3461
|
+
await mkdir5(dirname7(filePath), { recursive: true });
|
|
3344
3462
|
await writeFile4(filePath, existing + separator + section + "\n", "utf-8");
|
|
3345
3463
|
return { status: "installed", path: filePath };
|
|
3346
3464
|
}
|
|
@@ -3411,6 +3529,20 @@ async function setupAgent(agentName, auth, transport, scope) {
|
|
|
3411
3529
|
skillPath
|
|
3412
3530
|
};
|
|
3413
3531
|
}
|
|
3532
|
+
function logSkillStatus(skillStatus, skillPath) {
|
|
3533
|
+
const skillFailed = skillStatus.startsWith("failed:");
|
|
3534
|
+
const skillIcon = skillStatus === "installed" ? pc8.green("+") : skillFailed ? pc8.red("\u2716") : pc8.dim("~");
|
|
3535
|
+
log.plain(` ${skillIcon} Skill ${skillFailed ? "failed" : skillStatus}`);
|
|
3536
|
+
log.plain(` ${pc8.dim(skillPath)}`);
|
|
3537
|
+
if (skillFailed) {
|
|
3538
|
+
log.plain(` ${pc8.red(skillStatus.slice("failed: ".length))}`);
|
|
3539
|
+
if (skillStatus.includes("EACCES")) {
|
|
3540
|
+
log.plain(
|
|
3541
|
+
` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname7(dirname7(skillPath))}`)}`
|
|
3542
|
+
);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3414
3546
|
async function setupMcp(agents2, options, scope) {
|
|
3415
3547
|
const transport = resolveTransport(options);
|
|
3416
3548
|
if (transport === "stdio" && options.oauth) {
|
|
@@ -3439,14 +3571,7 @@ async function setupMcp(agents2, options, scope) {
|
|
|
3439
3571
|
const ruleIcon = r.ruleStatus === "installed" ? pc8.green("+") : pc8.dim("~");
|
|
3440
3572
|
log.plain(` ${ruleIcon} Rule ${r.ruleStatus}`);
|
|
3441
3573
|
log.plain(` ${pc8.dim(r.rulePath)}`);
|
|
3442
|
-
|
|
3443
|
-
log.plain(` ${skillIcon} Skill ${r.skillStatus}`);
|
|
3444
|
-
log.plain(` ${pc8.dim(r.skillPath)}`);
|
|
3445
|
-
if (r.skillStatus.includes("EACCES")) {
|
|
3446
|
-
log.plain(
|
|
3447
|
-
` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname6(dirname6(r.skillPath))}`)}`
|
|
3448
|
-
);
|
|
3449
|
-
}
|
|
3574
|
+
logSkillStatus(r.skillStatus, r.skillPath);
|
|
3450
3575
|
}
|
|
3451
3576
|
log.blank();
|
|
3452
3577
|
trackEvent("setup", { agents: agents2, scope, authMode: auth.mode });
|
|
@@ -3492,22 +3617,16 @@ async function setupCli(options) {
|
|
|
3492
3617
|
const installSpinner = ora4("Installing...").start();
|
|
3493
3618
|
const results = [];
|
|
3494
3619
|
for (const agentName of agents2) {
|
|
3495
|
-
|
|
3620
|
+
const agentDef = getAgent(agentName);
|
|
3621
|
+
installSpinner.text = `Setting up ${agentDef.displayName}...`;
|
|
3496
3622
|
const r = await setupCliAgent(agentName, scope, downloadData);
|
|
3497
|
-
results.push({ agent:
|
|
3623
|
+
results.push({ agent: agentDef.displayName, ...r });
|
|
3498
3624
|
}
|
|
3499
3625
|
installSpinner.succeed("Context7 CLI setup complete");
|
|
3500
3626
|
log.blank();
|
|
3501
3627
|
for (const r of results) {
|
|
3502
3628
|
log.plain(` ${pc8.bold(r.agent)}`);
|
|
3503
|
-
|
|
3504
|
-
log.plain(` ${skillIcon} Skill ${r.skillStatus}`);
|
|
3505
|
-
log.plain(` ${pc8.dim(r.skillPath)}`);
|
|
3506
|
-
if (r.skillStatus.includes("EACCES")) {
|
|
3507
|
-
log.plain(
|
|
3508
|
-
` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname6(dirname6(r.skillPath))}`)}`
|
|
3509
|
-
);
|
|
3510
|
-
}
|
|
3629
|
+
logSkillStatus(r.skillStatus, r.skillPath);
|
|
3511
3630
|
const ruleIcon = r.ruleStatus === "installed" || r.ruleStatus === "updated" ? pc8.green("+") : pc8.dim("~");
|
|
3512
3631
|
log.plain(` ${ruleIcon} Rule ${r.ruleStatus}`);
|
|
3513
3632
|
log.plain(` ${pc8.dim(r.rulePath)}`);
|
|
@@ -3538,7 +3657,7 @@ async function setupCommand(options) {
|
|
|
3538
3657
|
import pc9 from "picocolors";
|
|
3539
3658
|
import ora5 from "ora";
|
|
3540
3659
|
import { join as join9 } from "path";
|
|
3541
|
-
import { access as
|
|
3660
|
+
import { access as access5, readFile as readFile5, rm as rm3, writeFile as writeFile5 } from "fs/promises";
|
|
3542
3661
|
var CHECKBOX_THEME2 = {
|
|
3543
3662
|
style: {
|
|
3544
3663
|
highlight: (text) => pc9.green(text),
|
|
@@ -3638,7 +3757,7 @@ function resolveFlagModes(options) {
|
|
|
3638
3757
|
}
|
|
3639
3758
|
async function pathExists2(path2) {
|
|
3640
3759
|
try {
|
|
3641
|
-
await
|
|
3760
|
+
await access5(path2);
|
|
3642
3761
|
return true;
|
|
3643
3762
|
} catch {
|
|
3644
3763
|
return false;
|
|
@@ -3885,6 +4004,18 @@ async function removeCommand2(options) {
|
|
|
3885
4004
|
// src/commands/docs.ts
|
|
3886
4005
|
import pc10 from "picocolors";
|
|
3887
4006
|
import ora6 from "ora";
|
|
4007
|
+
|
|
4008
|
+
// src/utils/library-id.ts
|
|
4009
|
+
function recoverLibraryId(input2) {
|
|
4010
|
+
if (input2.startsWith("//")) return input2.replace(/^\/+/, "/");
|
|
4011
|
+
if (input2.startsWith("/")) return input2;
|
|
4012
|
+
if (!/^[A-Za-z]:[\\/]/.test(input2)) return input2;
|
|
4013
|
+
const normalized = input2.replace(/\\/g, "/");
|
|
4014
|
+
const match = normalized.match(/^[A-Za-z]:\/.*?\/(?:Git|PortableGit|git-bash)\/(.+)$/i);
|
|
4015
|
+
return match ? `/${match[1]}` : input2;
|
|
4016
|
+
}
|
|
4017
|
+
|
|
4018
|
+
// src/commands/docs.ts
|
|
3888
4019
|
var isTTY = process.stdout.isTTY;
|
|
3889
4020
|
function getReputationLabel(score) {
|
|
3890
4021
|
if (score === void 0 || score < 0) return "Unknown";
|
|
@@ -3970,10 +4101,16 @@ async function resolveCommand(library, query, options) {
|
|
|
3970
4101
|
}
|
|
3971
4102
|
async function queryCommand(libraryId, query, options) {
|
|
3972
4103
|
trackEvent("command", { name: "docs" });
|
|
4104
|
+
libraryId = recoverLibraryId(libraryId);
|
|
3973
4105
|
if (!libraryId.startsWith("/") || !/^\/[^/]+\/[^/]/.test(libraryId)) {
|
|
3974
4106
|
log.error(`Invalid library ID: "${libraryId}"`);
|
|
3975
4107
|
log.info(`Expected format: /owner/repo or /owner/repo/version (e.g., /facebook/react)`);
|
|
3976
4108
|
log.info(`Run "ctx7 library <name>" to find the correct ID`);
|
|
4109
|
+
if (process.platform === "win32") {
|
|
4110
|
+
log.info(
|
|
4111
|
+
`On Git Bash, prefix the ID with an extra slash to avoid path conversion: ctx7 docs "//facebook/react" "<your question>"`
|
|
4112
|
+
);
|
|
4113
|
+
}
|
|
3977
4114
|
process.exitCode = 1;
|
|
3978
4115
|
return;
|
|
3979
4116
|
}
|
|
@@ -4057,25 +4194,36 @@ import { spawn as spawn2 } from "child_process";
|
|
|
4057
4194
|
import pc11 from "picocolors";
|
|
4058
4195
|
|
|
4059
4196
|
// src/utils/update-check.ts
|
|
4060
|
-
import {
|
|
4061
|
-
import {
|
|
4062
|
-
import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
4197
|
+
import { dirname as dirname8 } from "path";
|
|
4198
|
+
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
4063
4199
|
var DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4064
|
-
var UPDATE_STATE_FILE = join10(homedir6(), ".context7", "cli-state.json");
|
|
4065
4200
|
function getStateFilePath(stateFile) {
|
|
4066
|
-
return stateFile ??
|
|
4201
|
+
return stateFile ?? getUpdateStateFilePath();
|
|
4202
|
+
}
|
|
4203
|
+
async function readStateFilePath(stateFile) {
|
|
4204
|
+
if (stateFile) {
|
|
4205
|
+
return stateFile;
|
|
4206
|
+
}
|
|
4207
|
+
return resolveReadPath(UPDATE_STATE_FILE_NAME, getUpdateStateFilePath());
|
|
4208
|
+
}
|
|
4209
|
+
async function writeStateFilePath(stateFile) {
|
|
4210
|
+
const path2 = getStateFilePath(stateFile);
|
|
4211
|
+
if (!stateFile) {
|
|
4212
|
+
await migrateLegacyFile(UPDATE_STATE_FILE_NAME, path2);
|
|
4213
|
+
}
|
|
4214
|
+
return path2;
|
|
4067
4215
|
}
|
|
4068
4216
|
async function readUpdateState(stateFile) {
|
|
4069
4217
|
try {
|
|
4070
|
-
const raw = await readFile6(
|
|
4218
|
+
const raw = await readFile6(await readStateFilePath(stateFile), "utf-8");
|
|
4071
4219
|
return JSON.parse(raw);
|
|
4072
4220
|
} catch {
|
|
4073
4221
|
return {};
|
|
4074
4222
|
}
|
|
4075
4223
|
}
|
|
4076
4224
|
async function writeUpdateState(state, stateFile) {
|
|
4077
|
-
const path2 =
|
|
4078
|
-
await
|
|
4225
|
+
const path2 = await writeStateFilePath(stateFile);
|
|
4226
|
+
await mkdir6(dirname8(path2), { recursive: true });
|
|
4079
4227
|
await writeFile6(path2, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
4080
4228
|
}
|
|
4081
4229
|
function compareVersions(a, b) {
|
|
@@ -4379,7 +4527,7 @@ var brand = {
|
|
|
4379
4527
|
dim: pc12.dim
|
|
4380
4528
|
};
|
|
4381
4529
|
var program = new Command();
|
|
4382
|
-
program.name("ctx7").description("Context7 CLI - Fetch documentation context and configure Context7").version(VERSION).option("--base-url <url>").hook("preAction", (thisCommand) => {
|
|
4530
|
+
program.name("ctx7").description("Context7 CLI - Fetch documentation context and configure Context7").version(VERSION, "-v, --version").option("--base-url <url>").hook("preAction", (thisCommand) => {
|
|
4383
4531
|
const opts = thisCommand.opts();
|
|
4384
4532
|
if (opts.baseUrl) {
|
|
4385
4533
|
setBaseUrl(opts.baseUrl);
|