@skunkceo/cli 2.0.2 → 2.2.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/skunk.js +174 -0
  2. package/package.json +1 -1
package/bin/skunk.js CHANGED
@@ -66,6 +66,15 @@ switch (command) {
66
66
  case 'plugins':
67
67
  listPlugins();
68
68
  break;
69
+ case 'status':
70
+ checkStatus();
71
+ break;
72
+ case 'versions':
73
+ showVersions(null);
74
+ break;
75
+ case 'version':
76
+ showVersions(args[1]);
77
+ break;
69
78
  case 'update':
70
79
  handleUpdate();
71
80
  break;
@@ -359,6 +368,168 @@ Pro versions: skunk install plugin <name>-pro --license=XXXX${colors.reset}
359
368
  `);
360
369
  }
361
370
 
371
+ // ═══════════════════════════════════════════════════════════════════════════
372
+ // Status - Check plugin versions
373
+ // ═══════════════════════════════════════════════════════════════════════════
374
+
375
+ async function checkStatus() {
376
+ console.log('Checking plugin versions...\n');
377
+
378
+ // Fetch latest versions from API
379
+ const versionsUrl = 'https://skunkglobal.com/api/plugins/versions';
380
+
381
+ try {
382
+ const latestVersions = await new Promise((resolve, reject) => {
383
+ https.get(versionsUrl, { headers: { 'User-Agent': 'skunk-cli' } }, (res) => {
384
+ let data = '';
385
+ res.on('data', chunk => data += chunk);
386
+ res.on('end', () => {
387
+ try {
388
+ resolve(JSON.parse(data));
389
+ } catch (e) {
390
+ reject(new Error('Invalid response'));
391
+ }
392
+ });
393
+ }).on('error', reject);
394
+ });
395
+
396
+ if (!latestVersions.plugins) {
397
+ error('Failed to fetch version info');
398
+ return;
399
+ }
400
+
401
+ // Check if we're in a WordPress context (wp or studio available)
402
+ const hasWpCli = commandExists('wp');
403
+ const hasStudio = commandExists('studio');
404
+ const inWordPress = hasWpCli || hasStudio;
405
+
406
+ // Get installed versions if in WordPress context
407
+ let installedVersions = {};
408
+ if (inWordPress) {
409
+ try {
410
+ const cmd = hasStudio ? 'studio wp plugin list --format=json' : 'wp plugin list --format=json';
411
+ const output = execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
412
+ const plugins = JSON.parse(output);
413
+
414
+ for (const p of plugins) {
415
+ // Map WP plugin slugs to our slugs
416
+ if (p.name === 'skunk-crm' || p.name === 'skunkcrm') {
417
+ installedVersions['skunkcrm'] = p.version;
418
+ } else if (p.name === 'skunk-forms' || p.name === 'skunkforms') {
419
+ installedVersions['skunkforms'] = p.version;
420
+ } else if (p.name === 'skunk-pages' || p.name === 'skunkpages') {
421
+ installedVersions['skunkpages'] = p.version;
422
+ }
423
+ }
424
+ } catch (e) {
425
+ // Couldn't get installed versions, that's ok
426
+ }
427
+ }
428
+
429
+ console.log(`${colors.bright}Plugin${colors.reset} ${colors.bright}Latest${colors.reset} ${inWordPress ? `${colors.bright}Installed${colors.reset}` : ''}`);
430
+ console.log('─'.repeat(inWordPress ? 50 : 30));
431
+
432
+ // Show free plugins
433
+ for (const [slug, info] of Object.entries(latestVersions.plugins)) {
434
+ if (info.type === 'free') {
435
+ const installed = installedVersions[slug];
436
+ const latestV = info.version;
437
+
438
+ let status = '';
439
+ if (installed) {
440
+ if (installed === latestV) {
441
+ status = `${colors.green}${installed}${colors.reset} ✓`;
442
+ } else {
443
+ status = `${colors.yellow}${installed}${colors.reset} → ${latestV}`;
444
+ }
445
+ } else if (inWordPress) {
446
+ status = `${colors.dim}not installed${colors.reset}`;
447
+ }
448
+
449
+ const padding = ' '.repeat(Math.max(0, 16 - slug.length));
450
+ const vPadding = ' '.repeat(Math.max(0, 10 - latestV.length));
451
+ console.log(`${slug}${padding}${latestV}${vPadding}${status}`);
452
+ }
453
+ }
454
+
455
+ console.log('');
456
+
457
+ // Show if updates available
458
+ const hasUpdates = Object.entries(installedVersions).some(([slug, v]) => {
459
+ const latest = latestVersions.plugins[slug];
460
+ return latest && latest.version !== v;
461
+ });
462
+
463
+ if (hasUpdates) {
464
+ console.log(`${colors.yellow}Updates available!${colors.reset} Run:`);
465
+ console.log(` skunk install plugin <name>\n`);
466
+ } else if (Object.keys(installedVersions).length > 0) {
467
+ console.log(`${colors.green}All plugins up to date!${colors.reset}\n`);
468
+ }
469
+
470
+ } catch (e) {
471
+ error('Failed to check versions: ' + e.message);
472
+ }
473
+ }
474
+
475
+ // ═══════════════════════════════════════════════════════════════════════════
476
+ // Versions - Simple version lookup
477
+ // ═══════════════════════════════════════════════════════════════════════════
478
+
479
+ async function showVersions(plugin) {
480
+ const versionsUrl = 'https://skunkglobal.com/api/plugins/versions';
481
+
482
+ try {
483
+ const data = await new Promise((resolve, reject) => {
484
+ https.get(versionsUrl, { headers: { 'User-Agent': 'skunk-cli' } }, (res) => {
485
+ let body = '';
486
+ res.on('data', chunk => body += chunk);
487
+ res.on('end', () => {
488
+ try {
489
+ resolve(JSON.parse(body));
490
+ } catch (e) {
491
+ reject(new Error('Invalid response'));
492
+ }
493
+ });
494
+ }).on('error', reject);
495
+ });
496
+
497
+ if (!data.plugins) {
498
+ error('Failed to fetch versions');
499
+ return;
500
+ }
501
+
502
+ // Single plugin lookup
503
+ if (plugin) {
504
+ // Normalize: skunkcrm, crm, skunk-crm all -> skunkcrm
505
+ const slug = plugin.replace(/^skunk-?/, '').toLowerCase();
506
+ const fullSlug = `skunk${slug}`;
507
+
508
+ const info = data.plugins[fullSlug] || data.plugins[slug];
509
+
510
+ if (info) {
511
+ console.log(info.version);
512
+ } else {
513
+ error(`Unknown plugin: ${plugin}`);
514
+ console.log(`\n${colors.dim}Available: skunkcrm, skunkforms, skunkpages${colors.reset}`);
515
+ }
516
+ return;
517
+ }
518
+
519
+ // All plugins
520
+ console.log('');
521
+ for (const [slug, info] of Object.entries(data.plugins)) {
522
+ if (info.type === 'free') {
523
+ console.log(`${info.name}: ${colors.cyan}${info.version}${colors.reset}`);
524
+ }
525
+ }
526
+ console.log('');
527
+
528
+ } catch (e) {
529
+ error('Failed to fetch versions: ' + e.message);
530
+ }
531
+ }
532
+
362
533
  // ═══════════════════════════════════════════════════════════════════════════
363
534
  // Update
364
535
  // ═══════════════════════════════════════════════════════════════════════════
@@ -420,6 +591,9 @@ ${colors.bright}Usage:${colors.reset}
420
591
  skunk list List installed skills
421
592
  skunk available List available skills
422
593
  skunk plugins List available plugins
594
+ skunk status Check plugin versions (+ compare if in WP site)
595
+ skunk versions Show latest versions of all plugins
596
+ skunk version <plugin> Show latest version of a specific plugin
423
597
  skunk update Update CLI and refresh skills
424
598
  skunk help Show this help
425
599
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skunkceo/cli",
3
- "version": "2.0.2",
3
+ "version": "2.2.0",
4
4
  "description": "Install and manage Skunk Global skills and WordPress plugins",
5
5
  "bin": {
6
6
  "skunk": "./bin/skunk.js"