ctx7 0.1.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +830 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Upstash, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ctx7
|
|
2
|
+
|
|
3
|
+
CLI for the Context7 Skills Registry - manage AI coding skills across different AI coding assistants.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Run directly with npx (no install needed)
|
|
9
|
+
npx ctx7
|
|
10
|
+
|
|
11
|
+
# Or install globally
|
|
12
|
+
npm install -g ctx7
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Install skills
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Install all skills from a project
|
|
21
|
+
ctx7 skills install /owner/repo
|
|
22
|
+
|
|
23
|
+
# Install specific skills
|
|
24
|
+
ctx7 skills install /owner/repo skill-name
|
|
25
|
+
|
|
26
|
+
# Install to a specific client
|
|
27
|
+
ctx7 skills install /owner/repo --cursor
|
|
28
|
+
ctx7 skills install /owner/repo --claude
|
|
29
|
+
|
|
30
|
+
# Install globally (home directory)
|
|
31
|
+
ctx7 skills install /owner/repo --global
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Search for skills
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ctx7 skills search <query>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### List installed skills
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
ctx7 skills list
|
|
44
|
+
ctx7 skills list --claude
|
|
45
|
+
ctx7 skills list --global
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Show skill information
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
ctx7 skills info /owner/repo
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Remove a skill
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
ctx7 skills remove <skill-name>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Supported Clients
|
|
61
|
+
|
|
62
|
+
- Claude Code (`.claude/skills/`)
|
|
63
|
+
- Cursor (`.cursor/skills/`)
|
|
64
|
+
- Codex (`.codex/skills/`)
|
|
65
|
+
- OpenCode (`.opencode/skill/`)
|
|
66
|
+
- Amp (`.agents/skills/`)
|
|
67
|
+
- Antigravity (`.agent/skills/`)
|
|
68
|
+
|
|
69
|
+
## Shortcuts
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ctx7 si /owner/repo # skills install
|
|
73
|
+
ctx7 ss <query> # skills search
|
|
74
|
+
```
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import pc4 from "picocolors";
|
|
6
|
+
import figlet from "figlet";
|
|
7
|
+
|
|
8
|
+
// src/commands/skill.ts
|
|
9
|
+
import pc3 from "picocolors";
|
|
10
|
+
import { checkbox as checkbox2, Separator } from "@inquirer/prompts";
|
|
11
|
+
import ora from "ora";
|
|
12
|
+
import { readdir, rm as rm2 } from "fs/promises";
|
|
13
|
+
import { join as join3 } from "path";
|
|
14
|
+
|
|
15
|
+
// src/utils/parse-input.ts
|
|
16
|
+
function parseSkillInput(input) {
|
|
17
|
+
const urlMatch = input.match(
|
|
18
|
+
/(?:https?:\/\/)?github\.com\/([^\/]+)\/([^\/]+)\/tree\/([^\/]+)\/(.+)/
|
|
19
|
+
);
|
|
20
|
+
if (urlMatch) {
|
|
21
|
+
const [, owner, repo, branch, path] = urlMatch;
|
|
22
|
+
const skillName = path.split("/").pop();
|
|
23
|
+
return { type: "url", owner, repo, branch, path, skillName };
|
|
24
|
+
}
|
|
25
|
+
const shortMatch = input.match(/^\/?([^\/]+)\/([^\/]+)(?:\/(.+))?$/);
|
|
26
|
+
if (shortMatch) {
|
|
27
|
+
const [, owner, repo, skillName] = shortMatch;
|
|
28
|
+
return { type: skillName ? "skill" : "repo", owner, repo, skillName };
|
|
29
|
+
}
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Invalid input format: ${input}
|
|
32
|
+
Expected: /owner/repo, /owner/repo/skill, or full GitHub URL`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/utils/github.ts
|
|
37
|
+
var GITHUB_API = "https://api.github.com";
|
|
38
|
+
var GITHUB_RAW = "https://raw.githubusercontent.com";
|
|
39
|
+
function parseGitHubUrl(url) {
|
|
40
|
+
try {
|
|
41
|
+
const urlObj = new URL(url);
|
|
42
|
+
const parts = urlObj.pathname.split("/").filter(Boolean);
|
|
43
|
+
if (urlObj.hostname === "raw.githubusercontent.com") {
|
|
44
|
+
if (parts.length < 5) return null;
|
|
45
|
+
const owner = parts[0];
|
|
46
|
+
const repo = parts[1];
|
|
47
|
+
if (parts[2] === "refs" && parts[3] === "heads") {
|
|
48
|
+
const branch2 = parts[4];
|
|
49
|
+
const pathParts2 = parts.slice(5);
|
|
50
|
+
if (pathParts2.length > 0 && pathParts2[pathParts2.length - 1].includes(".")) {
|
|
51
|
+
pathParts2.pop();
|
|
52
|
+
}
|
|
53
|
+
const path2 = pathParts2.join("/");
|
|
54
|
+
return { owner, repo, branch: branch2, path: path2 };
|
|
55
|
+
}
|
|
56
|
+
const branch = parts[2];
|
|
57
|
+
const pathParts = parts.slice(3);
|
|
58
|
+
if (pathParts.length > 0 && pathParts[pathParts.length - 1].includes(".")) {
|
|
59
|
+
pathParts.pop();
|
|
60
|
+
}
|
|
61
|
+
const path = pathParts.join("/");
|
|
62
|
+
return { owner, repo, branch, path };
|
|
63
|
+
}
|
|
64
|
+
if (urlObj.hostname === "github.com") {
|
|
65
|
+
if (parts.length < 4 || parts[2] !== "tree") return null;
|
|
66
|
+
const owner = parts[0];
|
|
67
|
+
const repo = parts[1];
|
|
68
|
+
const branch = parts[3];
|
|
69
|
+
const path = parts.slice(4).join("/");
|
|
70
|
+
return { owner, repo, branch, path };
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function downloadSkillFromGitHub(skill) {
|
|
78
|
+
try {
|
|
79
|
+
const parsed = parseGitHubUrl(skill.url);
|
|
80
|
+
if (!parsed) {
|
|
81
|
+
return { files: [], error: `Invalid GitHub URL: ${skill.url}` };
|
|
82
|
+
}
|
|
83
|
+
const { owner, repo, branch, path: skillPath } = parsed;
|
|
84
|
+
const treeUrl = `${GITHUB_API}/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`;
|
|
85
|
+
const treeResponse = await fetch(treeUrl, {
|
|
86
|
+
headers: {
|
|
87
|
+
Accept: "application/vnd.github.v3+json",
|
|
88
|
+
"User-Agent": "context7-cli"
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
if (!treeResponse.ok) {
|
|
92
|
+
return { files: [], error: `GitHub API error: ${treeResponse.status}` };
|
|
93
|
+
}
|
|
94
|
+
const treeData = await treeResponse.json();
|
|
95
|
+
const skillFiles = treeData.tree.filter(
|
|
96
|
+
(item) => item.type === "blob" && item.path.startsWith(skillPath + "/")
|
|
97
|
+
);
|
|
98
|
+
if (skillFiles.length === 0) {
|
|
99
|
+
return { files: [], error: `No files found in ${skillPath}` };
|
|
100
|
+
}
|
|
101
|
+
const files = [];
|
|
102
|
+
for (const item of skillFiles) {
|
|
103
|
+
const rawUrl = `${GITHUB_RAW}/${owner}/${repo}/${branch}/${item.path}`;
|
|
104
|
+
const fileResponse = await fetch(rawUrl);
|
|
105
|
+
if (!fileResponse.ok) {
|
|
106
|
+
console.warn(`Failed to fetch ${item.path}: ${fileResponse.status}`);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const content = await fileResponse.text();
|
|
110
|
+
const relativePath = item.path.slice(skillPath.length + 1);
|
|
111
|
+
files.push({
|
|
112
|
+
path: relativePath,
|
|
113
|
+
content
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return { files };
|
|
117
|
+
} catch (err) {
|
|
118
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
119
|
+
return { files: [], error: message };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/utils/api.ts
|
|
124
|
+
var baseUrl = "https://context7.com";
|
|
125
|
+
function setBaseUrl(url) {
|
|
126
|
+
baseUrl = url;
|
|
127
|
+
}
|
|
128
|
+
async function listProjectSkills(project) {
|
|
129
|
+
const params = new URLSearchParams({ project });
|
|
130
|
+
const response = await fetch(`${baseUrl}/api/v2/skills?${params}`);
|
|
131
|
+
return await response.json();
|
|
132
|
+
}
|
|
133
|
+
async function getSkill(project, skillName) {
|
|
134
|
+
const params = new URLSearchParams({ project, skill: skillName });
|
|
135
|
+
const response = await fetch(`${baseUrl}/api/v2/skills?${params}`);
|
|
136
|
+
return await response.json();
|
|
137
|
+
}
|
|
138
|
+
async function searchSkills(query) {
|
|
139
|
+
const params = new URLSearchParams({ query });
|
|
140
|
+
const response = await fetch(`${baseUrl}/api/v2/skills?${params}`);
|
|
141
|
+
return await response.json();
|
|
142
|
+
}
|
|
143
|
+
async function downloadSkill(project, skillName) {
|
|
144
|
+
const skillData = await getSkill(project, skillName);
|
|
145
|
+
if (skillData.error) {
|
|
146
|
+
return {
|
|
147
|
+
skill: { name: skillName, description: "", url: "", project },
|
|
148
|
+
files: [],
|
|
149
|
+
error: skillData.message || skillData.error
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const skill = {
|
|
153
|
+
name: skillData.name,
|
|
154
|
+
description: skillData.description,
|
|
155
|
+
url: skillData.url,
|
|
156
|
+
project: skillData.project
|
|
157
|
+
};
|
|
158
|
+
const { files, error } = await downloadSkillFromGitHub(skill);
|
|
159
|
+
if (error) {
|
|
160
|
+
return { skill, files: [], error };
|
|
161
|
+
}
|
|
162
|
+
return { skill, files };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/utils/logger.ts
|
|
166
|
+
import pc from "picocolors";
|
|
167
|
+
var log = {
|
|
168
|
+
info: (message) => console.log(pc.cyan(message)),
|
|
169
|
+
success: (message) => console.log(pc.green(`\u2714 ${message}`)),
|
|
170
|
+
warn: (message) => console.log(pc.yellow(`\u26A0 ${message}`)),
|
|
171
|
+
error: (message) => console.log(pc.red(`\u2716 ${message}`)),
|
|
172
|
+
dim: (message) => console.log(pc.dim(message)),
|
|
173
|
+
item: (message) => console.log(pc.green(` ${message}`)),
|
|
174
|
+
itemAdd: (message) => console.log(` ${pc.green("+")} ${message}`),
|
|
175
|
+
plain: (message) => console.log(message),
|
|
176
|
+
blank: () => console.log("")
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// src/utils/ide.ts
|
|
180
|
+
import pc2 from "picocolors";
|
|
181
|
+
import { select, checkbox } from "@inquirer/prompts";
|
|
182
|
+
import { access } from "fs/promises";
|
|
183
|
+
import { join } from "path";
|
|
184
|
+
import { homedir } from "os";
|
|
185
|
+
|
|
186
|
+
// src/types.ts
|
|
187
|
+
var IDE_PATHS = {
|
|
188
|
+
claude: ".claude/skills",
|
|
189
|
+
cursor: ".cursor/skills",
|
|
190
|
+
codex: ".codex/skills",
|
|
191
|
+
opencode: ".opencode/skill",
|
|
192
|
+
amp: ".agents/skills",
|
|
193
|
+
antigravity: ".agent/skills"
|
|
194
|
+
};
|
|
195
|
+
var IDE_NAMES = {
|
|
196
|
+
claude: "Claude Code",
|
|
197
|
+
cursor: "Cursor",
|
|
198
|
+
codex: "Codex",
|
|
199
|
+
opencode: "OpenCode",
|
|
200
|
+
amp: "Amp",
|
|
201
|
+
antigravity: "Antigravity"
|
|
202
|
+
};
|
|
203
|
+
var DEFAULT_CONFIG = {
|
|
204
|
+
defaultIde: "claude",
|
|
205
|
+
defaultScope: "project"
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/utils/ide.ts
|
|
209
|
+
function getSelectedIde(options) {
|
|
210
|
+
if (options.claude) return "claude";
|
|
211
|
+
if (options.cursor) return "cursor";
|
|
212
|
+
if (options.codex) return "codex";
|
|
213
|
+
if (options.opencode) return "opencode";
|
|
214
|
+
if (options.amp) return "amp";
|
|
215
|
+
if (options.antigravity) return "antigravity";
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
function hasExplicitIdeOption(options) {
|
|
219
|
+
return !!(options.claude || options.cursor || options.codex || options.opencode || options.amp || options.antigravity);
|
|
220
|
+
}
|
|
221
|
+
async function detectInstalledIdes() {
|
|
222
|
+
const allIdes = Object.keys(IDE_PATHS);
|
|
223
|
+
const projectIdes = [];
|
|
224
|
+
for (const ide of allIdes) {
|
|
225
|
+
const idePath = IDE_PATHS[ide];
|
|
226
|
+
const projectParent = join(process.cwd(), idePath.split("/")[0]);
|
|
227
|
+
try {
|
|
228
|
+
await access(projectParent);
|
|
229
|
+
projectIdes.push(ide);
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (projectIdes.length > 0) {
|
|
234
|
+
return { ides: projectIdes, scope: "project" };
|
|
235
|
+
}
|
|
236
|
+
const globalIdes = [];
|
|
237
|
+
for (const ide of allIdes) {
|
|
238
|
+
const idePath = IDE_PATHS[ide];
|
|
239
|
+
const globalParent = join(homedir(), idePath.split("/")[0]);
|
|
240
|
+
try {
|
|
241
|
+
await access(globalParent);
|
|
242
|
+
globalIdes.push(ide);
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (globalIdes.length > 0) {
|
|
247
|
+
return { ides: globalIdes, scope: "global" };
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
async function promptForInstallTargets(options) {
|
|
252
|
+
if (hasExplicitIdeOption(options)) {
|
|
253
|
+
const ide = getSelectedIde(options);
|
|
254
|
+
const scope = options.global ? "global" : "project";
|
|
255
|
+
return {
|
|
256
|
+
ides: [ide || DEFAULT_CONFIG.defaultIde],
|
|
257
|
+
scopes: [scope]
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const detected = await detectInstalledIdes();
|
|
261
|
+
if (detected) {
|
|
262
|
+
const scope = options.global ? "global" : "project";
|
|
263
|
+
return { ides: detected.ides, scopes: [scope] };
|
|
264
|
+
}
|
|
265
|
+
log.blank();
|
|
266
|
+
const ideChoices = Object.keys(IDE_NAMES).map((ide) => ({
|
|
267
|
+
name: `${IDE_NAMES[ide]} ${pc2.dim(`(${IDE_PATHS[ide]})`)}`,
|
|
268
|
+
value: ide,
|
|
269
|
+
checked: ide === DEFAULT_CONFIG.defaultIde
|
|
270
|
+
}));
|
|
271
|
+
let selectedIdes;
|
|
272
|
+
try {
|
|
273
|
+
selectedIdes = await checkbox({
|
|
274
|
+
message: "Which clients do you want to install the skill(s) for?",
|
|
275
|
+
choices: ideChoices,
|
|
276
|
+
required: true
|
|
277
|
+
});
|
|
278
|
+
} catch {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
if (selectedIdes.length === 0) {
|
|
282
|
+
log.warn("You must select at least one client");
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
const selectedScopes = options.global ? ["global"] : ["project"];
|
|
286
|
+
return { ides: selectedIdes, scopes: selectedScopes };
|
|
287
|
+
}
|
|
288
|
+
async function promptForSingleTarget(options) {
|
|
289
|
+
if (hasExplicitIdeOption(options)) {
|
|
290
|
+
const ide = getSelectedIde(options) || DEFAULT_CONFIG.defaultIde;
|
|
291
|
+
const scope = options.global ? "global" : "project";
|
|
292
|
+
return { ide, scope };
|
|
293
|
+
}
|
|
294
|
+
log.blank();
|
|
295
|
+
const ideChoices = Object.keys(IDE_NAMES).map((ide) => ({
|
|
296
|
+
name: `${IDE_NAMES[ide]} ${pc2.dim(`(${IDE_PATHS[ide]})`)}`,
|
|
297
|
+
value: ide
|
|
298
|
+
}));
|
|
299
|
+
let selectedIde;
|
|
300
|
+
try {
|
|
301
|
+
selectedIde = await select({
|
|
302
|
+
message: "Which client?",
|
|
303
|
+
choices: ideChoices,
|
|
304
|
+
default: DEFAULT_CONFIG.defaultIde
|
|
305
|
+
});
|
|
306
|
+
} catch {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
let selectedScope;
|
|
310
|
+
if (options.global !== void 0) {
|
|
311
|
+
selectedScope = options.global ? "global" : "project";
|
|
312
|
+
} else {
|
|
313
|
+
try {
|
|
314
|
+
selectedScope = await select({
|
|
315
|
+
message: "Which scope?",
|
|
316
|
+
choices: [
|
|
317
|
+
{
|
|
318
|
+
name: `Project ${pc2.dim("(current directory)")}`,
|
|
319
|
+
value: "project"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: `Global ${pc2.dim("(home directory)")}`,
|
|
323
|
+
value: "global"
|
|
324
|
+
}
|
|
325
|
+
],
|
|
326
|
+
default: DEFAULT_CONFIG.defaultScope
|
|
327
|
+
});
|
|
328
|
+
} catch {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return { ide: selectedIde, scope: selectedScope };
|
|
333
|
+
}
|
|
334
|
+
function getTargetDirs(targets) {
|
|
335
|
+
const dirs = [];
|
|
336
|
+
for (const ide of targets.ides) {
|
|
337
|
+
const idePath = IDE_PATHS[ide];
|
|
338
|
+
for (const scope of targets.scopes) {
|
|
339
|
+
if (scope === "global") {
|
|
340
|
+
dirs.push(join(homedir(), idePath));
|
|
341
|
+
} else {
|
|
342
|
+
dirs.push(join(process.cwd(), idePath));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return dirs;
|
|
347
|
+
}
|
|
348
|
+
function getTargetDirFromSelection(ide, scope) {
|
|
349
|
+
const idePath = IDE_PATHS[ide];
|
|
350
|
+
if (scope === "global") {
|
|
351
|
+
return join(homedir(), idePath);
|
|
352
|
+
}
|
|
353
|
+
return join(process.cwd(), idePath);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/utils/installer.ts
|
|
357
|
+
import { mkdir, writeFile, rm, symlink, lstat } from "fs/promises";
|
|
358
|
+
import { join as join2 } from "path";
|
|
359
|
+
async function installSkillFiles(skillName, files, targetDir) {
|
|
360
|
+
const skillDir = join2(targetDir, skillName);
|
|
361
|
+
for (const file of files) {
|
|
362
|
+
const filePath = join2(skillDir, file.path);
|
|
363
|
+
const fileDir = join2(filePath, "..");
|
|
364
|
+
await mkdir(fileDir, { recursive: true });
|
|
365
|
+
await writeFile(filePath, file.content);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async function symlinkSkill(skillName, sourcePath, targetDir) {
|
|
369
|
+
const targetPath = join2(targetDir, skillName);
|
|
370
|
+
try {
|
|
371
|
+
const stats = await lstat(targetPath);
|
|
372
|
+
if (stats.isSymbolicLink() || stats.isDirectory()) {
|
|
373
|
+
await rm(targetPath, { recursive: true });
|
|
374
|
+
}
|
|
375
|
+
} catch {
|
|
376
|
+
}
|
|
377
|
+
await mkdir(targetDir, { recursive: true });
|
|
378
|
+
await symlink(sourcePath, targetPath);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/commands/skill.ts
|
|
382
|
+
function registerSkillCommands(program2) {
|
|
383
|
+
const skill = program2.command("skills").alias("skill").description("Manage AI coding skills");
|
|
384
|
+
skill.command("install").alias("i").alias("add").argument("<project>", "Project (/owner/repo)").argument("[skills...]", "Specific skill names to install").option("--all", "Install all skills without prompting").option("--claude", "Install to .claude/skills/ (default)").option("--cursor", "Install to .cursor/skills/").option("--codex", "Install to .codex/skills/").option("--opencode", "Install to .opencode/skill/").option("--amp", "Install to .agents/skills/").option("--antigravity", "Install to .agent/skills/").option("--global", "Install globally instead of project-level").description("Install skills from a project").action(async (project, skillNames, options) => {
|
|
385
|
+
await installCommand(project, skillNames, options);
|
|
386
|
+
});
|
|
387
|
+
skill.command("search").alias("s").argument("<query>", "Search query").description("Search for skills across all indexed projects").action(async (query) => {
|
|
388
|
+
await searchCommand(query);
|
|
389
|
+
});
|
|
390
|
+
skill.command("list").alias("ls").option("--claude", "List from .claude/skills/").option("--cursor", "List from .cursor/skills/").option("--codex", "List from .codex/skills/").option("--opencode", "List from .opencode/skill/").option("--amp", "List from .agents/skills/").option("--antigravity", "List from .agent/skills/").option("--global", "List global skills").description("List installed skills").action(async (options) => {
|
|
391
|
+
await listCommand(options);
|
|
392
|
+
});
|
|
393
|
+
skill.command("remove").alias("rm").alias("delete").argument("<name>", "Skill name to remove").option("--claude", "Remove from .claude/skills/").option("--cursor", "Remove from .cursor/skills/").option("--codex", "Remove from .codex/skills/").option("--opencode", "Remove from .opencode/skill/").option("--amp", "Remove from .agents/skills/").option("--antigravity", "Remove from .agent/skills/").option("--global", "Remove from global skills").description("Remove an installed skill").action(async (name, options) => {
|
|
394
|
+
await removeCommand(name, options);
|
|
395
|
+
});
|
|
396
|
+
skill.command("info").argument("<project>", "Project to show info for (/owner/repo)").description("Show information about skills in a project").action(async (project) => {
|
|
397
|
+
await infoCommand(project);
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
function registerSkillAliases(program2) {
|
|
401
|
+
program2.command("si", { hidden: true }).argument("<project>", "Project (/owner/repo)").argument("[skills...]", "Specific skill names to install").option("--all", "Install all skills without prompting").option("--claude", "Install to .claude/skills/ (default)").option("--cursor", "Install to .cursor/skills/").option("--codex", "Install to .codex/skills/").option("--opencode", "Install to .opencode/skill/").option("--amp", "Install to .agents/skills/").option("--antigravity", "Install to .agent/skills/").option("--global", "Install globally instead of project-level").description("Install skills (alias for: skills install)").action(async (project, skillNames, options) => {
|
|
402
|
+
await installCommand(project, skillNames, options);
|
|
403
|
+
});
|
|
404
|
+
program2.command("ss", { hidden: true }).argument("<query>", "Search query").description("Search for skills (alias for: skills search)").action(async (query) => {
|
|
405
|
+
await searchCommand(query);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
async function installCommand(input, skillNames, options) {
|
|
409
|
+
const parsed = parseSkillInput(input);
|
|
410
|
+
const project = `/${parsed.owner}/${parsed.repo}`;
|
|
411
|
+
log.blank();
|
|
412
|
+
const spinner = ora(`Fetching skills from ${project}...`).start();
|
|
413
|
+
const data = await listProjectSkills(project);
|
|
414
|
+
if (data.error) {
|
|
415
|
+
spinner.fail(pc3.red(`Error: ${data.message || data.error}`));
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
if (!data.skills || data.skills.length === 0) {
|
|
419
|
+
spinner.warn(pc3.yellow(`No skills found in ${project}`));
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const skillsWithProject = data.skills.map((s) => ({ ...s, project }));
|
|
423
|
+
let selectedSkills;
|
|
424
|
+
if (skillNames.length > 0) {
|
|
425
|
+
selectedSkills = skillsWithProject.filter(
|
|
426
|
+
(s) => skillNames.some((name) => s.name.toLowerCase() === name.toLowerCase())
|
|
427
|
+
);
|
|
428
|
+
const foundNames = selectedSkills.map((s) => s.name.toLowerCase());
|
|
429
|
+
const notFound = skillNames.filter((name) => !foundNames.includes(name.toLowerCase()));
|
|
430
|
+
if (selectedSkills.length === 0) {
|
|
431
|
+
spinner.fail(pc3.red(`Skills not found: ${skillNames.join(", ")}`));
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
spinner.succeed(`Found ${selectedSkills.length} of ${skillNames.length} requested skill(s)`);
|
|
435
|
+
if (notFound.length > 0) {
|
|
436
|
+
log.warn(`Not found: ${notFound.join(", ")}`);
|
|
437
|
+
}
|
|
438
|
+
} else if (options.all || data.skills.length === 1) {
|
|
439
|
+
spinner.succeed(`Found ${data.skills.length} skill(s)`);
|
|
440
|
+
selectedSkills = skillsWithProject;
|
|
441
|
+
} else {
|
|
442
|
+
spinner.succeed(`Found ${data.skills.length} skill(s)`);
|
|
443
|
+
const maxNameLen = Math.min(25, Math.max(...data.skills.map((s) => s.name.length)));
|
|
444
|
+
const choices = skillsWithProject.map((s) => {
|
|
445
|
+
const paddedName = s.name.padEnd(maxNameLen);
|
|
446
|
+
const desc = s.description?.trim() ? s.description.slice(0, 60) + (s.description.length > 60 ? "..." : "") : "No description";
|
|
447
|
+
return {
|
|
448
|
+
name: `${paddedName} ${pc3.dim(desc)}`,
|
|
449
|
+
value: s,
|
|
450
|
+
short: s.name
|
|
451
|
+
};
|
|
452
|
+
});
|
|
453
|
+
log.blank();
|
|
454
|
+
try {
|
|
455
|
+
selectedSkills = await checkbox2({
|
|
456
|
+
message: "Select skills to install (space to select, enter to confirm):",
|
|
457
|
+
choices,
|
|
458
|
+
pageSize: 15
|
|
459
|
+
});
|
|
460
|
+
} catch {
|
|
461
|
+
log.warn("Installation cancelled");
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (selectedSkills.length === 0) {
|
|
466
|
+
log.warn("No skills selected");
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const targets = await promptForInstallTargets(options);
|
|
470
|
+
if (!targets) {
|
|
471
|
+
log.warn("Installation cancelled");
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const targetDirs = getTargetDirs(targets);
|
|
475
|
+
const installSpinner = ora("Installing skills...").start();
|
|
476
|
+
let installedCount = 0;
|
|
477
|
+
let permissionError = false;
|
|
478
|
+
const failedDirs = /* @__PURE__ */ new Set();
|
|
479
|
+
for (const skill of selectedSkills) {
|
|
480
|
+
try {
|
|
481
|
+
installSpinner.text = `Downloading ${skill.name}...`;
|
|
482
|
+
const downloadData = await downloadSkill(skill.project, skill.name);
|
|
483
|
+
if (downloadData.error) {
|
|
484
|
+
log.warn(`Failed to download ${skill.name}: ${downloadData.error}`);
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
installSpinner.text = `Installing ${skill.name}...`;
|
|
488
|
+
const [primaryDir2, ...symlinkDirs] = targetDirs;
|
|
489
|
+
try {
|
|
490
|
+
await installSkillFiles(skill.name, downloadData.files, primaryDir2);
|
|
491
|
+
} catch (dirErr) {
|
|
492
|
+
const error = dirErr;
|
|
493
|
+
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
494
|
+
permissionError = true;
|
|
495
|
+
failedDirs.add(primaryDir2);
|
|
496
|
+
}
|
|
497
|
+
throw dirErr;
|
|
498
|
+
}
|
|
499
|
+
const primarySkillDir = join3(primaryDir2, skill.name);
|
|
500
|
+
for (const targetDir of symlinkDirs) {
|
|
501
|
+
try {
|
|
502
|
+
await symlinkSkill(skill.name, primarySkillDir, targetDir);
|
|
503
|
+
} catch (dirErr) {
|
|
504
|
+
const error = dirErr;
|
|
505
|
+
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
506
|
+
permissionError = true;
|
|
507
|
+
failedDirs.add(targetDir);
|
|
508
|
+
}
|
|
509
|
+
throw dirErr;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
installedCount++;
|
|
513
|
+
} catch (err) {
|
|
514
|
+
const error = err;
|
|
515
|
+
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
519
|
+
log.warn(`Failed to install ${skill.name}: ${errMsg}`);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (permissionError) {
|
|
523
|
+
installSpinner.fail("Permission denied");
|
|
524
|
+
log.blank();
|
|
525
|
+
log.warn("Fix permissions with:");
|
|
526
|
+
for (const dir of failedDirs) {
|
|
527
|
+
const parentDir = join3(dir, "..");
|
|
528
|
+
log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
|
|
529
|
+
}
|
|
530
|
+
log.blank();
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
installSpinner.succeed(`Installed ${installedCount} skill(s)`);
|
|
534
|
+
const [primaryDir, ...symlinkedDirs] = targetDirs;
|
|
535
|
+
const installedNames = selectedSkills.map((s) => s.name);
|
|
536
|
+
log.blank();
|
|
537
|
+
log.dim(primaryDir);
|
|
538
|
+
for (const name of installedNames) {
|
|
539
|
+
log.itemAdd(name);
|
|
540
|
+
}
|
|
541
|
+
for (const dir of symlinkedDirs) {
|
|
542
|
+
log.dim(dir);
|
|
543
|
+
for (const name of installedNames) {
|
|
544
|
+
log.itemAdd(name);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
log.blank();
|
|
548
|
+
}
|
|
549
|
+
async function searchCommand(query) {
|
|
550
|
+
log.blank();
|
|
551
|
+
const spinner = ora(`Searching for "${query}"...`).start();
|
|
552
|
+
let data;
|
|
553
|
+
try {
|
|
554
|
+
data = await searchSkills(query);
|
|
555
|
+
} catch (err) {
|
|
556
|
+
spinner.fail(pc3.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (data.error) {
|
|
560
|
+
spinner.fail(pc3.red(`Error: ${data.message || data.error}`));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (!data.results || data.results.length === 0) {
|
|
564
|
+
spinner.warn(pc3.yellow(`No skills found matching "${query}"`));
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
spinner.succeed(`Found ${data.results.length} skill(s)`);
|
|
568
|
+
const groupedByProject = data.results.reduce(
|
|
569
|
+
(acc, skill) => {
|
|
570
|
+
if (!acc[skill.project]) acc[skill.project] = [];
|
|
571
|
+
acc[skill.project].push(skill);
|
|
572
|
+
return acc;
|
|
573
|
+
},
|
|
574
|
+
{}
|
|
575
|
+
);
|
|
576
|
+
const choices = [];
|
|
577
|
+
const allSkills = data.results;
|
|
578
|
+
const maxNameLen = Math.min(25, Math.max(...allSkills.map((s) => s.name.length)));
|
|
579
|
+
for (const [project, skills] of Object.entries(groupedByProject)) {
|
|
580
|
+
choices.push(new Separator(`
|
|
581
|
+
${pc3.bold(pc3.cyan(project))} ${pc3.dim(`(${skills.length})`)}`));
|
|
582
|
+
if (skills.length > 1) {
|
|
583
|
+
const skillNames = skills.map((s) => s.name).join(", ");
|
|
584
|
+
choices.push({
|
|
585
|
+
name: pc3.italic(` \u21B3 Select all ${skills.length} skills from this repo`),
|
|
586
|
+
value: { _selectAll: project },
|
|
587
|
+
short: skillNames
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
for (const s of skills) {
|
|
591
|
+
const paddedName = s.name.padEnd(maxNameLen);
|
|
592
|
+
const desc = s.description?.trim() ? s.description.slice(0, 60) + (s.description.length > 60 ? "..." : "") : "No description";
|
|
593
|
+
choices.push({
|
|
594
|
+
name: `${paddedName} ${pc3.dim(desc)}`,
|
|
595
|
+
value: s
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
log.blank();
|
|
600
|
+
let rawSelection;
|
|
601
|
+
try {
|
|
602
|
+
rawSelection = await checkbox2({
|
|
603
|
+
message: "Select skills to install (space to select, enter to confirm):",
|
|
604
|
+
choices,
|
|
605
|
+
pageSize: 18
|
|
606
|
+
});
|
|
607
|
+
} catch {
|
|
608
|
+
log.warn("Installation cancelled");
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const selectedSkills = [];
|
|
612
|
+
for (const item of rawSelection) {
|
|
613
|
+
if ("_selectAll" in item) {
|
|
614
|
+
selectedSkills.push(...groupedByProject[item._selectAll]);
|
|
615
|
+
} else {
|
|
616
|
+
selectedSkills.push(item);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
const uniqueSkills = [
|
|
620
|
+
...new Map(selectedSkills.map((s) => [`${s.project}:${s.name}`, s])).values()
|
|
621
|
+
];
|
|
622
|
+
if (uniqueSkills.length === 0) {
|
|
623
|
+
log.warn("No skills selected");
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const targets = await promptForInstallTargets({});
|
|
627
|
+
if (!targets) {
|
|
628
|
+
log.warn("Installation cancelled");
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
const targetDirs = getTargetDirs(targets);
|
|
632
|
+
const installSpinner = ora("Installing skills...").start();
|
|
633
|
+
let installedCount = 0;
|
|
634
|
+
let permissionError = false;
|
|
635
|
+
const failedDirs = /* @__PURE__ */ new Set();
|
|
636
|
+
for (const skill of uniqueSkills) {
|
|
637
|
+
try {
|
|
638
|
+
installSpinner.text = `Downloading ${skill.name}...`;
|
|
639
|
+
const downloadData = await downloadSkill(skill.project, skill.name);
|
|
640
|
+
if (downloadData.error) {
|
|
641
|
+
log.warn(`Failed to download ${skill.name}: ${downloadData.error}`);
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
installSpinner.text = `Installing ${skill.name}...`;
|
|
645
|
+
const [primaryDir2, ...symlinkDirs] = targetDirs;
|
|
646
|
+
try {
|
|
647
|
+
await installSkillFiles(skill.name, downloadData.files, primaryDir2);
|
|
648
|
+
} catch (dirErr) {
|
|
649
|
+
const error = dirErr;
|
|
650
|
+
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
651
|
+
permissionError = true;
|
|
652
|
+
failedDirs.add(primaryDir2);
|
|
653
|
+
}
|
|
654
|
+
throw dirErr;
|
|
655
|
+
}
|
|
656
|
+
const primarySkillDir = join3(primaryDir2, skill.name);
|
|
657
|
+
for (const targetDir of symlinkDirs) {
|
|
658
|
+
try {
|
|
659
|
+
await symlinkSkill(skill.name, primarySkillDir, targetDir);
|
|
660
|
+
} catch (dirErr) {
|
|
661
|
+
const error = dirErr;
|
|
662
|
+
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
663
|
+
permissionError = true;
|
|
664
|
+
failedDirs.add(targetDir);
|
|
665
|
+
}
|
|
666
|
+
throw dirErr;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
installedCount++;
|
|
670
|
+
} catch (err) {
|
|
671
|
+
const error = err;
|
|
672
|
+
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
676
|
+
log.warn(`Failed to install ${skill.name}: ${errMsg}`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
if (permissionError) {
|
|
680
|
+
installSpinner.fail("Permission denied");
|
|
681
|
+
log.blank();
|
|
682
|
+
log.warn("Fix permissions with:");
|
|
683
|
+
for (const dir of failedDirs) {
|
|
684
|
+
const parentDir = join3(dir, "..");
|
|
685
|
+
log.dim(` sudo chown -R $(whoami) "${parentDir}"`);
|
|
686
|
+
}
|
|
687
|
+
log.blank();
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
installSpinner.succeed(`Installed ${installedCount} skill(s)`);
|
|
691
|
+
const [primaryDir, ...symlinkedDirs] = targetDirs;
|
|
692
|
+
const installedNames = uniqueSkills.map((s) => s.name);
|
|
693
|
+
log.blank();
|
|
694
|
+
log.dim(primaryDir);
|
|
695
|
+
for (const name of installedNames) {
|
|
696
|
+
log.itemAdd(name);
|
|
697
|
+
}
|
|
698
|
+
for (const dir of symlinkedDirs) {
|
|
699
|
+
log.dim(dir);
|
|
700
|
+
for (const name of installedNames) {
|
|
701
|
+
log.itemAdd(name);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
log.blank();
|
|
705
|
+
}
|
|
706
|
+
async function listCommand(options) {
|
|
707
|
+
const target = await promptForSingleTarget(options);
|
|
708
|
+
if (!target) {
|
|
709
|
+
log.warn("Cancelled");
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
const skillsDir = getTargetDirFromSelection(target.ide, target.scope);
|
|
713
|
+
try {
|
|
714
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
715
|
+
const skillFolders = entries.filter((e) => e.isDirectory());
|
|
716
|
+
if (skillFolders.length === 0) {
|
|
717
|
+
log.warn(`No skills installed in ${skillsDir}`);
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
log.info(`
|
|
721
|
+
\u25C6 Installed skills (${skillsDir}):`);
|
|
722
|
+
for (const folder of skillFolders) {
|
|
723
|
+
log.item(folder.name);
|
|
724
|
+
}
|
|
725
|
+
log.success(`${skillFolders.length} skill(s) installed
|
|
726
|
+
`);
|
|
727
|
+
} catch {
|
|
728
|
+
log.warn(`No skills directory found at ${skillsDir}`);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
async function removeCommand(name, options) {
|
|
732
|
+
const target = await promptForSingleTarget(options);
|
|
733
|
+
if (!target) {
|
|
734
|
+
log.warn("Cancelled");
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const skillsDir = getTargetDirFromSelection(target.ide, target.scope);
|
|
738
|
+
const skillPath = join3(skillsDir, name);
|
|
739
|
+
try {
|
|
740
|
+
await rm2(skillPath, { recursive: true });
|
|
741
|
+
log.success(`Removed skill: ${name}`);
|
|
742
|
+
} catch (err) {
|
|
743
|
+
const error = err;
|
|
744
|
+
if (error.code === "ENOENT") {
|
|
745
|
+
log.error(`Skill not found: ${name}`);
|
|
746
|
+
} else if (error.code === "EACCES" || error.code === "EPERM") {
|
|
747
|
+
log.error(`Permission denied. Try: sudo rm -rf "${skillPath}"`);
|
|
748
|
+
} else {
|
|
749
|
+
log.error(`Failed to remove skill: ${error.message}`);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
async function infoCommand(input) {
|
|
754
|
+
const parsed = parseSkillInput(input);
|
|
755
|
+
const project = `/${parsed.owner}/${parsed.repo}`;
|
|
756
|
+
log.blank();
|
|
757
|
+
const spinner = ora(`Fetching skills from ${project}...`).start();
|
|
758
|
+
const data = await listProjectSkills(project);
|
|
759
|
+
if (data.error) {
|
|
760
|
+
spinner.fail(pc3.red(`Error: ${data.message || data.error}`));
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
if (!data.skills || data.skills.length === 0) {
|
|
764
|
+
spinner.warn(pc3.yellow(`No skills found in ${project}`));
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
spinner.succeed(`Found ${data.skills.length} skill(s)`);
|
|
768
|
+
log.blank();
|
|
769
|
+
for (const skill of data.skills) {
|
|
770
|
+
log.item(skill.name);
|
|
771
|
+
log.dim(` ${skill.description || "No description"}`);
|
|
772
|
+
log.dim(` URL: ${skill.url}`);
|
|
773
|
+
log.blank();
|
|
774
|
+
}
|
|
775
|
+
log.plain(
|
|
776
|
+
`${pc3.bold("Quick commands:")}
|
|
777
|
+
Install all: ${pc3.cyan(`ctx7 skills install ${project} --all`)}
|
|
778
|
+
Install one: ${pc3.cyan(`ctx7 skills install ${project} ${data.skills[0]?.name}`)}
|
|
779
|
+
`
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// src/constants.ts
|
|
784
|
+
import { readFileSync } from "fs";
|
|
785
|
+
import { fileURLToPath } from "url";
|
|
786
|
+
import { dirname, join as join4 } from "path";
|
|
787
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
788
|
+
var pkg = JSON.parse(readFileSync(join4(__dirname, "../package.json"), "utf-8"));
|
|
789
|
+
var VERSION = pkg.version;
|
|
790
|
+
var NAME = pkg.name;
|
|
791
|
+
|
|
792
|
+
// src/index.ts
|
|
793
|
+
var brand = {
|
|
794
|
+
primary: pc4.green,
|
|
795
|
+
dim: pc4.dim
|
|
796
|
+
};
|
|
797
|
+
var program = new Command();
|
|
798
|
+
program.name("ctx7").description("Context7 CLI - Manage AI coding skills and documentation context").version(VERSION).option("--base-url <url>").hook("preAction", (thisCommand) => {
|
|
799
|
+
const opts = thisCommand.opts();
|
|
800
|
+
if (opts.baseUrl) {
|
|
801
|
+
setBaseUrl(opts.baseUrl);
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
registerSkillCommands(program);
|
|
805
|
+
registerSkillAliases(program);
|
|
806
|
+
program.action(() => {
|
|
807
|
+
console.log("");
|
|
808
|
+
const banner = figlet.textSync("Context7", { font: "ANSI Shadow" });
|
|
809
|
+
console.log(brand.primary(banner));
|
|
810
|
+
console.log(brand.dim(" The open agent skills ecosystem"));
|
|
811
|
+
console.log("");
|
|
812
|
+
console.log(" Commands:");
|
|
813
|
+
console.log(
|
|
814
|
+
brand.primary(" ctx7 skills install <input>") + " Install skills from a repository"
|
|
815
|
+
);
|
|
816
|
+
console.log(brand.primary(" ctx7 skills search <query>") + " Search for skills");
|
|
817
|
+
console.log(brand.primary(" ctx7 skills list") + " List installed skills");
|
|
818
|
+
console.log(brand.primary(" ctx7 skills info <repo>") + " Show skill information");
|
|
819
|
+
console.log(brand.primary(" ctx7 skills remove <name>") + " Remove a skill");
|
|
820
|
+
console.log("");
|
|
821
|
+
console.log(" Examples:");
|
|
822
|
+
console.log(brand.dim(" ctx7 skills install /anthropics/skills"));
|
|
823
|
+
console.log(brand.dim(" ctx7 skills install /anthropics/skills pdf --cursor"));
|
|
824
|
+
console.log(brand.dim(" ctx7 skills search pdf"));
|
|
825
|
+
console.log("");
|
|
826
|
+
console.log(` Run ${brand.primary("ctx7 --help")} for more information`);
|
|
827
|
+
console.log("");
|
|
828
|
+
});
|
|
829
|
+
program.parse();
|
|
830
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/skill.ts","../src/utils/parse-input.ts","../src/utils/github.ts","../src/utils/api.ts","../src/utils/logger.ts","../src/utils/ide.ts","../src/types.ts","../src/utils/installer.ts","../src/constants.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport figlet from \"figlet\";\nimport { registerSkillCommands, registerSkillAliases } from \"./commands/skill.js\";\nimport { setBaseUrl } from \"./utils/api.js\";\nimport { VERSION } from \"./constants.js\";\n\nconst brand = {\n primary: pc.green,\n dim: pc.dim,\n};\n\nconst program = new Command();\n\nprogram\n .name(\"ctx7\")\n .description(\"Context7 CLI - Manage AI coding skills and documentation context\")\n .version(VERSION)\n .option(\"--base-url <url>\")\n .hook(\"preAction\", (thisCommand) => {\n const opts = thisCommand.opts();\n if (opts.baseUrl) {\n setBaseUrl(opts.baseUrl);\n }\n });\n\nregisterSkillCommands(program);\nregisterSkillAliases(program);\n\nprogram.action(() => {\n console.log(\"\");\n const banner = figlet.textSync(\"Context7\", { font: \"ANSI Shadow\" });\n console.log(brand.primary(banner));\n console.log(brand.dim(\" The open agent skills ecosystem\"));\n console.log(\"\");\n console.log(\" Commands:\");\n console.log(\n brand.primary(\" ctx7 skills install <input>\") + \" Install skills from a repository\"\n );\n console.log(brand.primary(\" ctx7 skills search <query>\") + \" Search for skills\");\n console.log(brand.primary(\" ctx7 skills list\") + \" List installed skills\");\n console.log(brand.primary(\" ctx7 skills info <repo>\") + \" Show skill information\");\n console.log(brand.primary(\" ctx7 skills remove <name>\") + \" Remove a skill\");\n console.log(\"\");\n console.log(\" Examples:\");\n console.log(brand.dim(\" ctx7 skills install /anthropics/skills\"));\n console.log(brand.dim(\" ctx7 skills install /anthropics/skills pdf --cursor\"));\n console.log(brand.dim(\" ctx7 skills search pdf\"));\n console.log(\"\");\n console.log(` Run ${brand.primary(\"ctx7 --help\")} for more information`);\n console.log(\"\");\n});\n\nprogram.parse();\n","import { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport { checkbox, Separator } from \"@inquirer/prompts\";\nimport ora from \"ora\";\nimport { readdir, rm } from \"fs/promises\";\nimport { join } from \"path\";\n\nimport { parseSkillInput } from \"../utils/parse-input.js\";\nimport { listProjectSkills, searchSkills, downloadSkill } from \"../utils/api.js\";\nimport { log } from \"../utils/logger.js\";\nimport {\n promptForInstallTargets,\n promptForSingleTarget,\n getTargetDirs,\n getTargetDirFromSelection,\n} from \"../utils/ide.js\";\nimport { installSkillFiles, symlinkSkill } from \"../utils/installer.js\";\nimport type { Skill, SkillSearchResult, AddOptions, ListOptions, RemoveOptions } from \"../types.js\";\n\nexport function registerSkillCommands(program: Command): void {\n const skill = program.command(\"skills\").alias(\"skill\").description(\"Manage AI coding skills\");\n\n skill\n .command(\"install\")\n .alias(\"i\")\n .alias(\"add\")\n .argument(\"<project>\", \"Project (/owner/repo)\")\n .argument(\"[skills...]\", \"Specific skill names to install\")\n .option(\"--all\", \"Install all skills without prompting\")\n .option(\"--claude\", \"Install to .claude/skills/ (default)\")\n .option(\"--cursor\", \"Install to .cursor/skills/\")\n .option(\"--codex\", \"Install to .codex/skills/\")\n .option(\"--opencode\", \"Install to .opencode/skill/\")\n .option(\"--amp\", \"Install to .agents/skills/\")\n .option(\"--antigravity\", \"Install to .agent/skills/\")\n .option(\"--global\", \"Install globally instead of project-level\")\n .description(\"Install skills from a project\")\n .action(async (project: string, skillNames: string[], options: AddOptions) => {\n await installCommand(project, skillNames, options);\n });\n\n skill\n .command(\"search\")\n .alias(\"s\")\n .argument(\"<query>\", \"Search query\")\n .description(\"Search for skills across all indexed projects\")\n .action(async (query: string) => {\n await searchCommand(query);\n });\n\n skill\n .command(\"list\")\n .alias(\"ls\")\n .option(\"--claude\", \"List from .claude/skills/\")\n .option(\"--cursor\", \"List from .cursor/skills/\")\n .option(\"--codex\", \"List from .codex/skills/\")\n .option(\"--opencode\", \"List from .opencode/skill/\")\n .option(\"--amp\", \"List from .agents/skills/\")\n .option(\"--antigravity\", \"List from .agent/skills/\")\n .option(\"--global\", \"List global skills\")\n .description(\"List installed skills\")\n .action(async (options: ListOptions) => {\n await listCommand(options);\n });\n\n skill\n .command(\"remove\")\n .alias(\"rm\")\n .alias(\"delete\")\n .argument(\"<name>\", \"Skill name to remove\")\n .option(\"--claude\", \"Remove from .claude/skills/\")\n .option(\"--cursor\", \"Remove from .cursor/skills/\")\n .option(\"--codex\", \"Remove from .codex/skills/\")\n .option(\"--opencode\", \"Remove from .opencode/skill/\")\n .option(\"--amp\", \"Remove from .agents/skills/\")\n .option(\"--antigravity\", \"Remove from .agent/skills/\")\n .option(\"--global\", \"Remove from global skills\")\n .description(\"Remove an installed skill\")\n .action(async (name: string, options: RemoveOptions) => {\n await removeCommand(name, options);\n });\n\n skill\n .command(\"info\")\n .argument(\"<project>\", \"Project to show info for (/owner/repo)\")\n .description(\"Show information about skills in a project\")\n .action(async (project: string) => {\n await infoCommand(project);\n });\n}\n\nexport function registerSkillAliases(program: Command): void {\n program\n .command(\"si\", { hidden: true })\n .argument(\"<project>\", \"Project (/owner/repo)\")\n .argument(\"[skills...]\", \"Specific skill names to install\")\n .option(\"--all\", \"Install all skills without prompting\")\n .option(\"--claude\", \"Install to .claude/skills/ (default)\")\n .option(\"--cursor\", \"Install to .cursor/skills/\")\n .option(\"--codex\", \"Install to .codex/skills/\")\n .option(\"--opencode\", \"Install to .opencode/skill/\")\n .option(\"--amp\", \"Install to .agents/skills/\")\n .option(\"--antigravity\", \"Install to .agent/skills/\")\n .option(\"--global\", \"Install globally instead of project-level\")\n .description(\"Install skills (alias for: skills install)\")\n .action(async (project: string, skillNames: string[], options: AddOptions) => {\n await installCommand(project, skillNames, options);\n });\n\n program\n .command(\"ss\", { hidden: true })\n .argument(\"<query>\", \"Search query\")\n .description(\"Search for skills (alias for: skills search)\")\n .action(async (query: string) => {\n await searchCommand(query);\n });\n}\n\nasync function installCommand(\n input: string,\n skillNames: string[],\n options: AddOptions\n): Promise<void> {\n const parsed = parseSkillInput(input);\n const project = `/${parsed.owner}/${parsed.repo}`;\n\n log.blank();\n const spinner = ora(`Fetching skills from ${project}...`).start();\n\n const data = await listProjectSkills(project);\n\n if (data.error) {\n spinner.fail(pc.red(`Error: ${data.message || data.error}`));\n return;\n }\n\n if (!data.skills || data.skills.length === 0) {\n spinner.warn(pc.yellow(`No skills found in ${project}`));\n return;\n }\n\n const skillsWithProject = data.skills.map((s) => ({ ...s, project }));\n\n let selectedSkills: (Skill & { project: string })[];\n\n if (skillNames.length > 0) {\n selectedSkills = skillsWithProject.filter((s) =>\n skillNames.some((name) => s.name.toLowerCase() === name.toLowerCase())\n );\n\n const foundNames = selectedSkills.map((s) => s.name.toLowerCase());\n const notFound = skillNames.filter((name) => !foundNames.includes(name.toLowerCase()));\n\n if (selectedSkills.length === 0) {\n spinner.fail(pc.red(`Skills not found: ${skillNames.join(\", \")}`));\n return;\n }\n\n spinner.succeed(`Found ${selectedSkills.length} of ${skillNames.length} requested skill(s)`);\n\n if (notFound.length > 0) {\n log.warn(`Not found: ${notFound.join(\", \")}`);\n }\n } else if (options.all || data.skills.length === 1) {\n spinner.succeed(`Found ${data.skills.length} skill(s)`);\n selectedSkills = skillsWithProject;\n } else {\n spinner.succeed(`Found ${data.skills.length} skill(s)`);\n const maxNameLen = Math.min(25, Math.max(...data.skills.map((s) => s.name.length)));\n const choices = skillsWithProject.map((s) => {\n const paddedName = s.name.padEnd(maxNameLen);\n const desc = s.description?.trim()\n ? s.description.slice(0, 60) + (s.description.length > 60 ? \"...\" : \"\")\n : \"No description\";\n\n return {\n name: `${paddedName} ${pc.dim(desc)}`,\n value: s,\n short: s.name,\n };\n });\n\n log.blank();\n\n try {\n selectedSkills = await checkbox({\n message: \"Select skills to install (space to select, enter to confirm):\",\n choices,\n pageSize: 15,\n });\n } catch {\n log.warn(\"Installation cancelled\");\n return;\n }\n }\n\n if (selectedSkills.length === 0) {\n log.warn(\"No skills selected\");\n return;\n }\n\n const targets = await promptForInstallTargets(options);\n if (!targets) {\n log.warn(\"Installation cancelled\");\n return;\n }\n\n const targetDirs = getTargetDirs(targets);\n\n const installSpinner = ora(\"Installing skills...\").start();\n\n let installedCount = 0;\n let permissionError = false;\n const failedDirs: Set<string> = new Set();\n\n for (const skill of selectedSkills) {\n try {\n installSpinner.text = `Downloading ${skill.name}...`;\n const downloadData = await downloadSkill(skill.project, skill.name);\n\n if (downloadData.error) {\n log.warn(`Failed to download ${skill.name}: ${downloadData.error}`);\n continue;\n }\n\n installSpinner.text = `Installing ${skill.name}...`;\n\n const [primaryDir, ...symlinkDirs] = targetDirs;\n\n try {\n await installSkillFiles(skill.name, downloadData.files, primaryDir);\n } catch (dirErr) {\n const error = dirErr as NodeJS.ErrnoException;\n if (error.code === \"EACCES\" || error.code === \"EPERM\") {\n permissionError = true;\n failedDirs.add(primaryDir);\n }\n throw dirErr;\n }\n\n const primarySkillDir = join(primaryDir, skill.name);\n for (const targetDir of symlinkDirs) {\n try {\n await symlinkSkill(skill.name, primarySkillDir, targetDir);\n } catch (dirErr) {\n const error = dirErr as NodeJS.ErrnoException;\n if (error.code === \"EACCES\" || error.code === \"EPERM\") {\n permissionError = true;\n failedDirs.add(targetDir);\n }\n throw dirErr;\n }\n }\n\n installedCount++;\n } catch (err) {\n const error = err as NodeJS.ErrnoException;\n if (error.code === \"EACCES\" || error.code === \"EPERM\") {\n continue;\n }\n const errMsg = err instanceof Error ? err.message : String(err);\n log.warn(`Failed to install ${skill.name}: ${errMsg}`);\n }\n }\n\n if (permissionError) {\n installSpinner.fail(\"Permission denied\");\n log.blank();\n log.warn(\"Fix permissions with:\");\n for (const dir of failedDirs) {\n const parentDir = join(dir, \"..\");\n log.dim(` sudo chown -R $(whoami) \"${parentDir}\"`);\n }\n log.blank();\n return;\n }\n\n installSpinner.succeed(`Installed ${installedCount} skill(s)`);\n\n const [primaryDir, ...symlinkedDirs] = targetDirs;\n const installedNames = selectedSkills.map((s) => s.name);\n\n log.blank();\n log.dim(primaryDir);\n for (const name of installedNames) {\n log.itemAdd(name);\n }\n\n for (const dir of symlinkedDirs) {\n log.dim(dir);\n for (const name of installedNames) {\n log.itemAdd(name);\n }\n }\n\n log.blank();\n}\n\nasync function searchCommand(query: string): Promise<void> {\n log.blank();\n const spinner = ora(`Searching for \"${query}\"...`).start();\n\n let data;\n try {\n data = await searchSkills(query);\n } catch (err) {\n spinner.fail(pc.red(`Error: ${err instanceof Error ? err.message : String(err)}`));\n return;\n }\n\n if (data.error) {\n spinner.fail(pc.red(`Error: ${data.message || data.error}`));\n return;\n }\n\n if (!data.results || data.results.length === 0) {\n spinner.warn(pc.yellow(`No skills found matching \"${query}\"`));\n return;\n }\n\n spinner.succeed(`Found ${data.results.length} skill(s)`);\n\n const groupedByProject = data.results.reduce(\n (acc, skill) => {\n if (!acc[skill.project]) acc[skill.project] = [];\n acc[skill.project].push(skill);\n return acc;\n },\n {} as Record<string, SkillSearchResult[]>\n );\n\n type ChoiceValue = SkillSearchResult | { _selectAll: string };\n const choices: Array<{ name: string; value: ChoiceValue; short?: string } | Separator> = [];\n const allSkills = data.results;\n const maxNameLen = Math.min(25, Math.max(...allSkills.map((s) => s.name.length)));\n\n for (const [project, skills] of Object.entries(groupedByProject)) {\n choices.push(new Separator(`\\n${pc.bold(pc.cyan(project))} ${pc.dim(`(${skills.length})`)}`));\n\n if (skills.length > 1) {\n const skillNames = skills.map((s) => s.name).join(\", \");\n choices.push({\n name: pc.italic(` ↳ Select all ${skills.length} skills from this repo`),\n value: { _selectAll: project },\n short: skillNames,\n });\n }\n\n for (const s of skills) {\n const paddedName = s.name.padEnd(maxNameLen);\n const desc = s.description?.trim()\n ? s.description.slice(0, 60) + (s.description.length > 60 ? \"...\" : \"\")\n : \"No description\";\n\n choices.push({\n name: `${paddedName} ${pc.dim(desc)}`,\n value: s,\n });\n }\n }\n\n log.blank();\n\n let rawSelection: ChoiceValue[];\n try {\n rawSelection = await checkbox({\n message: \"Select skills to install (space to select, enter to confirm):\",\n choices,\n pageSize: 18,\n });\n } catch {\n log.warn(\"Installation cancelled\");\n return;\n }\n\n const selectedSkills: SkillSearchResult[] = [];\n for (const item of rawSelection) {\n if (\"_selectAll\" in item) {\n selectedSkills.push(...groupedByProject[item._selectAll]);\n } else {\n selectedSkills.push(item);\n }\n }\n\n const uniqueSkills = [\n ...new Map(selectedSkills.map((s) => [`${s.project}:${s.name}`, s])).values(),\n ];\n\n if (uniqueSkills.length === 0) {\n log.warn(\"No skills selected\");\n return;\n }\n\n const targets = await promptForInstallTargets({});\n if (!targets) {\n log.warn(\"Installation cancelled\");\n return;\n }\n\n const targetDirs = getTargetDirs(targets);\n\n const installSpinner = ora(\"Installing skills...\").start();\n\n let installedCount = 0;\n let permissionError = false;\n const failedDirs: Set<string> = new Set();\n\n for (const skill of uniqueSkills) {\n try {\n installSpinner.text = `Downloading ${skill.name}...`;\n const downloadData = await downloadSkill(skill.project, skill.name);\n\n if (downloadData.error) {\n log.warn(`Failed to download ${skill.name}: ${downloadData.error}`);\n continue;\n }\n\n installSpinner.text = `Installing ${skill.name}...`;\n\n const [primaryDir, ...symlinkDirs] = targetDirs;\n\n try {\n await installSkillFiles(skill.name, downloadData.files, primaryDir);\n } catch (dirErr) {\n const error = dirErr as NodeJS.ErrnoException;\n if (error.code === \"EACCES\" || error.code === \"EPERM\") {\n permissionError = true;\n failedDirs.add(primaryDir);\n }\n throw dirErr;\n }\n\n const primarySkillDir = join(primaryDir, skill.name);\n for (const targetDir of symlinkDirs) {\n try {\n await symlinkSkill(skill.name, primarySkillDir, targetDir);\n } catch (dirErr) {\n const error = dirErr as NodeJS.ErrnoException;\n if (error.code === \"EACCES\" || error.code === \"EPERM\") {\n permissionError = true;\n failedDirs.add(targetDir);\n }\n throw dirErr;\n }\n }\n\n installedCount++;\n } catch (err) {\n const error = err as NodeJS.ErrnoException;\n if (error.code === \"EACCES\" || error.code === \"EPERM\") {\n continue;\n }\n const errMsg = err instanceof Error ? err.message : String(err);\n log.warn(`Failed to install ${skill.name}: ${errMsg}`);\n }\n }\n\n if (permissionError) {\n installSpinner.fail(\"Permission denied\");\n log.blank();\n log.warn(\"Fix permissions with:\");\n for (const dir of failedDirs) {\n const parentDir = join(dir, \"..\");\n log.dim(` sudo chown -R $(whoami) \"${parentDir}\"`);\n }\n log.blank();\n return;\n }\n\n installSpinner.succeed(`Installed ${installedCount} skill(s)`);\n\n const [primaryDir, ...symlinkedDirs] = targetDirs;\n const installedNames = uniqueSkills.map((s) => s.name);\n\n log.blank();\n log.dim(primaryDir);\n for (const name of installedNames) {\n log.itemAdd(name);\n }\n\n for (const dir of symlinkedDirs) {\n log.dim(dir);\n for (const name of installedNames) {\n log.itemAdd(name);\n }\n }\n\n log.blank();\n}\n\nasync function listCommand(options: ListOptions): Promise<void> {\n const target = await promptForSingleTarget(options);\n if (!target) {\n log.warn(\"Cancelled\");\n return;\n }\n\n const skillsDir = getTargetDirFromSelection(target.ide, target.scope);\n\n try {\n const entries = await readdir(skillsDir, { withFileTypes: true });\n const skillFolders = entries.filter((e) => e.isDirectory());\n\n if (skillFolders.length === 0) {\n log.warn(`No skills installed in ${skillsDir}`);\n return;\n }\n\n log.info(`\\n◆ Installed skills (${skillsDir}):`);\n\n for (const folder of skillFolders) {\n log.item(folder.name);\n }\n\n log.success(`${skillFolders.length} skill(s) installed\\n`);\n } catch {\n log.warn(`No skills directory found at ${skillsDir}`);\n }\n}\n\nasync function removeCommand(name: string, options: RemoveOptions): Promise<void> {\n const target = await promptForSingleTarget(options);\n if (!target) {\n log.warn(\"Cancelled\");\n return;\n }\n\n const skillsDir = getTargetDirFromSelection(target.ide, target.scope);\n const skillPath = join(skillsDir, name);\n\n try {\n await rm(skillPath, { recursive: true });\n log.success(`Removed skill: ${name}`);\n } catch (err) {\n const error = err as NodeJS.ErrnoException;\n if (error.code === \"ENOENT\") {\n log.error(`Skill not found: ${name}`);\n } else if (error.code === \"EACCES\" || error.code === \"EPERM\") {\n log.error(`Permission denied. Try: sudo rm -rf \"${skillPath}\"`);\n } else {\n log.error(`Failed to remove skill: ${error.message}`);\n }\n }\n}\n\nasync function infoCommand(input: string): Promise<void> {\n const parsed = parseSkillInput(input);\n const project = `/${parsed.owner}/${parsed.repo}`;\n\n log.blank();\n const spinner = ora(`Fetching skills from ${project}...`).start();\n\n const data = await listProjectSkills(project);\n\n if (data.error) {\n spinner.fail(pc.red(`Error: ${data.message || data.error}`));\n return;\n }\n\n if (!data.skills || data.skills.length === 0) {\n spinner.warn(pc.yellow(`No skills found in ${project}`));\n return;\n }\n\n spinner.succeed(`Found ${data.skills.length} skill(s)`);\n\n log.blank();\n for (const skill of data.skills) {\n log.item(skill.name);\n log.dim(` ${skill.description || \"No description\"}`);\n log.dim(` URL: ${skill.url}`);\n log.blank();\n }\n\n log.plain(\n `${pc.bold(\"Quick commands:\")}\\n` +\n ` Install all: ${pc.cyan(`ctx7 skills install ${project} --all`)}\\n` +\n ` Install one: ${pc.cyan(`ctx7 skills install ${project} ${data.skills[0]?.name}`)}\\n`\n );\n}\n","export interface ParsedSkillInput {\n type: \"repo\" | \"skill\" | \"url\";\n owner: string;\n repo: string;\n skillName?: string;\n branch?: string;\n path?: string;\n}\n\nexport function parseSkillInput(input: string): ParsedSkillInput {\n const urlMatch = input.match(\n /(?:https?:\\/\\/)?github\\.com\\/([^\\/]+)\\/([^\\/]+)\\/tree\\/([^\\/]+)\\/(.+)/\n );\n if (urlMatch) {\n const [, owner, repo, branch, path] = urlMatch;\n const skillName = path.split(\"/\").pop();\n return { type: \"url\", owner, repo, branch, path, skillName };\n }\n\n const shortMatch = input.match(/^\\/?([^\\/]+)\\/([^\\/]+)(?:\\/(.+))?$/);\n if (shortMatch) {\n const [, owner, repo, skillName] = shortMatch;\n return { type: skillName ? \"skill\" : \"repo\", owner, repo, skillName };\n }\n\n throw new Error(\n `Invalid input format: ${input}\\n` +\n `Expected: /owner/repo, /owner/repo/skill, or full GitHub URL`\n );\n}\n\nexport function buildSkillId(owner: string, repo: string, skillName: string): string {\n return `/${owner}/${repo}/${skillName}`;\n}\n\nexport function buildRepoId(owner: string, repo: string): string {\n return `/${owner}/${repo}`;\n}\n","import type { SkillFile, Skill } from \"../types.js\";\n\nconst GITHUB_API = \"https://api.github.com\";\nconst GITHUB_RAW = \"https://raw.githubusercontent.com\";\n\ninterface GitHubTreeItem {\n path: string;\n mode: string;\n type: \"blob\" | \"tree\";\n sha: string;\n size?: number;\n url: string;\n}\n\ninterface GitHubTreeResponse {\n sha: string;\n url: string;\n tree: GitHubTreeItem[];\n truncated: boolean;\n}\n\nfunction parseGitHubUrl(url: string): {\n owner: string;\n repo: string;\n branch: string;\n path: string;\n} | null {\n try {\n const urlObj = new URL(url);\n const parts = urlObj.pathname.split(\"/\").filter(Boolean);\n\n // Handle raw.githubusercontent.com URLs\n // Format: https://raw.githubusercontent.com/owner/repo/refs/heads/branch/path/SKILL.md\n if (urlObj.hostname === \"raw.githubusercontent.com\") {\n if (parts.length < 5) return null;\n\n const owner = parts[0];\n const repo = parts[1];\n\n // Handle refs/heads/branch format\n if (parts[2] === \"refs\" && parts[3] === \"heads\") {\n const branch = parts[4];\n // Get directory path (exclude the filename like SKILL.md)\n const pathParts = parts.slice(5);\n // Remove the last part if it looks like a file (has extension)\n if (pathParts.length > 0 && pathParts[pathParts.length - 1].includes(\".\")) {\n pathParts.pop();\n }\n const path = pathParts.join(\"/\");\n return { owner, repo, branch, path };\n }\n\n // Handle direct branch format: owner/repo/branch/path\n const branch = parts[2];\n const pathParts = parts.slice(3);\n if (pathParts.length > 0 && pathParts[pathParts.length - 1].includes(\".\")) {\n pathParts.pop();\n }\n const path = pathParts.join(\"/\");\n return { owner, repo, branch, path };\n }\n\n // Handle github.com tree URLs\n // Format: https://github.com/owner/repo/tree/branch/path\n if (urlObj.hostname === \"github.com\") {\n if (parts.length < 4 || parts[2] !== \"tree\") return null;\n\n const owner = parts[0];\n const repo = parts[1];\n const branch = parts[3];\n const path = parts.slice(4).join(\"/\");\n\n return { owner, repo, branch, path };\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\nexport async function downloadSkillFromGitHub(\n skill: Skill & { project: string }\n): Promise<{ files: SkillFile[]; error?: string }> {\n try {\n const parsed = parseGitHubUrl(skill.url);\n\n if (!parsed) {\n return { files: [], error: `Invalid GitHub URL: ${skill.url}` };\n }\n\n const { owner, repo, branch, path: skillPath } = parsed;\n\n const treeUrl = `${GITHUB_API}/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`;\n const treeResponse = await fetch(treeUrl, {\n headers: {\n Accept: \"application/vnd.github.v3+json\",\n \"User-Agent\": \"context7-cli\",\n },\n });\n\n if (!treeResponse.ok) {\n return { files: [], error: `GitHub API error: ${treeResponse.status}` };\n }\n\n const treeData = (await treeResponse.json()) as GitHubTreeResponse;\n\n const skillFiles = treeData.tree.filter(\n (item) => item.type === \"blob\" && item.path.startsWith(skillPath + \"/\")\n );\n\n if (skillFiles.length === 0) {\n return { files: [], error: `No files found in ${skillPath}` };\n }\n\n const files: SkillFile[] = [];\n for (const item of skillFiles) {\n const rawUrl = `${GITHUB_RAW}/${owner}/${repo}/${branch}/${item.path}`;\n const fileResponse = await fetch(rawUrl);\n\n if (!fileResponse.ok) {\n console.warn(`Failed to fetch ${item.path}: ${fileResponse.status}`);\n continue;\n }\n\n const content = await fileResponse.text();\n const relativePath = item.path.slice(skillPath.length + 1);\n\n files.push({\n path: relativePath,\n content,\n });\n }\n\n return { files };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { files: [], error: message };\n }\n}\n","import type {\n ListSkillsResponse,\n SingleSkillResponse,\n SearchResponse,\n DownloadResponse,\n} from \"../types.js\";\nimport { downloadSkillFromGitHub } from \"./github.js\";\n\nlet baseUrl = \"https://context7.com\";\n\nexport function setBaseUrl(url: string): void {\n baseUrl = url;\n}\n\nexport async function listProjectSkills(project: string): Promise<ListSkillsResponse> {\n const params = new URLSearchParams({ project });\n const response = await fetch(`${baseUrl}/api/v2/skills?${params}`);\n return (await response.json()) as ListSkillsResponse;\n}\n\nexport async function getSkill(project: string, skillName: string): Promise<SingleSkillResponse> {\n const params = new URLSearchParams({ project, skill: skillName });\n const response = await fetch(`${baseUrl}/api/v2/skills?${params}`);\n return (await response.json()) as SingleSkillResponse;\n}\n\nexport async function searchSkills(query: string): Promise<SearchResponse> {\n const params = new URLSearchParams({ query });\n const response = await fetch(`${baseUrl}/api/v2/skills?${params}`);\n return (await response.json()) as SearchResponse;\n}\n\nexport async function downloadSkill(project: string, skillName: string): Promise<DownloadResponse> {\n const skillData = await getSkill(project, skillName);\n\n if (skillData.error) {\n return {\n skill: { name: skillName, description: \"\", url: \"\", project },\n files: [],\n error: skillData.message || skillData.error,\n };\n }\n\n const skill = {\n name: skillData.name,\n description: skillData.description,\n url: skillData.url,\n project: skillData.project,\n };\n\n const { files, error } = await downloadSkillFromGitHub(skill);\n\n if (error) {\n return { skill, files: [], error };\n }\n\n return { skill, files };\n}\n","import pc from \"picocolors\";\n\nexport const log = {\n info: (message: string) => console.log(pc.cyan(message)),\n success: (message: string) => console.log(pc.green(`✔ ${message}`)),\n warn: (message: string) => console.log(pc.yellow(`⚠ ${message}`)),\n error: (message: string) => console.log(pc.red(`✖ ${message}`)),\n dim: (message: string) => console.log(pc.dim(message)),\n item: (message: string) => console.log(pc.green(` ${message}`)),\n itemAdd: (message: string) => console.log(` ${pc.green(\"+\")} ${message}`),\n plain: (message: string) => console.log(message),\n blank: () => console.log(\"\"),\n};\n","import pc from \"picocolors\";\nimport { select, checkbox } from \"@inquirer/prompts\";\nimport { access } from \"fs/promises\";\nimport { join } from \"path\";\nimport { homedir } from \"os\";\n\nimport { log } from \"./logger.js\";\nimport type {\n IDE,\n IDEOptions,\n Scope,\n AddOptions,\n ListOptions,\n RemoveOptions,\n InstallTargets,\n} from \"../types.js\";\nimport { IDE_PATHS, IDE_NAMES, DEFAULT_CONFIG } from \"../types.js\";\n\nexport function getSelectedIde(options: IDEOptions): IDE | null {\n if (options.claude) return \"claude\";\n if (options.cursor) return \"cursor\";\n if (options.codex) return \"codex\";\n if (options.opencode) return \"opencode\";\n if (options.amp) return \"amp\";\n if (options.antigravity) return \"antigravity\";\n return null;\n}\n\nexport function hasExplicitIdeOption(options: IDEOptions): boolean {\n return !!(\n options.claude ||\n options.cursor ||\n options.codex ||\n options.opencode ||\n options.amp ||\n options.antigravity\n );\n}\n\ninterface DetectedIdes {\n ides: IDE[];\n scope: Scope;\n}\n\nexport async function detectInstalledIdes(): Promise<DetectedIdes | null> {\n const allIdes = Object.keys(IDE_PATHS) as IDE[];\n\n const projectIdes: IDE[] = [];\n for (const ide of allIdes) {\n const idePath = IDE_PATHS[ide];\n const projectParent = join(process.cwd(), idePath.split(\"/\")[0]);\n try {\n await access(projectParent);\n projectIdes.push(ide);\n } catch {}\n }\n\n if (projectIdes.length > 0) {\n return { ides: projectIdes, scope: \"project\" };\n }\n\n const globalIdes: IDE[] = [];\n for (const ide of allIdes) {\n const idePath = IDE_PATHS[ide];\n const globalParent = join(homedir(), idePath.split(\"/\")[0]);\n try {\n await access(globalParent);\n globalIdes.push(ide);\n } catch {}\n }\n\n if (globalIdes.length > 0) {\n return { ides: globalIdes, scope: \"global\" };\n }\n\n return null;\n}\n\nexport async function promptForInstallTargets(options: AddOptions): Promise<InstallTargets | null> {\n if (hasExplicitIdeOption(options)) {\n const ide = getSelectedIde(options);\n const scope: Scope = options.global ? \"global\" : \"project\";\n return {\n ides: [ide || DEFAULT_CONFIG.defaultIde],\n scopes: [scope],\n };\n }\n\n const detected = await detectInstalledIdes();\n\n if (detected) {\n const scope: Scope = options.global ? \"global\" : \"project\";\n return { ides: detected.ides, scopes: [scope] };\n }\n\n log.blank();\n\n const ideChoices = (Object.keys(IDE_NAMES) as IDE[]).map((ide) => ({\n name: `${IDE_NAMES[ide]} ${pc.dim(`(${IDE_PATHS[ide]})`)}`,\n value: ide,\n checked: ide === DEFAULT_CONFIG.defaultIde,\n }));\n\n let selectedIdes: IDE[];\n try {\n selectedIdes = await checkbox({\n message: \"Which clients do you want to install the skill(s) for?\",\n choices: ideChoices,\n required: true,\n });\n } catch {\n return null;\n }\n\n if (selectedIdes.length === 0) {\n log.warn(\"You must select at least one client\");\n return null;\n }\n\n const selectedScopes: Scope[] = options.global ? [\"global\"] : [\"project\"];\n\n return { ides: selectedIdes, scopes: selectedScopes };\n}\n\nexport async function promptForSingleTarget(\n options: ListOptions | RemoveOptions\n): Promise<{ ide: IDE; scope: Scope } | null> {\n if (hasExplicitIdeOption(options)) {\n const ide = getSelectedIde(options) || DEFAULT_CONFIG.defaultIde;\n const scope: Scope = options.global ? \"global\" : \"project\";\n return { ide, scope };\n }\n\n log.blank();\n\n const ideChoices = (Object.keys(IDE_NAMES) as IDE[]).map((ide) => ({\n name: `${IDE_NAMES[ide]} ${pc.dim(`(${IDE_PATHS[ide]})`)}`,\n value: ide,\n }));\n\n let selectedIde: IDE;\n try {\n selectedIde = await select({\n message: \"Which client?\",\n choices: ideChoices,\n default: DEFAULT_CONFIG.defaultIde,\n });\n } catch {\n return null;\n }\n\n let selectedScope: Scope;\n if (options.global !== undefined) {\n selectedScope = options.global ? \"global\" : \"project\";\n } else {\n try {\n selectedScope = await select({\n message: \"Which scope?\",\n choices: [\n {\n name: `Project ${pc.dim(\"(current directory)\")}`,\n value: \"project\" as Scope,\n },\n {\n name: `Global ${pc.dim(\"(home directory)\")}`,\n value: \"global\" as Scope,\n },\n ],\n default: DEFAULT_CONFIG.defaultScope,\n });\n } catch {\n return null;\n }\n }\n\n return { ide: selectedIde, scope: selectedScope };\n}\n\nexport function getTargetDirs(targets: InstallTargets): string[] {\n const dirs: string[] = [];\n for (const ide of targets.ides) {\n const idePath = IDE_PATHS[ide];\n for (const scope of targets.scopes) {\n if (scope === \"global\") {\n dirs.push(join(homedir(), idePath));\n } else {\n dirs.push(join(process.cwd(), idePath));\n }\n }\n }\n return dirs;\n}\n\nexport function getTargetDirFromSelection(ide: IDE, scope: Scope): string {\n const idePath = IDE_PATHS[ide];\n if (scope === \"global\") {\n return join(homedir(), idePath);\n }\n return join(process.cwd(), idePath);\n}\n","export interface SkillFile {\n path: string;\n content: string;\n}\n\nexport interface Skill {\n name: string;\n description: string;\n url: string;\n}\n\nexport interface SkillSearchResult extends Skill {\n project: string;\n}\n\nexport interface ListSkillsResponse {\n project: string;\n skills: Skill[];\n error?: string;\n message?: string;\n}\n\nexport interface SingleSkillResponse extends Skill {\n project: string;\n error?: string;\n message?: string;\n}\n\nexport interface SearchResponse {\n results: SkillSearchResult[];\n error?: string;\n message?: string;\n}\n\nexport interface DownloadResponse {\n skill: Skill & { project: string };\n files: SkillFile[];\n error?: string;\n}\n\nexport type IDE = \"claude\" | \"cursor\" | \"codex\" | \"opencode\" | \"amp\" | \"antigravity\";\n\nexport type Scope = \"project\" | \"global\";\n\nexport interface IDEOptions {\n claude?: boolean;\n cursor?: boolean;\n codex?: boolean;\n opencode?: boolean;\n amp?: boolean;\n antigravity?: boolean;\n}\n\nexport interface ScopeOptions {\n global?: boolean;\n}\n\nexport type AddOptions = IDEOptions & ScopeOptions & { all?: boolean };\nexport type ListOptions = IDEOptions & ScopeOptions;\nexport type RemoveOptions = IDEOptions & ScopeOptions;\n\nexport interface InstallTargets {\n ides: IDE[];\n scopes: Scope[];\n}\n\nexport const IDE_PATHS: Record<IDE, string> = {\n claude: \".claude/skills\",\n cursor: \".cursor/skills\",\n codex: \".codex/skills\",\n opencode: \".opencode/skill\",\n amp: \".agents/skills\",\n antigravity: \".agent/skills\",\n};\n\nexport const IDE_NAMES: Record<IDE, string> = {\n claude: \"Claude Code\",\n cursor: \"Cursor\",\n codex: \"Codex\",\n opencode: \"OpenCode\",\n amp: \"Amp\",\n antigravity: \"Antigravity\",\n};\n\nexport interface C7Config {\n defaultIde: IDE;\n defaultScope: \"project\" | \"global\";\n}\n\nexport const DEFAULT_CONFIG: C7Config = {\n defaultIde: \"claude\",\n defaultScope: \"project\",\n};\n","import { mkdir, writeFile, rm, symlink, lstat } from \"fs/promises\";\nimport { join } from \"path\";\n\nimport type { SkillFile } from \"../types.js\";\n\nexport async function installSkillFiles(\n skillName: string,\n files: SkillFile[],\n targetDir: string\n): Promise<void> {\n const skillDir = join(targetDir, skillName);\n\n for (const file of files) {\n const filePath = join(skillDir, file.path);\n const fileDir = join(filePath, \"..\");\n\n await mkdir(fileDir, { recursive: true });\n await writeFile(filePath, file.content);\n }\n}\n\nexport async function symlinkSkill(\n skillName: string,\n sourcePath: string,\n targetDir: string\n): Promise<void> {\n const targetPath = join(targetDir, skillName);\n\n try {\n const stats = await lstat(targetPath);\n if (stats.isSymbolicLink() || stats.isDirectory()) {\n await rm(targetPath, { recursive: true });\n }\n } catch {}\n\n await mkdir(targetDir, { recursive: true });\n await symlink(sourcePath, targetPath);\n}\n","import { readFileSync } from \"fs\";\nimport { fileURLToPath } from \"url\";\nimport { dirname, join } from \"path\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, \"../package.json\"), \"utf-8\"));\n\nexport const VERSION: string = pkg.version;\nexport const NAME: string = pkg.name;\n"],"mappings":"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","names":["pc","pc","checkbox","rm","join","branch","pathParts","path","pc","pc","join","program","pc","checkbox","primaryDir","join","rm","join","pc"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ctx7",
|
|
3
|
+
"version": "0.1.0-canary.0",
|
|
4
|
+
"description": "Context7 CLI - Manage AI coding skills and documentation context",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ctx7": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@inquirer/prompts": "^8.2.0",
|
|
14
|
+
"commander": "^13.1.0",
|
|
15
|
+
"figlet": "^1.9.4",
|
|
16
|
+
"ora": "^9.0.0",
|
|
17
|
+
"picocolors": "^1.1.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/figlet": "^1.7.0",
|
|
21
|
+
"@types/node": "^22.19.1",
|
|
22
|
+
"@typescript-eslint/eslint-plugin": "^8.28.0",
|
|
23
|
+
"@typescript-eslint/parser": "^8.28.0",
|
|
24
|
+
"eslint": "^9.34.0",
|
|
25
|
+
"eslint-plugin-prettier": "^5.2.5",
|
|
26
|
+
"prettier": "^3.6.2",
|
|
27
|
+
"tsup": "^8.5.0",
|
|
28
|
+
"typescript": "^5.8.2",
|
|
29
|
+
"typescript-eslint": "^8.28.0"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"context7",
|
|
33
|
+
"cli",
|
|
34
|
+
"ai",
|
|
35
|
+
"skills",
|
|
36
|
+
"documentation",
|
|
37
|
+
"mcp"
|
|
38
|
+
],
|
|
39
|
+
"author": "Upstash",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/upstash/context7.git",
|
|
44
|
+
"directory": "packages/cli"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/upstash/context7/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://context7.com",
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"dev": "tsup --watch",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"lint": "eslint src --fix",
|
|
58
|
+
"lint:check": "eslint src",
|
|
59
|
+
"format": "prettier --write src",
|
|
60
|
+
"format:check": "prettier --check src",
|
|
61
|
+
"clean": "rm -rf dist node_modules"
|
|
62
|
+
}
|
|
63
|
+
}
|