mkt-skills 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.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # mkt-skills
2
+
3
+ Install Claude Code marketing skills from [marketingskills.directory](https://marketingskills.directory).
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx mkt-skills install seo-content
9
+ ```
10
+
11
+ This downloads the skill and saves it to `~/.claude/skills/seo-content/SKILL.md`, ready for Claude Code to use.
12
+
13
+ ## Commands
14
+
15
+ ### Install a skill
16
+
17
+ ```bash
18
+ npx mkt-skills install <slug>
19
+ npx mkt-skills install seo-content --force # overwrite existing
20
+ ```
21
+
22
+ ### List available skills
23
+
24
+ ```bash
25
+ npx mkt-skills list
26
+ npx mkt-skills list --category=seo
27
+ ```
28
+
29
+ ### Search skills
30
+
31
+ ```bash
32
+ npx mkt-skills search "email automation"
33
+ npx mkt-skills search content
34
+ ```
35
+
36
+ ### Skill details
37
+
38
+ ```bash
39
+ npx mkt-skills info marketing-audit
40
+ ```
41
+
42
+ ### Uninstall a skill
43
+
44
+ ```bash
45
+ npx mkt-skills uninstall seo-content
46
+ ```
47
+
48
+ ## Requirements
49
+
50
+ - Node.js 18 or later
51
+ - Claude Code (skills are installed to `~/.claude/skills/`)
52
+
53
+ ## Local Development
54
+
55
+ Override the API URL for testing against a local WordPress instance:
56
+
57
+ ```bash
58
+ MKT_API_URL=http://localhost:8080/wp-json/mkt/v1 npx mkt-skills list
59
+ ```
60
+
61
+ ## License
62
+
63
+ MIT
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { install, list, search, info, uninstall } from '../lib/commands.mjs';
4
+
5
+ const [,, command, ...args] = process.argv;
6
+
7
+ const commands = { install, list, search, info, uninstall };
8
+
9
+ if (!command || command === '--help' || command === '-h') {
10
+ showHelp();
11
+ process.exit(0);
12
+ }
13
+
14
+ if (command === '--version' || command === '-v') {
15
+ console.log('mkt-skills 1.0.0');
16
+ process.exit(0);
17
+ }
18
+
19
+ if (!commands[command]) {
20
+ console.error(`Unknown command: ${command}\nRun "mkt-skills --help" for usage.`);
21
+ process.exit(1);
22
+ }
23
+
24
+ try {
25
+ await commands[command](args);
26
+ } catch (err) {
27
+ if (err.name === 'AbortError') {
28
+ console.error('Request timed out. Check your internet connection.');
29
+ } else if (err.cause?.code === 'ENOTFOUND' || err.cause?.code === 'ECONNREFUSED') {
30
+ console.error('Could not connect to marketingskills.directory. Check your internet connection.');
31
+ } else {
32
+ console.error(`Error: ${err.message}`);
33
+ }
34
+ process.exit(1);
35
+ }
36
+
37
+ function showHelp() {
38
+ console.log(`
39
+ Marketing Skills Directory CLI
40
+
41
+ Usage:
42
+ npx mkt-skills <command> [args]
43
+
44
+ Commands:
45
+ install <slug> Install a skill to ~/.claude/skills/
46
+ list List all available skills
47
+ search <keyword> Search skills by keyword
48
+ info <slug> Show details about a skill
49
+ uninstall <slug> Remove an installed skill
50
+
51
+ Options:
52
+ --help, -h Show this help
53
+ --version, -v Show version
54
+
55
+ Examples:
56
+ npx mkt-skills install seo-content
57
+ npx mkt-skills search "email automation"
58
+ npx mkt-skills list --category=seo
59
+ npx mkt-skills info marketing-audit
60
+
61
+ https://marketingskills.directory
62
+ `);
63
+ }
package/lib/api.mjs ADDED
@@ -0,0 +1,43 @@
1
+ const BASE_URL = process.env.MKT_API_URL || 'https://marketingskills.directory/wp-json/mkt/v1';
2
+ const TIMEOUT = 30_000;
3
+
4
+ async function request(path) {
5
+ const controller = new AbortController();
6
+ const timer = setTimeout(() => controller.abort(), TIMEOUT);
7
+
8
+ try {
9
+ const res = await fetch(`${BASE_URL}${path}`, {
10
+ signal: controller.signal,
11
+ headers: { 'User-Agent': 'mkt-skills-cli/1.0.0' },
12
+ });
13
+ return res;
14
+ } finally {
15
+ clearTimeout(timer);
16
+ }
17
+ }
18
+
19
+ export async function fetchCatalog() {
20
+ const res = await request('/catalog');
21
+ if (!res.ok) throw new Error(`Failed to fetch catalog (HTTP ${res.status})`);
22
+ return res.json();
23
+ }
24
+
25
+ export async function fetchInstall(slug) {
26
+ const res = await request(`/install/${encodeURIComponent(slug)}`);
27
+
28
+ if (res.status === 403) {
29
+ const data = await res.json();
30
+ const err = new Error(data.message);
31
+ err.code = 'PREMIUM';
32
+ err.url = data.purchase_url;
33
+ throw err;
34
+ }
35
+
36
+ if (res.status === 404) {
37
+ const data = await res.json();
38
+ throw Object.assign(new Error(data.message), { code: 'NOT_FOUND' });
39
+ }
40
+
41
+ if (!res.ok) throw new Error(`Install failed (HTTP ${res.status})`);
42
+ return res.json();
43
+ }
@@ -0,0 +1,224 @@
1
+ import { mkdir, rm, access, writeFile } from 'fs/promises';
2
+ import { createInterface } from 'readline';
3
+ import { fetchCatalog, fetchInstall } from './api.mjs';
4
+ import { skillDir, skillFile, installedSlugs } from './paths.mjs';
5
+ import { bold, dim, green, red, yellow, cyan } from './ui.mjs';
6
+
7
+ // ── install ─────────────────────────────────────────────────
8
+
9
+ export async function install(args) {
10
+ const force = args.includes('--force') || args.includes('-f');
11
+ const slug = args.find(a => !a.startsWith('-'));
12
+
13
+ if (!slug) {
14
+ console.error(`Usage: mkt-skills install <slug> [--force]\n`);
15
+ process.exit(1);
16
+ }
17
+
18
+ const dest = skillFile(slug);
19
+
20
+ if (!force) {
21
+ try {
22
+ await access(dest);
23
+ const ok = await confirm(`Skill "${slug}" is already installed. Overwrite?`);
24
+ if (!ok) {
25
+ console.log('Aborted.');
26
+ return;
27
+ }
28
+ } catch { /* not installed — proceed */ }
29
+ }
30
+
31
+ let data;
32
+ try {
33
+ data = await fetchInstall(slug);
34
+ } catch (err) {
35
+ if (err.code === 'PREMIUM') {
36
+ console.log(`\n${red('x')} ${bold(`"${slug}"`)} is a premium skill.`);
37
+ console.log(` ${dim('->')} Purchase at: ${cyan(err.url)}\n`);
38
+ process.exit(1);
39
+ }
40
+ if (err.code === 'NOT_FOUND') {
41
+ console.log(`\n${red('x')} Skill "${slug}" not found.`);
42
+ console.log(` ${dim('->')} Browse available skills: ${cyan('npx mkt-skills list')}\n`);
43
+ process.exit(1);
44
+ }
45
+ throw err;
46
+ }
47
+
48
+ await mkdir(skillDir(slug), { recursive: true });
49
+ await writeFile(dest, data.content, 'utf-8');
50
+
51
+ console.log(`\n${green('+')} Installed ${bold(`"${data.title}"`)} (${data.lines} lines)`);
52
+ console.log(` ${dim('->')} ${dest}\n`);
53
+ }
54
+
55
+ // ── list ────────────────────────────────────────────────────
56
+
57
+ export async function list(args) {
58
+ const catFlag = args.find(a => a.startsWith('--category='));
59
+ const catFilter = catFlag ? catFlag.split('=')[1].toLowerCase() : null;
60
+
61
+ const catalog = await fetchCatalog();
62
+ const installed = new Set(await installedSlugs());
63
+
64
+ let items = catalog;
65
+ if (catFilter) {
66
+ items = items.filter(s => s.categories.some(c => c.toLowerCase().includes(catFilter)));
67
+ }
68
+
69
+ if (items.length === 0) {
70
+ console.log(catFilter
71
+ ? `No skills found in category "${catFilter}".`
72
+ : 'No skills available.');
73
+ return;
74
+ }
75
+
76
+ console.log(`\n${bold('Marketing Skills Directory')} (${items.length} skills)\n`);
77
+
78
+ const slugW = Math.min(30, Math.max(4, ...items.map(s => s.slug.length)));
79
+ const titleW = Math.min(35, Math.max(5, ...items.map(s => s.title.length)));
80
+
81
+ const header = [
82
+ 'SLUG'.padEnd(slugW),
83
+ 'TITLE'.padEnd(titleW),
84
+ 'LINES'.padStart(5),
85
+ 'PRICE'.padEnd(7),
86
+ 'CATEGORY',
87
+ ].join(' ');
88
+
89
+ console.log(dim(header));
90
+ console.log(dim('-'.repeat(header.length)));
91
+
92
+ for (const s of items) {
93
+ const inst = installed.has(s.slug) ? green(' [installed]') : '';
94
+ const price = s.price === 'paid' ? yellow('paid') : green('free');
95
+ const cats = s.categories.join(', ') || '-';
96
+ console.log([
97
+ s.slug.padEnd(slugW),
98
+ (s.title.length > titleW ? s.title.slice(0, titleW - 1) + '\u2026' : s.title).padEnd(titleW),
99
+ String(s.lines).padStart(5),
100
+ price.padEnd(7 + (price.length - (s.price === 'paid' ? 4 : 4))),
101
+ cats,
102
+ ].join(' ') + inst);
103
+ }
104
+
105
+ console.log(`\n${dim('Install:')} npx mkt-skills install <slug>\n`);
106
+ }
107
+
108
+ // ── search ──────────────────────────────────────────────────
109
+
110
+ export async function search(args) {
111
+ const query = args.filter(a => !a.startsWith('-')).join(' ').toLowerCase();
112
+
113
+ if (!query) {
114
+ console.error('Usage: mkt-skills search <keyword>\n');
115
+ process.exit(1);
116
+ }
117
+
118
+ const catalog = await fetchCatalog();
119
+ const results = catalog.filter(s =>
120
+ s.slug.includes(query) ||
121
+ s.title.toLowerCase().includes(query) ||
122
+ s.description.toLowerCase().includes(query) ||
123
+ s.categories.some(c => c.toLowerCase().includes(query))
124
+ );
125
+
126
+ if (results.length === 0) {
127
+ console.log(`\nNo skills found matching "${query}".`);
128
+ console.log(`${dim('->')} Browse all: ${cyan('npx mkt-skills list')}\n`);
129
+ return;
130
+ }
131
+
132
+ console.log(`\n${bold(`Search: "${query}"`)} (${results.length} results)\n`);
133
+
134
+ for (const s of results) {
135
+ const price = s.price === 'paid' ? yellow(' [premium]') : '';
136
+ const cats = s.categories.length ? dim(` (${s.categories.join(', ')})`) : '';
137
+ console.log(` ${bold(s.slug)}${price}${cats}`);
138
+ console.log(` ${s.description}`);
139
+ console.log('');
140
+ }
141
+
142
+ console.log(`${dim('Install:')} npx mkt-skills install <slug>\n`);
143
+ }
144
+
145
+ // ── info ────────────────────────────────────────────────────
146
+
147
+ export async function info(args) {
148
+ const slug = args.find(a => !a.startsWith('-'));
149
+
150
+ if (!slug) {
151
+ console.error('Usage: mkt-skills info <slug>\n');
152
+ process.exit(1);
153
+ }
154
+
155
+ const catalog = await fetchCatalog();
156
+ const skill = catalog.find(s => s.slug === slug);
157
+
158
+ if (!skill) {
159
+ console.log(`\n${red('x')} Skill "${slug}" not found.`);
160
+ // suggest close matches
161
+ const close = catalog.filter(s => s.slug.includes(slug) || slug.includes(s.slug)).slice(0, 3);
162
+ if (close.length) {
163
+ console.log(`\n Did you mean?`);
164
+ close.forEach(s => console.log(` ${cyan(s.slug)} ${s.title}`));
165
+ }
166
+ console.log('');
167
+ process.exit(1);
168
+ }
169
+
170
+ const installed = (await installedSlugs()).includes(slug);
171
+ const priceLabel = skill.price === 'paid' ? yellow('Premium') : green('Free');
172
+ const secLabel = skill.security_level === 'safe' ? green('safe') : (skill.security_level || dim('unknown'));
173
+
174
+ console.log(`\n${bold(skill.title)}\n`);
175
+ console.log(` Slug: ${skill.slug}`);
176
+ console.log(` Price: ${priceLabel}`);
177
+ console.log(` Lines: ${skill.lines}`);
178
+ console.log(` Categories: ${skill.categories.join(', ') || '-'}`);
179
+ console.log(` Security: ${secLabel}`);
180
+ console.log(` Downloads: ${skill.downloads}`);
181
+ console.log(` Installed: ${installed ? green('yes') : 'no'}`);
182
+ console.log(`\n ${dim('Description:')}`);
183
+ console.log(` ${skill.description}`);
184
+ console.log(`\n ${dim('Install:')}`);
185
+ console.log(` npx mkt-skills install ${skill.slug}`);
186
+ console.log(`\n ${dim('Web:')}`);
187
+ console.log(` https://marketingskills.directory/skill/${skill.slug}/\n`);
188
+ }
189
+
190
+ // ── uninstall ───────────────────────────────────────────────
191
+
192
+ export async function uninstall(args) {
193
+ const slug = args.find(a => !a.startsWith('-'));
194
+
195
+ if (!slug) {
196
+ console.error('Usage: mkt-skills uninstall <slug>\n');
197
+ process.exit(1);
198
+ }
199
+
200
+ const dir = skillDir(slug);
201
+
202
+ try {
203
+ await access(dir);
204
+ } catch {
205
+ console.log(`\n${red('x')} Skill "${slug}" is not installed.\n`);
206
+ process.exit(1);
207
+ }
208
+
209
+ await rm(dir, { recursive: true, force: true });
210
+ console.log(`\n${green('+')} Uninstalled "${slug}"`);
211
+ console.log(` ${dim('->')} Removed ${dir}\n`);
212
+ }
213
+
214
+ // ── helpers ─────────────────────────────────────────────────
215
+
216
+ function confirm(message) {
217
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
218
+ return new Promise(resolve => {
219
+ rl.question(`${message} (y/N) `, answer => {
220
+ rl.close();
221
+ resolve(answer.trim().toLowerCase() === 'y');
222
+ });
223
+ });
224
+ }
package/lib/paths.mjs ADDED
@@ -0,0 +1,22 @@
1
+ import { homedir } from 'os';
2
+ import { join } from 'path';
3
+ import { readdir } from 'fs/promises';
4
+
5
+ const SKILLS_ROOT = join(homedir(), '.claude', 'skills');
6
+
7
+ export function skillDir(slug) {
8
+ return join(SKILLS_ROOT, slug);
9
+ }
10
+
11
+ export function skillFile(slug) {
12
+ return join(skillDir(slug), 'SKILL.md');
13
+ }
14
+
15
+ export async function installedSlugs() {
16
+ try {
17
+ const entries = await readdir(SKILLS_ROOT, { withFileTypes: true });
18
+ return entries.filter(e => e.isDirectory()).map(e => e.name);
19
+ } catch {
20
+ return [];
21
+ }
22
+ }
package/lib/ui.mjs ADDED
@@ -0,0 +1,14 @@
1
+ // ANSI color helpers — zero dependencies.
2
+
3
+ const enabled = process.stdout.isTTY && !process.env.NO_COLOR;
4
+
5
+ const wrap = (code, reset) => enabled
6
+ ? s => `\x1b[${code}m${s}\x1b[${reset}m`
7
+ : s => s;
8
+
9
+ export const bold = wrap('1', '22');
10
+ export const dim = wrap('2', '22');
11
+ export const green = wrap('32', '39');
12
+ export const red = wrap('31', '39');
13
+ export const yellow = wrap('33', '39');
14
+ export const cyan = wrap('36', '39');
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "mkt-skills",
3
+ "version": "1.0.0",
4
+ "description": "Install Claude Code marketing skills from marketingskills.directory",
5
+ "type": "module",
6
+ "bin": {
7
+ "mkt-skills": "./bin/mkt-skills.mjs"
8
+ },
9
+ "engines": {
10
+ "node": ">=18.0.0"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "lib/",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "keywords": [
19
+ "claude",
20
+ "claude-code",
21
+ "skills",
22
+ "marketing",
23
+ "seo",
24
+ "ai",
25
+ "cli"
26
+ ],
27
+ "author": "Thrivemattic",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/sandeepkelvadi/mkt-skills-dir"
32
+ },
33
+ "homepage": "https://marketingskills.directory"
34
+ }