skill-forger 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-present Marc Mansour
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,143 @@
1
+ # 🤹 skill-forger
2
+
3
+ Manage project Agent [skills](https://skills.sh/) from `skills.json`. Uses [`skills`](https://github.com/vercel-labs/skills) CLI under the hood.
4
+
5
+ ## Usage
6
+
7
+ **Install all skills from `skills.json`:**
8
+
9
+ ```bash
10
+ npx skill-forger
11
+ ```
12
+
13
+ <p align="center">
14
+ <img src="./assets/install.svg" alt="Install preview">
15
+ </p>
16
+
17
+ **Add new skills to project:**
18
+
19
+ ```bash
20
+ npx skill-forger add skills.sh/vercel-labs/skills/find-skills
21
+
22
+ npx skill-forger add anthropics/skills:skill-creator
23
+ ```
24
+
25
+ <p align="center">
26
+ <img src="./assets/add.svg" alt="Install preview">
27
+ </p>
28
+
29
+ This creates a `skills.json` file:
30
+
31
+ ```json
32
+ {
33
+ "$schema": "https://unpkg.com/skill-forger/skills_schema.json",
34
+ "skills": [
35
+ { "source": "vercel-labs/skills", "skills": ["find-skills"] },
36
+ { "source": "anthropics/skills", "skills": ["skill-creator"] }
37
+ ]
38
+ }
39
+ ```
40
+
41
+ ## CLI Usage
42
+
43
+ ```sh
44
+ npx skill-forger # Install skills from skills.json (default)
45
+ npx skill-forger install, i # Same as above
46
+ npx skill-forger add <source>... # Add skill source(s) to skills.json
47
+ ```
48
+
49
+ ### Commands
50
+
51
+ #### `install` (default)
52
+
53
+ Installs all skills defined in `skills.json`.
54
+
55
+ ```sh
56
+ npx skill-forger install [options]
57
+ ```
58
+
59
+ | Option | Description |
60
+ | ---------------- | ------------------------------------------------- |
61
+ | `--agent <name>` | Target agent (default: `claude-code`, repeatable) |
62
+ | `-g, --global` | Install skills globally |
63
+ | `-h, --help` | Show help |
64
+
65
+ #### `add`
66
+
67
+ Adds skill source(s) to `skills.json` and installs them.
68
+
69
+ ```sh
70
+ npx skill-forger add <source>... [options]
71
+ ```
72
+
73
+ | Option | Description |
74
+ | ---------------- | ------------------------------------------------- |
75
+ | `--agent <name>` | Target agent (default: `claude-code`, repeatable) |
76
+ | `-h, --help` | Show help |
77
+
78
+ ### Source Formats
79
+
80
+ Sources can be specified in multiple formats:
81
+
82
+ ```sh
83
+ # GitHub owner/repo format
84
+ npx skill-forger add vercel-labs/skills
85
+
86
+ # skills.sh URL
87
+ npx skill-forger add https://skills.sh/vercel-labs/skills/find-skills
88
+ npx skill-forger add skills.sh/vercel-labs/skills/find-skills
89
+
90
+
91
+ # Multiple sources
92
+ npx skill-forger add org/repo-a:skill1 org/repo-b:skill2
93
+
94
+ # Specify skills (comma separated)
95
+ npx skill-forger add vercel-labs/agent-skills:vercel-deploy,vercel-react-native-skills
96
+ ```
97
+
98
+ ### Examples
99
+
100
+ ```sh
101
+ # Install all skills from skills.json
102
+ npx skill-forger
103
+
104
+ # Add a skill source (all skills)
105
+ npx skill-forger add vercel-labs/skills
106
+
107
+ # Add specific skills from a source
108
+ npx skill-forger add vercel-labs/agent-skills:vercel-deploy,vercel-react-native-skills
109
+
110
+ # Add from skills.sh URL
111
+ npx skill-forger add https://skills.sh/vercel-labs/skills/find-skills
112
+
113
+ # Install skills globally
114
+ npx skill-forger install --global
115
+
116
+ # Install for multiple agents
117
+ npx skill-forger install --agent claude-code --agent cursor
118
+ ```
119
+
120
+ ## Development
121
+
122
+ 🤖 Are you a robot? Read [AGENTS.md](./AGENTS.md).
123
+
124
+ <details>
125
+
126
+ <summary>local development</summary>
127
+
128
+ - Clone this repository
129
+ - Install [Bun](https://bun.sh)
130
+ - Install dependencies using `bun install`
131
+ - Run interactive tests using `bun run dev`
132
+
133
+ </details>
134
+
135
+ ## Alternatives
136
+
137
+ - Proposal PR for adding `skill-lock.json` ([vercel-labs/skills#234](https://github.com/vercel-labs/skills/pull/234))
138
+ - Proposal PR for adding `.skills` ([vercel-labs/skills#134](https://github.com/vercel-labs/skills/pull/134))
139
+ - [hairyf/skills-manifest](https://github.com/hairyf/skills-manifest)
140
+
141
+ ## License
142
+
143
+ Published under the [MIT](https://github.com/unjs/skill-forger/blob/main/LICENSE) license 💛.
package/dist/cli.d.mts ADDED
@@ -0,0 +1,8 @@
1
+ //#region src/cli.d.ts
2
+ declare function parseSource(input: string): {
3
+ source: string;
4
+ skills: string[];
5
+ };
6
+ declare function main(argv?: string[]): Promise<void>;
7
+ //#endregion
8
+ export { main, parseSource };
package/dist/cli.mjs ADDED
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import { parseArgs } from "node:util";
4
+ import { existsSync } from "node:fs";
5
+ import { readFile, writeFile } from "node:fs/promises";
6
+ import { dirname, join, resolve } from "node:path";
7
+ import { spawn } from "node:child_process";
8
+ import { fileURLToPath } from "node:url";
9
+ //#region src/config.ts
10
+ function findSkillsConfig(cwd = process.cwd()) {
11
+ let dir = resolve(cwd);
12
+ const root = dirname(dir);
13
+ while (dir !== root) {
14
+ const candidate = join(dir, "skills.json");
15
+ if (existsSync(candidate)) return candidate;
16
+ const parent = dirname(dir);
17
+ if (parent === dir) break;
18
+ dir = parent;
19
+ }
20
+ const rootCandidate = join(dir, "skills.json");
21
+ if (existsSync(rootCandidate)) return rootCandidate;
22
+ }
23
+ function defaultConfig() {
24
+ return { skills: [] };
25
+ }
26
+ async function readSkillsConfig(options = {}) {
27
+ const { cwd, createIfNotExists = false } = options;
28
+ const skillsPath = findSkillsConfig(cwd);
29
+ if (!skillsPath) {
30
+ if (createIfNotExists) {
31
+ const newPath = join(resolve(cwd ?? process.cwd()), "skills.json");
32
+ const config = defaultConfig();
33
+ await writeFile(newPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
34
+ return {
35
+ config,
36
+ path: newPath
37
+ };
38
+ }
39
+ throw new Error("skills.json not found in current directory or any parent directory.");
40
+ }
41
+ const raw = await readFile(skillsPath, "utf8");
42
+ return {
43
+ config: assertSkillsConfig(JSON.parse(raw)),
44
+ path: skillsPath
45
+ };
46
+ }
47
+ async function updateSkillsConfig(updater, options = {}) {
48
+ const { cwd, createIfNotExists = true } = options;
49
+ const { config, path } = await readSkillsConfig({
50
+ cwd,
51
+ createIfNotExists
52
+ });
53
+ const validated = assertSkillsConfig(await updater(config) ?? config);
54
+ await writeFile(path, `${JSON.stringify(validated, null, 2)}\n`, "utf8");
55
+ return {
56
+ config: validated,
57
+ path
58
+ };
59
+ }
60
+ function addSkill(source, skills = [], options = {}) {
61
+ return updateSkillsConfig((config) => {
62
+ const entry = config.skills.find((item) => item.source === source);
63
+ if (!entry) {
64
+ config.skills.push({
65
+ source,
66
+ skills: [...skills]
67
+ });
68
+ return config;
69
+ }
70
+ if (skills.length === 0 || !entry.skills?.length) {
71
+ entry.skills = [];
72
+ return config;
73
+ }
74
+ const merged = new Set(entry.skills);
75
+ for (const skill of skills) merged.add(skill);
76
+ entry.skills = [...merged];
77
+ return config;
78
+ }, options);
79
+ }
80
+ function assertSkillsConfig(value) {
81
+ if (!value || typeof value !== "object" || !("skills" in value)) throw new Error("Invalid skills.json: missing 'skills' key.");
82
+ if (!("$schema" in value)) return {
83
+ $schema: "https://unpkg.com/skill-forger/skills_schema.json",
84
+ ...value
85
+ };
86
+ return value;
87
+ }
88
+ //#endregion
89
+ //#region src/utils/colors.ts
90
+ const enabled = process.stdout.isTTY !== false;
91
+ function code(n) {
92
+ return enabled ? `\x1B[${n}m` : "";
93
+ }
94
+ const c = {
95
+ reset: code(0),
96
+ bold: code(1),
97
+ dim: code(2),
98
+ red: code(31),
99
+ green: code(32),
100
+ yellow: code(33),
101
+ blue: code(34),
102
+ magenta: code(35),
103
+ cyan: code(36)
104
+ };
105
+ //#endregion
106
+ //#region src/utils/gitignore.ts
107
+ function findGitignore(cwd = process.cwd()) {
108
+ let dir = resolve(cwd);
109
+ const root = dirname(dir);
110
+ while (dir !== root) {
111
+ const candidate = join(dir, ".gitignore");
112
+ if (existsSync(candidate)) return candidate;
113
+ const parent = dirname(dir);
114
+ if (parent === dir) break;
115
+ dir = parent;
116
+ }
117
+ const rootCandidate = join(dir, ".gitignore");
118
+ if (existsSync(rootCandidate)) return rootCandidate;
119
+ }
120
+ async function addGitignoreEntry(entries, options = {}) {
121
+ const { cwd, createIfNotExists = true } = options;
122
+ const resolvedCwd = resolve(cwd ?? process.cwd());
123
+ const uniqueEntries = [...new Set((Array.isArray(entries) ? entries : [entries]).map((e) => e.trim()).filter(Boolean))];
124
+ if (uniqueEntries.length === 0) return;
125
+ let gitignorePath = findGitignore(resolvedCwd);
126
+ if (!gitignorePath) {
127
+ if (!createIfNotExists) return;
128
+ gitignorePath = join(resolvedCwd, ".gitignore");
129
+ await writeFile(gitignorePath, `${uniqueEntries.join("\n")}\n`, "utf8");
130
+ return gitignorePath;
131
+ }
132
+ const content = await readFile(gitignorePath, "utf8");
133
+ const existingLines = new Set(content.split("\n").map((line) => line.trim()));
134
+ const newEntries = uniqueEntries.filter((entry) => !existingLines.has(entry));
135
+ if (newEntries.length === 0) return gitignorePath;
136
+ const newContent = `${content}${content.endsWith("\n") ? "" : "\n"}${newEntries.join("\n")}\n`;
137
+ await writeFile(gitignorePath, newContent, "utf8");
138
+ return gitignorePath;
139
+ }
140
+ //#endregion
141
+ //#region src/skills.ts
142
+ let _skillsBinaryCache = null;
143
+ function findSkillsBinary(options) {
144
+ if (options?.cache !== false && _skillsBinaryCache !== null) return _skillsBinaryCache;
145
+ let dir = dirname(fileURLToPath(import.meta.url));
146
+ while (true) {
147
+ const candidate = join(dir, "node_modules", ".bin", "skills");
148
+ if (existsSync(candidate)) {
149
+ _skillsBinaryCache = candidate;
150
+ return candidate;
151
+ }
152
+ const parent = dirname(dir);
153
+ if (parent === dir) break;
154
+ dir = parent;
155
+ }
156
+ _skillsBinaryCache = void 0;
157
+ }
158
+ async function installSkills(options = {}) {
159
+ const { config, path: configPath } = await readSkillsConfig({ cwd: options.cwd });
160
+ await addGitignoreEntry(".agents", { cwd: dirname(configPath) });
161
+ const total = config.skills.length;
162
+ const totalStart = performance.now();
163
+ const globalPrefix = options.global ? `${c.magenta}[ global ]${c.reset} ` : "";
164
+ console.log(`${globalPrefix}🤹 Installing ${total} skill${total === 1 ? "" : "s"}...\n`);
165
+ let i = 0;
166
+ for (const entry of config.skills) {
167
+ i++;
168
+ await installSkillSource(entry, {
169
+ ...options,
170
+ prefix: `[${i}/${total}] `
171
+ });
172
+ }
173
+ const totalDuration = formatDuration(performance.now() - totalStart);
174
+ console.log(`${globalPrefix}🎉 Done! ${total} skill${total === 1 ? "" : "s"} installed in ${c.green}${totalDuration}${c.reset}.`);
175
+ }
176
+ async function installSkillSource(entry, options) {
177
+ const skillsBinary = findSkillsBinary();
178
+ const globalPrefix = options.global ? `${c.magenta}[ global ]${c.reset} ` : "";
179
+ const skillList = (entry.skills?.length || 0) > 0 ? entry.skills?.join(", ") : "all skills";
180
+ console.log(`${globalPrefix}${c.cyan}◐${c.reset} ${options.prefix || ""}Installing ${c.cyan}${entry.source}${c.reset} ${c.dim}(${skillList})${c.reset}`);
181
+ const [command, args] = skillsBinary ? [skillsBinary, ["add", entry.source]] : ["npx", [
182
+ "skills",
183
+ "add",
184
+ entry.source
185
+ ]];
186
+ if (entry.skills && entry.skills.length > 0) args.push("--skill", ...entry.skills);
187
+ else args.push("--skill", "*");
188
+ if (options.agents && options.agents.length > 0) args.push("--agent", ...options.agents);
189
+ if (options.global) args.push("--global");
190
+ if (options.yes) args.push("--yes");
191
+ if (process.env.DEBUG) console.log(`${c.dim}$ ${[command.replace(process.cwd(), "."), ...args].join(" ")}${c.reset}\n`);
192
+ const skillStart = performance.now();
193
+ await runCommand(command, args);
194
+ const skillDuration = formatDuration(performance.now() - skillStart);
195
+ console.log(`${globalPrefix}${c.green}✔${c.reset} Installed ${entry.source} ${c.dim}(${skillDuration})${c.reset}\n`);
196
+ }
197
+ function formatDuration(ms) {
198
+ const rounded = Math.round(ms);
199
+ if (rounded < 1e3) return `${rounded}ms`;
200
+ const seconds = Math.round(rounded / 1e3);
201
+ return seconds < 60 ? `${seconds}s` : `${Math.round(seconds / 60)}m`;
202
+ }
203
+ function runCommand(command, args) {
204
+ return new Promise((resolve, reject) => {
205
+ const stdout = [];
206
+ const stderr = [];
207
+ const child = spawn(command, args, { stdio: "pipe" });
208
+ child.stdout.on("data", (data) => stdout.push(data));
209
+ child.stderr.on("data", (data) => stderr.push(data));
210
+ child.on("error", reject);
211
+ child.on("close", (code) => {
212
+ if (code === 0) {
213
+ resolve();
214
+ return;
215
+ }
216
+ const output = [Buffer.concat(stdout).toString(), Buffer.concat(stderr).toString()].filter(Boolean).join("\n");
217
+ if (output) console.error(output);
218
+ reject(/* @__PURE__ */ new Error(`${command} exited with code ${code ?? "unknown"}.`));
219
+ });
220
+ });
221
+ }
222
+ //#endregion
223
+ //#region src/cli.ts
224
+ const { version } = createRequire(import.meta.url)("../package.json");
225
+ const name = "skill-forger";
226
+ const SKILLS_SH_RE = /^(?:https?:\/\/)?skills\.sh\/(.+)/;
227
+ function parseSource(input) {
228
+ const skillsShMatch = input.match(SKILLS_SH_RE)?.[1];
229
+ if (skillsShMatch) {
230
+ const [namespace, repo, ...skills] = skillsShMatch.split("/");
231
+ if (!(namespace && repo)) return {
232
+ source: input,
233
+ skills: []
234
+ };
235
+ const filtered = skills.flatMap((s) => s.split(",")).map((s) => s.trim()).filter(Boolean);
236
+ return {
237
+ source: `${namespace}/${repo}`,
238
+ skills: filtered.includes("*") ? [] : filtered
239
+ };
240
+ }
241
+ const [source = "", ...parts] = input.split(":");
242
+ const skills = parts.flatMap((p) => p.split(",")).map((s) => s.trim()).filter(Boolean);
243
+ return {
244
+ source,
245
+ skills: skills.includes("*") ? [] : skills
246
+ };
247
+ }
248
+ async function handleInstall(values) {
249
+ if (!findSkillsConfig()) {
250
+ console.log(`${c.yellow}No skills.json found.${c.reset}
251
+
252
+ Get started by adding a skill source:
253
+
254
+ ${c.dim}$${c.reset} npx ${name} add ${c.cyan}vercel-labs/skills${c.reset}
255
+ `);
256
+ return;
257
+ }
258
+ await installSkills({
259
+ yes: true,
260
+ agents: values.agent || ["claude-code"],
261
+ global: values.global
262
+ });
263
+ }
264
+ async function handleAdd(positionals, values) {
265
+ const sources = positionals.slice(1);
266
+ if (sources.length === 0) {
267
+ showUsage("add");
268
+ throw new Error("Missing skill source.");
269
+ }
270
+ const parsedSources = [];
271
+ for (const rawSource of sources) {
272
+ const { source, skills } = parseSource(rawSource);
273
+ const existing = parsedSources.find((p) => p.source === source);
274
+ if (existing) if (skills.length === 0 || existing.skills.length === 0) existing.skills = [];
275
+ else existing.skills = [...new Set([...existing.skills, ...skills])];
276
+ else parsedSources.push({
277
+ source,
278
+ skills: [...skills]
279
+ });
280
+ }
281
+ const agents = values.agent || ["claude-code"];
282
+ const globalPrefix = values.global ? `${c.magenta}[ global ]${c.reset} ` : "";
283
+ for (const { source, skills } of parsedSources) {
284
+ await installSkillSource({
285
+ source,
286
+ skills
287
+ }, {
288
+ agents,
289
+ yes: true,
290
+ global: values.global
291
+ });
292
+ await addSkill(source, skills);
293
+ console.log(`${globalPrefix}${c.green}✔${c.reset} Added ${c.cyan}${source}${c.reset} to skills.json`);
294
+ }
295
+ }
296
+ async function main(argv = process.argv.slice(2)) {
297
+ const { values, positionals } = parseArgs({
298
+ args: argv,
299
+ allowPositionals: true,
300
+ options: {
301
+ agent: {
302
+ type: "string",
303
+ multiple: true
304
+ },
305
+ global: {
306
+ type: "boolean",
307
+ short: "g"
308
+ },
309
+ help: {
310
+ type: "boolean",
311
+ short: "h"
312
+ },
313
+ version: {
314
+ type: "boolean",
315
+ short: "v"
316
+ }
317
+ }
318
+ });
319
+ if (values.version) {
320
+ console.log(`${name} ${version}`);
321
+ return;
322
+ }
323
+ const command = positionals[0];
324
+ if (values.help) {
325
+ showUsage(command);
326
+ return;
327
+ }
328
+ if (!command || command === "install" || command === "i") {
329
+ await handleInstall(values);
330
+ return;
331
+ }
332
+ if (command === "add") {
333
+ await handleAdd(positionals, values);
334
+ return;
335
+ }
336
+ throw new Error(`Unknown command: ${command}`);
337
+ }
338
+ function showUsage(command) {
339
+ if (command === "add") {
340
+ console.log(`
341
+ ${c.bold}Usage:${c.reset} ${c.cyan}${name} add${c.reset} <source>... [options]
342
+
343
+ ${c.bold}Arguments:${c.reset}
344
+ ${c.cyan}<source>${c.reset} Skill source ${c.dim}(e.g., vercel-labs/skills:pdf,commit)${c.reset}
345
+
346
+ ${c.bold}Options:${c.reset}
347
+ ${c.cyan}--agent${c.reset} <name> Target agent ${c.dim}(default: claude-code, can be repeated)${c.reset}
348
+ ${c.cyan}-h, --help${c.reset} Show this help message
349
+
350
+ ${c.bold}Examples:${c.reset}
351
+ ${c.dim}$${c.reset} ${name} add vercel-labs/skills
352
+ ${c.dim}$${c.reset} ${name} add vercel-labs/skills:pdf,commit
353
+ ${c.dim}$${c.reset} ${name} add vercel-labs/skills:find-skills anthropics/skills:skill-creator
354
+ ${c.dim}$${c.reset} ${name} add https://skills.sh/vercel-labs/skills/pdf
355
+ `);
356
+ return;
357
+ }
358
+ if (command === "install" || command === "i") {
359
+ console.log(`
360
+ ${c.bold}Usage:${c.reset} ${c.cyan}${name} install${c.reset} [options]
361
+
362
+ ${c.bold}Options:${c.reset}
363
+ ${c.cyan}--agent${c.reset} <name> Target agent ${c.dim}(default: claude-code, can be repeated)${c.reset}
364
+ ${c.cyan}-g, --global${c.reset} Install skills globally
365
+ ${c.cyan}-h, --help${c.reset} Show this help message
366
+
367
+ ${c.bold}Examples:${c.reset}
368
+ ${c.dim}$${c.reset} npx ${name} install
369
+ ${c.dim}$${c.reset} npx ${name} install --global
370
+ ${c.dim}$${c.reset} npx ${name} install --agent claude-code --agent cursor
371
+ `);
372
+ return;
373
+ }
374
+ console.log(`
375
+ ${c.bold}${name}${c.reset} ${c.dim}v${version}${c.reset}
376
+
377
+ Manage project skills declaratively with ${c.cyan}skills.json${c.reset}
378
+
379
+ ${c.bold}Usage:${c.reset} ${c.cyan}${name}${c.reset} <command> [options]
380
+
381
+ ${c.bold}Commands:${c.reset}
382
+ ${c.cyan}install, i${c.reset} Install skills from skills.json ${c.dim}(default)${c.reset}
383
+ ${c.cyan}add${c.reset} Add a skill source to skills.json
384
+
385
+ ${c.bold}Options:${c.reset}
386
+ ${c.cyan}-h, --help${c.reset} Show help for a command
387
+ ${c.cyan}-v, --version${c.reset} Show version number
388
+
389
+ ${c.bold}Examples:${c.reset}
390
+ ${c.dim}$${c.reset} ${name} ${c.dim}# Install all skills${c.reset}
391
+ ${c.dim}$${c.reset} ${name} add vercel-labs/skills ${c.dim}# Add a skill source${c.reset}
392
+ ${c.dim}$${c.reset} ${name} add owner/repo:pdf,commit ${c.dim}# Add specific skills${c.reset}
393
+ ${c.dim}$${c.reset} ${name} add org/a:skill1 org/b:skill2 ${c.dim}# Add multiple sources${c.reset}
394
+
395
+ Run ${c.cyan}${name} <command> --help${c.reset} for command-specific help.
396
+ `);
397
+ }
398
+ main().catch((error) => {
399
+ console.error(`${c.red}error:${c.reset} ${error instanceof Error ? error.message : error}`);
400
+ process.exitCode = 1;
401
+ });
402
+ //#endregion
403
+ export { main, parseSource };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "skill-forger",
3
+ "version": "0.1.0",
4
+ "description": "Manage project Agent skills declaratively with skills.json",
5
+ "license": "MIT",
6
+ "author": "Marc Mansour",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/marcm8793/skillforge.git"
10
+ },
11
+ "type": "module",
12
+ "bin": {
13
+ "skill-forger": "./dist/cli.mjs",
14
+ "sf": "./dist/cli.mjs"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "skills_schema.json"
19
+ ],
20
+ "keywords": [
21
+ "skills",
22
+ "cli",
23
+ "agent-skills",
24
+ "ai-agents",
25
+ "claude-code"
26
+ ],
27
+ "scripts": {
28
+ "build": "obuild",
29
+ "dev": "vitest --watch",
30
+ "test": "vitest run",
31
+ "check": "ultracite check",
32
+ "fix": "ultracite fix",
33
+ "typecheck": "tsc --noEmit",
34
+ "prepack": "bun run build"
35
+ },
36
+ "dependencies": {
37
+ "skills": "^1.4.6"
38
+ },
39
+ "devDependencies": {
40
+ "@biomejs/biome": "2.4.7",
41
+ "@types/bun": "latest",
42
+ "jiti": "^2.6.1",
43
+ "obuild": "^0.4.32",
44
+ "typescript": "^5",
45
+ "ultracite": "7.3.2",
46
+ "vitest": "^4.1.2"
47
+ }
48
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://unpkg.com/skill-forger/skills_schema.json",
4
+ "title": "Skills Config",
5
+ "description": "Configuration file for skill-forger - manages project skills",
6
+ "type": "object",
7
+ "required": ["skills"],
8
+ "properties": {
9
+ "$schema": {
10
+ "type": "string",
11
+ "description": "JSON Schema reference"
12
+ },
13
+ "skills": {
14
+ "type": "array",
15
+ "description": "List of skill sources to install",
16
+ "items": {
17
+ "type": "object",
18
+ "required": ["source"],
19
+ "properties": {
20
+ "source": {
21
+ "type": "string",
22
+ "description": "Skill source identifier (e.g., 'vercel-labs/skills')"
23
+ },
24
+ "skills": {
25
+ "type": "array",
26
+ "description": "Specific skills to install from this source (empty array or omitted = all)",
27
+ "items": {
28
+ "type": "string"
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }