skills-ws 1.0.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.
Files changed (2) hide show
  1. package/bin/skillsadd.mjs +147 -0
  2. package/package.json +16 -0
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { mkdirSync, writeFileSync, existsSync, readdirSync } from "fs";
4
+ import { join, resolve } from "path";
5
+ import { get } from "https";
6
+
7
+ const GREEN = "\x1b[32m";
8
+ const DIM = "\x1b[2m";
9
+ const BOLD = "\x1b[1m";
10
+ const RESET = "\x1b[0m";
11
+
12
+ const SKILLS_JSON = "https://skills-ws.vercel.app/skills.json";
13
+ const RAW_BASE = "https://raw.githubusercontent.com/san-npm/skills-ws/main/skills-data";
14
+
15
+ function fetch(url) {
16
+ return new Promise((resolve, reject) => {
17
+ get(url, (res) => {
18
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
19
+ return fetch(res.headers.location).then(resolve, reject);
20
+ }
21
+ if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`));
22
+ const chunks = [];
23
+ res.on("data", (c) => chunks.push(c));
24
+ res.on("end", () => resolve(Buffer.concat(chunks).toString()));
25
+ res.on("error", reject);
26
+ }).on("error", reject);
27
+ });
28
+ }
29
+
30
+ function detectTarget() {
31
+ const cwd = process.cwd();
32
+ if (existsSync(join(cwd, ".claude")) || existsSync(join(cwd, "AGENTS.md")))
33
+ return join(cwd, ".claude", "skills");
34
+ if (existsSync(join(cwd, ".cursor")))
35
+ return join(cwd, ".cursor", "skills");
36
+ if (existsSync(join(cwd, ".codex")))
37
+ return join(cwd, ".codex", "skills");
38
+ const home = process.env.HOME || process.env.USERPROFILE;
39
+ if (existsSync(join(home, "openclaw")))
40
+ return join(home, "openclaw", "skills");
41
+ return join(cwd, ".claude", "skills");
42
+ }
43
+
44
+ function parseArgs() {
45
+ const args = process.argv.slice(2);
46
+ const opts = { skill: null, dir: null, list: false, help: false };
47
+
48
+ for (let i = 0; i < args.length; i++) {
49
+ if (args[i] === "--skill" && args[i + 1]) { opts.skill = args[++i]; }
50
+ else if (args[i] === "--dir" && args[i + 1]) { opts.dir = args[++i]; }
51
+ else if (args[i] === "--list" || args[i] === "list") { opts.list = true; }
52
+ else if (args[i] === "--help" || args[i] === "-h" || args[i] === "help") { opts.help = true; }
53
+ else if (!args[i].startsWith("-") && i === 0) {
54
+ // First positional arg — could be a pack name, ignore for now
55
+ }
56
+ }
57
+ return opts;
58
+ }
59
+
60
+ async function main() {
61
+ const opts = parseArgs();
62
+
63
+ if (opts.help) {
64
+ console.log(`
65
+ ${GREEN}${BOLD}skillsadd${RESET} — install agent skills from skills.ws
66
+
67
+ ${BOLD}Usage:${RESET}
68
+ npx skills-ws Install all skills
69
+ npx skills-ws --skill seo-geo Install a single skill
70
+ npx skills-ws --list List available skills
71
+ npx skills-ws --dir ./my-skills Custom install directory
72
+
73
+ ${BOLD}Options:${RESET}
74
+ --skill <name> Install a specific skill
75
+ --dir <path> Override install directory
76
+ --list List all available skills
77
+ --help Show this help
78
+
79
+ ${DIM}https://skills.ws${RESET}
80
+ `);
81
+ return;
82
+ }
83
+
84
+ // Fetch skills list
85
+ let data;
86
+ try {
87
+ const raw = await fetch(SKILLS_JSON);
88
+ data = JSON.parse(raw);
89
+ } catch (e) {
90
+ console.error("Failed to fetch skills list from skills.ws");
91
+ process.exit(1);
92
+ }
93
+
94
+ if (opts.list) {
95
+ console.log(`\n${GREEN}${BOLD}${data.skills.length} skills available:${RESET}\n`);
96
+ const cats = {};
97
+ for (const s of data.skills) {
98
+ (cats[s.category] = cats[s.category] || []).push(s.name);
99
+ }
100
+ for (const [cat, skills] of Object.entries(cats).sort()) {
101
+ console.log(` ${BOLD}${cat}${RESET}`);
102
+ for (const name of skills.sort()) {
103
+ console.log(` ${DIM}-${RESET} ${name}`);
104
+ }
105
+ console.log();
106
+ }
107
+ return;
108
+ }
109
+
110
+ const targetDir = opts.dir ? resolve(opts.dir) : detectTarget();
111
+ const skillNames = opts.skill
112
+ ? [opts.skill]
113
+ : data.skills.map((s) => s.name);
114
+
115
+ console.log(`\n${GREEN}${BOLD}skills.ws${RESET} — installing ${skillNames.length} skill${skillNames.length > 1 ? "s" : ""}`);
116
+ console.log(`${DIM}Target: ${targetDir}${RESET}\n`);
117
+
118
+ let installed = 0;
119
+ let failed = 0;
120
+
121
+ for (const name of skillNames) {
122
+ const skillDir = join(targetDir, name);
123
+ const url = `${RAW_BASE}/${name}/SKILL.md`;
124
+
125
+ try {
126
+ const content = await fetch(url);
127
+ mkdirSync(skillDir, { recursive: true });
128
+ writeFileSync(join(skillDir, "SKILL.md"), content);
129
+ console.log(` ${GREEN}+${RESET} ${name}`);
130
+ installed++;
131
+ } catch {
132
+ console.log(` ${DIM}x ${name} (not found)${RESET}`);
133
+ failed++;
134
+ }
135
+ }
136
+
137
+ console.log(`\n${GREEN}${BOLD}Done.${RESET} Installed ${installed} skills to ${targetDir}`);
138
+ if (failed > 0) {
139
+ console.log(`${DIM}${failed} skills could not be fetched${RESET}`);
140
+ }
141
+ console.log();
142
+ }
143
+
144
+ main().catch((e) => {
145
+ console.error(e.message);
146
+ process.exit(1);
147
+ });
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "skills-ws",
3
+ "version": "1.0.0",
4
+ "description": "Install agent skills from skills.ws — marketing, growth, analytics & conversion skills for AI coding assistants",
5
+ "bin": {
6
+ "skills-ws": "./bin/skillsadd.mjs"
7
+ },
8
+ "keywords": ["ai", "agent", "skills", "claude", "cursor", "codex", "openclaw", "marketing", "growth", "seo", "analytics"],
9
+ "author": "Commit Media",
10
+ "license": "MIT",
11
+ "homepage": "https://skills.ws",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/san-npm/skills-ws"
15
+ }
16
+ }