ctx7 0.3.9 → 0.3.11
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 +6 -0
- package/dist/index.js +207 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -177,6 +177,12 @@ ctx7 skills install /anthropics/skills pdf --universal
|
|
|
177
177
|
|
|
178
178
|
# Install globally (home directory instead of current project)
|
|
179
179
|
ctx7 skills install /anthropics/skills pdf --global
|
|
180
|
+
|
|
181
|
+
# Install non-interactively
|
|
182
|
+
ctx7 skills install /anthropics/skills pdf --global --universal --yes
|
|
183
|
+
|
|
184
|
+
# Install to all supported agent locations
|
|
185
|
+
ctx7 skills install /anthropics/skills pdf --all-agents
|
|
180
186
|
```
|
|
181
187
|
|
|
182
188
|
### Search for skills
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ function parseSkillInput(input2) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// src/utils/github.ts
|
|
32
|
+
import { execSync } from "child_process";
|
|
32
33
|
var GITHUB_API = "https://api.github.com";
|
|
33
34
|
var GITHUB_RAW = "https://raw.githubusercontent.com";
|
|
34
35
|
function parseGitHubUrl(url) {
|
|
@@ -69,6 +70,115 @@ function parseGitHubUrl(url) {
|
|
|
69
70
|
return null;
|
|
70
71
|
}
|
|
71
72
|
}
|
|
73
|
+
function getGitHubToken() {
|
|
74
|
+
const envToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
75
|
+
if (envToken) return envToken;
|
|
76
|
+
try {
|
|
77
|
+
return execSync("gh auth token", { stdio: ["pipe", "pipe", "ignore"] }).toString().trim();
|
|
78
|
+
} catch {
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function parseSkillFrontmatter(content) {
|
|
83
|
+
const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
84
|
+
if (!frontmatterMatch) return null;
|
|
85
|
+
const frontmatter = frontmatterMatch[1];
|
|
86
|
+
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
|
|
87
|
+
if (!nameMatch) return null;
|
|
88
|
+
const name = nameMatch[1].trim().replace(/^["']|["']$/g, "").toLowerCase();
|
|
89
|
+
let description = "";
|
|
90
|
+
const multiLineMatch = frontmatter.match(/^description:\s*([|>])-?\s*$/m);
|
|
91
|
+
if (multiLineMatch) {
|
|
92
|
+
const descLineIndex = frontmatter.indexOf("description:");
|
|
93
|
+
const lines = frontmatter.slice(descLineIndex).split("\n").slice(1);
|
|
94
|
+
const indentedLines = [];
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
if (line.trim() === "") {
|
|
97
|
+
indentedLines.push("");
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (/^\s+/.test(line)) {
|
|
101
|
+
indentedLines.push(line);
|
|
102
|
+
} else {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const firstNonEmpty = indentedLines.find((l) => l.trim().length > 0);
|
|
107
|
+
const indent = firstNonEmpty?.match(/^(\s+)/)?.[1].length ?? 0;
|
|
108
|
+
description = indentedLines.map((line) => line.slice(indent)).join(" ").replace(/\s+/g, " ").trim();
|
|
109
|
+
} else {
|
|
110
|
+
const singleMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
111
|
+
if (singleMatch) {
|
|
112
|
+
const value = singleMatch[1].trim();
|
|
113
|
+
if (!["|", ">", "|-", ">-"].includes(value)) {
|
|
114
|
+
description = value.replace(/^["']|["']$/g, "");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!description) return null;
|
|
119
|
+
return { name, description };
|
|
120
|
+
}
|
|
121
|
+
function getGitHubHeaders() {
|
|
122
|
+
const ghToken = getGitHubToken();
|
|
123
|
+
return {
|
|
124
|
+
Accept: "application/vnd.github.v3+json",
|
|
125
|
+
"User-Agent": "context7-cli",
|
|
126
|
+
...ghToken && { Authorization: `token ${ghToken}` }
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async function fetchRepoTree(owner, repo, branch, headers) {
|
|
130
|
+
const treeUrl = `${GITHUB_API}/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`;
|
|
131
|
+
const response = await fetch(treeUrl, { headers });
|
|
132
|
+
if (!response.ok) return null;
|
|
133
|
+
return await response.json();
|
|
134
|
+
}
|
|
135
|
+
async function fetchDefaultBranch(owner, repo, headers) {
|
|
136
|
+
const response = await fetch(`${GITHUB_API}/repos/${owner}/${repo}`, { headers });
|
|
137
|
+
if (!response.ok) return { status: response.status };
|
|
138
|
+
const data = await response.json();
|
|
139
|
+
return { branch: data.default_branch };
|
|
140
|
+
}
|
|
141
|
+
async function listSkillsFromGitHub(project) {
|
|
142
|
+
try {
|
|
143
|
+
const parts = project.split("/").filter(Boolean);
|
|
144
|
+
if (parts.length < 2) return { status: "error", error: "Invalid project format" };
|
|
145
|
+
const [owner, repo] = parts;
|
|
146
|
+
const headers = getGitHubHeaders();
|
|
147
|
+
const branchResult = await fetchDefaultBranch(owner, repo, headers);
|
|
148
|
+
if ("status" in branchResult) return { status: "repo_not_found" };
|
|
149
|
+
const treeData = await fetchRepoTree(owner, repo, branchResult.branch, headers);
|
|
150
|
+
if (!treeData) return { status: "error", error: "Could not fetch repository tree" };
|
|
151
|
+
const skillMdFiles = treeData.tree.filter(
|
|
152
|
+
(item) => item.type === "blob" && item.path.toLowerCase().endsWith("skill.md")
|
|
153
|
+
);
|
|
154
|
+
const skills = [];
|
|
155
|
+
for (const item of skillMdFiles) {
|
|
156
|
+
const rawUrl = `${GITHUB_RAW}/${owner}/${repo}/${branchResult.branch}/${item.path}`;
|
|
157
|
+
const response = await fetch(rawUrl, { headers });
|
|
158
|
+
if (!response.ok) continue;
|
|
159
|
+
const content = await response.text();
|
|
160
|
+
const meta = parseSkillFrontmatter(content);
|
|
161
|
+
if (!meta) continue;
|
|
162
|
+
const skillDir = item.path.split("/").slice(0, -1).join("/");
|
|
163
|
+
skills.push({
|
|
164
|
+
name: meta.name,
|
|
165
|
+
description: meta.description,
|
|
166
|
+
url: `https://github.com/${owner}/${repo}/tree/${branchResult.branch}/${skillDir}`,
|
|
167
|
+
project
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return { status: "ok", skills };
|
|
171
|
+
} catch (err) {
|
|
172
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
173
|
+
return { status: "error", error: message };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function getSkillFromGitHub(project, skillName) {
|
|
177
|
+
const result = await listSkillsFromGitHub(project);
|
|
178
|
+
if (result.status !== "ok") return result;
|
|
179
|
+
const skill = result.skills.find((s) => s.name === skillName.toLowerCase());
|
|
180
|
+
return { ...result, skill };
|
|
181
|
+
}
|
|
72
182
|
async function downloadSkillFromGitHub(skill) {
|
|
73
183
|
try {
|
|
74
184
|
const parsed = parseGitHubUrl(skill.url);
|
|
@@ -76,17 +186,11 @@ async function downloadSkillFromGitHub(skill) {
|
|
|
76
186
|
return { files: [], error: `Invalid GitHub URL: ${skill.url}` };
|
|
77
187
|
}
|
|
78
188
|
const { owner, repo, branch, path: skillPath } = parsed;
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"User-Agent": "context7-cli"
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
if (!treeResponse.ok) {
|
|
87
|
-
return { files: [], error: `GitHub API error: ${treeResponse.status}` };
|
|
189
|
+
const ghHeaders = getGitHubHeaders();
|
|
190
|
+
const treeData = await fetchRepoTree(owner, repo, branch, ghHeaders);
|
|
191
|
+
if (!treeData) {
|
|
192
|
+
return { files: [], error: `GitHub API error` };
|
|
88
193
|
}
|
|
89
|
-
const treeData = await treeResponse.json();
|
|
90
194
|
const skillFiles = treeData.tree.filter(
|
|
91
195
|
(item) => item.type === "blob" && item.path.startsWith(skillPath + "/")
|
|
92
196
|
);
|
|
@@ -96,7 +200,7 @@ async function downloadSkillFromGitHub(skill) {
|
|
|
96
200
|
const files = [];
|
|
97
201
|
for (const item of skillFiles) {
|
|
98
202
|
const rawUrl = `${GITHUB_RAW}/${owner}/${repo}/${branch}/${item.path}`;
|
|
99
|
-
const fileResponse = await fetch(rawUrl);
|
|
203
|
+
const fileResponse = await fetch(rawUrl, { headers: ghHeaders });
|
|
100
204
|
if (!fileResponse.ok) {
|
|
101
205
|
console.warn(`Failed to fetch ${item.path}: ${fileResponse.status}`);
|
|
102
206
|
continue;
|
|
@@ -167,11 +271,19 @@ async function suggestSkills(dependencies, accessToken) {
|
|
|
167
271
|
async function downloadSkill(project, skillName) {
|
|
168
272
|
const skillData = await getSkill(project, skillName);
|
|
169
273
|
if (skillData.error) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
274
|
+
const ghResult = await getSkillFromGitHub(project, skillName);
|
|
275
|
+
if (ghResult.status !== "ok" || !ghResult.skill) {
|
|
276
|
+
return {
|
|
277
|
+
skill: { name: skillName, description: "", url: "", project },
|
|
278
|
+
files: [],
|
|
279
|
+
error: skillData.message || skillData.error
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const { files: files2, error: error2 } = await downloadSkillFromGitHub(ghResult.skill);
|
|
283
|
+
if (error2) {
|
|
284
|
+
return { skill: ghResult.skill, files: [], error: error2 };
|
|
285
|
+
}
|
|
286
|
+
return { skill: ghResult.skill, files: files2 };
|
|
175
287
|
}
|
|
176
288
|
const skill = {
|
|
177
289
|
name: skillData.name,
|
|
@@ -511,6 +623,9 @@ var DEFAULT_CONFIG = {
|
|
|
511
623
|
|
|
512
624
|
// src/utils/ide.ts
|
|
513
625
|
function getSelectedIdes(options) {
|
|
626
|
+
if (options.allAgents) {
|
|
627
|
+
return ["universal", ...VENDOR_SPECIFIC_AGENTS];
|
|
628
|
+
}
|
|
514
629
|
const ides = [];
|
|
515
630
|
if (options.claude) ides.push("claude");
|
|
516
631
|
if (options.cursor) ides.push("cursor");
|
|
@@ -519,7 +634,7 @@ function getSelectedIdes(options) {
|
|
|
519
634
|
return ides;
|
|
520
635
|
}
|
|
521
636
|
function hasExplicitIdeOption(options) {
|
|
522
|
-
return !!(options.claude || options.cursor || options.universal || options.antigravity);
|
|
637
|
+
return !!(options.allAgents || options.claude || options.cursor || options.universal || options.antigravity);
|
|
523
638
|
}
|
|
524
639
|
async function detectVendorSpecificAgents(scope) {
|
|
525
640
|
const baseDir = scope === "global" ? homedir() : process.cwd();
|
|
@@ -1775,7 +1890,7 @@ function logInstallSummary(targets, targetDirs, skillNames) {
|
|
|
1775
1890
|
function registerSkillCommands(program2) {
|
|
1776
1891
|
const skill = program2.command("skills").alias("skill").description("Manage AI coding skills");
|
|
1777
1892
|
registerGenerateCommand(skill);
|
|
1778
|
-
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("--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) => {
|
|
1893
|
+
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) => {
|
|
1779
1894
|
await installCommand(project, skillName, options);
|
|
1780
1895
|
});
|
|
1781
1896
|
skill.command("search").alias("s").argument("<keywords...>", "Search keywords").description("Search for skills across all indexed repositories").action(async (keywords) => {
|
|
@@ -1795,7 +1910,7 @@ function registerSkillCommands(program2) {
|
|
|
1795
1910
|
});
|
|
1796
1911
|
}
|
|
1797
1912
|
function registerSkillAliases(program2) {
|
|
1798
|
-
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("--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) => {
|
|
1913
|
+
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) => {
|
|
1799
1914
|
await installCommand(project, skillName, options);
|
|
1800
1915
|
});
|
|
1801
1916
|
program2.command("ss", { hidden: true }).argument("<keywords...>", "Search keywords").description("Search for skills (alias for: skills search)").action(async (keywords) => {
|
|
@@ -1826,23 +1941,45 @@ async function installCommand(input2, skillName, options) {
|
|
|
1826
1941
|
if (skillData.error === "prompt_injection_detected") {
|
|
1827
1942
|
spinner.fail(pc7.red(`Prompt injection detected in skill: ${skillName}`));
|
|
1828
1943
|
log.warn("This skill contains potentially malicious content and cannot be installed.");
|
|
1829
|
-
|
|
1944
|
+
return;
|
|
1945
|
+
}
|
|
1946
|
+
spinner.text = `Fetching skill from GitHub: ${skillName}...`;
|
|
1947
|
+
const ghResult = await getSkillFromGitHub(repo, skillName);
|
|
1948
|
+
if (ghResult.status === "repo_not_found") {
|
|
1949
|
+
spinner.fail(pc7.red(`Repository not found: ${repo}`));
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
if (ghResult.status !== "ok" || !ghResult.skill) {
|
|
1830
1953
|
spinner.fail(pc7.red(`Skill not found: ${skillName}`));
|
|
1954
|
+
return;
|
|
1831
1955
|
}
|
|
1832
|
-
|
|
1956
|
+
spinner.succeed(`Found skill: ${skillName}`);
|
|
1957
|
+
selectedSkills = [ghResult.skill];
|
|
1958
|
+
} else {
|
|
1959
|
+
spinner.succeed(`Found skill: ${skillName}`);
|
|
1960
|
+
selectedSkills = [
|
|
1961
|
+
{
|
|
1962
|
+
name: skillData.name,
|
|
1963
|
+
description: skillData.description,
|
|
1964
|
+
url: skillData.url,
|
|
1965
|
+
project: repo
|
|
1966
|
+
}
|
|
1967
|
+
];
|
|
1833
1968
|
}
|
|
1834
|
-
spinner.succeed(`Found skill: ${skillName}`);
|
|
1835
|
-
selectedSkills = [
|
|
1836
|
-
{
|
|
1837
|
-
name: skillData.name,
|
|
1838
|
-
description: skillData.description,
|
|
1839
|
-
url: skillData.url,
|
|
1840
|
-
project: repo
|
|
1841
|
-
}
|
|
1842
|
-
];
|
|
1843
1969
|
} else {
|
|
1844
|
-
|
|
1845
|
-
if (data.error) {
|
|
1970
|
+
let data = await listProjectSkills(repo);
|
|
1971
|
+
if ((data.error || !data.skills || data.skills.length === 0) && !data.blockedSkillsCount) {
|
|
1972
|
+
spinner.text = `Fetching skills from GitHub...`;
|
|
1973
|
+
const ghResult = await listSkillsFromGitHub(repo);
|
|
1974
|
+
if (ghResult.status === "repo_not_found") {
|
|
1975
|
+
spinner.fail(pc7.red(`Repository not found: ${repo}`));
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
if (ghResult.status === "ok" && ghResult.skills.length > 0) {
|
|
1979
|
+
data = { project: repo, skills: ghResult.skills };
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
if (data.error && (!data.skills || data.skills.length === 0)) {
|
|
1846
1983
|
spinner.fail(pc7.red(`Error: ${data.message || data.error}`));
|
|
1847
1984
|
return;
|
|
1848
1985
|
}
|
|
@@ -2438,7 +2575,8 @@ var SETUP_AGENT_NAMES = {
|
|
|
2438
2575
|
claude: "Claude Code",
|
|
2439
2576
|
cursor: "Cursor",
|
|
2440
2577
|
opencode: "OpenCode",
|
|
2441
|
-
codex: "Codex"
|
|
2578
|
+
codex: "Codex",
|
|
2579
|
+
gemini: "Gemini CLI"
|
|
2442
2580
|
};
|
|
2443
2581
|
var AUTH_MODE_LABELS = {
|
|
2444
2582
|
oauth: "OAuth",
|
|
@@ -2557,6 +2695,29 @@ var agents = {
|
|
|
2557
2695
|
projectPaths: [".codex"],
|
|
2558
2696
|
globalPaths: [join8(homedir5(), ".codex")]
|
|
2559
2697
|
}
|
|
2698
|
+
},
|
|
2699
|
+
gemini: {
|
|
2700
|
+
name: "gemini",
|
|
2701
|
+
displayName: "Gemini CLI",
|
|
2702
|
+
mcp: {
|
|
2703
|
+
projectPaths: [join8(".gemini", "settings.json")],
|
|
2704
|
+
globalPaths: [join8(homedir5(), ".gemini", "settings.json")],
|
|
2705
|
+
configKey: "mcpServers",
|
|
2706
|
+
buildEntry: (auth) => withHeaders({ httpUrl: mcpUrl(auth) }, auth)
|
|
2707
|
+
},
|
|
2708
|
+
rule: {
|
|
2709
|
+
kind: "append",
|
|
2710
|
+
file: (scope) => scope === "global" ? join8(homedir5(), ".gemini", "GEMINI.md") : "GEMINI.md",
|
|
2711
|
+
sectionMarker: "<!-- context7 -->"
|
|
2712
|
+
},
|
|
2713
|
+
skill: {
|
|
2714
|
+
name: "context7-mcp",
|
|
2715
|
+
dir: (scope) => scope === "global" ? join8(homedir5(), ".gemini", "skills") : join8(".gemini", "skills")
|
|
2716
|
+
},
|
|
2717
|
+
detect: {
|
|
2718
|
+
projectPaths: [".gemini"],
|
|
2719
|
+
globalPaths: [join8(homedir5(), ".gemini")]
|
|
2720
|
+
}
|
|
2560
2721
|
}
|
|
2561
2722
|
};
|
|
2562
2723
|
function getAgent(name) {
|
|
@@ -2597,7 +2758,7 @@ Do not use for: refactoring, writing scripts from scratch, debugging business lo
|
|
|
2597
2758
|
|
|
2598
2759
|
## Steps
|
|
2599
2760
|
|
|
2600
|
-
1. \`resolve-library-id\` with the library name and the user's question
|
|
2761
|
+
1. \`resolve-library-id\` with the library name and the user's question. Use the official library name with proper punctuation (e.g., "Next.js" not "nextjs", "Customer.io" not "customerio", "Three.js" not "threejs")
|
|
2601
2762
|
2. Pick the best match by: exact name match, description relevance, code snippet count, source reputation (High/Medium preferred), and benchmark score (higher is better). Use version-specific IDs when the user mentions a version
|
|
2602
2763
|
3. \`query-docs\` with the selected library ID and the user's full question (not single words)
|
|
2603
2764
|
4. Answer using the fetched docs
|
|
@@ -2775,10 +2936,11 @@ function getSelectedAgents(options) {
|
|
|
2775
2936
|
if (options.cursor) agents2.push("cursor");
|
|
2776
2937
|
if (options.opencode) agents2.push("opencode");
|
|
2777
2938
|
if (options.codex) agents2.push("codex");
|
|
2939
|
+
if (options.gemini) agents2.push("gemini");
|
|
2778
2940
|
return agents2;
|
|
2779
2941
|
}
|
|
2780
2942
|
function registerSetupCommand(program2) {
|
|
2781
|
-
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("--universal", "Set up for Universal (.agents/skills)").option("--antigravity", "Set up for Antigravity (.agent/skills)").option("--opencode", "Set up for OpenCode").option("--codex", "Set up for Codex").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)").action(async (options) => {
|
|
2943
|
+
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("--universal", "Set up for Universal (.agents/skills)").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)").action(async (options) => {
|
|
2782
2944
|
await setupCommand(options);
|
|
2783
2945
|
});
|
|
2784
2946
|
}
|
|
@@ -3007,6 +3169,11 @@ async function setupMcp(agents2, options, scope) {
|
|
|
3007
3169
|
const skillIcon = r.skillStatus === "installed" ? pc8.green("+") : pc8.dim("~");
|
|
3008
3170
|
log.plain(` ${skillIcon} Skill ${r.skillStatus}`);
|
|
3009
3171
|
log.plain(` ${pc8.dim(r.skillPath)}`);
|
|
3172
|
+
if (r.skillStatus.includes("EACCES")) {
|
|
3173
|
+
log.plain(
|
|
3174
|
+
` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname5(dirname5(r.skillPath))}`)}`
|
|
3175
|
+
);
|
|
3176
|
+
}
|
|
3010
3177
|
}
|
|
3011
3178
|
log.blank();
|
|
3012
3179
|
trackEvent("setup", { agents: agents2, scope, authMode: auth.mode });
|
|
@@ -3062,6 +3229,11 @@ async function setupCli(options) {
|
|
|
3062
3229
|
const skillIcon = r.skillStatus === "installed" ? pc8.green("+") : pc8.dim("~");
|
|
3063
3230
|
log.plain(` ${skillIcon} Skill ${r.skillStatus}`);
|
|
3064
3231
|
log.plain(` ${pc8.dim(r.skillPath)}`);
|
|
3232
|
+
if (r.skillStatus.includes("EACCES")) {
|
|
3233
|
+
log.plain(
|
|
3234
|
+
` ${pc8.yellow("tip:")} fix permissions with: ${pc8.cyan(`sudo chown -R $(whoami) ${dirname5(dirname5(r.skillPath))}`)}`
|
|
3235
|
+
);
|
|
3236
|
+
}
|
|
3065
3237
|
const ruleIcon = r.ruleStatus === "installed" || r.ruleStatus === "updated" ? pc8.green("+") : pc8.dim("~");
|
|
3066
3238
|
log.plain(` ${ruleIcon} Rule ${r.ruleStatus}`);
|
|
3067
3239
|
log.plain(` ${pc8.dim(r.rulePath)}`);
|