@skunkceo/cli 1.0.6 → 2.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 +38 -27
- package/bin/skunk.js +360 -80
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Skunk CLI
|
|
2
2
|
|
|
3
|
-
Install and manage
|
|
3
|
+
Install and manage Skunk Global AI skills and WordPress plugins.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -11,49 +11,60 @@ npm install -g @skunkceo/cli
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
+
# Interactive setup wizard
|
|
14
15
|
skunk setup
|
|
15
|
-
```
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
2. Install essential skills (WordPress, WooCommerce, SkunkCRM, SkunkForms, SkunkPages)
|
|
20
|
-
3. Guide you through next steps
|
|
17
|
+
# Install an AI skill (teaches your AI assistant)
|
|
18
|
+
skunk install skill skunkforms
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
# Install a WordPress plugin
|
|
21
|
+
skunk install plugin skunkforms
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
skunk
|
|
26
|
-
skunk install <skill> # Install a specific skill
|
|
27
|
-
skunk remove <skill> # Remove an installed skill
|
|
28
|
-
skunk list # List installed skills
|
|
29
|
-
skunk available # See all available skills
|
|
30
|
-
skunk help # Show help
|
|
23
|
+
# Install Pro version with license
|
|
24
|
+
skunk install plugin skunkcrm-pro --license=YOUR_LICENSE_KEY
|
|
31
25
|
```
|
|
32
26
|
|
|
33
|
-
##
|
|
27
|
+
## Commands
|
|
28
|
+
|
|
29
|
+
| Command | Description |
|
|
30
|
+
|---------|-------------|
|
|
31
|
+
| `skunk setup` | Interactive setup wizard |
|
|
32
|
+
| `skunk install skill <name>` | Install an AI skill |
|
|
33
|
+
| `skunk install plugin <name>` | Install a WordPress plugin |
|
|
34
|
+
| `skunk remove skill <name>` | Remove an installed skill |
|
|
35
|
+
| `skunk list` | List installed skills |
|
|
36
|
+
| `skunk available` | List available skills |
|
|
37
|
+
| `skunk plugins` | List available plugins |
|
|
38
|
+
| `skunk update` | Update CLI and refresh skills |
|
|
39
|
+
| `skunk help` | Show help |
|
|
34
40
|
|
|
35
|
-
Skills
|
|
41
|
+
## Skills vs Plugins
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
- How to interpret results
|
|
39
|
-
- Best practices for the tool
|
|
43
|
+
**Skills** teach your AI assistant (OpenClaw, Claude, etc.) how to work with Skunk products. They're installed to `~/.openclaw/skills/`.
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
**Plugins** are the actual WordPress plugins that run on your site. They're installed via WP-CLI or WordPress Studio.
|
|
42
46
|
|
|
47
|
+
For the best experience, install both:
|
|
43
48
|
```bash
|
|
44
|
-
|
|
49
|
+
# Install the skill so your AI knows how to use it
|
|
50
|
+
skunk install skill skunkforms
|
|
51
|
+
|
|
52
|
+
# Install the plugin on your WordPress site
|
|
53
|
+
skunk install plugin skunkforms
|
|
45
54
|
```
|
|
46
55
|
|
|
47
|
-
## Available
|
|
56
|
+
## Available Products
|
|
48
57
|
|
|
49
|
-
|
|
58
|
+
- **skunkcrm** / **skunkcrm-pro** - CRM & contact management
|
|
59
|
+
- **skunkforms** / **skunkforms-pro** - Form builder
|
|
60
|
+
- **skunkpages** / **skunkpages-pro** - Landing page builder
|
|
50
61
|
|
|
51
|
-
##
|
|
62
|
+
## Requirements
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
- Node.js 18+
|
|
65
|
+
- For plugin installation: WP-CLI or WordPress Studio
|
|
54
66
|
|
|
55
67
|
## Links
|
|
56
68
|
|
|
69
|
+
- [OpenClaw WordPress Guide](https://skunkglobal.com/guides/openclaw-wordpress)
|
|
57
70
|
- [Skunk Global](https://skunkglobal.com)
|
|
58
|
-
- [Skills Repository](https://github.com/skunkceo/openclaw-skills)
|
|
59
|
-
- [OpenClaw](https://openclaw.ai)
|
package/bin/skunk.js
CHANGED
|
@@ -3,61 +3,218 @@
|
|
|
3
3
|
const https = require('https');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
-
const { execSync } = require('child_process');
|
|
6
|
+
const { execSync, spawn } = require('child_process');
|
|
7
7
|
|
|
8
8
|
const SKILLS_REPO = 'skunkceo/openclaw-skills';
|
|
9
9
|
const SKILLS_BRANCH = 'main';
|
|
10
10
|
const OPENCLAW_DIR = path.join(process.env.HOME, '.openclaw', 'skills');
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
// Plugin registry - maps plugin names to download URLs
|
|
13
|
+
const PLUGIN_REGISTRY = {
|
|
14
|
+
'skunkcrm': {
|
|
15
|
+
free: 'https://skunkcrm.com/api/download/free',
|
|
16
|
+
pro: 'https://skunkcrm.com/api/download/pro',
|
|
17
|
+
name: 'SkunkCRM',
|
|
18
|
+
slug: 'skunk-crm',
|
|
19
|
+
requiresLicense: false, // Pro requires license
|
|
20
|
+
},
|
|
21
|
+
'skunkforms': {
|
|
22
|
+
free: 'https://skunkforms.com/api/download/free',
|
|
23
|
+
pro: 'https://skunkforms.com/api/download/pro',
|
|
24
|
+
name: 'SkunkForms',
|
|
25
|
+
slug: 'skunk-forms',
|
|
26
|
+
requiresLicense: false,
|
|
27
|
+
},
|
|
28
|
+
'skunkpages': {
|
|
29
|
+
free: 'https://skunkpages.com/api/download/free',
|
|
30
|
+
pro: 'https://skunkpages.com/api/download/pro',
|
|
31
|
+
name: 'SkunkPages',
|
|
32
|
+
slug: 'skunk-pages',
|
|
33
|
+
requiresLicense: false,
|
|
34
|
+
},
|
|
19
35
|
};
|
|
20
36
|
|
|
37
|
+
const colors = {
|
|
38
|
+
reset: '\x1b[0m',
|
|
39
|
+
bright: '\x1b[1m',
|
|
40
|
+
dim: '\x1b[2m',
|
|
41
|
+
green: '\x1b[32m',
|
|
42
|
+
yellow: '\x1b[33m',
|
|
43
|
+
red: '\x1b[31m',
|
|
44
|
+
cyan: '\x1b[36m',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function success(msg) { console.log(`${colors.green}✓${colors.reset} ${msg}`); }
|
|
48
|
+
function warn(msg) { console.log(`${colors.yellow}!${colors.reset} ${msg}`); }
|
|
49
|
+
function error(msg) { console.log(`${colors.red}✗${colors.reset} ${msg}`); }
|
|
50
|
+
|
|
51
|
+
// Parse arguments
|
|
21
52
|
const args = process.argv.slice(2);
|
|
22
53
|
const command = args[0] || 'help';
|
|
23
|
-
const skillName = args[1];
|
|
24
|
-
|
|
25
|
-
if (commands[command]) {
|
|
26
|
-
commands[command](skillName);
|
|
27
|
-
} else {
|
|
28
|
-
console.log(`Unknown command: ${command}`);
|
|
29
|
-
showHelp();
|
|
30
|
-
}
|
|
31
54
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
// Route commands
|
|
56
|
+
switch (command) {
|
|
57
|
+
case 'install':
|
|
58
|
+
handleInstall(args.slice(1));
|
|
59
|
+
break;
|
|
60
|
+
case 'remove':
|
|
61
|
+
handleRemove(args.slice(1));
|
|
62
|
+
break;
|
|
63
|
+
case 'list':
|
|
64
|
+
listSkills();
|
|
65
|
+
break;
|
|
66
|
+
case 'available':
|
|
67
|
+
listAvailable();
|
|
68
|
+
break;
|
|
69
|
+
case 'plugins':
|
|
70
|
+
listPlugins();
|
|
71
|
+
break;
|
|
72
|
+
case 'update':
|
|
73
|
+
handleUpdate();
|
|
74
|
+
break;
|
|
75
|
+
case 'setup':
|
|
76
|
+
runSetup();
|
|
77
|
+
break;
|
|
78
|
+
case 'help':
|
|
79
|
+
case '--help':
|
|
80
|
+
case '-h':
|
|
81
|
+
showHelp();
|
|
82
|
+
break;
|
|
83
|
+
default:
|
|
84
|
+
console.log(`Unknown command: ${command}`);
|
|
85
|
+
showHelp();
|
|
35
86
|
}
|
|
36
87
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
88
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
89
|
+
// Install Handler
|
|
90
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
40
91
|
|
|
92
|
+
async function handleInstall(args) {
|
|
93
|
+
const type = args[0];
|
|
94
|
+
const name = args[1];
|
|
95
|
+
const extraArgs = args.slice(2);
|
|
96
|
+
|
|
97
|
+
if (!type || !name) {
|
|
98
|
+
console.log(`
|
|
41
99
|
Usage:
|
|
42
|
-
skunk
|
|
43
|
-
skunk install <
|
|
44
|
-
skunk remove <skill> Remove an installed skill
|
|
45
|
-
skunk list List installed skills
|
|
46
|
-
skunk available List available skills
|
|
47
|
-
skunk help Show this help
|
|
100
|
+
skunk install skill <name> Install an AI skill
|
|
101
|
+
skunk install plugin <name> Install a WordPress plugin
|
|
48
102
|
|
|
49
103
|
Examples:
|
|
50
|
-
skunk
|
|
51
|
-
skunk install
|
|
52
|
-
skunk
|
|
104
|
+
skunk install skill skunkforms
|
|
105
|
+
skunk install plugin skunkforms
|
|
106
|
+
skunk install plugin skunkcrm-pro --license=XXXX
|
|
53
107
|
|
|
54
|
-
|
|
108
|
+
Run "skunk available" for skills or "skunk plugins" for plugins.
|
|
55
109
|
`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (type === 'skill') {
|
|
114
|
+
await installSkill(name);
|
|
115
|
+
} else if (type === 'plugin') {
|
|
116
|
+
await installPlugin(name, extraArgs);
|
|
117
|
+
} else {
|
|
118
|
+
// Backwards compat: treat as skill name
|
|
119
|
+
console.log(`${colors.yellow}Hint: Use "skunk install skill ${type}" or "skunk install plugin ${type}"${colors.reset}\n`);
|
|
120
|
+
await installSkill(type);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
125
|
+
// Remove Handler
|
|
126
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
127
|
+
|
|
128
|
+
function handleRemove(args) {
|
|
129
|
+
const type = args[0];
|
|
130
|
+
const name = args[1];
|
|
131
|
+
|
|
132
|
+
if (!type) {
|
|
133
|
+
console.log('Usage: skunk remove skill <name>');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (type === 'skill' && name) {
|
|
138
|
+
removeSkill(name);
|
|
139
|
+
} else {
|
|
140
|
+
// Backwards compat
|
|
141
|
+
removeSkill(type);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
146
|
+
// Skill Management
|
|
147
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
148
|
+
|
|
149
|
+
async function installSkill(name) {
|
|
150
|
+
if (!name) {
|
|
151
|
+
console.log('Usage: skunk install skill <skill-name>');
|
|
152
|
+
console.log('Run "skunk available" to see available skills');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log(`Installing skill: ${name}...`);
|
|
157
|
+
|
|
158
|
+
if (!fs.existsSync(OPENCLAW_DIR)) {
|
|
159
|
+
fs.mkdirSync(OPENCLAW_DIR, { recursive: true });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const skillDir = path.join(OPENCLAW_DIR, name);
|
|
163
|
+
|
|
164
|
+
if (fs.existsSync(skillDir)) {
|
|
165
|
+
console.log(`Skill ${name} is already installed. Remove it first with: skunk remove skill ${name}`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const files = ['SKILL.md', 'config.json', 'README.md'];
|
|
170
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
171
|
+
|
|
172
|
+
let installed = false;
|
|
173
|
+
|
|
174
|
+
for (const file of files) {
|
|
175
|
+
const url = `https://raw.githubusercontent.com/${SKILLS_REPO}/${SKILLS_BRANCH}/skills/${name}/${file}`;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
const content = await fetchFile(url);
|
|
179
|
+
if (content) {
|
|
180
|
+
fs.writeFileSync(path.join(skillDir, file), content);
|
|
181
|
+
if (file === 'SKILL.md') installed = true;
|
|
182
|
+
}
|
|
183
|
+
} catch (e) {
|
|
184
|
+
// Optional files may not exist
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (installed) {
|
|
189
|
+
success(`Installed skill "${name}" to ${skillDir}`);
|
|
190
|
+
console.log(`\n${colors.dim}Restart your AI assistant to load the new skill.${colors.reset}`);
|
|
191
|
+
} else {
|
|
192
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
193
|
+
error(`Skill "${name}" not found. Run "skunk available" to see available skills.`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function removeSkill(name) {
|
|
198
|
+
if (!name) {
|
|
199
|
+
console.log('Usage: skunk remove skill <skill-name>');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const skillDir = path.join(OPENCLAW_DIR, name);
|
|
204
|
+
|
|
205
|
+
if (!fs.existsSync(skillDir)) {
|
|
206
|
+
console.log(`Skill ${name} is not installed.`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
211
|
+
success(`Removed skill "${name}"`);
|
|
56
212
|
}
|
|
57
213
|
|
|
58
214
|
function listSkills() {
|
|
59
215
|
if (!fs.existsSync(OPENCLAW_DIR)) {
|
|
60
216
|
console.log('No skills installed yet.');
|
|
217
|
+
console.log('Run "skunk available" to see available skills.');
|
|
61
218
|
return;
|
|
62
219
|
}
|
|
63
220
|
|
|
@@ -70,8 +227,9 @@ function listSkills() {
|
|
|
70
227
|
if (skills.length === 0) {
|
|
71
228
|
console.log('No skills installed yet.');
|
|
72
229
|
} else {
|
|
73
|
-
console.log('Installed skills
|
|
74
|
-
skills.forEach(s => console.log(`
|
|
230
|
+
console.log('Installed skills:\n');
|
|
231
|
+
skills.forEach(s => console.log(` ${colors.green}●${colors.reset} ${s}`));
|
|
232
|
+
console.log(`\n${colors.dim}Skills location: ${OPENCLAW_DIR}${colors.reset}`);
|
|
75
233
|
}
|
|
76
234
|
}
|
|
77
235
|
|
|
@@ -86,84 +244,206 @@ async function listAvailable() {
|
|
|
86
244
|
res.on('end', () => {
|
|
87
245
|
try {
|
|
88
246
|
const skills = JSON.parse(data);
|
|
89
|
-
console.log('Available skills
|
|
247
|
+
console.log('Available AI skills:\n');
|
|
90
248
|
skills.filter(s => s.type === 'dir').forEach(s => {
|
|
91
|
-
console.log(`
|
|
249
|
+
console.log(` ${colors.cyan}●${colors.reset} ${s.name}`);
|
|
92
250
|
});
|
|
93
|
-
console.log(
|
|
251
|
+
console.log(`\n${colors.dim}Install with: skunk install skill <name>${colors.reset}`);
|
|
94
252
|
} catch (e) {
|
|
95
|
-
|
|
253
|
+
error('Failed to fetch skills list');
|
|
96
254
|
}
|
|
97
255
|
});
|
|
98
256
|
}).on('error', (e) => {
|
|
99
|
-
|
|
257
|
+
error('Failed to fetch skills: ' + e.message);
|
|
100
258
|
});
|
|
101
259
|
}
|
|
102
260
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
261
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
262
|
+
// Plugin Management
|
|
263
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
264
|
+
|
|
265
|
+
async function installPlugin(name, extraArgs) {
|
|
266
|
+
// Parse name for -pro suffix
|
|
267
|
+
let pluginKey = name.replace(/-pro$/, '');
|
|
268
|
+
let isPro = name.endsWith('-pro');
|
|
269
|
+
|
|
270
|
+
const plugin = PLUGIN_REGISTRY[pluginKey];
|
|
271
|
+
|
|
272
|
+
if (!plugin) {
|
|
273
|
+
error(`Unknown plugin: ${name}`);
|
|
274
|
+
console.log('\nAvailable plugins:');
|
|
275
|
+
listPlugins();
|
|
107
276
|
return;
|
|
108
277
|
}
|
|
109
278
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
279
|
+
// Parse license from args
|
|
280
|
+
let license = null;
|
|
281
|
+
for (const arg of extraArgs) {
|
|
282
|
+
if (arg.startsWith('--license=')) {
|
|
283
|
+
license = arg.split('=')[1];
|
|
284
|
+
}
|
|
115
285
|
}
|
|
116
286
|
|
|
117
|
-
|
|
287
|
+
// Detect WP-CLI or WordPress Studio
|
|
288
|
+
const hasWpCli = commandExists('wp');
|
|
289
|
+
const hasStudio = commandExists('studio');
|
|
118
290
|
|
|
119
|
-
if (
|
|
120
|
-
|
|
291
|
+
if (!hasWpCli && !hasStudio) {
|
|
292
|
+
error('No WordPress CLI found.');
|
|
293
|
+
console.log(`
|
|
294
|
+
To install WordPress plugins, you need either:
|
|
295
|
+
|
|
296
|
+
${colors.cyan}WP-CLI${colors.reset} (for server/local installs)
|
|
297
|
+
https://wp-cli.org/
|
|
298
|
+
|
|
299
|
+
${colors.cyan}WordPress Studio${colors.reset} (for local development)
|
|
300
|
+
https://developer.wordpress.org/studio/
|
|
301
|
+
macOS: brew install --cask wordpress-studio
|
|
302
|
+
`);
|
|
121
303
|
return;
|
|
122
304
|
}
|
|
123
305
|
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
fs.mkdirSync(skillDir, { recursive: true });
|
|
306
|
+
const downloadUrl = isPro ? plugin.pro : plugin.free;
|
|
307
|
+
const displayName = isPro ? `${plugin.name} Pro` : plugin.name;
|
|
127
308
|
|
|
128
|
-
|
|
309
|
+
console.log(`Installing ${displayName}...`);
|
|
129
310
|
|
|
130
|
-
|
|
131
|
-
|
|
311
|
+
// Build the command
|
|
312
|
+
let cmd;
|
|
313
|
+
if (hasStudio) {
|
|
314
|
+
cmd = `studio wp plugin install "${downloadUrl}" --activate`;
|
|
315
|
+
} else {
|
|
316
|
+
cmd = `wp plugin install "${downloadUrl}" --activate`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Add license if Pro and license provided
|
|
320
|
+
if (isPro && license) {
|
|
321
|
+
console.log(`${colors.dim}License key provided${colors.reset}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log(`${colors.dim}Running: ${cmd}${colors.reset}\n`);
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
328
|
+
success(`Installed ${displayName}`);
|
|
132
329
|
|
|
133
|
-
try
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
fs.writeFileSync(path.join(skillDir, file), content);
|
|
137
|
-
if (file === 'SKILL.md') success = true;
|
|
138
|
-
}
|
|
139
|
-
} catch (e) {
|
|
140
|
-
// README might not exist, that's ok
|
|
330
|
+
// If Pro and license provided, try to activate
|
|
331
|
+
if (isPro && license) {
|
|
332
|
+
console.log(`\n${colors.yellow}!${colors.reset} License activation may require manual setup in WordPress admin.`);
|
|
141
333
|
}
|
|
334
|
+
|
|
335
|
+
// Suggest installing the skill too
|
|
336
|
+
console.log(`\n${colors.dim}Tip: Install the AI skill to let your assistant manage ${plugin.name}:${colors.reset}`);
|
|
337
|
+
console.log(` skunk install skill ${pluginKey}\n`);
|
|
338
|
+
|
|
339
|
+
} catch (e) {
|
|
340
|
+
error(`Failed to install ${displayName}`);
|
|
341
|
+
console.log(`\n${colors.dim}If using WordPress Studio, make sure you have a site selected.${colors.reset}`);
|
|
142
342
|
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function listPlugins() {
|
|
346
|
+
console.log('Available WordPress plugins:\n');
|
|
143
347
|
|
|
144
|
-
|
|
145
|
-
console.log(
|
|
146
|
-
}
|
|
147
|
-
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
148
|
-
console.log(`✗ Skill "${name}" not found. Run "skunk available" to see available skills.`);
|
|
348
|
+
for (const [key, plugin] of Object.entries(PLUGIN_REGISTRY)) {
|
|
349
|
+
console.log(` ${colors.cyan}●${colors.reset} ${key}${colors.dim} (${plugin.name} Free)${colors.reset}`);
|
|
350
|
+
console.log(` ${colors.cyan}●${colors.reset} ${key}-pro${colors.dim} (${plugin.name} Pro)${colors.reset}`);
|
|
149
351
|
}
|
|
352
|
+
|
|
353
|
+
console.log(`
|
|
354
|
+
${colors.dim}Install with: skunk install plugin <name>
|
|
355
|
+
Pro versions: skunk install plugin <name>-pro --license=XXXX${colors.reset}
|
|
356
|
+
`);
|
|
150
357
|
}
|
|
151
358
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
359
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
360
|
+
// Update
|
|
361
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
362
|
+
|
|
363
|
+
function handleUpdate() {
|
|
364
|
+
console.log('Updating Skunk CLI...\n');
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
execSync('npm update -g @skunkceo/cli', { stdio: 'inherit' });
|
|
368
|
+
success('Skunk CLI updated');
|
|
369
|
+
} catch (e) {
|
|
370
|
+
error('Failed to update CLI');
|
|
371
|
+
console.log(`${colors.dim}Try: sudo npm update -g @skunkceo/cli${colors.reset}`);
|
|
155
372
|
return;
|
|
156
373
|
}
|
|
157
374
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
375
|
+
// Refresh installed skills
|
|
376
|
+
if (fs.existsSync(OPENCLAW_DIR)) {
|
|
377
|
+
const skills = fs.readdirSync(OPENCLAW_DIR).filter(f => {
|
|
378
|
+
const skillPath = path.join(OPENCLAW_DIR, f);
|
|
379
|
+
return fs.statSync(skillPath).isDirectory();
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
if (skills.length > 0) {
|
|
383
|
+
console.log('\nRefreshing installed skills...\n');
|
|
384
|
+
|
|
385
|
+
for (const skill of skills) {
|
|
386
|
+
const skillDir = path.join(OPENCLAW_DIR, skill);
|
|
387
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
388
|
+
// Re-fetch (sync for simplicity in update flow)
|
|
389
|
+
console.log(` Updating ${skill}...`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
console.log(`\n${colors.dim}Run "skunk list" to verify skills.${colors.reset}`);
|
|
393
|
+
}
|
|
163
394
|
}
|
|
164
395
|
|
|
165
|
-
|
|
166
|
-
|
|
396
|
+
console.log('\n' + success('Update complete'));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
400
|
+
// Setup & Help
|
|
401
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
402
|
+
|
|
403
|
+
function runSetup() {
|
|
404
|
+
const setupPath = path.join(__dirname, 'setup.js');
|
|
405
|
+
require(setupPath);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function showHelp() {
|
|
409
|
+
console.log(`
|
|
410
|
+
${colors.bright}🦨 Skunk CLI${colors.reset} - AI-Powered WordPress Toolkit
|
|
411
|
+
|
|
412
|
+
${colors.bright}Usage:${colors.reset}
|
|
413
|
+
skunk setup Interactive setup wizard
|
|
414
|
+
skunk install skill <name> Install an AI skill
|
|
415
|
+
skunk install plugin <name> Install a WordPress plugin
|
|
416
|
+
skunk remove skill <name> Remove an installed skill
|
|
417
|
+
skunk list List installed skills
|
|
418
|
+
skunk available List available skills
|
|
419
|
+
skunk plugins List available plugins
|
|
420
|
+
skunk update Update CLI and refresh skills
|
|
421
|
+
skunk help Show this help
|
|
422
|
+
|
|
423
|
+
${colors.bright}Examples:${colors.reset}
|
|
424
|
+
skunk setup # Full guided setup
|
|
425
|
+
skunk install skill skunkforms # Install SkunkForms AI skill
|
|
426
|
+
skunk install plugin skunkforms # Install SkunkForms WP plugin
|
|
427
|
+
skunk install plugin skunkcrm-pro --license=XXXX
|
|
428
|
+
|
|
429
|
+
${colors.bright}Skills${colors.reset} teach your AI assistant how to use Skunk products.
|
|
430
|
+
${colors.bright}Plugins${colors.reset} are the actual WordPress plugins that run on your site.
|
|
431
|
+
|
|
432
|
+
${colors.dim}Docs: https://skunkglobal.com/guides/openclaw-wordpress${colors.reset}
|
|
433
|
+
`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
437
|
+
// Utilities
|
|
438
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
439
|
+
|
|
440
|
+
function commandExists(cmd) {
|
|
441
|
+
try {
|
|
442
|
+
execSync(`which ${cmd}`, { stdio: 'ignore' });
|
|
443
|
+
return true;
|
|
444
|
+
} catch {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
167
447
|
}
|
|
168
448
|
|
|
169
449
|
function fetchFile(url) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skunkceo/cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Install and manage Skunk Global skills
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Install and manage Skunk Global skills and WordPress plugins",
|
|
5
5
|
"bin": {
|
|
6
6
|
"skunk": "./bin/skunk.js"
|
|
7
7
|
},
|