@skunkceo/cli 1.0.0 → 1.0.2
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/bin/setup.js +272 -0
- package/bin/skunk.js +12 -5
- package/package.json +1 -1
package/bin/setup.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const https = require('https');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
const readline = require('readline');
|
|
8
|
+
|
|
9
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
10
|
+
// ASCII Art
|
|
11
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
12
|
+
|
|
13
|
+
const SKUNK_LOGO = `
|
|
14
|
+
███████╗██╗ ██╗██╗ ██╗███╗ ██╗██╗ ██╗
|
|
15
|
+
██╔════╝██║ ██╔╝██║ ██║████╗ ██║██║ ██╔╝
|
|
16
|
+
███████╗█████╔╝ ██║ ██║██╔██╗ ██║█████╔╝
|
|
17
|
+
╚════██║██╔═██╗ ██║ ██║██║╚██╗██║██╔═██╗
|
|
18
|
+
███████║██║ ██╗╚██████╔╝██║ ╚████║██║ ██╗
|
|
19
|
+
╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝
|
|
20
|
+
GLOBAL
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
24
|
+
// Utilities
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
26
|
+
|
|
27
|
+
const colors = {
|
|
28
|
+
reset: '\x1b[0m',
|
|
29
|
+
bright: '\x1b[1m',
|
|
30
|
+
dim: '\x1b[2m',
|
|
31
|
+
green: '\x1b[32m',
|
|
32
|
+
yellow: '\x1b[33m',
|
|
33
|
+
red: '\x1b[31m',
|
|
34
|
+
cyan: '\x1b[36m',
|
|
35
|
+
magenta: '\x1b[35m',
|
|
36
|
+
white: '\x1b[37m',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function log(msg = '') {
|
|
40
|
+
console.log(msg);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function success(msg) {
|
|
44
|
+
console.log(`${colors.green}✓${colors.reset} ${msg}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function warn(msg) {
|
|
48
|
+
console.log(`${colors.yellow}!${colors.reset} ${msg}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function error(msg) {
|
|
52
|
+
console.log(`${colors.red}✗${colors.reset} ${msg}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function step(num, total, msg) {
|
|
56
|
+
console.log(`\n${colors.cyan}[${num}/${total}]${colors.reset} ${colors.bright}${msg}${colors.reset}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function commandExists(cmd) {
|
|
60
|
+
try {
|
|
61
|
+
execSync(`which ${cmd}`, { stdio: 'ignore' });
|
|
62
|
+
return true;
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getVersion(cmd) {
|
|
69
|
+
try {
|
|
70
|
+
return execSync(`${cmd} --version`, { encoding: 'utf8' }).trim().split('\n')[0];
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
77
|
+
// Prompts
|
|
78
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
79
|
+
|
|
80
|
+
const rl = readline.createInterface({
|
|
81
|
+
input: process.stdin,
|
|
82
|
+
output: process.stdout,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
function ask(question) {
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
rl.question(question, resolve);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function confirm(question, defaultYes = true) {
|
|
92
|
+
const hint = defaultYes ? '(Y/n)' : '(y/N)';
|
|
93
|
+
const answer = await ask(`${question} ${hint} `);
|
|
94
|
+
if (!answer) return defaultYes;
|
|
95
|
+
return answer.toLowerCase().startsWith('y');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
99
|
+
// Skill Installation
|
|
100
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
101
|
+
|
|
102
|
+
async function installSkill(skillName) {
|
|
103
|
+
const skillsDir = path.join(process.env.HOME, '.openclaw', 'skills');
|
|
104
|
+
const skillDir = path.join(skillsDir, skillName);
|
|
105
|
+
|
|
106
|
+
if (fs.existsSync(skillDir)) {
|
|
107
|
+
return { status: 'exists' };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Fetch from GitHub
|
|
111
|
+
const files = ['SKILL.md', 'config.json'];
|
|
112
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
113
|
+
|
|
114
|
+
for (const file of files) {
|
|
115
|
+
const url = `https://raw.githubusercontent.com/skunkceo/openclaw-skills/main/skills/${skillName}/${file}`;
|
|
116
|
+
try {
|
|
117
|
+
const content = await fetchFile(url);
|
|
118
|
+
if (content) {
|
|
119
|
+
fs.writeFileSync(path.join(skillDir, file), content);
|
|
120
|
+
}
|
|
121
|
+
} catch (e) {
|
|
122
|
+
// Optional files may not exist
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (fs.existsSync(path.join(skillDir, 'SKILL.md'))) {
|
|
127
|
+
return { status: 'installed' };
|
|
128
|
+
} else {
|
|
129
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
130
|
+
return { status: 'failed' };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function fetchFile(url) {
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
https.get(url, { headers: { 'User-Agent': 'skunk-cli' } }, (res) => {
|
|
137
|
+
if (res.statusCode === 404) {
|
|
138
|
+
resolve(null);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (res.statusCode !== 200) {
|
|
142
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
let data = '';
|
|
146
|
+
res.on('data', (chunk) => (data += chunk));
|
|
147
|
+
res.on('end', () => resolve(data));
|
|
148
|
+
}).on('error', reject);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
153
|
+
// Main Setup Flow
|
|
154
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
155
|
+
|
|
156
|
+
async function main() {
|
|
157
|
+
console.clear();
|
|
158
|
+
|
|
159
|
+
// Show splash
|
|
160
|
+
log(colors.bright + SKUNK_LOGO + colors.reset);
|
|
161
|
+
log('');
|
|
162
|
+
log(`${colors.dim}Welcome! Let's get your AI-powered WordPress toolkit ready.${colors.reset}`);
|
|
163
|
+
log('');
|
|
164
|
+
|
|
165
|
+
const totalSteps = 3;
|
|
166
|
+
|
|
167
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
168
|
+
// Step 1: Environment checks
|
|
169
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
170
|
+
step(1, totalSteps, 'Checking environment...');
|
|
171
|
+
|
|
172
|
+
// Node.js
|
|
173
|
+
const nodeVersion = getVersion('node');
|
|
174
|
+
if (nodeVersion) {
|
|
175
|
+
success(`Node.js ${nodeVersion}`);
|
|
176
|
+
} else {
|
|
177
|
+
error('Node.js not found');
|
|
178
|
+
log('\n Install Node.js: https://nodejs.org/');
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// WordPress Studio
|
|
183
|
+
let studioExists = commandExists('studio');
|
|
184
|
+
|
|
185
|
+
if (studioExists) {
|
|
186
|
+
const studioVersion = getVersion('studio');
|
|
187
|
+
success(`WordPress Studio installed ${studioVersion ? `(${studioVersion})` : ''}`);
|
|
188
|
+
} else {
|
|
189
|
+
warn('WordPress Studio not found');
|
|
190
|
+
log('');
|
|
191
|
+
log(' WordPress Studio is needed to create and manage WordPress sites.');
|
|
192
|
+
log('');
|
|
193
|
+
log(' Install it from: https://developer.wordpress.org/studio/');
|
|
194
|
+
log('');
|
|
195
|
+
log(' macOS: brew install --cask wordpress-studio');
|
|
196
|
+
log(' Other: Download from the website');
|
|
197
|
+
log('');
|
|
198
|
+
|
|
199
|
+
const proceed = await confirm('Continue setup anyway?', true);
|
|
200
|
+
if (!proceed) {
|
|
201
|
+
log('\n Run `skunk setup` again after installing WordPress Studio.');
|
|
202
|
+
rl.close();
|
|
203
|
+
process.exit(0);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
208
|
+
// Step 2: Install AI Skills
|
|
209
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
210
|
+
step(2, totalSteps, 'Installing AI skills...');
|
|
211
|
+
log('');
|
|
212
|
+
log(` ${colors.dim}Skills teach your AI assistant how to work with WordPress${colors.reset}`);
|
|
213
|
+
log(` ${colors.dim}and Skunk products. Installing the essentials...${colors.reset}`);
|
|
214
|
+
log('');
|
|
215
|
+
|
|
216
|
+
// Core skills that enable WordPress + Skunk workflow
|
|
217
|
+
const coreSkills = [
|
|
218
|
+
{ name: 'wordpress-studio', desc: 'WordPress site management' },
|
|
219
|
+
{ name: 'woocommerce', desc: 'WooCommerce store operations' },
|
|
220
|
+
{ name: 'skunkcrm', desc: 'SkunkCRM contact & pipeline management' },
|
|
221
|
+
{ name: 'skunkforms', desc: 'SkunkForms form building' },
|
|
222
|
+
{ name: 'skunkpages', desc: 'SkunkPages landing page optimization' },
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
for (const skill of coreSkills) {
|
|
226
|
+
process.stdout.write(` ${skill.name} `);
|
|
227
|
+
const result = await installSkill(skill.name);
|
|
228
|
+
if (result.status === 'installed') {
|
|
229
|
+
log(`${colors.green}✓${colors.reset} ${colors.dim}${skill.desc}${colors.reset}`);
|
|
230
|
+
} else if (result.status === 'exists') {
|
|
231
|
+
log(`${colors.dim}✓ already installed${colors.reset}`);
|
|
232
|
+
} else {
|
|
233
|
+
log(`${colors.yellow}! not available yet${colors.reset}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
238
|
+
// Step 3: Done!
|
|
239
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
240
|
+
step(3, totalSteps, 'Ready!');
|
|
241
|
+
log('');
|
|
242
|
+
log(` ${colors.green}Your AI assistant now knows WordPress and Skunk!${colors.reset}`);
|
|
243
|
+
log('');
|
|
244
|
+
log(' Next steps:');
|
|
245
|
+
log('');
|
|
246
|
+
|
|
247
|
+
if (studioExists) {
|
|
248
|
+
log(' 1. Create a WordPress site:');
|
|
249
|
+
log(` ${colors.cyan}studio create my-site${colors.reset}`);
|
|
250
|
+
log('');
|
|
251
|
+
log(' 2. Start chatting with your AI:');
|
|
252
|
+
log(` ${colors.cyan}"Install WooCommerce"${colors.reset}`);
|
|
253
|
+
log(` ${colors.cyan}"Install SkunkCRM"${colors.reset}`);
|
|
254
|
+
log(` ${colors.cyan}"Create a contact form"${colors.reset}`);
|
|
255
|
+
} else {
|
|
256
|
+
log(' 1. Install WordPress Studio');
|
|
257
|
+
log(' 2. Create a site: studio create my-site');
|
|
258
|
+
log(' 3. Chat with your AI to set up plugins');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
log('');
|
|
262
|
+
log(` ${colors.dim}Guide: https://skunkglobal.com/guides/openclaw-wordpress${colors.reset}`);
|
|
263
|
+
log('');
|
|
264
|
+
|
|
265
|
+
rl.close();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Run
|
|
269
|
+
main().catch((err) => {
|
|
270
|
+
console.error('Setup failed:', err.message);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
});
|
package/bin/skunk.js
CHANGED
|
@@ -14,6 +14,7 @@ const commands = {
|
|
|
14
14
|
list: listSkills,
|
|
15
15
|
available: listAvailable,
|
|
16
16
|
remove: removeSkill,
|
|
17
|
+
setup: runSetup,
|
|
17
18
|
help: showHelp,
|
|
18
19
|
};
|
|
19
20
|
|
|
@@ -28,11 +29,17 @@ if (commands[command]) {
|
|
|
28
29
|
showHelp();
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
function runSetup() {
|
|
33
|
+
const setupPath = path.join(__dirname, 'setup.js');
|
|
34
|
+
require(setupPath);
|
|
35
|
+
}
|
|
36
|
+
|
|
31
37
|
function showHelp() {
|
|
32
38
|
console.log(`
|
|
33
|
-
🦨 Skunk CLI -
|
|
39
|
+
🦨 Skunk CLI - Skunk Global Suite for OpenClaw
|
|
34
40
|
|
|
35
41
|
Usage:
|
|
42
|
+
skunk setup Interactive setup wizard (start here!)
|
|
36
43
|
skunk install <skill> Install a skill
|
|
37
44
|
skunk remove <skill> Remove an installed skill
|
|
38
45
|
skunk list List installed skills
|
|
@@ -40,11 +47,11 @@ Usage:
|
|
|
40
47
|
skunk help Show this help
|
|
41
48
|
|
|
42
49
|
Examples:
|
|
43
|
-
skunk
|
|
44
|
-
skunk install
|
|
45
|
-
skunk list
|
|
50
|
+
skunk setup # Full suite setup wizard
|
|
51
|
+
skunk install wordpress-studio # Install individual skill
|
|
52
|
+
skunk list # See what's installed
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
Docs: https://skunkglobal.com/guides/openclaw-wordpress
|
|
48
55
|
`);
|
|
49
56
|
}
|
|
50
57
|
|