skild 0.0.8 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/index.js +184 -304
- package/package.json +5 -7
package/README.md
CHANGED
|
@@ -23,4 +23,13 @@ skild install https://github.com/anthropics/skills/tree/main/skills/pdf -t codex
|
|
|
23
23
|
|
|
24
24
|
# List installed skills
|
|
25
25
|
skild list -t codex --local
|
|
26
|
+
|
|
27
|
+
# Inspect / Validate / Update / Uninstall
|
|
28
|
+
skild info pdf -t codex --local
|
|
29
|
+
skild validate pdf -t codex --local
|
|
30
|
+
skild update pdf -t codex --local
|
|
31
|
+
skild uninstall pdf -t codex --local
|
|
32
|
+
|
|
33
|
+
# Create a new Skill
|
|
34
|
+
skild init my-skill
|
|
26
35
|
```
|
package/dist/index.js
CHANGED
|
@@ -2,260 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk9 from "chalk";
|
|
6
6
|
import { createRequire } from "module";
|
|
7
7
|
|
|
8
8
|
// src/commands/install.ts
|
|
9
9
|
import chalk2 from "chalk";
|
|
10
|
-
|
|
11
|
-
// src/services/skill-installer.ts
|
|
12
|
-
import degit from "degit";
|
|
13
|
-
import path4 from "path";
|
|
14
|
-
|
|
15
|
-
// src/utils/fs-helpers.ts
|
|
16
|
-
import fs from "fs";
|
|
17
|
-
import path from "path";
|
|
18
|
-
function isDirEmpty(dir) {
|
|
19
|
-
try {
|
|
20
|
-
const entries = fs.readdirSync(dir);
|
|
21
|
-
return entries.length === 0;
|
|
22
|
-
} catch {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function safeRemoveDir(dir) {
|
|
27
|
-
if (fs.existsSync(dir)) {
|
|
28
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function isDirectory(filePath) {
|
|
32
|
-
try {
|
|
33
|
-
return fs.statSync(filePath).isDirectory();
|
|
34
|
-
} catch {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
function pathExists(filePath) {
|
|
39
|
-
return fs.existsSync(filePath);
|
|
40
|
-
}
|
|
41
|
-
function copyDir(src, dest) {
|
|
42
|
-
fs.cpSync(src, dest, { recursive: true });
|
|
43
|
-
}
|
|
44
|
-
function hasSkillMd(skillPath) {
|
|
45
|
-
return fs.existsSync(path.join(skillPath, "SKILL.md"));
|
|
46
|
-
}
|
|
47
|
-
function getSubdirectories(dir) {
|
|
48
|
-
try {
|
|
49
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
50
|
-
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
51
|
-
} catch {
|
|
52
|
-
return [];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function sanitizeForPathSegment(value) {
|
|
56
|
-
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
57
|
-
}
|
|
58
|
-
function createTempDir(parentDir, prefix) {
|
|
59
|
-
const safePrefix = sanitizeForPathSegment(prefix || "tmp");
|
|
60
|
-
const template = path.join(parentDir, `.skild-${safePrefix}-`);
|
|
61
|
-
return fs.mkdtempSync(template);
|
|
62
|
-
}
|
|
63
|
-
function replaceDirAtomic(sourceDir, destDir) {
|
|
64
|
-
const backupDir = fs.existsSync(destDir) ? `${destDir}.bak-${Date.now()}` : null;
|
|
65
|
-
try {
|
|
66
|
-
if (backupDir) {
|
|
67
|
-
fs.renameSync(destDir, backupDir);
|
|
68
|
-
}
|
|
69
|
-
fs.renameSync(sourceDir, destDir);
|
|
70
|
-
if (backupDir) {
|
|
71
|
-
safeRemoveDir(backupDir);
|
|
72
|
-
}
|
|
73
|
-
} catch (error) {
|
|
74
|
-
try {
|
|
75
|
-
if (!fs.existsSync(destDir) && backupDir && fs.existsSync(backupDir)) {
|
|
76
|
-
fs.renameSync(backupDir, destDir);
|
|
77
|
-
}
|
|
78
|
-
} catch {
|
|
79
|
-
}
|
|
80
|
-
try {
|
|
81
|
-
if (fs.existsSync(sourceDir)) {
|
|
82
|
-
safeRemoveDir(sourceDir);
|
|
83
|
-
}
|
|
84
|
-
} catch {
|
|
85
|
-
}
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// src/services/source-parser.ts
|
|
91
|
-
import path2 from "path";
|
|
92
|
-
function classifySource(source) {
|
|
93
|
-
const resolvedPath = path2.resolve(source);
|
|
94
|
-
if (pathExists(resolvedPath)) {
|
|
95
|
-
return "local";
|
|
96
|
-
}
|
|
97
|
-
if (/^https?:\/\//i.test(source) || source.includes("github.com")) {
|
|
98
|
-
return "github-url";
|
|
99
|
-
}
|
|
100
|
-
if (/^[^/]+\/[^/]+/.test(source)) {
|
|
101
|
-
return "degit-shorthand";
|
|
102
|
-
}
|
|
103
|
-
return "unknown";
|
|
104
|
-
}
|
|
105
|
-
function extractSkillName(url) {
|
|
106
|
-
const maybeLocalPath = path2.resolve(url);
|
|
107
|
-
if (pathExists(maybeLocalPath)) {
|
|
108
|
-
return path2.basename(maybeLocalPath) || "unknown-skill";
|
|
109
|
-
}
|
|
110
|
-
const cleaned = url.replace(/[#?].*$/, "");
|
|
111
|
-
const treeMatch = cleaned.match(/\/tree\/[^/]+\/(.+?)(?:\/)?$/);
|
|
112
|
-
if (treeMatch) {
|
|
113
|
-
return treeMatch[1].split("/").pop() || "unknown-skill";
|
|
114
|
-
}
|
|
115
|
-
const repoMatch = cleaned.match(/github\.com\/[^/]+\/([^/]+)/);
|
|
116
|
-
if (repoMatch) {
|
|
117
|
-
return repoMatch[1].replace(/\.git$/, "");
|
|
118
|
-
}
|
|
119
|
-
const parts = cleaned.split("/").filter(Boolean);
|
|
120
|
-
if (parts.length >= 2) {
|
|
121
|
-
return parts[parts.length - 1] || "unknown-skill";
|
|
122
|
-
}
|
|
123
|
-
return cleaned || "unknown-skill";
|
|
124
|
-
}
|
|
125
|
-
function toDegitPath(url) {
|
|
126
|
-
const treeMatch = url.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+?)(?:\/)?$/);
|
|
127
|
-
if (treeMatch) {
|
|
128
|
-
const [, owner, repo, branch, subpath] = treeMatch;
|
|
129
|
-
return `${owner}/${repo}/${subpath}#${branch}`;
|
|
130
|
-
}
|
|
131
|
-
const repoMatch = url.match(/github\.com\/([^/]+\/[^/]+)/);
|
|
132
|
-
if (repoMatch) {
|
|
133
|
-
return repoMatch[1].replace(/\.git$/, "");
|
|
134
|
-
}
|
|
135
|
-
return url;
|
|
136
|
-
}
|
|
137
|
-
function resolveLocalPath(source) {
|
|
138
|
-
const resolvedPath = path2.resolve(source);
|
|
139
|
-
return pathExists(resolvedPath) ? resolvedPath : null;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// src/utils/config.ts
|
|
143
|
-
import os from "os";
|
|
144
|
-
import path3 from "path";
|
|
145
|
-
import fs2 from "fs";
|
|
146
|
-
|
|
147
|
-
// src/constants.ts
|
|
148
|
-
var PLATFORMS = ["claude", "codex", "copilot"];
|
|
149
|
-
var DEFAULT_PLATFORM = "claude";
|
|
150
|
-
var ERROR_MESSAGES = {
|
|
151
|
-
EMPTY_INSTALL_DIR: (source) => `Installed directory is empty for source: ${source}
|
|
152
|
-
Source likely does not point to a valid subdirectory.
|
|
153
|
-
Try: https://github.com/<owner>/<repo>/tree/<branch>/skills/<skill-name>
|
|
154
|
-
Example: https://github.com/anthropics/skills/tree/main/skills/pdf`,
|
|
155
|
-
INVALID_SOURCE: (source) => `Unsupported source "${source}". Use a Git URL (e.g. https://github.com/owner/repo) or degit shorthand (e.g. owner/repo[/subdir][#ref]).`,
|
|
156
|
-
NOT_A_DIRECTORY: (path6) => `Source path is not a directory: ${path6}`,
|
|
157
|
-
NO_SKILLS_INSTALLED: "No skills installed.",
|
|
158
|
-
INSTALL_HINT: "Use `skild install <url>` to install a skill."
|
|
159
|
-
};
|
|
160
|
-
var SUCCESS_MESSAGES = {
|
|
161
|
-
SKILL_MD_FOUND: "SKILL.md found \u2713",
|
|
162
|
-
SKILL_MD_WARNING: "Warning: No SKILL.md found"
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
// src/utils/config.ts
|
|
166
|
-
function getSkillsDir(platform = DEFAULT_PLATFORM, projectLevel = false) {
|
|
167
|
-
const base = projectLevel ? process.cwd() : os.homedir();
|
|
168
|
-
switch (platform) {
|
|
169
|
-
case "claude":
|
|
170
|
-
return path3.join(base, ".claude", "skills");
|
|
171
|
-
case "codex":
|
|
172
|
-
return path3.join(base, ".codex", "skills");
|
|
173
|
-
case "copilot":
|
|
174
|
-
return path3.join(base, ".github", "skills");
|
|
175
|
-
default:
|
|
176
|
-
return path3.join(base, ".claude", "skills");
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
var SKILLS_DIR = getSkillsDir(DEFAULT_PLATFORM, false);
|
|
180
|
-
function ensureSkillsDir(platform = DEFAULT_PLATFORM, projectLevel = false) {
|
|
181
|
-
const dir = getSkillsDir(platform, projectLevel);
|
|
182
|
-
if (!fs2.existsSync(dir)) {
|
|
183
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
184
|
-
}
|
|
185
|
-
return dir;
|
|
186
|
-
}
|
|
187
|
-
function getSkillPath(skillName, platform = DEFAULT_PLATFORM, projectLevel = false) {
|
|
188
|
-
return path3.join(getSkillsDir(platform, projectLevel), skillName);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// src/services/skill-installer.ts
|
|
192
|
-
async function cloneRemote(degitSrc, targetPath) {
|
|
193
|
-
const emitter = degit(degitSrc, { force: true, verbose: false });
|
|
194
|
-
await emitter.clone(targetPath);
|
|
195
|
-
}
|
|
196
|
-
function copyLocal(sourcePath, targetPath) {
|
|
197
|
-
if (!isDirectory(sourcePath)) {
|
|
198
|
-
throw new Error(ERROR_MESSAGES.NOT_A_DIRECTORY(sourcePath));
|
|
199
|
-
}
|
|
200
|
-
copyDir(sourcePath, targetPath);
|
|
201
|
-
}
|
|
202
|
-
function validateStaging(stagingPath, source) {
|
|
203
|
-
if (isDirEmpty(stagingPath)) {
|
|
204
|
-
throw new Error(ERROR_MESSAGES.EMPTY_INSTALL_DIR(source));
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
function resolveInstallContext(source, options = {}) {
|
|
208
|
-
const platform = options.target || DEFAULT_PLATFORM;
|
|
209
|
-
const projectLevel = options.local || false;
|
|
210
|
-
const skillName = extractSkillName(source);
|
|
211
|
-
const skillsDir = getSkillsDir(platform, projectLevel);
|
|
212
|
-
const targetPath = getSkillPath(skillName, platform, projectLevel);
|
|
213
|
-
const locationLabel = projectLevel ? "project" : "global";
|
|
214
|
-
const localPath = resolveLocalPath(source);
|
|
215
|
-
const sourceType = localPath ? "local" : classifySource(source);
|
|
216
|
-
if (!localPath && sourceType === "unknown") {
|
|
217
|
-
throw new Error(ERROR_MESSAGES.INVALID_SOURCE(source));
|
|
218
|
-
}
|
|
219
|
-
const degitPath = localPath ? null : toDegitPath(source);
|
|
220
|
-
return {
|
|
221
|
-
source,
|
|
222
|
-
sourceType,
|
|
223
|
-
localPath,
|
|
224
|
-
degitPath,
|
|
225
|
-
skillName,
|
|
226
|
-
platform,
|
|
227
|
-
projectLevel,
|
|
228
|
-
skillsDir,
|
|
229
|
-
targetPath,
|
|
230
|
-
locationLabel
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
async function installSkillFromContext(context) {
|
|
234
|
-
ensureSkillsDir(context.platform, context.projectLevel);
|
|
235
|
-
const tempRoot = createTempDir(context.skillsDir, context.skillName);
|
|
236
|
-
const stagingPath = path4.join(tempRoot, "staging");
|
|
237
|
-
try {
|
|
238
|
-
if (context.localPath) {
|
|
239
|
-
copyLocal(context.localPath, stagingPath);
|
|
240
|
-
} else {
|
|
241
|
-
if (!context.degitPath) {
|
|
242
|
-
throw new Error(ERROR_MESSAGES.INVALID_SOURCE(context.source));
|
|
243
|
-
}
|
|
244
|
-
await cloneRemote(context.degitPath, stagingPath);
|
|
245
|
-
}
|
|
246
|
-
validateStaging(stagingPath, context.source);
|
|
247
|
-
replaceDirAtomic(stagingPath, context.targetPath);
|
|
248
|
-
return {
|
|
249
|
-
skillName: context.skillName,
|
|
250
|
-
platform: context.platform,
|
|
251
|
-
projectLevel: context.projectLevel,
|
|
252
|
-
targetPath: context.targetPath,
|
|
253
|
-
hasSkillMd: hasSkillMd(context.targetPath)
|
|
254
|
-
};
|
|
255
|
-
} finally {
|
|
256
|
-
safeRemoveDir(tempRoot);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
10
|
+
import { installSkill, SkildError } from "@skild/core";
|
|
259
11
|
|
|
260
12
|
// src/utils/logger.ts
|
|
261
13
|
import chalk from "chalk";
|
|
@@ -303,10 +55,10 @@ var logger = {
|
|
|
303
55
|
/**
|
|
304
56
|
* Log a skill entry with status indicator.
|
|
305
57
|
*/
|
|
306
|
-
skillEntry: (name,
|
|
307
|
-
const status =
|
|
58
|
+
skillEntry: (name, path, hasSkillMd) => {
|
|
59
|
+
const status = hasSkillMd ? chalk.green("\u2713") : chalk.yellow("\u26A0");
|
|
308
60
|
console.log(` ${status} ${chalk.cyan(name)}`);
|
|
309
|
-
console.log(chalk.dim(` \u2514\u2500 ${
|
|
61
|
+
console.log(chalk.dim(` \u2514\u2500 ${path}`));
|
|
310
62
|
},
|
|
311
63
|
/**
|
|
312
64
|
* Log installation result details.
|
|
@@ -319,84 +71,212 @@ var logger = {
|
|
|
319
71
|
|
|
320
72
|
// src/commands/install.ts
|
|
321
73
|
async function install(source, options = {}) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
} catch (error) {
|
|
326
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
327
|
-
console.error(chalk2.red(message));
|
|
328
|
-
process.exitCode = 1;
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
const spinner = createSpinner(
|
|
332
|
-
`Installing ${chalk2.cyan(context.skillName)} to ${chalk2.dim(context.platform)} (${context.locationLabel})...`
|
|
333
|
-
);
|
|
74
|
+
const platform = options.target || "claude";
|
|
75
|
+
const scope = options.local ? "project" : "global";
|
|
76
|
+
const spinner = createSpinner(`Installing ${chalk2.cyan(source)} to ${chalk2.dim(platform)} (${scope})...`);
|
|
334
77
|
try {
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
78
|
+
const record = await installSkill(
|
|
79
|
+
{ source },
|
|
80
|
+
{
|
|
81
|
+
platform,
|
|
82
|
+
scope,
|
|
83
|
+
force: Boolean(options.force)
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
spinner.succeed(`Installed ${chalk2.green(record.name)} to ${chalk2.dim(record.installDir)}`);
|
|
87
|
+
if (options.json) {
|
|
88
|
+
console.log(JSON.stringify(record, null, 2));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (record.hasSkillMd) {
|
|
92
|
+
logger.installDetail("SKILL.md found \u2713");
|
|
339
93
|
} else {
|
|
340
|
-
logger.installDetail(
|
|
94
|
+
logger.installDetail("Warning: No SKILL.md found", true);
|
|
95
|
+
}
|
|
96
|
+
if (record.skill?.validation && !record.skill.validation.ok) {
|
|
97
|
+
logger.installDetail(`Validation: ${chalk2.yellow("failed")} (${record.skill.validation.issues.length} issues)`, true);
|
|
98
|
+
} else if (record.skill?.validation?.ok) {
|
|
99
|
+
logger.installDetail(`Validation: ${chalk2.green("ok")}`);
|
|
341
100
|
}
|
|
342
101
|
} catch (error) {
|
|
343
|
-
spinner.fail(`Failed to install ${chalk2.red(
|
|
344
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
102
|
+
spinner.fail(`Failed to install ${chalk2.red(source)}`);
|
|
103
|
+
const message = error instanceof SkildError ? error.message : error instanceof Error ? error.message : String(error);
|
|
345
104
|
console.error(chalk2.red(message));
|
|
346
105
|
process.exitCode = 1;
|
|
347
106
|
}
|
|
348
107
|
}
|
|
349
108
|
|
|
350
109
|
// src/commands/list.ts
|
|
351
|
-
import
|
|
110
|
+
import chalk3 from "chalk";
|
|
111
|
+
import { listSkills } from "@skild/core";
|
|
352
112
|
async function list(options = {}) {
|
|
353
|
-
const platform = options.target ||
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
|
|
113
|
+
const platform = options.target || "claude";
|
|
114
|
+
const scope = options.local ? "project" : "global";
|
|
115
|
+
const skills = listSkills({ platform, scope });
|
|
116
|
+
if (options.json) {
|
|
117
|
+
console.log(JSON.stringify(skills, null, 2));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
357
120
|
if (skills.length === 0) {
|
|
358
|
-
|
|
359
|
-
|
|
121
|
+
console.log(chalk3.dim("No skills installed."));
|
|
122
|
+
console.log(chalk3.dim(`Use ${chalk3.cyan("skild install <source>")} to install a skill.`));
|
|
360
123
|
return;
|
|
361
124
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
logger.skillEntry(skillName, skillPath, hasSkill);
|
|
125
|
+
console.log(chalk3.bold(`
|
|
126
|
+
\u{1F4E6} Installed Skills (${skills.length}) \u2014 ${platform} (${scope}):
|
|
127
|
+
`));
|
|
128
|
+
for (const s of skills) {
|
|
129
|
+
const status = s.hasSkillMd ? chalk3.green("\u2713") : chalk3.yellow("\u26A0");
|
|
130
|
+
console.log(` ${status} ${chalk3.cyan(s.name)}`);
|
|
131
|
+
console.log(chalk3.dim(` \u2514\u2500 ${s.installDir}`));
|
|
370
132
|
}
|
|
371
133
|
console.log("");
|
|
372
134
|
}
|
|
373
135
|
|
|
374
|
-
// src/
|
|
375
|
-
|
|
376
|
-
|
|
136
|
+
// src/commands/info.ts
|
|
137
|
+
import chalk4 from "chalk";
|
|
138
|
+
import { getSkillInfo, SkildError as SkildError2 } from "@skild/core";
|
|
139
|
+
async function info(skill, options = {}) {
|
|
140
|
+
const platform = options.target || "claude";
|
|
141
|
+
const scope = options.local ? "project" : "global";
|
|
142
|
+
try {
|
|
143
|
+
const record = getSkillInfo(skill, { platform, scope });
|
|
144
|
+
if (options.json) {
|
|
145
|
+
console.log(JSON.stringify(record, null, 2));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
console.log(chalk4.bold(`
|
|
149
|
+
${chalk4.cyan(record.name)}
|
|
150
|
+
`));
|
|
151
|
+
console.log(` ${chalk4.dim("Path:")} ${record.installDir}`);
|
|
152
|
+
console.log(` ${chalk4.dim("Source:")} ${record.source}`);
|
|
153
|
+
console.log(` ${chalk4.dim("Target:")} ${record.platform} (${record.scope})`);
|
|
154
|
+
console.log(` ${chalk4.dim("Installed:")} ${record.installedAt}`);
|
|
155
|
+
if (record.updatedAt) console.log(` ${chalk4.dim("Updated:")} ${record.updatedAt}`);
|
|
156
|
+
console.log(` ${chalk4.dim("Hash:")} ${record.contentHash}`);
|
|
157
|
+
console.log(` ${chalk4.dim("SKILL.md:")} ${record.hasSkillMd ? chalk4.green("yes") : chalk4.yellow("no")}`);
|
|
158
|
+
const validation = record.skill?.validation;
|
|
159
|
+
if (validation) {
|
|
160
|
+
console.log(` ${chalk4.dim("Validate:")} ${validation.ok ? chalk4.green("ok") : chalk4.red("failed")}`);
|
|
161
|
+
if (!validation.ok) {
|
|
162
|
+
for (const issue of validation.issues) {
|
|
163
|
+
const color = issue.level === "error" ? chalk4.red : chalk4.yellow;
|
|
164
|
+
console.log(` - ${color(issue.level)}: ${issue.message}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
console.log("");
|
|
169
|
+
} catch (error) {
|
|
170
|
+
const message = error instanceof SkildError2 ? error.message : error instanceof Error ? error.message : String(error);
|
|
171
|
+
console.error(chalk4.red(message));
|
|
172
|
+
process.exitCode = 1;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/commands/uninstall.ts
|
|
177
|
+
import chalk5 from "chalk";
|
|
178
|
+
import { uninstallSkill, SkildError as SkildError3 } from "@skild/core";
|
|
179
|
+
async function uninstall(skill, options = {}) {
|
|
180
|
+
const platform = options.target || "claude";
|
|
181
|
+
const scope = options.local ? "project" : "global";
|
|
182
|
+
const spinner = createSpinner(`Uninstalling ${chalk5.cyan(skill)} from ${chalk5.dim(platform)} (${scope})...`);
|
|
183
|
+
try {
|
|
184
|
+
uninstallSkill(skill, { platform, scope, allowMissingMetadata: Boolean(options.force) });
|
|
185
|
+
spinner.succeed(`Uninstalled ${chalk5.green(skill)}`);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
spinner.fail(`Failed to uninstall ${chalk5.red(skill)}`);
|
|
188
|
+
const message = error instanceof SkildError3 ? error.message : error instanceof Error ? error.message : String(error);
|
|
189
|
+
console.error(chalk5.red(message));
|
|
190
|
+
process.exitCode = 1;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/commands/update.ts
|
|
195
|
+
import chalk6 from "chalk";
|
|
196
|
+
import { updateSkill, SkildError as SkildError4 } from "@skild/core";
|
|
197
|
+
async function update(skill, options = {}) {
|
|
198
|
+
const platform = options.target || "claude";
|
|
199
|
+
const scope = options.local ? "project" : "global";
|
|
200
|
+
const label = skill ? skill : "all skills";
|
|
201
|
+
const spinner = createSpinner(`Updating ${chalk6.cyan(label)} on ${chalk6.dim(platform)} (${scope})...`);
|
|
202
|
+
try {
|
|
203
|
+
const results = await updateSkill(skill, { platform, scope });
|
|
204
|
+
spinner.succeed(`Updated ${chalk6.green(results.length.toString())} skill(s).`);
|
|
205
|
+
if (options.json) {
|
|
206
|
+
console.log(JSON.stringify(results, null, 2));
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
spinner.fail(`Failed to update ${chalk6.red(label)}`);
|
|
210
|
+
const message = error instanceof SkildError4 ? error.message : error instanceof Error ? error.message : String(error);
|
|
211
|
+
console.error(chalk6.red(message));
|
|
212
|
+
process.exitCode = 1;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/commands/validate.ts
|
|
217
|
+
import chalk7 from "chalk";
|
|
218
|
+
import { validateSkill } from "@skild/core";
|
|
219
|
+
async function validate(target, options = {}) {
|
|
220
|
+
const platform = options.target || "claude";
|
|
221
|
+
const scope = options.local ? "project" : "global";
|
|
222
|
+
const value = target || ".";
|
|
223
|
+
const result = validateSkill(value, { platform, scope });
|
|
224
|
+
if (options.json) {
|
|
225
|
+
console.log(JSON.stringify(result, null, 2));
|
|
226
|
+
process.exitCode = result.ok ? 0 : 1;
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (result.ok) {
|
|
230
|
+
console.log(chalk7.green("\u2713"), "Valid skill");
|
|
231
|
+
if (result.frontmatter?.name) console.log(chalk7.dim(` name: ${result.frontmatter.name}`));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
console.error(chalk7.red("\u2717"), "Invalid skill");
|
|
235
|
+
for (const issue of result.issues) {
|
|
236
|
+
const color = issue.level === "error" ? chalk7.red : chalk7.yellow;
|
|
237
|
+
console.error(` - ${color(issue.level)}: ${issue.message}`);
|
|
238
|
+
}
|
|
239
|
+
process.exitCode = 1;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/commands/init.ts
|
|
243
|
+
import chalk8 from "chalk";
|
|
244
|
+
import { initSkill, SkildError as SkildError5 } from "@skild/core";
|
|
245
|
+
async function init(name, options = {}) {
|
|
246
|
+
const spinner = createSpinner(`Initializing ${chalk8.cyan(name)}...`);
|
|
247
|
+
try {
|
|
248
|
+
const createdDir = initSkill(name, {
|
|
249
|
+
dir: options.dir,
|
|
250
|
+
description: options.description,
|
|
251
|
+
force: Boolean(options.force)
|
|
252
|
+
});
|
|
253
|
+
spinner.succeed(`Created ${chalk8.green(name)} at ${chalk8.dim(createdDir)}`);
|
|
254
|
+
console.log(chalk8.dim(`Next: cd ${createdDir} && skild validate .`));
|
|
255
|
+
} catch (error) {
|
|
256
|
+
spinner.fail(`Failed to init ${chalk8.red(name)}`);
|
|
257
|
+
const message = error instanceof SkildError5 ? error.message : error instanceof Error ? error.message : String(error);
|
|
258
|
+
console.error(chalk8.red(message));
|
|
259
|
+
process.exitCode = 1;
|
|
260
|
+
}
|
|
377
261
|
}
|
|
378
262
|
|
|
379
263
|
// src/index.ts
|
|
264
|
+
import { PLATFORMS } from "@skild/core";
|
|
380
265
|
var require2 = createRequire(import.meta.url);
|
|
381
266
|
var { version } = require2("../package.json");
|
|
382
267
|
var program = new Command();
|
|
383
268
|
program.name("skild").description("The npm for Agent Skills \u2014 Discover, install, manage, and publish AI Agent Skills with ease.").version(version);
|
|
384
|
-
program.command("install <source>").alias("i").description("Install a Skill from a Git URL, degit shorthand, or local directory").option("-t, --target <platform>",
|
|
385
|
-
|
|
386
|
-
await install(source, {
|
|
387
|
-
target: platform,
|
|
388
|
-
local: options.local
|
|
389
|
-
});
|
|
390
|
-
});
|
|
391
|
-
program.command("list").alias("ls").description("List installed Skills").option("-t, --target <platform>", "Target platform: claude, codex, copilot", DEFAULT_PLATFORM).option("-l, --local", "List project-level directory instead of global").action(async (options) => {
|
|
392
|
-
const platform = isPlatform(options.target) ? options.target : DEFAULT_PLATFORM;
|
|
393
|
-
await list({
|
|
394
|
-
target: platform,
|
|
395
|
-
local: options.local
|
|
396
|
-
});
|
|
269
|
+
program.command("install <source>").alias("i").description("Install a Skill from a Git URL, degit shorthand, or local directory").option("-t, --target <platform>", `Target platform: ${PLATFORMS.join(", ")}`, "claude").option("-l, --local", "Install to project-level directory instead of global").option("-f, --force", "Overwrite existing installation").option("--json", "Output JSON").action(async (source, options) => {
|
|
270
|
+
await install(source, options);
|
|
397
271
|
});
|
|
272
|
+
program.command("list").alias("ls").description("List installed Skills").option("-t, --target <platform>", `Target platform: ${PLATFORMS.join(", ")}`, "claude").option("-l, --local", "List project-level directory instead of global").option("--json", "Output JSON").action(async (options) => list(options));
|
|
273
|
+
program.command("info <skill>").description("Show installed Skill details").option("-t, --target <platform>", `Target platform: ${PLATFORMS.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("--json", "Output JSON").action(async (skill, options) => info(skill, options));
|
|
274
|
+
program.command("uninstall <skill>").alias("rm").description("Uninstall a Skill").option("-t, --target <platform>", `Target platform: ${PLATFORMS.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("-f, --force", "Uninstall even if metadata is missing").action(async (skill, options) => uninstall(skill, options));
|
|
275
|
+
program.command("update [skill]").alias("up").description("Update one or all installed Skills").option("-t, --target <platform>", `Target platform: ${PLATFORMS.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("--json", "Output JSON").action(async (skill, options) => update(skill, options));
|
|
276
|
+
program.command("validate [target]").alias("v").description("Validate a Skill folder (path) or an installed Skill name").option("-t, --target <platform>", `Target platform: ${PLATFORMS.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("--json", "Output JSON").action(async (target, options) => validate(target, options));
|
|
277
|
+
program.command("init <name>").description("Create a new Skill project").option("--dir <path>", "Target directory (defaults to <name>)").option("--description <text>", "Skill description").option("-f, --force", "Overwrite target directory if it exists").action(async (name, options) => init(name, options));
|
|
398
278
|
program.action(() => {
|
|
399
|
-
console.log(
|
|
279
|
+
console.log(chalk9.bold("\n\u{1F6E1}\uFE0F skild \u2014 Get your agents skilled.\n"));
|
|
400
280
|
program.outputHelp();
|
|
401
281
|
});
|
|
402
282
|
var argv = process.argv.slice();
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skild",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "The npm for Agent Skills — Discover, install, manage, and publish AI Agent Skills with ease.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"skild": "
|
|
8
|
+
"skild": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
@@ -35,12 +35,11 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"chalk": "^5.3.0",
|
|
37
37
|
"commander": "^12.1.0",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
38
|
+
"ora": "^8.0.1",
|
|
39
|
+
"@skild/core": "^0.1.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^20.10.0",
|
|
43
|
-
"bumpp": "^10.3.2",
|
|
44
43
|
"tsup": "^8.0.0",
|
|
45
44
|
"typescript": "^5.3.0"
|
|
46
45
|
},
|
|
@@ -51,7 +50,6 @@
|
|
|
51
50
|
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
52
51
|
"dev": "tsup src/index.ts --format esm --watch",
|
|
53
52
|
"start": "node dist/index.js",
|
|
54
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
55
|
-
"release": "pnpm build && bumpp && pnpm publish --no-git-checks"
|
|
53
|
+
"typecheck": "pnpm -C ../core build && tsc -p tsconfig.json --noEmit"
|
|
56
54
|
}
|
|
57
55
|
}
|