ctx7 0.3.13 → 0.4.1
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/README.md +30 -5
- package/dist/index.js +771 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import pc12 from "picocolors";
|
|
6
6
|
import figlet from "figlet";
|
|
7
7
|
|
|
8
8
|
// src/commands/skill.ts
|
|
@@ -453,8 +453,9 @@ async function getLibraryContext(libraryId, query, options, accessToken) {
|
|
|
453
453
|
if (options?.type) {
|
|
454
454
|
params.set("type", options.type);
|
|
455
455
|
}
|
|
456
|
+
const headers = getAuthHeaders(accessToken);
|
|
456
457
|
const response = await fetch(`${baseUrl}/api/v2/context?${params}`, {
|
|
457
|
-
headers
|
|
458
|
+
headers
|
|
458
459
|
});
|
|
459
460
|
if (!response.ok) {
|
|
460
461
|
const errorData = await response.json().catch(() => ({}));
|
|
@@ -482,6 +483,24 @@ async function getLibraryContext(libraryId, query, options, accessToken) {
|
|
|
482
483
|
|
|
483
484
|
// src/utils/logger.ts
|
|
484
485
|
import pc from "picocolors";
|
|
486
|
+
var ANSI_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
487
|
+
function visibleLength(text) {
|
|
488
|
+
return text.replace(ANSI_PATTERN, "").length;
|
|
489
|
+
}
|
|
490
|
+
function padVisible(text, width) {
|
|
491
|
+
const padding = Math.max(0, width - visibleLength(text));
|
|
492
|
+
return text + " ".repeat(padding);
|
|
493
|
+
}
|
|
494
|
+
function box(lines, color = pc.green) {
|
|
495
|
+
const contentWidth = Math.max(...lines.map((line) => visibleLength(line)), 0);
|
|
496
|
+
const top = color(`\u250C${"\u2500".repeat(contentWidth + 2)}\u2510`);
|
|
497
|
+
const bottom = color(`\u2514${"\u2500".repeat(contentWidth + 2)}\u2518`);
|
|
498
|
+
console.log(top);
|
|
499
|
+
for (const line of lines) {
|
|
500
|
+
console.log(color("\u2502 ") + padVisible(line, contentWidth) + color(" \u2502"));
|
|
501
|
+
}
|
|
502
|
+
console.log(bottom);
|
|
503
|
+
}
|
|
485
504
|
var log = {
|
|
486
505
|
info: (message) => console.log(pc.cyan(message)),
|
|
487
506
|
success: (message) => console.log(pc.green(`\u2714 ${message}`)),
|
|
@@ -491,7 +510,8 @@ var log = {
|
|
|
491
510
|
item: (message) => console.log(pc.green(` ${message}`)),
|
|
492
511
|
itemAdd: (message) => console.log(` ${pc.green("+")} ${message}`),
|
|
493
512
|
plain: (message) => console.log(message),
|
|
494
|
-
blank: () => console.log("")
|
|
513
|
+
blank: () => console.log(""),
|
|
514
|
+
box
|
|
495
515
|
};
|
|
496
516
|
|
|
497
517
|
// src/utils/ide.ts
|
|
@@ -2886,6 +2906,24 @@ function mergeServerEntry(existing, configKey, serverName, entry) {
|
|
|
2886
2906
|
alreadyExists
|
|
2887
2907
|
};
|
|
2888
2908
|
}
|
|
2909
|
+
function removeServerEntry(existing, configKey, serverName) {
|
|
2910
|
+
const section = existing[configKey];
|
|
2911
|
+
if (!section || typeof section !== "object" || Array.isArray(section)) {
|
|
2912
|
+
return { config: existing, removed: false };
|
|
2913
|
+
}
|
|
2914
|
+
const current = section;
|
|
2915
|
+
if (!(serverName in current)) {
|
|
2916
|
+
return { config: existing, removed: false };
|
|
2917
|
+
}
|
|
2918
|
+
const rest = Object.fromEntries(Object.entries(current).filter(([key]) => key !== serverName));
|
|
2919
|
+
const next = { ...existing };
|
|
2920
|
+
if (Object.keys(rest).length === 0) {
|
|
2921
|
+
delete next[configKey];
|
|
2922
|
+
} else {
|
|
2923
|
+
next[configKey] = rest;
|
|
2924
|
+
}
|
|
2925
|
+
return { config: next, removed: true };
|
|
2926
|
+
}
|
|
2889
2927
|
async function resolveMcpPath(candidates) {
|
|
2890
2928
|
for (const candidate of candidates) {
|
|
2891
2929
|
try {
|
|
@@ -2900,6 +2938,14 @@ async function writeJsonConfig(filePath, config) {
|
|
|
2900
2938
|
await mkdir3(dirname4(filePath), { recursive: true });
|
|
2901
2939
|
await writeFile3(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2902
2940
|
}
|
|
2941
|
+
async function readTomlServerExists(filePath, serverName) {
|
|
2942
|
+
try {
|
|
2943
|
+
const raw = await readFile3(filePath, "utf-8");
|
|
2944
|
+
return raw.includes(`[mcp_servers.${serverName}]`);
|
|
2945
|
+
} catch {
|
|
2946
|
+
return false;
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2903
2949
|
function buildTomlServerBlock(serverName, entry) {
|
|
2904
2950
|
const lines = [`[mcp_servers.${serverName}]`];
|
|
2905
2951
|
const headers = entry.headers;
|
|
@@ -2954,6 +3000,39 @@ async function appendTomlServer(filePath, serverName, entry) {
|
|
|
2954
3000
|
}
|
|
2955
3001
|
return { alreadyExists };
|
|
2956
3002
|
}
|
|
3003
|
+
async function removeTomlServer(filePath, serverName) {
|
|
3004
|
+
let existing = "";
|
|
3005
|
+
try {
|
|
3006
|
+
existing = await readFile3(filePath, "utf-8");
|
|
3007
|
+
} catch {
|
|
3008
|
+
return { removed: false };
|
|
3009
|
+
}
|
|
3010
|
+
const sectionHeader = `[mcp_servers.${serverName}]`;
|
|
3011
|
+
const startIdx = existing.indexOf(sectionHeader);
|
|
3012
|
+
if (startIdx === -1) {
|
|
3013
|
+
return { removed: false };
|
|
3014
|
+
}
|
|
3015
|
+
const subPrefix = `[mcp_servers.${serverName}.`;
|
|
3016
|
+
const rest = existing.slice(startIdx + sectionHeader.length);
|
|
3017
|
+
let endOffset = rest.length;
|
|
3018
|
+
const re = /^\[/gm;
|
|
3019
|
+
let match;
|
|
3020
|
+
while ((match = re.exec(rest)) !== null) {
|
|
3021
|
+
const lineEnd = rest.indexOf("\n", match.index);
|
|
3022
|
+
const line = rest.slice(match.index, lineEnd === -1 ? void 0 : lineEnd);
|
|
3023
|
+
if (!line.startsWith(subPrefix)) {
|
|
3024
|
+
endOffset = match.index;
|
|
3025
|
+
break;
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
const rawBefore = existing.slice(0, startIdx).replace(/\n+$/, "");
|
|
3029
|
+
const rawAfter = existing.slice(startIdx + sectionHeader.length + endOffset).replace(/^\n+/, "");
|
|
3030
|
+
const content = [rawBefore, rawAfter].filter(Boolean).join("\n\n");
|
|
3031
|
+
await mkdir3(dirname4(filePath), { recursive: true });
|
|
3032
|
+
await writeFile3(filePath, content.length > 0 ? `${content}
|
|
3033
|
+
` : "", "utf-8");
|
|
3034
|
+
return { removed: true };
|
|
3035
|
+
}
|
|
2957
3036
|
|
|
2958
3037
|
// src/commands/setup.ts
|
|
2959
3038
|
var CHECKBOX_THEME = {
|
|
@@ -3293,9 +3372,344 @@ async function setupCommand(options) {
|
|
|
3293
3372
|
}
|
|
3294
3373
|
}
|
|
3295
3374
|
|
|
3296
|
-
// src/commands/
|
|
3375
|
+
// src/commands/remove.ts
|
|
3297
3376
|
import pc9 from "picocolors";
|
|
3298
3377
|
import ora5 from "ora";
|
|
3378
|
+
import { join as join10 } from "path";
|
|
3379
|
+
import { access as access4, readFile as readFile5, rm as rm3, writeFile as writeFile5 } from "fs/promises";
|
|
3380
|
+
var CHECKBOX_THEME2 = {
|
|
3381
|
+
style: {
|
|
3382
|
+
highlight: (text) => pc9.green(text),
|
|
3383
|
+
disabledChoice: (text) => ` ${pc9.dim("\u25EF")} ${pc9.dim(text)}`
|
|
3384
|
+
}
|
|
3385
|
+
};
|
|
3386
|
+
var CONTEXT7_SECTION_MARKER = "<!-- context7 -->";
|
|
3387
|
+
var MODE_SKILLS = {
|
|
3388
|
+
mcp: ["context7-mcp"],
|
|
3389
|
+
cli: ["find-docs"]
|
|
3390
|
+
};
|
|
3391
|
+
var MODE_LABELS = {
|
|
3392
|
+
mcp: "MCP",
|
|
3393
|
+
cli: "CLI + Skills"
|
|
3394
|
+
};
|
|
3395
|
+
function registerRemoveCommand(program2) {
|
|
3396
|
+
program2.command("remove").alias("uninstall").description("Remove Context7 setup from your AI coding agent").option("--claude", "Remove from Claude Code").option("--cursor", "Remove from Cursor").option("--opencode", "Remove from OpenCode").option("--codex", "Remove from Codex").option("--gemini", "Remove from Gemini CLI").option("--all", "Remove both MCP setup and CLI + Skills setup").option("--mcp", "Remove MCP setup").option("--cli", "Remove CLI + Skills setup").option("-p, --project", "Remove from the current project instead of global config").option("-y, --yes", "Skip confirmation prompts").action(async (options) => {
|
|
3397
|
+
await removeCommand2(options);
|
|
3398
|
+
});
|
|
3399
|
+
}
|
|
3400
|
+
function getSelectedAgents2(options) {
|
|
3401
|
+
const agents2 = [];
|
|
3402
|
+
if (options.claude) agents2.push("claude");
|
|
3403
|
+
if (options.cursor) agents2.push("cursor");
|
|
3404
|
+
if (options.opencode) agents2.push("opencode");
|
|
3405
|
+
if (options.codex) agents2.push("codex");
|
|
3406
|
+
if (options.gemini) agents2.push("gemini");
|
|
3407
|
+
return agents2;
|
|
3408
|
+
}
|
|
3409
|
+
async function promptAgents2(detected) {
|
|
3410
|
+
const choices = detected.map((name) => ({
|
|
3411
|
+
name: SETUP_AGENT_NAMES[name],
|
|
3412
|
+
value: name
|
|
3413
|
+
}));
|
|
3414
|
+
if (detected.length > 0) {
|
|
3415
|
+
log.dim(`Detected: ${detected.map((agent) => SETUP_AGENT_NAMES[agent]).join(", ")}`);
|
|
3416
|
+
}
|
|
3417
|
+
try {
|
|
3418
|
+
return await checkboxWithHover(
|
|
3419
|
+
{
|
|
3420
|
+
message: "Which agents do you want to remove Context7 setup from?",
|
|
3421
|
+
choices,
|
|
3422
|
+
loop: false,
|
|
3423
|
+
theme: CHECKBOX_THEME2
|
|
3424
|
+
},
|
|
3425
|
+
{ getName: (agent) => SETUP_AGENT_NAMES[agent] }
|
|
3426
|
+
);
|
|
3427
|
+
} catch {
|
|
3428
|
+
return null;
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
async function promptModes(modes) {
|
|
3432
|
+
const choices = modes.map((mode) => ({
|
|
3433
|
+
name: MODE_LABELS[mode],
|
|
3434
|
+
value: mode
|
|
3435
|
+
}));
|
|
3436
|
+
try {
|
|
3437
|
+
return await checkboxWithHover(
|
|
3438
|
+
{
|
|
3439
|
+
message: "Which Context7 setup modes do you want to remove?",
|
|
3440
|
+
choices,
|
|
3441
|
+
loop: false,
|
|
3442
|
+
theme: CHECKBOX_THEME2
|
|
3443
|
+
},
|
|
3444
|
+
{ getName: (mode) => MODE_LABELS[mode] }
|
|
3445
|
+
);
|
|
3446
|
+
} catch {
|
|
3447
|
+
return null;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
async function resolveAgents2(options, scope) {
|
|
3451
|
+
const explicit = getSelectedAgents2(options);
|
|
3452
|
+
if (explicit.length > 0) return explicit;
|
|
3453
|
+
const detected = await detectConfiguredAgents(scope);
|
|
3454
|
+
if (detected.length > 0 && options.yes) return detected;
|
|
3455
|
+
if (detected.length === 0) {
|
|
3456
|
+
log.warn(
|
|
3457
|
+
"No Context7 setup detected. Pass --claude, --cursor, --opencode, --codex, or --gemini."
|
|
3458
|
+
);
|
|
3459
|
+
return [];
|
|
3460
|
+
}
|
|
3461
|
+
log.blank();
|
|
3462
|
+
const selected = await promptAgents2(detected);
|
|
3463
|
+
if (!selected) {
|
|
3464
|
+
log.warn("Remove cancelled");
|
|
3465
|
+
return [];
|
|
3466
|
+
}
|
|
3467
|
+
return selected;
|
|
3468
|
+
}
|
|
3469
|
+
function resolveFlagModes(options) {
|
|
3470
|
+
if (options.all) return ["mcp", "cli"];
|
|
3471
|
+
const selected = [];
|
|
3472
|
+
if (options.mcp) selected.push("mcp");
|
|
3473
|
+
if (options.cli) selected.push("cli");
|
|
3474
|
+
return selected.length > 0 ? selected : ["mcp", "cli"];
|
|
3475
|
+
}
|
|
3476
|
+
async function pathExists2(path2) {
|
|
3477
|
+
try {
|
|
3478
|
+
await access4(path2);
|
|
3479
|
+
return true;
|
|
3480
|
+
} catch {
|
|
3481
|
+
return false;
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
async function hasMcpConfig(agentName, scope) {
|
|
3485
|
+
const agent = getAgent(agentName);
|
|
3486
|
+
const candidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join10(process.cwd(), path2));
|
|
3487
|
+
const mcpPath = await resolveMcpPath(candidates);
|
|
3488
|
+
if (mcpPath.endsWith(".toml")) {
|
|
3489
|
+
return readTomlServerExists(mcpPath, "context7");
|
|
3490
|
+
}
|
|
3491
|
+
const existing = await readJsonConfig(mcpPath);
|
|
3492
|
+
const section = existing[agent.mcp.configKey];
|
|
3493
|
+
return !!section && typeof section === "object" && !Array.isArray(section) && "context7" in section;
|
|
3494
|
+
}
|
|
3495
|
+
async function hasRule(agentName, scope) {
|
|
3496
|
+
const agent = getAgent(agentName);
|
|
3497
|
+
const rule = agent.rule;
|
|
3498
|
+
if (rule.kind === "file") {
|
|
3499
|
+
const ruleDir = scope === "global" ? rule.dir("global") : join10(process.cwd(), rule.dir("project"));
|
|
3500
|
+
return pathExists2(join10(ruleDir, rule.filename));
|
|
3501
|
+
}
|
|
3502
|
+
const filePath = scope === "global" ? rule.file("global") : join10(process.cwd(), rule.file("project"));
|
|
3503
|
+
try {
|
|
3504
|
+
const existing = await readFile5(filePath, "utf-8");
|
|
3505
|
+
return existing.includes(CONTEXT7_SECTION_MARKER);
|
|
3506
|
+
} catch {
|
|
3507
|
+
return false;
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
async function hasSkill(agentName, scope, skillName) {
|
|
3511
|
+
const agent = getAgent(agentName);
|
|
3512
|
+
const skillsDir = scope === "global" ? agent.skill.dir("global") : join10(process.cwd(), agent.skill.dir("project"));
|
|
3513
|
+
return pathExists2(join10(skillsDir, skillName));
|
|
3514
|
+
}
|
|
3515
|
+
async function detectAvailableModes(agents2, scope) {
|
|
3516
|
+
let hasMcpArtifacts = false;
|
|
3517
|
+
let hasCliArtifacts = false;
|
|
3518
|
+
let hasRuleArtifacts = false;
|
|
3519
|
+
for (const agent of agents2) {
|
|
3520
|
+
hasMcpArtifacts = hasMcpArtifacts || await hasMcpConfig(agent, scope) || await hasSkill(agent, scope, MODE_SKILLS.mcp[0]);
|
|
3521
|
+
hasCliArtifacts = hasCliArtifacts || await hasSkill(agent, scope, MODE_SKILLS.cli[0]);
|
|
3522
|
+
hasRuleArtifacts = hasRuleArtifacts || await hasRule(agent, scope);
|
|
3523
|
+
}
|
|
3524
|
+
const modes = [];
|
|
3525
|
+
if (hasMcpArtifacts) modes.push("mcp");
|
|
3526
|
+
if (hasCliArtifacts) modes.push("cli");
|
|
3527
|
+
if (modes.length === 0 && hasRuleArtifacts) {
|
|
3528
|
+
return ["mcp", "cli"];
|
|
3529
|
+
}
|
|
3530
|
+
return modes;
|
|
3531
|
+
}
|
|
3532
|
+
async function hasAnyContext7Artifacts(agent, scope) {
|
|
3533
|
+
return await hasMcpConfig(agent, scope) || await hasRule(agent, scope) || await hasSkill(agent, scope, MODE_SKILLS.mcp[0]) || await hasSkill(agent, scope, MODE_SKILLS.cli[0]);
|
|
3534
|
+
}
|
|
3535
|
+
async function detectConfiguredAgents(scope) {
|
|
3536
|
+
const detected = [];
|
|
3537
|
+
for (const agent of ALL_AGENT_NAMES) {
|
|
3538
|
+
if (await hasAnyContext7Artifacts(agent, scope)) {
|
|
3539
|
+
detected.push(agent);
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
return detected;
|
|
3543
|
+
}
|
|
3544
|
+
async function resolveModes(options, agents2, scope) {
|
|
3545
|
+
if (options.all || options.mcp || options.cli) {
|
|
3546
|
+
return resolveFlagModes(options);
|
|
3547
|
+
}
|
|
3548
|
+
const detectedModes = await detectAvailableModes(agents2, scope);
|
|
3549
|
+
if (detectedModes.length <= 1) {
|
|
3550
|
+
return detectedModes.length === 1 ? detectedModes : ["mcp", "cli"];
|
|
3551
|
+
}
|
|
3552
|
+
if (options.yes) {
|
|
3553
|
+
return detectedModes;
|
|
3554
|
+
}
|
|
3555
|
+
log.blank();
|
|
3556
|
+
const selected = await promptModes(detectedModes);
|
|
3557
|
+
if (!selected) {
|
|
3558
|
+
log.warn("Remove cancelled");
|
|
3559
|
+
return [];
|
|
3560
|
+
}
|
|
3561
|
+
return selected;
|
|
3562
|
+
}
|
|
3563
|
+
async function uninstallMcp(agentName, scope) {
|
|
3564
|
+
const agent = getAgent(agentName);
|
|
3565
|
+
const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join10(process.cwd(), path2));
|
|
3566
|
+
const mcpPath = await resolveMcpPath(mcpCandidates);
|
|
3567
|
+
try {
|
|
3568
|
+
if (mcpPath.endsWith(".toml")) {
|
|
3569
|
+
const { removed: removed2 } = await removeTomlServer(mcpPath, "context7");
|
|
3570
|
+
return { status: removed2 ? "removed" : "not found", path: mcpPath };
|
|
3571
|
+
}
|
|
3572
|
+
const existing = await readJsonConfig(mcpPath);
|
|
3573
|
+
const { config, removed } = removeServerEntry(existing, agent.mcp.configKey, "context7");
|
|
3574
|
+
if (removed) {
|
|
3575
|
+
await writeJsonConfig(mcpPath, config);
|
|
3576
|
+
}
|
|
3577
|
+
return { status: removed ? "removed" : "not found", path: mcpPath };
|
|
3578
|
+
} catch (err) {
|
|
3579
|
+
return { status: `failed: ${err instanceof Error ? err.message : String(err)}`, path: mcpPath };
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
async function uninstallRule(agentName, scope) {
|
|
3583
|
+
const agent = getAgent(agentName);
|
|
3584
|
+
const rule = agent.rule;
|
|
3585
|
+
if (rule.kind === "file") {
|
|
3586
|
+
const rulePath = scope === "global" ? rule.dir("global") : join10(process.cwd(), rule.dir("project"));
|
|
3587
|
+
const targetPath = join10(rulePath, rule.filename);
|
|
3588
|
+
try {
|
|
3589
|
+
await rm3(targetPath);
|
|
3590
|
+
return { status: "removed", path: targetPath };
|
|
3591
|
+
} catch (err) {
|
|
3592
|
+
const error = err;
|
|
3593
|
+
if (error.code === "ENOENT") return { status: "not found", path: targetPath };
|
|
3594
|
+
return { status: `failed: ${error.message}`, path: targetPath };
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
const filePath = scope === "global" ? rule.file("global") : join10(process.cwd(), rule.file("project"));
|
|
3598
|
+
try {
|
|
3599
|
+
const existing = await readFile5(filePath, "utf-8");
|
|
3600
|
+
if (!existing.includes(CONTEXT7_SECTION_MARKER)) {
|
|
3601
|
+
return { status: "not found", path: filePath };
|
|
3602
|
+
}
|
|
3603
|
+
const escapedMarker = CONTEXT7_SECTION_MARKER.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3604
|
+
const updated = existing.replace(new RegExp(`\\n?${escapedMarker}\\n[\\s\\S]*?${escapedMarker}\\n?`, "m"), "").replace(/\n{3,}/g, "\n\n").replace(/^\n+/, "").trimEnd();
|
|
3605
|
+
if (updated.length === 0) {
|
|
3606
|
+
await rm3(filePath);
|
|
3607
|
+
} else {
|
|
3608
|
+
await writeFile5(filePath, `${updated}
|
|
3609
|
+
`, "utf-8");
|
|
3610
|
+
}
|
|
3611
|
+
return { status: "removed", path: filePath };
|
|
3612
|
+
} catch (err) {
|
|
3613
|
+
const error = err;
|
|
3614
|
+
if (error.code === "ENOENT") return { status: "not found", path: filePath };
|
|
3615
|
+
return { status: `failed: ${error.message}`, path: filePath };
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
async function uninstallSkills(agentName, scope, skillNames) {
|
|
3619
|
+
const agent = getAgent(agentName);
|
|
3620
|
+
const skillsDir = scope === "global" ? agent.skill.dir("global") : join10(process.cwd(), agent.skill.dir("project"));
|
|
3621
|
+
const results = [];
|
|
3622
|
+
for (const skillName of skillNames) {
|
|
3623
|
+
const skillPath = join10(skillsDir, skillName);
|
|
3624
|
+
try {
|
|
3625
|
+
await rm3(skillPath, { recursive: true });
|
|
3626
|
+
results.push({ name: skillName, status: "removed", path: skillPath });
|
|
3627
|
+
} catch (err) {
|
|
3628
|
+
const error = err;
|
|
3629
|
+
if (error.code === "ENOENT") {
|
|
3630
|
+
results.push({ name: skillName, status: "not found", path: skillPath });
|
|
3631
|
+
} else {
|
|
3632
|
+
results.push({ name: skillName, status: `failed: ${error.message}`, path: skillPath });
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
return results;
|
|
3637
|
+
}
|
|
3638
|
+
async function uninstallAgent(agentName, scope, modes) {
|
|
3639
|
+
const result = { agent: getAgent(agentName).displayName };
|
|
3640
|
+
const skillNames = Array.from(new Set(modes.flatMap((mode) => [...MODE_SKILLS[mode]])));
|
|
3641
|
+
const shouldRemoveRule = modes.includes("mcp") || modes.includes("cli");
|
|
3642
|
+
if (modes.includes("mcp")) {
|
|
3643
|
+
result.mcp = await uninstallMcp(agentName, scope);
|
|
3644
|
+
}
|
|
3645
|
+
if (shouldRemoveRule) {
|
|
3646
|
+
result.rule = await uninstallRule(agentName, scope);
|
|
3647
|
+
}
|
|
3648
|
+
if (skillNames.length > 0) {
|
|
3649
|
+
result.skills = await uninstallSkills(agentName, scope, skillNames);
|
|
3650
|
+
}
|
|
3651
|
+
return result;
|
|
3652
|
+
}
|
|
3653
|
+
function iconForStatus(status) {
|
|
3654
|
+
if (status === "removed") return pc9.green("-");
|
|
3655
|
+
if (status === "not found") return pc9.dim("~");
|
|
3656
|
+
return pc9.red("!");
|
|
3657
|
+
}
|
|
3658
|
+
function printResults(results, modes) {
|
|
3659
|
+
log.blank();
|
|
3660
|
+
const shouldPrintRule = modes.includes("mcp") || modes.includes("cli");
|
|
3661
|
+
let hasVisibleResults = false;
|
|
3662
|
+
for (const result of results) {
|
|
3663
|
+
const visibleSkills = result.skills?.filter((skill) => skill.status !== "not found") ?? [];
|
|
3664
|
+
const showMcp = modes.includes("mcp") && result.mcp && result.mcp.status !== "not found";
|
|
3665
|
+
const showRule = shouldPrintRule && result.rule && result.rule.status !== "not found";
|
|
3666
|
+
if (!showMcp && !showRule && visibleSkills.length === 0) {
|
|
3667
|
+
continue;
|
|
3668
|
+
}
|
|
3669
|
+
hasVisibleResults = true;
|
|
3670
|
+
log.plain(` ${pc9.bold(result.agent)}`);
|
|
3671
|
+
if (showMcp && result.mcp) {
|
|
3672
|
+
log.plain(` ${iconForStatus(result.mcp.status)} MCP config ${result.mcp.status}`);
|
|
3673
|
+
log.plain(` ${pc9.dim(result.mcp.path)}`);
|
|
3674
|
+
}
|
|
3675
|
+
if (showRule && result.rule) {
|
|
3676
|
+
log.plain(` ${iconForStatus(result.rule.status)} Rule ${result.rule.status}`);
|
|
3677
|
+
log.plain(` ${pc9.dim(result.rule.path)}`);
|
|
3678
|
+
}
|
|
3679
|
+
for (const skill of visibleSkills) {
|
|
3680
|
+
log.plain(` ${iconForStatus(skill.status)} Skill ${skill.name} ${skill.status}`);
|
|
3681
|
+
log.plain(` ${pc9.dim(skill.path)}`);
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
if (hasVisibleResults) {
|
|
3685
|
+
log.blank();
|
|
3686
|
+
} else {
|
|
3687
|
+
log.plain(` ${pc9.dim("No matching Context7 setup was found to remove.")}`);
|
|
3688
|
+
log.blank();
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
async function removeCommand2(options) {
|
|
3692
|
+
trackEvent("command", { name: "remove" });
|
|
3693
|
+
const scope = options.project ? "project" : "global";
|
|
3694
|
+
const agents2 = await resolveAgents2(options, scope);
|
|
3695
|
+
if (agents2.length === 0) return;
|
|
3696
|
+
const modes = await resolveModes(options, agents2, scope);
|
|
3697
|
+
if (modes.length === 0) return;
|
|
3698
|
+
log.blank();
|
|
3699
|
+
const spinner = ora5("Removing Context7 setup...").start();
|
|
3700
|
+
const results = [];
|
|
3701
|
+
for (const agentName of agents2) {
|
|
3702
|
+
spinner.text = `Cleaning up ${getAgent(agentName).displayName}...`;
|
|
3703
|
+
results.push(await uninstallAgent(agentName, scope, modes));
|
|
3704
|
+
}
|
|
3705
|
+
spinner.succeed("Context7 cleanup complete");
|
|
3706
|
+
printResults(results, modes);
|
|
3707
|
+
trackEvent("remove", { agents: agents2, scope, modes });
|
|
3708
|
+
}
|
|
3709
|
+
|
|
3710
|
+
// src/commands/docs.ts
|
|
3711
|
+
import pc10 from "picocolors";
|
|
3712
|
+
import ora6 from "ora";
|
|
3299
3713
|
var isTTY = process.stdout.isTTY;
|
|
3300
3714
|
function getReputationLabel(score) {
|
|
3301
3715
|
if (score === void 0 || score < 0) return "Unknown";
|
|
@@ -3310,28 +3724,28 @@ function getAccessToken() {
|
|
|
3310
3724
|
}
|
|
3311
3725
|
function formatLibraryResult(lib, index) {
|
|
3312
3726
|
const lines = [];
|
|
3313
|
-
lines.push(`${
|
|
3314
|
-
lines.push(` ${
|
|
3727
|
+
lines.push(`${pc10.dim(`${index + 1}.`)} ${pc10.bold(`Title: ${lib.title}`)}`);
|
|
3728
|
+
lines.push(` ${pc10.cyan(`Context7-compatible library ID: ${lib.id}`)}`);
|
|
3315
3729
|
if (lib.description) {
|
|
3316
|
-
lines.push(` ${
|
|
3730
|
+
lines.push(` ${pc10.dim(`Description: ${lib.description}`)}`);
|
|
3317
3731
|
}
|
|
3318
3732
|
if (lib.totalSnippets) {
|
|
3319
|
-
lines.push(` ${
|
|
3733
|
+
lines.push(` ${pc10.dim(`Code Snippets: ${lib.totalSnippets}`)}`);
|
|
3320
3734
|
}
|
|
3321
3735
|
if (lib.trustScore !== void 0) {
|
|
3322
|
-
lines.push(` ${
|
|
3736
|
+
lines.push(` ${pc10.dim(`Source Reputation: ${getReputationLabel(lib.trustScore)}`)}`);
|
|
3323
3737
|
}
|
|
3324
3738
|
if (lib.benchmarkScore !== void 0 && lib.benchmarkScore > 0) {
|
|
3325
|
-
lines.push(` ${
|
|
3739
|
+
lines.push(` ${pc10.dim(`Benchmark Score: ${lib.benchmarkScore}`)}`);
|
|
3326
3740
|
}
|
|
3327
3741
|
if (lib.versions && lib.versions.length > 0) {
|
|
3328
|
-
lines.push(` ${
|
|
3742
|
+
lines.push(` ${pc10.dim(`Versions: ${lib.versions.join(", ")}`)}`);
|
|
3329
3743
|
}
|
|
3330
3744
|
return lines.join("\n");
|
|
3331
3745
|
}
|
|
3332
3746
|
async function resolveCommand(library, query, options) {
|
|
3333
3747
|
trackEvent("command", { name: "library" });
|
|
3334
|
-
const spinner = isTTY ?
|
|
3748
|
+
const spinner = isTTY ? ora6(`Searching for "${library}"...`).start() : null;
|
|
3335
3749
|
const accessToken = getAccessToken();
|
|
3336
3750
|
let data;
|
|
3337
3751
|
try {
|
|
@@ -3373,8 +3787,8 @@ async function resolveCommand(library, query, options) {
|
|
|
3373
3787
|
if (isTTY && results.length > 0) {
|
|
3374
3788
|
const best = results[0];
|
|
3375
3789
|
log.plain(
|
|
3376
|
-
`${
|
|
3377
|
-
${
|
|
3790
|
+
`${pc10.bold("Quick command:")}
|
|
3791
|
+
${pc10.cyan(`ctx7 docs "${best.id}" "<your question>"`)}`
|
|
3378
3792
|
);
|
|
3379
3793
|
log.blank();
|
|
3380
3794
|
}
|
|
@@ -3388,8 +3802,8 @@ async function queryCommand(libraryId, query, options) {
|
|
|
3388
3802
|
process.exitCode = 1;
|
|
3389
3803
|
return;
|
|
3390
3804
|
}
|
|
3391
|
-
const spinner = isTTY ? ora5(`Fetching docs for "${libraryId}"...`).start() : null;
|
|
3392
3805
|
const accessToken = getAccessToken();
|
|
3806
|
+
const spinner = isTTY ? ora6(`Fetching docs for "${libraryId}"...`).start() : null;
|
|
3393
3807
|
const outputType = options.json ? "json" : "txt";
|
|
3394
3808
|
let result;
|
|
3395
3809
|
try {
|
|
@@ -3410,8 +3824,8 @@ async function queryCommand(libraryId, query, options) {
|
|
|
3410
3824
|
if (ctx.redirectUrl) {
|
|
3411
3825
|
spinner?.warn("Library has been redirected");
|
|
3412
3826
|
if (!spinner) log.warn("Library has been redirected");
|
|
3413
|
-
log.info(`New ID: ${
|
|
3414
|
-
log.info(`Run: ${
|
|
3827
|
+
log.info(`New ID: ${pc10.cyan(ctx.redirectUrl)}`);
|
|
3828
|
+
log.info(`Run: ${pc10.cyan(`ctx7 docs "${ctx.redirectUrl}" "${query}"`)}`);
|
|
3415
3829
|
process.exitCode = 1;
|
|
3416
3830
|
return;
|
|
3417
3831
|
}
|
|
@@ -3434,7 +3848,7 @@ async function queryCommand(libraryId, query, options) {
|
|
|
3434
3848
|
log.blank();
|
|
3435
3849
|
if (ctx.codeSnippets) {
|
|
3436
3850
|
for (const snippet of ctx.codeSnippets) {
|
|
3437
|
-
log.plain(
|
|
3851
|
+
log.plain(pc10.bold(snippet.codeTitle));
|
|
3438
3852
|
if (snippet.codeDescription) log.dim(snippet.codeDescription);
|
|
3439
3853
|
log.blank();
|
|
3440
3854
|
for (const code of snippet.codeList) {
|
|
@@ -3447,7 +3861,7 @@ async function queryCommand(libraryId, query, options) {
|
|
|
3447
3861
|
}
|
|
3448
3862
|
if (ctx.infoSnippets) {
|
|
3449
3863
|
for (const snippet of ctx.infoSnippets) {
|
|
3450
|
-
if (snippet.breadcrumb) log.plain(
|
|
3864
|
+
if (snippet.breadcrumb) log.plain(pc10.bold(snippet.breadcrumb));
|
|
3451
3865
|
log.plain(snippet.content);
|
|
3452
3866
|
log.blank();
|
|
3453
3867
|
}
|
|
@@ -3462,10 +3876,332 @@ function registerDocsCommands(program2) {
|
|
|
3462
3876
|
});
|
|
3463
3877
|
}
|
|
3464
3878
|
|
|
3879
|
+
// src/commands/upgrade.ts
|
|
3880
|
+
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
3881
|
+
import { spawn as spawn2 } from "child_process";
|
|
3882
|
+
import pc11 from "picocolors";
|
|
3883
|
+
|
|
3884
|
+
// src/utils/update-check.ts
|
|
3885
|
+
import { homedir as homedir6 } from "os";
|
|
3886
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
3887
|
+
import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
3888
|
+
var DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3889
|
+
var UPDATE_STATE_FILE = join11(homedir6(), ".context7", "cli-state.json");
|
|
3890
|
+
function getStateFilePath(stateFile) {
|
|
3891
|
+
return stateFile ?? UPDATE_STATE_FILE;
|
|
3892
|
+
}
|
|
3893
|
+
async function readUpdateState(stateFile) {
|
|
3894
|
+
try {
|
|
3895
|
+
const raw = await readFile6(getStateFilePath(stateFile), "utf-8");
|
|
3896
|
+
return JSON.parse(raw);
|
|
3897
|
+
} catch {
|
|
3898
|
+
return {};
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
async function writeUpdateState(state, stateFile) {
|
|
3902
|
+
const path2 = getStateFilePath(stateFile);
|
|
3903
|
+
await mkdir5(dirname6(path2), { recursive: true });
|
|
3904
|
+
await writeFile6(path2, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
3905
|
+
}
|
|
3906
|
+
function compareVersions(a, b) {
|
|
3907
|
+
const normalize = (version) => version.split("-", 1)[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
3908
|
+
const left = normalize(a);
|
|
3909
|
+
const right = normalize(b);
|
|
3910
|
+
const max = Math.max(left.length, right.length);
|
|
3911
|
+
for (let i = 0; i < max; i++) {
|
|
3912
|
+
const diff = (left[i] ?? 0) - (right[i] ?? 0);
|
|
3913
|
+
if (diff !== 0) return diff;
|
|
3914
|
+
}
|
|
3915
|
+
return 0;
|
|
3916
|
+
}
|
|
3917
|
+
function detectInstallMethod(env = process.env) {
|
|
3918
|
+
const execPath = env.npm_execpath?.toLowerCase() ?? "";
|
|
3919
|
+
const npmCommand = env.npm_command?.toLowerCase() ?? "";
|
|
3920
|
+
const userAgent = env.npm_config_user_agent?.toLowerCase() ?? "";
|
|
3921
|
+
if (execPath.includes("pnpm") && npmCommand === "dlx") return "pnpm-dlx";
|
|
3922
|
+
if (execPath.includes("pnpm")) return "pnpm-global";
|
|
3923
|
+
if (execPath.includes("bun") && npmCommand === "x") return "bunx";
|
|
3924
|
+
if (execPath.includes("bun")) return "bun-global";
|
|
3925
|
+
if (execPath.includes("npm") && npmCommand === "exec") return "npx";
|
|
3926
|
+
if (execPath.includes("npm")) return "npm-global";
|
|
3927
|
+
if (userAgent.startsWith("pnpm/")) return "pnpm-global";
|
|
3928
|
+
if (userAgent.startsWith("bun/")) return "bun-global";
|
|
3929
|
+
if (userAgent.startsWith("npm/")) return "npm-global";
|
|
3930
|
+
return "unknown";
|
|
3931
|
+
}
|
|
3932
|
+
function getUpgradePlan(installMethod = detectInstallMethod(), packageName = NAME) {
|
|
3933
|
+
switch (installMethod) {
|
|
3934
|
+
case "pnpm-global":
|
|
3935
|
+
return {
|
|
3936
|
+
installMethod,
|
|
3937
|
+
command: "pnpm",
|
|
3938
|
+
args: ["add", "-g", `${packageName}@latest`],
|
|
3939
|
+
displayCommand: `pnpm add -g ${packageName}@latest`,
|
|
3940
|
+
canRun: true,
|
|
3941
|
+
needsExplicitVersion: false
|
|
3942
|
+
};
|
|
3943
|
+
case "bun-global":
|
|
3944
|
+
return {
|
|
3945
|
+
installMethod,
|
|
3946
|
+
command: "bun",
|
|
3947
|
+
args: ["add", "-g", `${packageName}@latest`],
|
|
3948
|
+
displayCommand: `bun add -g ${packageName}@latest`,
|
|
3949
|
+
canRun: true,
|
|
3950
|
+
needsExplicitVersion: false
|
|
3951
|
+
};
|
|
3952
|
+
case "npx":
|
|
3953
|
+
return {
|
|
3954
|
+
installMethod,
|
|
3955
|
+
command: "npx",
|
|
3956
|
+
args: [`${packageName}@latest`],
|
|
3957
|
+
displayCommand: `npx ${packageName}@latest <command>`,
|
|
3958
|
+
canRun: false,
|
|
3959
|
+
needsExplicitVersion: true
|
|
3960
|
+
};
|
|
3961
|
+
case "pnpm-dlx":
|
|
3962
|
+
return {
|
|
3963
|
+
installMethod,
|
|
3964
|
+
command: "pnpm",
|
|
3965
|
+
args: ["dlx", `${packageName}@latest`],
|
|
3966
|
+
displayCommand: `pnpm dlx ${packageName}@latest <command>`,
|
|
3967
|
+
canRun: false,
|
|
3968
|
+
needsExplicitVersion: true
|
|
3969
|
+
};
|
|
3970
|
+
case "bunx":
|
|
3971
|
+
return {
|
|
3972
|
+
installMethod,
|
|
3973
|
+
command: "bunx",
|
|
3974
|
+
args: [`${packageName}@latest`],
|
|
3975
|
+
displayCommand: `bunx ${packageName}@latest <command>`,
|
|
3976
|
+
canRun: false,
|
|
3977
|
+
needsExplicitVersion: true
|
|
3978
|
+
};
|
|
3979
|
+
case "unknown":
|
|
3980
|
+
return {
|
|
3981
|
+
installMethod,
|
|
3982
|
+
command: "npm",
|
|
3983
|
+
args: ["install", "-g", `${packageName}@latest`],
|
|
3984
|
+
displayCommand: `npm install -g ${packageName}@latest`,
|
|
3985
|
+
canRun: false,
|
|
3986
|
+
needsExplicitVersion: false
|
|
3987
|
+
};
|
|
3988
|
+
case "npm-global":
|
|
3989
|
+
default:
|
|
3990
|
+
return {
|
|
3991
|
+
installMethod: "npm-global",
|
|
3992
|
+
command: "npm",
|
|
3993
|
+
args: ["install", "-g", `${packageName}@latest`],
|
|
3994
|
+
displayCommand: `npm install -g ${packageName}@latest`,
|
|
3995
|
+
canRun: true,
|
|
3996
|
+
needsExplicitVersion: false
|
|
3997
|
+
};
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
async function fetchLatestVersion(packageName = NAME) {
|
|
4001
|
+
try {
|
|
4002
|
+
const response = await fetch(
|
|
4003
|
+
`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`,
|
|
4004
|
+
{
|
|
4005
|
+
headers: { Accept: "application/json" },
|
|
4006
|
+
signal: AbortSignal.timeout(1500)
|
|
4007
|
+
}
|
|
4008
|
+
);
|
|
4009
|
+
if (!response.ok) return null;
|
|
4010
|
+
const data = await response.json();
|
|
4011
|
+
return typeof data.version === "string" ? data.version : null;
|
|
4012
|
+
} catch {
|
|
4013
|
+
return null;
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
async function checkForUpdates(options = {}) {
|
|
4017
|
+
const now = options.now ?? Date.now();
|
|
4018
|
+
const cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
4019
|
+
const stateFile = options.stateFile;
|
|
4020
|
+
const state = await readUpdateState(stateFile);
|
|
4021
|
+
const isStale = options.force || !state.lastCheckedAt || now - state.lastCheckedAt >= cacheTtlMs || !state.latestVersion;
|
|
4022
|
+
let latestVersion = state.latestVersion ?? null;
|
|
4023
|
+
if (isStale) {
|
|
4024
|
+
const fetchedVersion = await fetchLatestVersion();
|
|
4025
|
+
if (fetchedVersion) {
|
|
4026
|
+
latestVersion = fetchedVersion;
|
|
4027
|
+
await writeUpdateState(
|
|
4028
|
+
{
|
|
4029
|
+
...state,
|
|
4030
|
+
latestVersion: fetchedVersion,
|
|
4031
|
+
lastCheckedAt: now
|
|
4032
|
+
},
|
|
4033
|
+
stateFile
|
|
4034
|
+
);
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
if (!latestVersion) return null;
|
|
4038
|
+
const installMethod = detectInstallMethod();
|
|
4039
|
+
return {
|
|
4040
|
+
currentVersion: VERSION,
|
|
4041
|
+
latestVersion,
|
|
4042
|
+
updateAvailable: compareVersions(latestVersion, VERSION) > 0,
|
|
4043
|
+
installMethod,
|
|
4044
|
+
upgradePlan: getUpgradePlan(installMethod)
|
|
4045
|
+
};
|
|
4046
|
+
}
|
|
4047
|
+
async function shouldShowUpdateNotification(info, options = {}) {
|
|
4048
|
+
if (!info.updateAvailable) return false;
|
|
4049
|
+
const now = options.now ?? Date.now();
|
|
4050
|
+
const cooldownMs = options.cooldownMs ?? DEFAULT_CACHE_TTL_MS;
|
|
4051
|
+
const state = await readUpdateState(options.stateFile);
|
|
4052
|
+
if (state.notifiedVersion === info.latestVersion && state.lastNotifiedAt && now - state.lastNotifiedAt < cooldownMs) {
|
|
4053
|
+
return false;
|
|
4054
|
+
}
|
|
4055
|
+
return true;
|
|
4056
|
+
}
|
|
4057
|
+
async function markUpdateNotificationShown(latestVersion, options = {}) {
|
|
4058
|
+
const now = options.now ?? Date.now();
|
|
4059
|
+
const state = await readUpdateState(options.stateFile);
|
|
4060
|
+
await writeUpdateState(
|
|
4061
|
+
{
|
|
4062
|
+
...state,
|
|
4063
|
+
notifiedVersion: latestVersion,
|
|
4064
|
+
lastNotifiedAt: now
|
|
4065
|
+
},
|
|
4066
|
+
options.stateFile
|
|
4067
|
+
);
|
|
4068
|
+
}
|
|
4069
|
+
function shouldSkipUpdateNotifier(argv = process.argv) {
|
|
4070
|
+
return argv.includes("--json") || argv.includes("-v") || argv.includes("--version");
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4073
|
+
// src/commands/upgrade.ts
|
|
4074
|
+
function registerUpgradeCommand(program2) {
|
|
4075
|
+
program2.command("upgrade").description("Check for a newer ctx7 version and upgrade when possible").option("-y, --yes", "Run the suggested upgrade command without prompting").option("--check", "Only check for updates without running the upgrade command").action(async (options) => {
|
|
4076
|
+
await upgradeCommand(options);
|
|
4077
|
+
});
|
|
4078
|
+
}
|
|
4079
|
+
function runCommand(command, args) {
|
|
4080
|
+
return new Promise((resolve2, reject) => {
|
|
4081
|
+
const child = spawn2(command, args, {
|
|
4082
|
+
stdio: "inherit",
|
|
4083
|
+
shell: process.platform === "win32"
|
|
4084
|
+
});
|
|
4085
|
+
child.on("error", reject);
|
|
4086
|
+
child.on("close", (code) => resolve2(code));
|
|
4087
|
+
});
|
|
4088
|
+
}
|
|
4089
|
+
async function runUpgradePlan(plan) {
|
|
4090
|
+
return runCommand(plan.command, plan.args);
|
|
4091
|
+
}
|
|
4092
|
+
function showUpgradeFailureHelp(plan) {
|
|
4093
|
+
log.info(`Try rerunning: ${pc11.cyan(plan.displayCommand)}`);
|
|
4094
|
+
const isGlobalNpmInstall = (plan.installMethod === "npm-global" || plan.installMethod === "unknown") && plan.command === "npm" && plan.args.includes("-g");
|
|
4095
|
+
const isGlobalAltInstall = (plan.installMethod === "pnpm-global" || plan.installMethod === "bun-global") && plan.args.includes("-g");
|
|
4096
|
+
if (isGlobalNpmInstall) {
|
|
4097
|
+
log.dim(
|
|
4098
|
+
"If this failed due to permissions, your global npm directory may require elevated privileges on this machine."
|
|
4099
|
+
);
|
|
4100
|
+
} else if (isGlobalAltInstall) {
|
|
4101
|
+
log.dim(
|
|
4102
|
+
"If this failed due to permissions, your global package manager install location may require additional privileges on this machine."
|
|
4103
|
+
);
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
async function maybeShowUpgradeNotice(options = {}) {
|
|
4107
|
+
const actionName = options.actionName ?? "";
|
|
4108
|
+
const argv = options.argv ?? process.argv;
|
|
4109
|
+
const isInteractive = options.isInteractive ?? Boolean(process.stdout.isTTY && process.stdin.isTTY);
|
|
4110
|
+
if (!isInteractive || shouldSkipUpdateNotifier(argv) || actionName === "upgrade") {
|
|
4111
|
+
return;
|
|
4112
|
+
}
|
|
4113
|
+
const info = await checkForUpdates();
|
|
4114
|
+
if (!info || !info.updateAvailable || !await shouldShowUpdateNotification(info)) {
|
|
4115
|
+
return;
|
|
4116
|
+
}
|
|
4117
|
+
log.blank();
|
|
4118
|
+
if (info.upgradePlan.needsExplicitVersion) {
|
|
4119
|
+
log.box([
|
|
4120
|
+
`${pc11.white(pc11.bold("Update available:"))} ${pc11.green(pc11.bold(`v${info.currentVersion}`))} ${pc11.dim("->")} ${pc11.green(pc11.bold(`v${info.latestVersion}`))}`,
|
|
4121
|
+
`${pc11.white("Use")} ${pc11.yellow(pc11.bold(info.upgradePlan.displayCommand))} ${pc11.white("to run the latest version")}`
|
|
4122
|
+
]);
|
|
4123
|
+
await markUpdateNotificationShown(info.latestVersion);
|
|
4124
|
+
log.blank();
|
|
4125
|
+
return;
|
|
4126
|
+
}
|
|
4127
|
+
if (!info.upgradePlan.canRun) {
|
|
4128
|
+
log.box([
|
|
4129
|
+
`${pc11.white(pc11.bold("Update available:"))} ${pc11.green(pc11.bold(`v${info.currentVersion}`))} ${pc11.dim("->")} ${pc11.green(pc11.bold(`v${info.latestVersion}`))}`,
|
|
4130
|
+
`${pc11.white("Run")} ${pc11.yellow(pc11.bold("ctx7 upgrade"))} ${pc11.white("for update steps")}`,
|
|
4131
|
+
`${pc11.white("Or run")} ${pc11.yellow(info.upgradePlan.displayCommand)}`
|
|
4132
|
+
]);
|
|
4133
|
+
await markUpdateNotificationShown(info.latestVersion);
|
|
4134
|
+
log.blank();
|
|
4135
|
+
return;
|
|
4136
|
+
}
|
|
4137
|
+
log.box([
|
|
4138
|
+
`${pc11.white(pc11.bold("Update available:"))} ${pc11.green(pc11.bold(`v${info.currentVersion}`))} ${pc11.dim("->")} ${pc11.green(pc11.bold(`v${info.latestVersion}`))}`,
|
|
4139
|
+
`${pc11.white("Run")} ${pc11.yellow(pc11.bold("ctx7 upgrade"))} ${pc11.white("to update now")}`,
|
|
4140
|
+
`${pc11.white("Or run")} ${pc11.yellow(info.upgradePlan.displayCommand)}`
|
|
4141
|
+
]);
|
|
4142
|
+
await markUpdateNotificationShown(info.latestVersion);
|
|
4143
|
+
log.blank();
|
|
4144
|
+
}
|
|
4145
|
+
async function upgradeCommand(options) {
|
|
4146
|
+
trackEvent("command", { name: "upgrade" });
|
|
4147
|
+
const info = await checkForUpdates({ force: true });
|
|
4148
|
+
const plan = info?.upgradePlan ?? getUpgradePlan();
|
|
4149
|
+
if (!info) {
|
|
4150
|
+
log.warn("Couldn't check for updates right now.");
|
|
4151
|
+
log.info(`Try again later or run ${pc11.cyan(plan.displayCommand)} manually.`);
|
|
4152
|
+
return;
|
|
4153
|
+
}
|
|
4154
|
+
if (!info.updateAvailable) {
|
|
4155
|
+
log.success(`ctx7 is up to date (${pc11.bold(`v${VERSION}`)})`);
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
4158
|
+
log.blank();
|
|
4159
|
+
log.info(
|
|
4160
|
+
`Update available: ${pc11.bold(`v${info.currentVersion}`)} ${pc11.dim("->")} ${pc11.bold(`v${info.latestVersion}`)}`
|
|
4161
|
+
);
|
|
4162
|
+
if (plan.needsExplicitVersion) {
|
|
4163
|
+
log.info(`You're using an ephemeral runner (${plan.installMethod}).`);
|
|
4164
|
+
log.info(`Use ${pc11.cyan(plan.displayCommand)} to run the latest version immediately.`);
|
|
4165
|
+
log.info(`Or install globally with ${pc11.cyan("npm install -g ctx7@latest")}.`);
|
|
4166
|
+
return;
|
|
4167
|
+
}
|
|
4168
|
+
if (!plan.canRun) {
|
|
4169
|
+
log.info(`Run ${pc11.cyan(plan.displayCommand)} to update your installed version.`);
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
log.info(`Upgrade command: ${pc11.cyan(plan.displayCommand)}`);
|
|
4173
|
+
if (options.check) {
|
|
4174
|
+
return;
|
|
4175
|
+
}
|
|
4176
|
+
let shouldRun = options.yes ?? false;
|
|
4177
|
+
if (!shouldRun && process.stdout.isTTY) {
|
|
4178
|
+
shouldRun = await confirm2({
|
|
4179
|
+
message: `Run ${plan.displayCommand} now?`,
|
|
4180
|
+
default: true
|
|
4181
|
+
});
|
|
4182
|
+
}
|
|
4183
|
+
if (!shouldRun) {
|
|
4184
|
+
log.dim("Upgrade skipped.");
|
|
4185
|
+
return;
|
|
4186
|
+
}
|
|
4187
|
+
log.blank();
|
|
4188
|
+
const exitCode = await runUpgradePlan(plan);
|
|
4189
|
+
if (exitCode === 0) {
|
|
4190
|
+
log.blank();
|
|
4191
|
+
log.success("Upgrade complete.");
|
|
4192
|
+
log.info(`Run ${pc11.cyan("ctx7 --version")} to verify the installed version.`);
|
|
4193
|
+
return;
|
|
4194
|
+
}
|
|
4195
|
+
log.blank();
|
|
4196
|
+
log.error(`Upgrade command exited with code ${exitCode ?? "unknown"}.`);
|
|
4197
|
+
showUpgradeFailureHelp(plan);
|
|
4198
|
+
process.exitCode = 1;
|
|
4199
|
+
}
|
|
4200
|
+
|
|
3465
4201
|
// src/index.ts
|
|
3466
4202
|
var brand = {
|
|
3467
|
-
primary:
|
|
3468
|
-
dim:
|
|
4203
|
+
primary: pc12.green,
|
|
4204
|
+
dim: pc12.dim
|
|
3469
4205
|
};
|
|
3470
4206
|
var program = new Command();
|
|
3471
4207
|
program.name("ctx7").description("Context7 CLI - Manage AI coding skills and documentation context").version(VERSION).option("--base-url <url>").hook("preAction", (thisCommand) => {
|
|
@@ -3474,6 +4210,11 @@ program.name("ctx7").description("Context7 CLI - Manage AI coding skills and doc
|
|
|
3474
4210
|
setBaseUrl(opts.baseUrl);
|
|
3475
4211
|
setAuthBaseUrl(opts.baseUrl);
|
|
3476
4212
|
}
|
|
4213
|
+
}).hook("preAction", async (_thisCommand, actionCommand) => {
|
|
4214
|
+
await maybeShowUpgradeNotice({
|
|
4215
|
+
actionName: actionCommand.name(),
|
|
4216
|
+
argv: process.argv
|
|
4217
|
+
});
|
|
3477
4218
|
}).addHelpText(
|
|
3478
4219
|
"after",
|
|
3479
4220
|
`
|
|
@@ -3494,6 +4235,12 @@ Examples:
|
|
|
3494
4235
|
${brand.primary("npx ctx7 skills list --claude")}
|
|
3495
4236
|
${brand.primary("npx ctx7 skills remove pdf")}
|
|
3496
4237
|
|
|
4238
|
+
${brand.dim("# Remove Context7 setup")}
|
|
4239
|
+
${brand.primary("npx ctx7 remove --cursor")}
|
|
4240
|
+
${brand.primary("npx ctx7 remove --cursor --all")}
|
|
4241
|
+
${brand.primary("npx ctx7 remove --cursor --cli")}
|
|
4242
|
+
${brand.primary("npx ctx7 remove --claude --mcp")}
|
|
4243
|
+
|
|
3497
4244
|
${brand.dim("# Query library documentation")}
|
|
3498
4245
|
${brand.primary('npx ctx7 library react "how to use hooks"')}
|
|
3499
4246
|
${brand.primary('npx ctx7 docs /facebook/react "useEffect examples"')}
|
|
@@ -3505,7 +4252,9 @@ registerSkillCommands(program);
|
|
|
3505
4252
|
registerSkillAliases(program);
|
|
3506
4253
|
registerAuthCommands(program);
|
|
3507
4254
|
registerSetupCommand(program);
|
|
4255
|
+
registerRemoveCommand(program);
|
|
3508
4256
|
registerDocsCommands(program);
|
|
4257
|
+
registerUpgradeCommand(program);
|
|
3509
4258
|
program.action(() => {
|
|
3510
4259
|
console.log("");
|
|
3511
4260
|
const banner = figlet.textSync("Context7", { font: "ANSI Shadow" });
|
|
@@ -3520,5 +4269,5 @@ program.action(() => {
|
|
|
3520
4269
|
console.log(` Visit ${brand.primary("https://context7.com")} to browse skills`);
|
|
3521
4270
|
console.log("");
|
|
3522
4271
|
});
|
|
3523
|
-
program.
|
|
4272
|
+
await program.parseAsync();
|
|
3524
4273
|
//# sourceMappingURL=index.js.map
|