ctx7 0.2.4 → 0.3.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 +38 -0
- package/dist/index.js +438 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,18 @@ npm install -g ctx7
|
|
|
16
16
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
|
+
```bash
|
|
20
|
+
# Set up Context7 MCP for your coding agents
|
|
21
|
+
ctx7 setup
|
|
22
|
+
|
|
23
|
+
# Target a specific agent
|
|
24
|
+
ctx7 setup --cursor
|
|
25
|
+
ctx7 setup --claude
|
|
26
|
+
ctx7 setup --opencode
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Skills
|
|
30
|
+
|
|
19
31
|
```bash
|
|
20
32
|
# Search for skills
|
|
21
33
|
ctx7 skills search pdf
|
|
@@ -32,6 +44,32 @@ ctx7 skills list --claude
|
|
|
32
44
|
|
|
33
45
|
## Usage
|
|
34
46
|
|
|
47
|
+
### Setup
|
|
48
|
+
|
|
49
|
+
Configure Context7 MCP and a rule for your AI coding agents. Authenticates via OAuth, generates an API key, and writes the config.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Interactive (prompts for agent selection)
|
|
53
|
+
ctx7 setup
|
|
54
|
+
|
|
55
|
+
# Target specific agents
|
|
56
|
+
ctx7 setup --cursor
|
|
57
|
+
ctx7 setup --claude
|
|
58
|
+
ctx7 setup --opencode
|
|
59
|
+
|
|
60
|
+
# Use an existing API key instead of OAuth
|
|
61
|
+
ctx7 setup --api-key YOUR_API_KEY
|
|
62
|
+
|
|
63
|
+
# Use OAuth endpoint (IDE handles auth flow)
|
|
64
|
+
ctx7 setup --oauth
|
|
65
|
+
|
|
66
|
+
# Configure for current project only (default is global)
|
|
67
|
+
ctx7 setup --project
|
|
68
|
+
|
|
69
|
+
# Skip prompts
|
|
70
|
+
ctx7 setup --yes
|
|
71
|
+
```
|
|
72
|
+
|
|
35
73
|
### Generate skills
|
|
36
74
|
|
|
37
75
|
Generate custom skills tailored to your use case using AI. Requires authentication.
|
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 pc9 from "picocolors";
|
|
6
6
|
import figlet from "figlet";
|
|
7
7
|
|
|
8
8
|
// src/commands/skill.ts
|
|
@@ -2256,19 +2256,450 @@ ${headerLine}`,
|
|
|
2256
2256
|
logInstallSummary(targets, targetDirs, installedNames);
|
|
2257
2257
|
}
|
|
2258
2258
|
|
|
2259
|
+
// src/commands/setup.ts
|
|
2260
|
+
import pc8 from "picocolors";
|
|
2261
|
+
import ora4 from "ora";
|
|
2262
|
+
import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
|
|
2263
|
+
import { dirname as dirname3, join as join8 } from "path";
|
|
2264
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
2265
|
+
|
|
2266
|
+
// src/setup/agents.ts
|
|
2267
|
+
import { access as access2 } from "fs/promises";
|
|
2268
|
+
import { join as join7 } from "path";
|
|
2269
|
+
import { homedir as homedir5 } from "os";
|
|
2270
|
+
var SETUP_AGENT_NAMES = {
|
|
2271
|
+
claude: "Claude Code",
|
|
2272
|
+
cursor: "Cursor",
|
|
2273
|
+
opencode: "OpenCode"
|
|
2274
|
+
};
|
|
2275
|
+
var AUTH_MODE_LABELS = {
|
|
2276
|
+
oauth: "OAuth",
|
|
2277
|
+
"api-key": "API Key"
|
|
2278
|
+
};
|
|
2279
|
+
var MCP_BASE_URL = "https://mcp.context7.com";
|
|
2280
|
+
function mcpUrl(auth) {
|
|
2281
|
+
return auth.mode === "oauth" ? `${MCP_BASE_URL}/mcp/oauth` : `${MCP_BASE_URL}/mcp`;
|
|
2282
|
+
}
|
|
2283
|
+
function withHeaders(base, auth) {
|
|
2284
|
+
if (auth.mode === "api-key" && auth.apiKey) {
|
|
2285
|
+
return { ...base, headers: { CONTEXT7_API_KEY: auth.apiKey } };
|
|
2286
|
+
}
|
|
2287
|
+
return base;
|
|
2288
|
+
}
|
|
2289
|
+
var agents = {
|
|
2290
|
+
claude: {
|
|
2291
|
+
name: "claude",
|
|
2292
|
+
displayName: "Claude Code",
|
|
2293
|
+
mcp: {
|
|
2294
|
+
projectPath: ".mcp.json",
|
|
2295
|
+
globalPath: join7(homedir5(), ".claude.json"),
|
|
2296
|
+
configKey: "mcpServers",
|
|
2297
|
+
buildEntry: (auth) => withHeaders({ type: "http", url: mcpUrl(auth) }, auth)
|
|
2298
|
+
},
|
|
2299
|
+
rule: {
|
|
2300
|
+
dir: (scope) => scope === "global" ? join7(homedir5(), ".claude", "rules") : join7(".claude", "rules"),
|
|
2301
|
+
filename: "context7.md"
|
|
2302
|
+
},
|
|
2303
|
+
skill: {
|
|
2304
|
+
name: "documentation-lookup",
|
|
2305
|
+
dir: (scope) => scope === "global" ? join7(homedir5(), ".claude", "skills") : join7(".claude", "skills")
|
|
2306
|
+
},
|
|
2307
|
+
detect: {
|
|
2308
|
+
projectPaths: [".mcp.json", ".claude"],
|
|
2309
|
+
globalPaths: [join7(homedir5(), ".claude")]
|
|
2310
|
+
}
|
|
2311
|
+
},
|
|
2312
|
+
cursor: {
|
|
2313
|
+
name: "cursor",
|
|
2314
|
+
displayName: "Cursor",
|
|
2315
|
+
mcp: {
|
|
2316
|
+
projectPath: join7(".cursor", "mcp.json"),
|
|
2317
|
+
globalPath: join7(homedir5(), ".cursor", "mcp.json"),
|
|
2318
|
+
configKey: "mcpServers",
|
|
2319
|
+
buildEntry: (auth) => withHeaders({ url: mcpUrl(auth) }, auth)
|
|
2320
|
+
},
|
|
2321
|
+
rule: {
|
|
2322
|
+
dir: (scope) => scope === "global" ? join7(homedir5(), ".cursor", "rules") : join7(".cursor", "rules"),
|
|
2323
|
+
filename: "context7.mdc"
|
|
2324
|
+
},
|
|
2325
|
+
skill: {
|
|
2326
|
+
name: "documentation-lookup",
|
|
2327
|
+
dir: (scope) => scope === "global" ? join7(homedir5(), ".cursor", "skills") : join7(".cursor", "skills")
|
|
2328
|
+
},
|
|
2329
|
+
detect: {
|
|
2330
|
+
projectPaths: [".cursor"],
|
|
2331
|
+
globalPaths: [join7(homedir5(), ".cursor")]
|
|
2332
|
+
}
|
|
2333
|
+
},
|
|
2334
|
+
opencode: {
|
|
2335
|
+
name: "opencode",
|
|
2336
|
+
displayName: "OpenCode",
|
|
2337
|
+
mcp: {
|
|
2338
|
+
projectPath: ".opencode.json",
|
|
2339
|
+
globalPath: join7(homedir5(), ".config", "opencode", "opencode.json"),
|
|
2340
|
+
configKey: "mcp",
|
|
2341
|
+
buildEntry: (auth) => withHeaders({ type: "remote", url: mcpUrl(auth), enabled: true }, auth)
|
|
2342
|
+
},
|
|
2343
|
+
rule: {
|
|
2344
|
+
dir: (scope) => scope === "global" ? join7(homedir5(), ".config", "opencode", "rules") : join7(".opencode", "rules"),
|
|
2345
|
+
filename: "context7.md",
|
|
2346
|
+
instructionsGlob: (scope) => scope === "global" ? join7(homedir5(), ".config", "opencode", "rules", "*.md") : ".opencode/rules/*.md"
|
|
2347
|
+
},
|
|
2348
|
+
skill: {
|
|
2349
|
+
name: "documentation-lookup",
|
|
2350
|
+
dir: (scope) => scope === "global" ? join7(homedir5(), ".agents", "skills") : join7(".agents", "skills")
|
|
2351
|
+
},
|
|
2352
|
+
detect: {
|
|
2353
|
+
projectPaths: [".opencode.json"],
|
|
2354
|
+
globalPaths: [join7(homedir5(), ".config", "opencode")]
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
};
|
|
2358
|
+
function getAgent(name) {
|
|
2359
|
+
return agents[name];
|
|
2360
|
+
}
|
|
2361
|
+
var ALL_AGENT_NAMES = Object.keys(agents);
|
|
2362
|
+
async function pathExists(p) {
|
|
2363
|
+
try {
|
|
2364
|
+
await access2(p);
|
|
2365
|
+
return true;
|
|
2366
|
+
} catch {
|
|
2367
|
+
return false;
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
async function detectAgents(scope) {
|
|
2371
|
+
const detected = [];
|
|
2372
|
+
for (const agent of Object.values(agents)) {
|
|
2373
|
+
const paths = scope === "global" ? agent.detect.globalPaths : agent.detect.projectPaths;
|
|
2374
|
+
for (const p of paths) {
|
|
2375
|
+
const fullPath = scope === "global" ? p : join7(process.cwd(), p);
|
|
2376
|
+
if (await pathExists(fullPath)) {
|
|
2377
|
+
detected.push(agent.name);
|
|
2378
|
+
break;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
return detected;
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
// src/setup/templates.ts
|
|
2386
|
+
var SKILL_CONTENT = `---
|
|
2387
|
+
name: documentation-lookup
|
|
2388
|
+
description: This skill should be used when the user asks about libraries, frameworks, API references, or needs code examples. Activates for setup questions, code generation involving libraries, or mentions of specific frameworks like React, Vue, Next.js, Prisma, Supabase, etc.
|
|
2389
|
+
---
|
|
2390
|
+
|
|
2391
|
+
When the user asks about libraries, frameworks, or needs code examples, use Context7 to fetch current documentation instead of relying on training data.
|
|
2392
|
+
|
|
2393
|
+
## When to Use This Skill
|
|
2394
|
+
|
|
2395
|
+
Activate this skill when the user:
|
|
2396
|
+
|
|
2397
|
+
- Asks setup or configuration questions ("How do I configure Next.js middleware?")
|
|
2398
|
+
- Requests code involving libraries ("Write a Prisma query for...")
|
|
2399
|
+
- Needs API references ("What are the Supabase auth methods?")
|
|
2400
|
+
- Mentions specific frameworks (React, Vue, Svelte, Express, Tailwind, etc.)
|
|
2401
|
+
|
|
2402
|
+
## How to Fetch Documentation
|
|
2403
|
+
|
|
2404
|
+
### Step 1: Resolve the Library ID
|
|
2405
|
+
|
|
2406
|
+
Call \`resolve-library-id\` with:
|
|
2407
|
+
|
|
2408
|
+
- \`libraryName\`: The library name extracted from the user's question
|
|
2409
|
+
- \`query\`: The user's full question (improves relevance ranking)
|
|
2410
|
+
|
|
2411
|
+
### Step 2: Select the Best Match
|
|
2412
|
+
|
|
2413
|
+
From the resolution results, choose based on:
|
|
2414
|
+
|
|
2415
|
+
- Exact or closest name match to what the user asked for
|
|
2416
|
+
- Higher benchmark scores indicate better documentation quality
|
|
2417
|
+
- If the user mentioned a version (e.g., "React 19"), prefer version-specific IDs
|
|
2418
|
+
|
|
2419
|
+
### Step 3: Fetch the Documentation
|
|
2420
|
+
|
|
2421
|
+
Call \`query-docs\` with:
|
|
2422
|
+
|
|
2423
|
+
- \`libraryId\`: The selected Context7 library ID (e.g., \`/vercel/next.js\`)
|
|
2424
|
+
- \`query\`: The user's specific question
|
|
2425
|
+
|
|
2426
|
+
### Step 4: Use the Documentation
|
|
2427
|
+
|
|
2428
|
+
Incorporate the fetched documentation into your response:
|
|
2429
|
+
|
|
2430
|
+
- Answer the user's question using current, accurate information
|
|
2431
|
+
- Include relevant code examples from the docs
|
|
2432
|
+
- Cite the library version when relevant
|
|
2433
|
+
|
|
2434
|
+
## Guidelines
|
|
2435
|
+
|
|
2436
|
+
- **Be specific**: Pass the user's full question as the query for better results
|
|
2437
|
+
- **Version awareness**: When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step
|
|
2438
|
+
- **Prefer official sources**: When multiple matches exist, prefer official/primary packages over community forks
|
|
2439
|
+
`;
|
|
2440
|
+
var RULE_CONTENT = `---
|
|
2441
|
+
alwaysApply: true
|
|
2442
|
+
---
|
|
2443
|
+
|
|
2444
|
+
When working with libraries, frameworks, or APIs \u2014 use Context7 MCP to fetch current documentation instead of relying on training data. This includes setup questions, code generation, API references, and anything involving specific packages.
|
|
2445
|
+
|
|
2446
|
+
## Steps
|
|
2447
|
+
|
|
2448
|
+
1. Call \`resolve-library-id\` with the library name and the user's question
|
|
2449
|
+
2. Pick the best match \u2014 prefer exact names and version-specific IDs when a version is mentioned
|
|
2450
|
+
3. Call \`query-docs\` with the selected library ID and the user's question
|
|
2451
|
+
4. Answer using the fetched docs \u2014 include code examples and cite the version
|
|
2452
|
+
`;
|
|
2453
|
+
|
|
2454
|
+
// src/setup/mcp-writer.ts
|
|
2455
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
2456
|
+
import { dirname as dirname2 } from "path";
|
|
2457
|
+
async function readJsonConfig(filePath) {
|
|
2458
|
+
let raw;
|
|
2459
|
+
try {
|
|
2460
|
+
raw = await readFile3(filePath, "utf-8");
|
|
2461
|
+
} catch {
|
|
2462
|
+
return {};
|
|
2463
|
+
}
|
|
2464
|
+
raw = raw.trim();
|
|
2465
|
+
if (!raw) return {};
|
|
2466
|
+
return JSON.parse(raw);
|
|
2467
|
+
}
|
|
2468
|
+
function mergeServerEntry(existing, configKey, serverName, entry) {
|
|
2469
|
+
const section = existing[configKey] ?? {};
|
|
2470
|
+
if (serverName in section) {
|
|
2471
|
+
return { config: existing, alreadyExists: true };
|
|
2472
|
+
}
|
|
2473
|
+
return {
|
|
2474
|
+
config: {
|
|
2475
|
+
...existing,
|
|
2476
|
+
[configKey]: {
|
|
2477
|
+
...section,
|
|
2478
|
+
[serverName]: entry
|
|
2479
|
+
}
|
|
2480
|
+
},
|
|
2481
|
+
alreadyExists: false
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
function mergeInstructions(config, glob) {
|
|
2485
|
+
const instructions = config.instructions ?? [];
|
|
2486
|
+
if (instructions.includes(glob)) return config;
|
|
2487
|
+
return { ...config, instructions: [...instructions, glob] };
|
|
2488
|
+
}
|
|
2489
|
+
async function writeJsonConfig(filePath, config) {
|
|
2490
|
+
await mkdir3(dirname2(filePath), { recursive: true });
|
|
2491
|
+
await writeFile3(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
// src/commands/setup.ts
|
|
2495
|
+
var CHECKBOX_THEME = {
|
|
2496
|
+
style: {
|
|
2497
|
+
highlight: (text) => pc8.green(text),
|
|
2498
|
+
disabledChoice: (text) => ` ${pc8.dim("\u25EF")} ${pc8.dim(text)}`
|
|
2499
|
+
}
|
|
2500
|
+
};
|
|
2501
|
+
function getSelectedAgents(options) {
|
|
2502
|
+
const agents2 = [];
|
|
2503
|
+
if (options.claude) agents2.push("claude");
|
|
2504
|
+
if (options.cursor) agents2.push("cursor");
|
|
2505
|
+
if (options.opencode) agents2.push("opencode");
|
|
2506
|
+
return agents2;
|
|
2507
|
+
}
|
|
2508
|
+
function registerSetupCommand(program2) {
|
|
2509
|
+
program2.command("setup").description("Set up Context7 MCP and rule for your AI coding agent").option("--claude", "Set up for Claude Code").option("--cursor", "Set up for Cursor").option("--opencode", "Set up for OpenCode").option("-p, --project", "Configure for current project instead of globally").option("-y, --yes", "Skip confirmation prompts").option("--api-key <key>", "Use API key authentication").option("--oauth", "Use OAuth endpoint (IDE handles auth flow)").action(async (options) => {
|
|
2510
|
+
await setupCommand(options);
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
async function authenticateAndGenerateKey() {
|
|
2514
|
+
const existingTokens = loadTokens();
|
|
2515
|
+
const accessToken = existingTokens && !isTokenExpired(existingTokens) ? existingTokens.access_token : await performLogin();
|
|
2516
|
+
if (!accessToken) return null;
|
|
2517
|
+
const spinner = ora4("Configuring authentication...").start();
|
|
2518
|
+
try {
|
|
2519
|
+
const response = await fetch(`${getBaseUrl()}/api/dashboard/api-keys`, {
|
|
2520
|
+
method: "POST",
|
|
2521
|
+
headers: {
|
|
2522
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2523
|
+
"Content-Type": "application/json"
|
|
2524
|
+
},
|
|
2525
|
+
body: JSON.stringify({ name: `ctx7-cli-${randomBytes2(3).toString("hex")}` })
|
|
2526
|
+
});
|
|
2527
|
+
if (!response.ok) {
|
|
2528
|
+
const err = await response.json().catch(() => ({}));
|
|
2529
|
+
spinner.fail("Authentication failed");
|
|
2530
|
+
log.error(err.message || err.error || `HTTP ${response.status}`);
|
|
2531
|
+
return null;
|
|
2532
|
+
}
|
|
2533
|
+
const result = await response.json();
|
|
2534
|
+
spinner.succeed("Authenticated");
|
|
2535
|
+
return result.data.apiKey;
|
|
2536
|
+
} catch (err) {
|
|
2537
|
+
spinner.fail("Authentication failed");
|
|
2538
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
2539
|
+
return null;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
async function resolveAuth(options) {
|
|
2543
|
+
if (options.apiKey) return { mode: "api-key", apiKey: options.apiKey };
|
|
2544
|
+
if (options.oauth) return { mode: "oauth" };
|
|
2545
|
+
const apiKey = await authenticateAndGenerateKey();
|
|
2546
|
+
if (!apiKey) return null;
|
|
2547
|
+
return { mode: "api-key", apiKey };
|
|
2548
|
+
}
|
|
2549
|
+
async function isAlreadyConfigured(agentName, scope) {
|
|
2550
|
+
const agent = getAgent(agentName);
|
|
2551
|
+
const mcpPath = scope === "global" ? agent.mcp.globalPath : join8(process.cwd(), agent.mcp.projectPath);
|
|
2552
|
+
try {
|
|
2553
|
+
const existing = await readJsonConfig(mcpPath);
|
|
2554
|
+
const section = existing[agent.mcp.configKey] ?? {};
|
|
2555
|
+
return "context7" in section;
|
|
2556
|
+
} catch {
|
|
2557
|
+
return false;
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
async function promptAgents(scope) {
|
|
2561
|
+
const choices = await Promise.all(
|
|
2562
|
+
ALL_AGENT_NAMES.map(async (name) => {
|
|
2563
|
+
const configured = await isAlreadyConfigured(name, scope);
|
|
2564
|
+
return {
|
|
2565
|
+
name: SETUP_AGENT_NAMES[name],
|
|
2566
|
+
value: name,
|
|
2567
|
+
disabled: configured ? "(already configured)" : false
|
|
2568
|
+
};
|
|
2569
|
+
})
|
|
2570
|
+
);
|
|
2571
|
+
if (choices.every((c) => c.disabled)) {
|
|
2572
|
+
log.info("Context7 is already configured for all detected agents.");
|
|
2573
|
+
return null;
|
|
2574
|
+
}
|
|
2575
|
+
try {
|
|
2576
|
+
return await checkboxWithHover(
|
|
2577
|
+
{
|
|
2578
|
+
message: "Which agents do you want to set up?",
|
|
2579
|
+
choices,
|
|
2580
|
+
loop: false,
|
|
2581
|
+
theme: CHECKBOX_THEME
|
|
2582
|
+
},
|
|
2583
|
+
{ getName: (a) => SETUP_AGENT_NAMES[a] }
|
|
2584
|
+
);
|
|
2585
|
+
} catch {
|
|
2586
|
+
return null;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
async function resolveAgents(options, scope) {
|
|
2590
|
+
const explicit = getSelectedAgents(options);
|
|
2591
|
+
if (explicit.length > 0) return explicit;
|
|
2592
|
+
const detected = await detectAgents(scope);
|
|
2593
|
+
if (detected.length > 0 && options.yes) return detected;
|
|
2594
|
+
log.blank();
|
|
2595
|
+
const selected = await promptAgents(scope);
|
|
2596
|
+
if (!selected) {
|
|
2597
|
+
log.warn("Setup cancelled");
|
|
2598
|
+
return [];
|
|
2599
|
+
}
|
|
2600
|
+
return selected;
|
|
2601
|
+
}
|
|
2602
|
+
async function setupAgent(agentName, auth, scope) {
|
|
2603
|
+
const agent = getAgent(agentName);
|
|
2604
|
+
const mcpPath = scope === "global" ? agent.mcp.globalPath : join8(process.cwd(), agent.mcp.projectPath);
|
|
2605
|
+
let mcpStatus;
|
|
2606
|
+
try {
|
|
2607
|
+
const existing = await readJsonConfig(mcpPath);
|
|
2608
|
+
const { config, alreadyExists } = mergeServerEntry(
|
|
2609
|
+
existing,
|
|
2610
|
+
agent.mcp.configKey,
|
|
2611
|
+
"context7",
|
|
2612
|
+
agent.mcp.buildEntry(auth)
|
|
2613
|
+
);
|
|
2614
|
+
if (alreadyExists) {
|
|
2615
|
+
mcpStatus = "already configured";
|
|
2616
|
+
} else {
|
|
2617
|
+
mcpStatus = `configured with ${AUTH_MODE_LABELS[auth.mode]}`;
|
|
2618
|
+
}
|
|
2619
|
+
const finalConfig = agent.rule.instructionsGlob ? mergeInstructions(config, agent.rule.instructionsGlob(scope)) : config;
|
|
2620
|
+
if (finalConfig !== existing) {
|
|
2621
|
+
await writeJsonConfig(mcpPath, finalConfig);
|
|
2622
|
+
}
|
|
2623
|
+
} catch (err) {
|
|
2624
|
+
mcpStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
2625
|
+
}
|
|
2626
|
+
const rulePath = scope === "global" ? join8(agent.rule.dir("global"), agent.rule.filename) : join8(process.cwd(), agent.rule.dir("project"), agent.rule.filename);
|
|
2627
|
+
let ruleStatus;
|
|
2628
|
+
try {
|
|
2629
|
+
await mkdir4(dirname3(rulePath), { recursive: true });
|
|
2630
|
+
await writeFile4(rulePath, RULE_CONTENT, "utf-8");
|
|
2631
|
+
ruleStatus = "installed";
|
|
2632
|
+
} catch (err) {
|
|
2633
|
+
ruleStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
2634
|
+
}
|
|
2635
|
+
const skillDir = scope === "global" ? agent.skill.dir("global") : join8(process.cwd(), agent.skill.dir("project"));
|
|
2636
|
+
const skillPath = join8(skillDir, agent.skill.name, "SKILL.md");
|
|
2637
|
+
let skillStatus;
|
|
2638
|
+
try {
|
|
2639
|
+
await mkdir4(dirname3(skillPath), { recursive: true });
|
|
2640
|
+
await writeFile4(skillPath, SKILL_CONTENT, "utf-8");
|
|
2641
|
+
skillStatus = "installed";
|
|
2642
|
+
} catch (err) {
|
|
2643
|
+
skillStatus = `failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
2644
|
+
}
|
|
2645
|
+
return {
|
|
2646
|
+
agent: agent.displayName,
|
|
2647
|
+
mcpStatus,
|
|
2648
|
+
mcpPath,
|
|
2649
|
+
ruleStatus,
|
|
2650
|
+
rulePath,
|
|
2651
|
+
skillStatus,
|
|
2652
|
+
skillPath
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2655
|
+
async function setupCommand(options) {
|
|
2656
|
+
trackEvent("command", { name: "setup" });
|
|
2657
|
+
const scope = options.project ? "project" : "global";
|
|
2658
|
+
const agents2 = await resolveAgents(options, scope);
|
|
2659
|
+
if (agents2.length === 0) return;
|
|
2660
|
+
const auth = await resolveAuth(options);
|
|
2661
|
+
if (!auth) {
|
|
2662
|
+
log.warn("Setup cancelled");
|
|
2663
|
+
return;
|
|
2664
|
+
}
|
|
2665
|
+
log.blank();
|
|
2666
|
+
const spinner = ora4("Setting up Context7...").start();
|
|
2667
|
+
const results = [];
|
|
2668
|
+
for (const agentName of agents2) {
|
|
2669
|
+
spinner.text = `Setting up ${getAgent(agentName).displayName}...`;
|
|
2670
|
+
results.push(await setupAgent(agentName, auth, scope));
|
|
2671
|
+
}
|
|
2672
|
+
spinner.succeed("Context7 setup complete");
|
|
2673
|
+
log.blank();
|
|
2674
|
+
for (const r of results) {
|
|
2675
|
+
log.plain(` ${pc8.bold(r.agent)}`);
|
|
2676
|
+
const mcpIcon = r.mcpStatus.startsWith("configured") ? pc8.green("+") : pc8.dim("~");
|
|
2677
|
+
log.plain(` ${mcpIcon} MCP server ${r.mcpStatus}`);
|
|
2678
|
+
log.plain(` ${pc8.dim(r.mcpPath)}`);
|
|
2679
|
+
const ruleIcon = r.ruleStatus === "installed" ? pc8.green("+") : pc8.dim("~");
|
|
2680
|
+
log.plain(` ${ruleIcon} Rule ${r.ruleStatus}`);
|
|
2681
|
+
log.plain(` ${pc8.dim(r.rulePath)}`);
|
|
2682
|
+
const skillIcon = r.skillStatus === "installed" ? pc8.green("+") : pc8.dim("~");
|
|
2683
|
+
log.plain(` ${skillIcon} Skill ${r.skillStatus}`);
|
|
2684
|
+
log.plain(` ${pc8.dim(r.skillPath)}`);
|
|
2685
|
+
}
|
|
2686
|
+
log.blank();
|
|
2687
|
+
trackEvent("setup", { agents: agents2, scope, authMode: auth.mode });
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2259
2690
|
// src/constants.ts
|
|
2260
2691
|
import { readFileSync as readFileSync2 } from "fs";
|
|
2261
2692
|
import { fileURLToPath } from "url";
|
|
2262
|
-
import { dirname as
|
|
2263
|
-
var __dirname =
|
|
2264
|
-
var pkg = JSON.parse(readFileSync2(
|
|
2693
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
2694
|
+
var __dirname = dirname4(fileURLToPath(import.meta.url));
|
|
2695
|
+
var pkg = JSON.parse(readFileSync2(join9(__dirname, "../package.json"), "utf-8"));
|
|
2265
2696
|
var VERSION = pkg.version;
|
|
2266
2697
|
var NAME = pkg.name;
|
|
2267
2698
|
|
|
2268
2699
|
// src/index.ts
|
|
2269
2700
|
var brand = {
|
|
2270
|
-
primary:
|
|
2271
|
-
dim:
|
|
2701
|
+
primary: pc9.green,
|
|
2702
|
+
dim: pc9.dim
|
|
2272
2703
|
};
|
|
2273
2704
|
var program = new Command();
|
|
2274
2705
|
program.name("ctx7").description("Context7 CLI - Manage AI coding skills and documentation context").version(VERSION).option("--base-url <url>").hook("preAction", (thisCommand) => {
|
|
@@ -2303,6 +2734,7 @@ Visit ${brand.primary("https://context7.com")} to browse skills
|
|
|
2303
2734
|
registerSkillCommands(program);
|
|
2304
2735
|
registerSkillAliases(program);
|
|
2305
2736
|
registerAuthCommands(program);
|
|
2737
|
+
registerSetupCommand(program);
|
|
2306
2738
|
program.action(() => {
|
|
2307
2739
|
console.log("");
|
|
2308
2740
|
const banner = figlet.textSync("Context7", { font: "ANSI Shadow" });
|