ctx7 0.4.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -137
- package/dist/index.js +259 -42
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# ctx7
|
|
2
2
|
|
|
3
|
-
CLI for [Context7](https://context7.com) - query up-to-date library documentation and
|
|
4
|
-
|
|
5
|
-
Skills are reusable prompt instructions that enhance your AI coding assistant with specialized capabilities like working with specific frameworks, libraries, or coding patterns.
|
|
3
|
+
CLI for [Context7](https://context7.com) - query up-to-date library documentation and configure Context7 for AI coding agents.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
@@ -41,22 +39,6 @@ ctx7 docs /facebook/react "useEffect cleanup"
|
|
|
41
39
|
ctx7 docs /vercel/next.js "middleware"
|
|
42
40
|
```
|
|
43
41
|
|
|
44
|
-
### Skills
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
# Search for skills
|
|
48
|
-
ctx7 skills search pdf
|
|
49
|
-
|
|
50
|
-
# Install a skill
|
|
51
|
-
ctx7 skills install /anthropics/skills pdf
|
|
52
|
-
|
|
53
|
-
# Generate a custom skill with AI
|
|
54
|
-
ctx7 skills generate
|
|
55
|
-
|
|
56
|
-
# List installed skills
|
|
57
|
-
ctx7 skills list --claude
|
|
58
|
-
```
|
|
59
|
-
|
|
60
42
|
## Usage
|
|
61
43
|
|
|
62
44
|
### Find a library
|
|
@@ -133,42 +115,9 @@ ctx7 remove --claude --mcp
|
|
|
133
115
|
|
|
134
116
|
If you installed the CLI itself globally with `npm install -g ctx7`, remove that separately with `npm uninstall -g ctx7`. If you use `npx ctx7`, there is no permanent CLI install to remove.
|
|
135
117
|
|
|
136
|
-
### Generate skills
|
|
137
|
-
|
|
138
|
-
Generate custom skills tailored to your use case using AI. Requires authentication.
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
# Log in first
|
|
142
|
-
ctx7 login
|
|
143
|
-
|
|
144
|
-
# Generate a skill (interactive)
|
|
145
|
-
ctx7 skills generate
|
|
146
|
-
|
|
147
|
-
# Short aliases
|
|
148
|
-
ctx7 skills gen
|
|
149
|
-
ctx7 skills g
|
|
150
|
-
|
|
151
|
-
# Generate and install to a specific client
|
|
152
|
-
ctx7 skills generate --cursor
|
|
153
|
-
ctx7 skills generate --claude
|
|
154
|
-
ctx7 skills generate --universal
|
|
155
|
-
|
|
156
|
-
# Generate globally
|
|
157
|
-
ctx7 skills generate --global
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
The generate flow:
|
|
161
|
-
|
|
162
|
-
1. Describe the expertise you want (e.g., "OAuth authentication with NextAuth.js")
|
|
163
|
-
2. Select relevant libraries from search results
|
|
164
|
-
3. Answer 3 clarifying questions to focus the skill
|
|
165
|
-
4. Review the generated skill, request changes if needed, then install
|
|
166
|
-
|
|
167
|
-
Weekly generation limits apply: free accounts get 6 generations/week, Pro accounts get 10.
|
|
168
|
-
|
|
169
118
|
### Authentication
|
|
170
119
|
|
|
171
|
-
Log in to access
|
|
120
|
+
Log in to access authenticated setup and higher documentation rate limits.
|
|
172
121
|
|
|
173
122
|
```bash
|
|
174
123
|
# Log in (opens browser for OAuth)
|
|
@@ -181,79 +130,9 @@ ctx7 whoami
|
|
|
181
130
|
ctx7 logout
|
|
182
131
|
```
|
|
183
132
|
|
|
184
|
-
### Install skills
|
|
185
|
-
|
|
186
|
-
Install skills from a project repository to your AI coding assistant's skills directory.
|
|
187
|
-
|
|
188
|
-
```bash
|
|
189
|
-
# Install all skills from a project (interactive selection)
|
|
190
|
-
ctx7 skills install /anthropics/skills
|
|
191
|
-
|
|
192
|
-
# Install a specific skill
|
|
193
|
-
ctx7 skills install /anthropics/skills pdf
|
|
194
|
-
|
|
195
|
-
# Install multiple skills at once
|
|
196
|
-
ctx7 skills install /anthropics/skills pdf commit
|
|
197
|
-
|
|
198
|
-
# Install to a specific client
|
|
199
|
-
ctx7 skills install /anthropics/skills pdf --cursor
|
|
200
|
-
ctx7 skills install /anthropics/skills pdf --claude
|
|
201
|
-
ctx7 skills install /anthropics/skills pdf --universal
|
|
202
|
-
|
|
203
|
-
# Install globally (home directory instead of current project)
|
|
204
|
-
ctx7 skills install /anthropics/skills pdf --global
|
|
205
|
-
|
|
206
|
-
# Install non-interactively
|
|
207
|
-
ctx7 skills install /anthropics/skills pdf --global --universal --yes
|
|
208
|
-
|
|
209
|
-
# Install to all supported agent locations
|
|
210
|
-
ctx7 skills install /anthropics/skills pdf --all-agents
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Search for skills
|
|
214
|
-
|
|
215
|
-
Find skills across all indexed projects in the registry.
|
|
216
|
-
|
|
217
|
-
```bash
|
|
218
|
-
ctx7 skills search pdf
|
|
219
|
-
ctx7 skills search typescript
|
|
220
|
-
ctx7 skills search react testing
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### List installed skills
|
|
224
|
-
|
|
225
|
-
View skills installed in your project or globally.
|
|
226
|
-
|
|
227
|
-
```bash
|
|
228
|
-
ctx7 skills list
|
|
229
|
-
ctx7 skills list --claude
|
|
230
|
-
ctx7 skills list --cursor
|
|
231
|
-
ctx7 skills list --universal
|
|
232
|
-
ctx7 skills list --global
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### Show skill information
|
|
236
|
-
|
|
237
|
-
Get details about available skills in a project.
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
ctx7 skills info /anthropics/skills
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### Remove a skill
|
|
244
|
-
|
|
245
|
-
Uninstall a skill from your project.
|
|
246
|
-
|
|
247
|
-
```bash
|
|
248
|
-
ctx7 skills remove pdf
|
|
249
|
-
ctx7 skills remove pdf --claude
|
|
250
|
-
ctx7 skills remove pdf --universal
|
|
251
|
-
ctx7 skills remove pdf --global
|
|
252
|
-
```
|
|
253
|
-
|
|
254
133
|
## Supported Clients
|
|
255
134
|
|
|
256
|
-
The CLI automatically detects which AI coding assistants you have installed and
|
|
135
|
+
The CLI automatically detects which AI coding assistants you have installed and configures Context7 for them:
|
|
257
136
|
|
|
258
137
|
| Client | Skills Directory |
|
|
259
138
|
| ------------------------------------------------------------------- | ----------------- |
|
|
@@ -262,24 +141,13 @@ The CLI automatically detects which AI coding assistants you have installed and
|
|
|
262
141
|
| Cursor | `.cursor/skills/` |
|
|
263
142
|
| Antigravity | `.agent/skills/` |
|
|
264
143
|
|
|
265
|
-
## Shortcuts
|
|
266
|
-
|
|
267
|
-
For faster usage, the CLI provides short aliases:
|
|
268
|
-
|
|
269
|
-
```bash
|
|
270
|
-
ctx7 si /anthropics/skills pdf # skills install
|
|
271
|
-
ctx7 ss pdf # skills search
|
|
272
|
-
ctx7 skills gen # skills generate
|
|
273
|
-
ctx7 skills g # skills generate
|
|
274
|
-
```
|
|
275
|
-
|
|
276
144
|
## Disabling Telemetry
|
|
277
145
|
|
|
278
146
|
The CLI collects anonymous usage data to help improve the product. To disable telemetry, set the `CTX7_TELEMETRY_DISABLED` environment variable:
|
|
279
147
|
|
|
280
148
|
```bash
|
|
281
149
|
# For a single command
|
|
282
|
-
CTX7_TELEMETRY_DISABLED=1 ctx7
|
|
150
|
+
CTX7_TELEMETRY_DISABLED=1 ctx7 docs /facebook/react "useEffect examples"
|
|
283
151
|
|
|
284
152
|
# Or export in your shell profile (~/.bashrc, ~/.zshrc, etc.)
|
|
285
153
|
export CTX7_TELEMETRY_DISABLED=1
|
|
@@ -287,4 +155,4 @@ export CTX7_TELEMETRY_DISABLED=1
|
|
|
287
155
|
|
|
288
156
|
## Learn More
|
|
289
157
|
|
|
290
|
-
Visit [context7.com](https://context7.com)
|
|
158
|
+
Visit [context7.com](https://context7.com) for documentation lookup and setup guides.
|
package/dist/index.js
CHANGED
|
@@ -1155,17 +1155,88 @@ function buildAuthorizationUrl(baseUrl3, clientId, redirectUri, codeChallenge, s
|
|
|
1155
1155
|
url.searchParams.set("response_type", "code");
|
|
1156
1156
|
return url.toString();
|
|
1157
1157
|
}
|
|
1158
|
+
var DEVICE_CODE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
1159
|
+
var DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;
|
|
1160
|
+
function shouldUseDeviceFlow() {
|
|
1161
|
+
if (process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY) return true;
|
|
1162
|
+
if (process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
|
|
1163
|
+
return true;
|
|
1164
|
+
}
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1167
|
+
async function startDeviceAuthorization(baseUrl3, clientId) {
|
|
1168
|
+
const params = new URLSearchParams({ client_id: clientId });
|
|
1169
|
+
try {
|
|
1170
|
+
const hostname2 = os.hostname();
|
|
1171
|
+
if (hostname2) params.set("hostname", hostname2);
|
|
1172
|
+
} catch {
|
|
1173
|
+
}
|
|
1174
|
+
const response = await fetch(`${baseUrl3}/api/oauth/device/code`, {
|
|
1175
|
+
method: "POST",
|
|
1176
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1177
|
+
body: params.toString()
|
|
1178
|
+
});
|
|
1179
|
+
if (!response.ok) {
|
|
1180
|
+
const err = await response.json().catch(() => ({}));
|
|
1181
|
+
throw new Error(err.error_description || err.error || "Failed to start device authorization");
|
|
1182
|
+
}
|
|
1183
|
+
return await response.json();
|
|
1184
|
+
}
|
|
1185
|
+
async function pollDeviceToken(baseUrl3, clientId, deviceCode) {
|
|
1186
|
+
let response;
|
|
1187
|
+
try {
|
|
1188
|
+
response = await fetch(`${baseUrl3}/api/oauth/device/token`, {
|
|
1189
|
+
method: "POST",
|
|
1190
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1191
|
+
body: new URLSearchParams({
|
|
1192
|
+
grant_type: DEVICE_CODE_GRANT,
|
|
1193
|
+
device_code: deviceCode,
|
|
1194
|
+
client_id: clientId
|
|
1195
|
+
}).toString()
|
|
1196
|
+
});
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
return {
|
|
1199
|
+
status: "transient",
|
|
1200
|
+
errorMessage: error instanceof Error ? error.message : "network error"
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
if (response.ok) {
|
|
1204
|
+
const tokens = await response.json();
|
|
1205
|
+
return { status: "approved", tokens };
|
|
1206
|
+
}
|
|
1207
|
+
if (response.status >= 500) {
|
|
1208
|
+
const err2 = await response.json().catch(() => ({}));
|
|
1209
|
+
return {
|
|
1210
|
+
status: "transient",
|
|
1211
|
+
errorMessage: err2.error_description || err2.error || `HTTP ${response.status}`
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
const err = await response.json().catch(() => ({}));
|
|
1215
|
+
switch (err.error) {
|
|
1216
|
+
case "authorization_pending":
|
|
1217
|
+
return { status: "pending" };
|
|
1218
|
+
case "slow_down":
|
|
1219
|
+
return { status: "slow_down" };
|
|
1220
|
+
case "access_denied":
|
|
1221
|
+
return { status: "denied" };
|
|
1222
|
+
case "expired_token":
|
|
1223
|
+
return { status: "expired" };
|
|
1224
|
+
default:
|
|
1225
|
+
throw new Error(err.error_description || err.error || "Device token poll failed");
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1158
1228
|
|
|
1159
1229
|
// src/commands/auth.ts
|
|
1160
1230
|
import pc4 from "picocolors";
|
|
1161
1231
|
import ora from "ora";
|
|
1162
1232
|
import open from "open";
|
|
1233
|
+
import boxen from "boxen";
|
|
1163
1234
|
var baseUrl2 = "https://context7.com";
|
|
1164
1235
|
function setAuthBaseUrl(url) {
|
|
1165
1236
|
baseUrl2 = url;
|
|
1166
1237
|
}
|
|
1167
1238
|
function registerAuthCommands(program2) {
|
|
1168
|
-
program2.command("login").description("Log in to Context7").option("--no-browser", "Don't open browser automatically").action(async (options) => {
|
|
1239
|
+
program2.command("login").description("Log in to Context7").option("--no-browser", "Don't open browser automatically").option("--device", "Force device-code flow (use on SSH / headless hosts)").action(async (options) => {
|
|
1169
1240
|
await loginCommand(options);
|
|
1170
1241
|
});
|
|
1171
1242
|
program2.command("logout").description("Log out of Context7").action(() => {
|
|
@@ -1175,7 +1246,131 @@ function registerAuthCommands(program2) {
|
|
|
1175
1246
|
await whoamiCommand();
|
|
1176
1247
|
});
|
|
1177
1248
|
}
|
|
1178
|
-
|
|
1249
|
+
function renderDeviceCodeBox(userCode, verificationUri, verificationUriComplete) {
|
|
1250
|
+
const codeLine = `${pc4.dim("Your one-time code:")}
|
|
1251
|
+
|
|
1252
|
+
${pc4.green(pc4.bold(userCode))}`;
|
|
1253
|
+
const linkLine = verificationUriComplete ? `${pc4.dim("Open this link to approve:")}
|
|
1254
|
+
${pc4.cyan(verificationUriComplete)}
|
|
1255
|
+
|
|
1256
|
+
${pc4.dim("Or visit")} ${pc4.cyan(verificationUri)} ${pc4.dim("and enter the code above.")}` : `${pc4.dim("Visit:")} ${pc4.cyan(verificationUri)}`;
|
|
1257
|
+
return boxen(`${codeLine}
|
|
1258
|
+
|
|
1259
|
+
${linkLine}`, {
|
|
1260
|
+
title: "Sign in to Context7",
|
|
1261
|
+
titleAlignment: "left",
|
|
1262
|
+
padding: 1,
|
|
1263
|
+
margin: { top: 1, bottom: 1, left: 2, right: 2 },
|
|
1264
|
+
borderStyle: "round",
|
|
1265
|
+
borderColor: "gray"
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
function waitForEnter(prompt) {
|
|
1269
|
+
if (!process.stdin.isTTY) return Promise.resolve();
|
|
1270
|
+
return new Promise((resolve3) => {
|
|
1271
|
+
process.stdout.write(` ${pc4.dim(prompt)} `);
|
|
1272
|
+
const onData = (chunk) => {
|
|
1273
|
+
if (chunk[0] === 3) {
|
|
1274
|
+
process.stdin.removeListener("data", onData);
|
|
1275
|
+
process.stdin.setRawMode?.(false);
|
|
1276
|
+
process.stdin.pause();
|
|
1277
|
+
process.stdout.write("\n");
|
|
1278
|
+
process.exit(130);
|
|
1279
|
+
}
|
|
1280
|
+
process.stdin.removeListener("data", onData);
|
|
1281
|
+
process.stdin.setRawMode?.(false);
|
|
1282
|
+
process.stdin.pause();
|
|
1283
|
+
process.stdout.write("\n");
|
|
1284
|
+
resolve3();
|
|
1285
|
+
};
|
|
1286
|
+
process.stdin.setRawMode?.(true);
|
|
1287
|
+
process.stdin.resume();
|
|
1288
|
+
process.stdin.on("data", onData);
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
async function announceIdentity(accessToken) {
|
|
1292
|
+
try {
|
|
1293
|
+
const whoami = await fetchWhoami(accessToken);
|
|
1294
|
+
const name = whoami.email || whoami.name;
|
|
1295
|
+
if (!name) return "Login successful!";
|
|
1296
|
+
const team = whoami.teamspace?.name;
|
|
1297
|
+
return team ? `Logged in as ${pc4.bold(name)} ${pc4.dim(`(${team})`)}` : `Logged in as ${pc4.bold(name)}`;
|
|
1298
|
+
} catch {
|
|
1299
|
+
return "Login successful!";
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
async function performDeviceLogin(openBrowser = true) {
|
|
1303
|
+
const spinner = ora("Preparing login...").start();
|
|
1304
|
+
let authorization;
|
|
1305
|
+
try {
|
|
1306
|
+
authorization = await startDeviceAuthorization(baseUrl2, CLI_CLIENT_ID);
|
|
1307
|
+
} catch (error) {
|
|
1308
|
+
spinner.fail(pc4.red("Login failed"));
|
|
1309
|
+
if (error instanceof Error) console.error(pc4.red(error.message));
|
|
1310
|
+
return null;
|
|
1311
|
+
}
|
|
1312
|
+
spinner.stop();
|
|
1313
|
+
console.log(
|
|
1314
|
+
renderDeviceCodeBox(
|
|
1315
|
+
authorization.user_code,
|
|
1316
|
+
authorization.verification_uri,
|
|
1317
|
+
authorization.verification_uri_complete
|
|
1318
|
+
)
|
|
1319
|
+
);
|
|
1320
|
+
const target = authorization.verification_uri_complete ?? authorization.verification_uri;
|
|
1321
|
+
if (openBrowser) {
|
|
1322
|
+
await waitForEnter("Press Enter to open the browser, or Ctrl-C to quit...");
|
|
1323
|
+
try {
|
|
1324
|
+
await open(target);
|
|
1325
|
+
} catch {
|
|
1326
|
+
console.log(pc4.dim(` Couldn't open a browser \u2014 visit the link above manually.`));
|
|
1327
|
+
}
|
|
1328
|
+
} else {
|
|
1329
|
+
console.log(pc4.dim(" Open the link above in any browser to continue."));
|
|
1330
|
+
console.log("");
|
|
1331
|
+
}
|
|
1332
|
+
const waitingSpinner = ora({ text: "Waiting for authorization...", indent: 2 }).start();
|
|
1333
|
+
const deadline = Date.now() + authorization.expires_in * 1e3;
|
|
1334
|
+
let intervalMs = (authorization.interval ?? DEFAULT_DEVICE_POLL_INTERVAL_SECONDS) * 1e3;
|
|
1335
|
+
while (Date.now() < deadline) {
|
|
1336
|
+
await new Promise((resolve3) => setTimeout(resolve3, intervalMs));
|
|
1337
|
+
try {
|
|
1338
|
+
const result = await pollDeviceToken(baseUrl2, CLI_CLIENT_ID, authorization.device_code);
|
|
1339
|
+
if (result.status === "approved" && result.tokens) {
|
|
1340
|
+
saveTokens(result.tokens);
|
|
1341
|
+
const successText = await announceIdentity(result.tokens.access_token);
|
|
1342
|
+
waitingSpinner.succeed(pc4.green(successText));
|
|
1343
|
+
return result.tokens.access_token;
|
|
1344
|
+
}
|
|
1345
|
+
if (result.status === "slow_down") {
|
|
1346
|
+
intervalMs += 5e3;
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
if (result.status === "denied") {
|
|
1350
|
+
waitingSpinner.fail(pc4.red("Authorization denied."));
|
|
1351
|
+
return null;
|
|
1352
|
+
}
|
|
1353
|
+
if (result.status === "expired") {
|
|
1354
|
+
waitingSpinner.fail(pc4.red("Code expired. Run login again."));
|
|
1355
|
+
return null;
|
|
1356
|
+
}
|
|
1357
|
+
if (result.status === "transient") {
|
|
1358
|
+
intervalMs += 5e3;
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
waitingSpinner.fail(pc4.red("Login failed"));
|
|
1363
|
+
if (error instanceof Error) console.error(pc4.red(error.message));
|
|
1364
|
+
return null;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
waitingSpinner.fail(pc4.red("Code expired without approval."));
|
|
1368
|
+
return null;
|
|
1369
|
+
}
|
|
1370
|
+
async function performLogin(openBrowser = true, forceDevice = false) {
|
|
1371
|
+
if (forceDevice || shouldUseDeviceFlow()) {
|
|
1372
|
+
return performDeviceLogin(openBrowser);
|
|
1373
|
+
}
|
|
1179
1374
|
const spinner = ora("Preparing login...").start();
|
|
1180
1375
|
try {
|
|
1181
1376
|
const { codeVerifier, codeChallenge } = generatePKCE();
|
|
@@ -1242,7 +1437,7 @@ async function loginCommand(options) {
|
|
|
1242
1437
|
return;
|
|
1243
1438
|
}
|
|
1244
1439
|
clearTokens();
|
|
1245
|
-
const token = await performLogin(options.browser);
|
|
1440
|
+
const token = await performLogin(options.browser, options.device ?? false);
|
|
1246
1441
|
if (!token) {
|
|
1247
1442
|
process.exit(1);
|
|
1248
1443
|
}
|
|
@@ -1911,6 +2106,11 @@ async function detectProjectDependencies(cwd) {
|
|
|
1911
2106
|
}
|
|
1912
2107
|
|
|
1913
2108
|
// src/commands/skill.ts
|
|
2109
|
+
var SKILL_HUB_DEPRECATION_WARNING = "Warning: Skill commands are deprecated and will stop working in the next major release.";
|
|
2110
|
+
function warnSkillHubDeprecated() {
|
|
2111
|
+
console.error(pc7.yellow(SKILL_HUB_DEPRECATION_WARNING));
|
|
2112
|
+
console.error("");
|
|
2113
|
+
}
|
|
1914
2114
|
function logInstallSummary(targets, targetDirs, skillNames) {
|
|
1915
2115
|
log.blank();
|
|
1916
2116
|
const hasUniversal = targets.ides.some((ide) => ide === "universal");
|
|
@@ -1934,7 +2134,9 @@ function logInstallSummary(targets, targetDirs, skillNames) {
|
|
|
1934
2134
|
log.blank();
|
|
1935
2135
|
}
|
|
1936
2136
|
function registerSkillCommands(program2) {
|
|
1937
|
-
const skill = program2.command("skills").alias("skill").description("Manage AI coding skills")
|
|
2137
|
+
const skill = program2.command("skills", { hidden: true }).alias("skill").description("Manage AI coding skills").hook("preAction", () => {
|
|
2138
|
+
warnSkillHubDeprecated();
|
|
2139
|
+
});
|
|
1938
2140
|
registerGenerateCommand(skill);
|
|
1939
2141
|
skill.command("install").alias("i").alias("add").argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--all-agents", "Install to all supported agent locations").option("-y, --yes", "Skip confirmation prompts").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Install skills from a repository").action(async (project, skillName, options) => {
|
|
1940
2142
|
await installCommand(project, skillName, options);
|
|
@@ -1957,12 +2159,15 @@ function registerSkillCommands(program2) {
|
|
|
1957
2159
|
}
|
|
1958
2160
|
function registerSkillAliases(program2) {
|
|
1959
2161
|
program2.command("si", { hidden: true }).argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--all-agents", "Install to all supported agent locations").option("-y, --yes", "Skip confirmation prompts").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Install skills (alias for: skills install)").action(async (project, skillName, options) => {
|
|
2162
|
+
warnSkillHubDeprecated();
|
|
1960
2163
|
await installCommand(project, skillName, options);
|
|
1961
2164
|
});
|
|
1962
2165
|
program2.command("ss", { hidden: true }).argument("<keywords...>", "Search keywords").description("Search for skills (alias for: skills search)").action(async (keywords) => {
|
|
2166
|
+
warnSkillHubDeprecated();
|
|
1963
2167
|
await searchCommand(keywords.join(" "));
|
|
1964
2168
|
});
|
|
1965
2169
|
program2.command("ssg", { hidden: true }).option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Suggest skills (alias for: skills suggest)").action(async (options) => {
|
|
2170
|
+
warnSkillHubDeprecated();
|
|
1966
2171
|
await suggestCommand(options);
|
|
1967
2172
|
});
|
|
1968
2173
|
}
|
|
@@ -2053,7 +2258,7 @@ async function installCommand(input2, skillName, options) {
|
|
|
2053
2258
|
const paddedName = s.name.padEnd(maxNameLen);
|
|
2054
2259
|
const popularity = formatPopularity(s.installCount) + " ".repeat(popularityColWidth - 4);
|
|
2055
2260
|
const trust = formatTrust(s.trustScore);
|
|
2056
|
-
const skillUrl = `https://
|
|
2261
|
+
const skillUrl = s.url || `https://github.com${s.project}`;
|
|
2057
2262
|
const skillLink = terminalLink(s.name, skillUrl, pc7.white);
|
|
2058
2263
|
const repoLink = terminalLink(s.project, `https://github.com${s.project}`, pc7.white);
|
|
2059
2264
|
const metadataLines = [
|
|
@@ -2204,11 +2409,7 @@ async function searchCommand(query) {
|
|
|
2204
2409
|
const displayName = nameWithRepo(s) + " ".repeat(maxNameLen - rawLen);
|
|
2205
2410
|
const popularity = formatPopularity(s.installCount) + " ".repeat(popularityColWidth - 4);
|
|
2206
2411
|
const trust = formatTrust(s.trustScore);
|
|
2207
|
-
const skillLink = terminalLink(
|
|
2208
|
-
s.name,
|
|
2209
|
-
`https://context7.com/skills${s.project}/${s.name}`,
|
|
2210
|
-
pc7.white
|
|
2211
|
-
);
|
|
2412
|
+
const skillLink = terminalLink(s.name, s.url || `https://github.com${s.project}`, pc7.white);
|
|
2212
2413
|
const repoLink = terminalLink(s.project, `https://github.com${s.project}`, pc7.white);
|
|
2213
2414
|
const metadataLines = [
|
|
2214
2415
|
pc7.dim("\u2500".repeat(50)),
|
|
@@ -2513,11 +2714,7 @@ async function suggestCommand(options) {
|
|
|
2513
2714
|
const trustLabel = getTrustLabel(s.trustScore);
|
|
2514
2715
|
const trust = formatTrust(s.trustScore) + " ".repeat(trustColWidth - trustLabel.length);
|
|
2515
2716
|
const matched = pc7.yellow(s.matchedDep.padEnd(maxMatchedLen));
|
|
2516
|
-
const skillLink = terminalLink(
|
|
2517
|
-
s.name,
|
|
2518
|
-
`https://context7.com/skills${s.project}/${s.name}`,
|
|
2519
|
-
pc7.white
|
|
2520
|
-
);
|
|
2717
|
+
const skillLink = terminalLink(s.name, s.url || `https://github.com${s.project}`, pc7.white);
|
|
2521
2718
|
const repoLink = terminalLink(s.project, `https://github.com${s.project}`, pc7.white);
|
|
2522
2719
|
const metadataLines = [
|
|
2523
2720
|
pc7.dim("\u2500".repeat(50)),
|
|
@@ -2651,6 +2848,7 @@ var SETUP_AGENT_NAMES = {
|
|
|
2651
2848
|
cursor: "Cursor",
|
|
2652
2849
|
opencode: "OpenCode",
|
|
2653
2850
|
codex: "Codex",
|
|
2851
|
+
antigravity: "Antigravity",
|
|
2654
2852
|
gemini: "Gemini CLI"
|
|
2655
2853
|
};
|
|
2656
2854
|
var AUTH_MODE_LABELS = {
|
|
@@ -2789,6 +2987,33 @@ var agents = {
|
|
|
2789
2987
|
globalPaths: [join7(homedir5(), ".codex")]
|
|
2790
2988
|
}
|
|
2791
2989
|
},
|
|
2990
|
+
// Antigravity is built on Gemini infrastructure and shares ~/.gemini/. Per
|
|
2991
|
+
// the official Codelabs guide, Antigravity 2.0/IDE/CLI read MCP servers from
|
|
2992
|
+
// ~/.gemini/config/mcp_config.json globally; there is no project-level MCP
|
|
2993
|
+
// config, so projectPaths is empty and setupAgent falls back to global.
|
|
2994
|
+
antigravity: {
|
|
2995
|
+
name: "antigravity",
|
|
2996
|
+
displayName: "Antigravity",
|
|
2997
|
+
mcp: {
|
|
2998
|
+
projectPaths: [],
|
|
2999
|
+
globalPaths: [join7(homedir5(), ".gemini", "config", "mcp_config.json")],
|
|
3000
|
+
configKey: "mcpServers",
|
|
3001
|
+
buildEntry: (auth, transport) => transport === "stdio" ? stdioEntry(auth) : withHeaders({ serverUrl: mcpUrl(auth) }, auth)
|
|
3002
|
+
},
|
|
3003
|
+
rule: {
|
|
3004
|
+
kind: "append",
|
|
3005
|
+
file: (scope) => scope === "global" ? join7(homedir5(), ".gemini", "GEMINI.md") : "GEMINI.md",
|
|
3006
|
+
sectionMarker: "<!-- context7 -->"
|
|
3007
|
+
},
|
|
3008
|
+
skill: {
|
|
3009
|
+
name: "context7-mcp",
|
|
3010
|
+
dir: (scope) => scope === "global" ? join7(homedir5(), ".agent", "skills") : join7(".agent", "skills")
|
|
3011
|
+
},
|
|
3012
|
+
detect: {
|
|
3013
|
+
projectPaths: [".agent"],
|
|
3014
|
+
globalPaths: [join7(homedir5(), ".gemini", "antigravity"), join7(homedir5(), ".agent")]
|
|
3015
|
+
}
|
|
3016
|
+
},
|
|
2792
3017
|
gemini: {
|
|
2793
3018
|
name: "gemini",
|
|
2794
3019
|
displayName: "Gemini CLI",
|
|
@@ -3187,11 +3412,12 @@ function getSelectedAgents(options) {
|
|
|
3187
3412
|
if (options.cursor) agents2.push("cursor");
|
|
3188
3413
|
if (options.opencode) agents2.push("opencode");
|
|
3189
3414
|
if (options.codex) agents2.push("codex");
|
|
3415
|
+
if (options.antigravity) agents2.push("antigravity");
|
|
3190
3416
|
if (options.gemini) agents2.push("gemini");
|
|
3191
3417
|
return agents2;
|
|
3192
3418
|
}
|
|
3193
3419
|
function registerSetupCommand(program2) {
|
|
3194
|
-
program2.command("setup").description("Set up Context7 for your AI coding agent").option("--claude", "Set up for Claude Code").option("--cursor", "Set up for Cursor").option("--
|
|
3420
|
+
program2.command("setup").description("Set up Context7 for your AI coding agent").option("--claude", "Set up for Claude Code").option("--cursor", "Set up for Cursor").option("--antigravity", "Set up for Antigravity (.agent/skills)").option("--opencode", "Set up for OpenCode").option("--codex", "Set up for Codex").option("--gemini", "Set up for Gemini CLI").option("--mcp", "Set up MCP server mode").option("--cli", "Set up CLI + Skills mode (no MCP server)").option("-p, --project", "Configure for current project instead of globally").option("-y, --yes", "Skip confirmation prompts").option("--api-key <key>", "Use API key authentication").option("--oauth", "Use OAuth endpoint (IDE handles auth flow)").option("--stdio", "Configure the MCP server as a local stdio process (default: HTTP)").action(async (options) => {
|
|
3195
3421
|
await setupCommand(options);
|
|
3196
3422
|
});
|
|
3197
3423
|
}
|
|
@@ -3232,7 +3458,7 @@ async function resolveAuth(options) {
|
|
|
3232
3458
|
}
|
|
3233
3459
|
async function resolveMode(options) {
|
|
3234
3460
|
if (options.cli) return "cli";
|
|
3235
|
-
if (options.mcp || options.yes || options.oauth || options.
|
|
3461
|
+
if (options.mcp || options.yes || options.oauth || options.stdio) return "mcp";
|
|
3236
3462
|
return select3({
|
|
3237
3463
|
message: "How should your agent access Context7?",
|
|
3238
3464
|
choices: [
|
|
@@ -3343,7 +3569,7 @@ function resolveEntryToWrite(agent, auth, transport, existingEntry) {
|
|
|
3343
3569
|
}
|
|
3344
3570
|
async function setupAgent(agentName, auth, transport, scope) {
|
|
3345
3571
|
const agent = getAgent(agentName);
|
|
3346
|
-
const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((p) => join8(process.cwd(), p));
|
|
3572
|
+
const mcpCandidates = scope === "global" || agent.mcp.projectPaths.length === 0 ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((p) => join8(process.cwd(), p));
|
|
3347
3573
|
const mcpPath = await resolveMcpPath(mcpCandidates);
|
|
3348
3574
|
let mcpStatus;
|
|
3349
3575
|
try {
|
|
@@ -3545,7 +3771,7 @@ var MODE_LABELS = {
|
|
|
3545
3771
|
cli: "CLI + Skills"
|
|
3546
3772
|
};
|
|
3547
3773
|
function registerRemoveCommand(program2) {
|
|
3548
|
-
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) => {
|
|
3774
|
+
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("--antigravity", "Remove from Antigravity").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) => {
|
|
3549
3775
|
await removeCommand2(options);
|
|
3550
3776
|
});
|
|
3551
3777
|
}
|
|
@@ -3555,6 +3781,7 @@ function getSelectedAgents2(options) {
|
|
|
3555
3781
|
if (options.cursor) agents2.push("cursor");
|
|
3556
3782
|
if (options.opencode) agents2.push("opencode");
|
|
3557
3783
|
if (options.codex) agents2.push("codex");
|
|
3784
|
+
if (options.antigravity) agents2.push("antigravity");
|
|
3558
3785
|
if (options.gemini) agents2.push("gemini");
|
|
3559
3786
|
return agents2;
|
|
3560
3787
|
}
|
|
@@ -3606,7 +3833,7 @@ async function resolveAgents2(options, scope) {
|
|
|
3606
3833
|
if (detected.length > 0 && options.yes) return detected;
|
|
3607
3834
|
if (detected.length === 0) {
|
|
3608
3835
|
log.warn(
|
|
3609
|
-
"No Context7 setup detected. Pass --claude, --cursor, --opencode, --codex, or --gemini."
|
|
3836
|
+
"No Context7 setup detected. Pass --claude, --cursor, --opencode, --codex, --antigravity, or --gemini."
|
|
3610
3837
|
);
|
|
3611
3838
|
return [];
|
|
3612
3839
|
}
|
|
@@ -3635,6 +3862,7 @@ async function pathExists2(path2) {
|
|
|
3635
3862
|
}
|
|
3636
3863
|
async function hasMcpConfig(agentName, scope) {
|
|
3637
3864
|
const agent = getAgent(agentName);
|
|
3865
|
+
if (scope === "project" && agent.mcp.projectPaths.length === 0) return false;
|
|
3638
3866
|
const candidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join9(process.cwd(), path2));
|
|
3639
3867
|
const mcpPath = await resolveMcpPath(candidates);
|
|
3640
3868
|
if (mcpPath.endsWith(".toml")) {
|
|
@@ -3722,6 +3950,9 @@ async function resolveModes(options, agents2, scope) {
|
|
|
3722
3950
|
}
|
|
3723
3951
|
async function uninstallMcp(agentName, scope) {
|
|
3724
3952
|
const agent = getAgent(agentName);
|
|
3953
|
+
if (scope === "project" && agent.mcp.projectPaths.length === 0) {
|
|
3954
|
+
return { status: "not found", path: "" };
|
|
3955
|
+
}
|
|
3725
3956
|
const mcpCandidates = scope === "global" ? agent.mcp.globalPaths : agent.mcp.projectPaths.map((path2) => join9(process.cwd(), path2));
|
|
3726
3957
|
const mcpPath = await resolveMcpPath(mcpCandidates);
|
|
3727
3958
|
try {
|
|
@@ -4364,7 +4595,7 @@ var brand = {
|
|
|
4364
4595
|
dim: pc12.dim
|
|
4365
4596
|
};
|
|
4366
4597
|
var program = new Command();
|
|
4367
|
-
program.name("ctx7").description("Context7 CLI -
|
|
4598
|
+
program.name("ctx7").description("Context7 CLI - Fetch documentation context and configure Context7").version(VERSION).option("--base-url <url>").hook("preAction", (thisCommand) => {
|
|
4368
4599
|
const opts = thisCommand.opts();
|
|
4369
4600
|
if (opts.baseUrl) {
|
|
4370
4601
|
setBaseUrl(opts.baseUrl);
|
|
@@ -4379,21 +4610,10 @@ program.name("ctx7").description("Context7 CLI - Manage AI coding skills and doc
|
|
|
4379
4610
|
"after",
|
|
4380
4611
|
`
|
|
4381
4612
|
Examples:
|
|
4382
|
-
${brand.dim("#
|
|
4383
|
-
${brand.primary("npx ctx7
|
|
4384
|
-
${brand.primary("npx ctx7
|
|
4385
|
-
|
|
4386
|
-
${brand.dim("# Install from a repository")}
|
|
4387
|
-
${brand.primary("npx ctx7 skills install /anthropics/skills")}
|
|
4388
|
-
${brand.primary("npx ctx7 skills install /anthropics/skills pdf")}
|
|
4389
|
-
|
|
4390
|
-
${brand.dim("# Install to specific client")}
|
|
4391
|
-
${brand.primary("npx ctx7 skills install /anthropics/skills --cursor")}
|
|
4392
|
-
${brand.primary("npx ctx7 skills install /anthropics/skills --global")}
|
|
4393
|
-
|
|
4394
|
-
${brand.dim("# List and manage installed skills")}
|
|
4395
|
-
${brand.primary("npx ctx7 skills list --claude")}
|
|
4396
|
-
${brand.primary("npx ctx7 skills remove pdf")}
|
|
4613
|
+
${brand.dim("# Configure Context7 for your coding agent")}
|
|
4614
|
+
${brand.primary("npx ctx7 setup")}
|
|
4615
|
+
${brand.primary("npx ctx7 setup --mcp")}
|
|
4616
|
+
${brand.primary("npx ctx7 setup --cli")}
|
|
4397
4617
|
|
|
4398
4618
|
${brand.dim("# Remove Context7 setup")}
|
|
4399
4619
|
${brand.primary("npx ctx7 remove --cursor")}
|
|
@@ -4404,8 +4624,6 @@ Examples:
|
|
|
4404
4624
|
${brand.dim("# Query library documentation")}
|
|
4405
4625
|
${brand.primary('npx ctx7 library react "how to use hooks"')}
|
|
4406
4626
|
${brand.primary('npx ctx7 docs /facebook/react "useEffect examples"')}
|
|
4407
|
-
|
|
4408
|
-
Visit ${brand.primary("https://context7.com")} to browse skills
|
|
4409
4627
|
`
|
|
4410
4628
|
);
|
|
4411
4629
|
registerSkillCommands(program);
|
|
@@ -4419,14 +4637,13 @@ program.action(() => {
|
|
|
4419
4637
|
console.log("");
|
|
4420
4638
|
const banner = figlet.textSync("Context7", { font: "ANSI Shadow" });
|
|
4421
4639
|
console.log(brand.primary(banner));
|
|
4422
|
-
console.log(brand.dim("
|
|
4640
|
+
console.log(brand.dim(" Documentation context for AI coding agents"));
|
|
4423
4641
|
console.log("");
|
|
4424
4642
|
console.log(" Quick start:");
|
|
4425
|
-
console.log(` ${brand.primary("npx ctx7
|
|
4426
|
-
console.log(` ${brand.primary(
|
|
4643
|
+
console.log(` ${brand.primary("npx ctx7 setup")}`);
|
|
4644
|
+
console.log(` ${brand.primary('npx ctx7 docs /facebook/react "useEffect examples"')}`);
|
|
4427
4645
|
console.log("");
|
|
4428
4646
|
console.log(` Run ${brand.primary("npx ctx7 --help")} for all commands and options`);
|
|
4429
|
-
console.log(` Visit ${brand.primary("https://context7.com")} to browse skills`);
|
|
4430
4647
|
console.log("");
|
|
4431
4648
|
});
|
|
4432
4649
|
await program.parseAsync();
|