ring-skills-mcp 1.0.3 → 1.0.5
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/dist/index.js +126 -39
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -52,19 +52,97 @@ async function fetchSkills(host, search = "", pageSize = 5000, token = "") {
|
|
|
52
52
|
throw new Error("Failed to fetch Skills list: Unknown error");
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
function parseGitUrl(gitUrl) {
|
|
56
|
+
// GitHub URL pattern: https://github.com/{owner}/{repo}/tree/{branch}/{path}
|
|
57
|
+
const githubMatch = gitUrl.match(/https?:\/\/github\.com\/([^\/]+)\/([^\/]+)\/tree\/([^\/]+)\/(.+)/);
|
|
58
|
+
if (githubMatch) {
|
|
59
|
+
const skillPath = githubMatch[4];
|
|
60
|
+
const skillName = skillPath.split("/").pop() || "";
|
|
61
|
+
return {
|
|
62
|
+
type: "github",
|
|
63
|
+
host: "github.com",
|
|
64
|
+
owner: githubMatch[1],
|
|
65
|
+
repo: githubMatch[2],
|
|
66
|
+
branch: githubMatch[3],
|
|
67
|
+
skillPath,
|
|
68
|
+
skillName: skillName.toLowerCase(),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// GitLab URL pattern: https://{host}/{owner}/{repo}/-/tree/{branch}/{path}
|
|
72
|
+
const gitlabMatch = gitUrl.match(/https?:\/\/([^\/]+)\/([^\/]+)\/([^\/]+)\/-\/tree\/([^\/]+)\/(.+)/);
|
|
73
|
+
if (gitlabMatch) {
|
|
74
|
+
const skillPath = gitlabMatch[5];
|
|
75
|
+
const skillName = skillPath.split("/").pop() || "";
|
|
76
|
+
return {
|
|
77
|
+
type: "gitlab",
|
|
78
|
+
host: gitlabMatch[1],
|
|
79
|
+
owner: gitlabMatch[2],
|
|
80
|
+
repo: gitlabMatch[3],
|
|
81
|
+
branch: gitlabMatch[4],
|
|
82
|
+
skillPath,
|
|
83
|
+
skillName: skillName.toLowerCase(),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Fallback: try to extract skill name from URL
|
|
87
|
+
const skillMatch = gitUrl.match(/\/skills\/([^\/\s]+)/);
|
|
88
|
+
if (skillMatch) {
|
|
89
|
+
return {
|
|
90
|
+
type: "unknown",
|
|
91
|
+
host: "",
|
|
92
|
+
owner: "",
|
|
93
|
+
repo: "",
|
|
94
|
+
branch: "main",
|
|
95
|
+
skillPath: "",
|
|
96
|
+
skillName: skillMatch[1].toLowerCase(),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
55
101
|
/**
|
|
56
|
-
*
|
|
57
|
-
* Example: "https://github.com/anthropics/skills/tree/main/skills/webapp-testing" -> "webapp-testing"
|
|
102
|
+
* Build degit source path from git URL info
|
|
58
103
|
*/
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
104
|
+
function buildDegitSource(urlInfo) {
|
|
105
|
+
if (urlInfo.type === "github") {
|
|
106
|
+
// GitHub: owner/repo/path#branch
|
|
107
|
+
return `${urlInfo.owner}/${urlInfo.repo}/${urlInfo.skillPath}#${urlInfo.branch}`;
|
|
108
|
+
}
|
|
109
|
+
else if (urlInfo.type === "gitlab") {
|
|
110
|
+
// GitLab (self-hosted): host:owner/repo/path#branch
|
|
111
|
+
return `${urlInfo.host}:${urlInfo.owner}/${urlInfo.repo}/${urlInfo.skillPath}#${urlInfo.branch}`;
|
|
112
|
+
}
|
|
113
|
+
return "";
|
|
63
114
|
}
|
|
64
115
|
/**
|
|
65
|
-
* Install Skill to specified project
|
|
116
|
+
* Install Skill to specified project using gitUrl
|
|
66
117
|
*/
|
|
67
|
-
async function
|
|
118
|
+
async function installSkillFromGitUrl(gitUrl, projectPath, targetDir = ".claude/skills") {
|
|
119
|
+
const urlInfo = parseGitUrl(gitUrl);
|
|
120
|
+
if (!urlInfo) {
|
|
121
|
+
throw new Error(`Could not parse git URL: ${gitUrl}`);
|
|
122
|
+
}
|
|
123
|
+
const skillName = urlInfo.skillName;
|
|
124
|
+
const destPath = path.join(projectPath, targetDir, skillName);
|
|
125
|
+
const sourcePath = buildDegitSource(urlInfo);
|
|
126
|
+
if (!sourcePath) {
|
|
127
|
+
throw new Error(`Could not build degit source from URL: ${gitUrl}`);
|
|
128
|
+
}
|
|
129
|
+
const command = `npx degit ${sourcePath} ${destPath}`;
|
|
130
|
+
try {
|
|
131
|
+
const { stdout, stderr } = await execAsync(command);
|
|
132
|
+
const output = stdout || stderr || "Installation completed";
|
|
133
|
+
return `✅ Skill "${skillName}" has been successfully installed to ${destPath}\n\nSource: ${sourcePath}\n${output}`;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error instanceof Error) {
|
|
137
|
+
throw new Error(`Failed to install Skill: ${error.message}`);
|
|
138
|
+
}
|
|
139
|
+
throw new Error("Failed to install Skill: Unknown error");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Install Skill to specified project using skill name and repo
|
|
144
|
+
*/
|
|
145
|
+
async function installSkillFromRepo(skillName, projectPath, targetDir = ".claude/skills", repo = DEFAULT_SKILLS_REPO) {
|
|
68
146
|
// Convert skill name to lowercase
|
|
69
147
|
const normalizedName = skillName.toLowerCase();
|
|
70
148
|
// Build full installation path
|
|
@@ -164,7 +242,7 @@ server.tool("install_skill", "Install skill to local project by skill name or gi
|
|
|
164
242
|
targetDir: z
|
|
165
243
|
.string()
|
|
166
244
|
.optional()
|
|
167
|
-
.describe("Installation target directory, default: skills"),
|
|
245
|
+
.describe("Installation target directory, default: .claude/skills"),
|
|
168
246
|
repo: z
|
|
169
247
|
.string()
|
|
170
248
|
.optional()
|
|
@@ -176,23 +254,32 @@ server.tool("install_skill", "Install skill to local project by skill name or gi
|
|
|
176
254
|
content: [
|
|
177
255
|
{
|
|
178
256
|
type: "text",
|
|
179
|
-
text: "❌ Please provide project path (projectPath).\n\nYou can provide the project root directory path of the currently opened file in IDE, skill will be installed to the skills directory of that project.\n\nExample: /Users/xxx/my-project",
|
|
257
|
+
text: "❌ Please provide project path (projectPath).\n\nYou can provide the project root directory path of the currently opened file in IDE, skill will be installed to the .claude/skills directory of that project.\n\nExample: /Users/xxx/my-project",
|
|
180
258
|
},
|
|
181
259
|
],
|
|
182
260
|
isError: true,
|
|
183
261
|
};
|
|
184
262
|
}
|
|
185
|
-
// Determine the actual skill name to use
|
|
186
|
-
let actualSkillName = null;
|
|
187
263
|
// Priority: gitUrl > skillName
|
|
188
264
|
if (gitUrl) {
|
|
189
|
-
|
|
190
|
-
|
|
265
|
+
// Install from gitUrl (supports GitHub and GitLab)
|
|
266
|
+
try {
|
|
267
|
+
const result = await installSkillFromGitUrl(gitUrl, projectPath, targetDir || ".claude/skills");
|
|
191
268
|
return {
|
|
192
269
|
content: [
|
|
193
270
|
{
|
|
194
271
|
type: "text",
|
|
195
|
-
text:
|
|
272
|
+
text: result,
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
return {
|
|
279
|
+
content: [
|
|
280
|
+
{
|
|
281
|
+
type: "text",
|
|
282
|
+
text: `❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
196
283
|
},
|
|
197
284
|
],
|
|
198
285
|
isError: true,
|
|
@@ -200,9 +287,31 @@ server.tool("install_skill", "Install skill to local project by skill name or gi
|
|
|
200
287
|
}
|
|
201
288
|
}
|
|
202
289
|
else if (skillName) {
|
|
203
|
-
|
|
290
|
+
// Install from repo using skill name
|
|
291
|
+
try {
|
|
292
|
+
const result = await installSkillFromRepo(skillName, projectPath, targetDir || ".claude/skills", repo || DEFAULT_SKILLS_REPO);
|
|
293
|
+
return {
|
|
294
|
+
content: [
|
|
295
|
+
{
|
|
296
|
+
type: "text",
|
|
297
|
+
text: result,
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
return {
|
|
304
|
+
content: [
|
|
305
|
+
{
|
|
306
|
+
type: "text",
|
|
307
|
+
text: `❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
isError: true,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
204
313
|
}
|
|
205
|
-
|
|
314
|
+
else {
|
|
206
315
|
return {
|
|
207
316
|
content: [
|
|
208
317
|
{
|
|
@@ -213,28 +322,6 @@ server.tool("install_skill", "Install skill to local project by skill name or gi
|
|
|
213
322
|
isError: true,
|
|
214
323
|
};
|
|
215
324
|
}
|
|
216
|
-
try {
|
|
217
|
-
const result = await installSkill(actualSkillName, projectPath, targetDir || "skills", repo || DEFAULT_SKILLS_REPO);
|
|
218
|
-
return {
|
|
219
|
-
content: [
|
|
220
|
-
{
|
|
221
|
-
type: "text",
|
|
222
|
-
text: result,
|
|
223
|
-
},
|
|
224
|
-
],
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
return {
|
|
229
|
-
content: [
|
|
230
|
-
{
|
|
231
|
-
type: "text",
|
|
232
|
-
text: `❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
isError: true,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
325
|
});
|
|
239
326
|
// Start server
|
|
240
327
|
async function main() {
|