antigravity-seo-kit 1.2.6 → 1.2.8

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 CHANGED
@@ -4,6 +4,24 @@
4
4
 
5
5
  ## ⚡ Quick Install
6
6
 
7
+ You can install SEO Kit either globally as an Antigravity plugin (recommended) or locally into a specific workspace.
8
+
9
+ ### 1. Global Plugin Installation (Recommended)
10
+ This installs SEO Kit globally into your Antigravity config directory so it is available across all workspaces.
11
+
12
+ ```bash
13
+ npx antigravity-seo-kit install-plugin --key=SK-XXXX-XXXX-XXXX
14
+ # OR: npx antigravity-seo-kit install --global --key=SK-XXXX-XXXX-XXXX
15
+ ```
16
+
17
+ When you start work in a new workspace, run the following command to sync the agent rules and folders locally:
18
+ ```bash
19
+ npx antigravity-seo-kit setup-workspace
20
+ ```
21
+
22
+ ### 2. Local Workspace Installation
23
+ This installs SEO Kit files directly into your current workspace.
24
+
7
25
  ```bash
8
26
  npx antigravity-seo-kit install --key=SK-XXXX-XXXX-XXXX
9
27
  ```
@@ -25,7 +43,13 @@ npx antigravity-seo-kit install --key=SK-XXXX-XXXX-XXXX
25
43
  ## CLI Commands
26
44
 
27
45
  ```bash
28
- # Install SEO Kit
46
+ # Install globally as a plugin (recommended)
47
+ npx antigravity-seo-kit install-plugin --key=SK-XXXX-XXXX-XXXX
48
+
49
+ # Replicate .agent config directory from global plugin to local workspace
50
+ npx antigravity-seo-kit setup-workspace
51
+
52
+ # Install locally into current workspace
29
53
  npx antigravity-seo-kit install --key=SK-XXXX-XXXX-XXXX
30
54
 
31
55
  # Update to latest version
package/bin/cli.js CHANGED
@@ -42,7 +42,36 @@ async function main() {
42
42
  error('Invalid key format. Expected: SK-XXXX-XXXX-XXXX');
43
43
  process.exit(1);
44
44
  }
45
- await installer.install(key, cwd);
45
+ if (getFlag('global')) {
46
+ await installer.installPlugin(key);
47
+ } else {
48
+ await installer.install(key, cwd);
49
+ }
50
+ break;
51
+ }
52
+
53
+ case 'install-plugin': {
54
+ showBanner();
55
+ const key = getArg('key');
56
+ if (!key) {
57
+ error('License key required.');
58
+ console.log('');
59
+ console.log(` Usage: ${colorize('cyan', 'npx antigravity-seo-kit install-plugin --key=SK-XXXX-XXXX-XXXX')}`);
60
+ console.log('');
61
+ console.log(` Purchase: ${colorize('cyan', 'https://antigravity-seokit.solann.io/')}`);
62
+ process.exit(1);
63
+ }
64
+ if (!isValidKeyFormat(key)) {
65
+ error('Invalid key format. Expected: SK-XXXX-XXXX-XXXX');
66
+ process.exit(1);
67
+ }
68
+ await installer.installPlugin(key);
69
+ break;
70
+ }
71
+
72
+ case 'setup-workspace': {
73
+ showBanner();
74
+ await installer.setupWorkspace(cwd);
46
75
  break;
47
76
  }
48
77
 
package/lib/installer.js CHANGED
@@ -4,9 +4,9 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const {
6
6
  success, error, warn, info, step, file, dir,
7
- spinner, removeRecursive,
7
+ spinner, removeRecursive, copyRecursive,
8
8
  writeLicenseFile, removeLicenseFile, readLicenseFile,
9
- colorize, PACKAGE_VERSION,
9
+ colorize, PACKAGE_VERSION, getGlobalPluginDir,
10
10
  } = require('./utils');
11
11
  const { generateFingerprint, getDeviceName } = require('./fingerprint');
12
12
  const api = require('./api');
@@ -383,6 +383,193 @@ function maskKey(key) {
383
383
  return key.substring(0, 7) + '*'.repeat(key.length - 11) + key.substring(key.length - 4);
384
384
  }
385
385
 
386
+ async function installPlugin(licenseKey) {
387
+ const globalPluginDir = getGlobalPluginDir();
388
+ const deviceId = generateFingerprint();
389
+ const deviceName = getDeviceName();
390
+
391
+ // Step 1: Verify license
392
+ const spin = spinner('Verifying license key...').start();
393
+ let verifyResult;
394
+ try {
395
+ verifyResult = await api.verifyLicense(licenseKey, deviceId, deviceName);
396
+ spin.succeed('License verified!');
397
+ } catch (err) {
398
+ spin.fail('License verification failed');
399
+ const errCode = err.responseData?.error?.code || '';
400
+ if (errCode.includes('DeviceLimitReached') || err.message.includes('Device limit')) {
401
+ console.log('');
402
+ error(`Device limit reached. You have ${err.responseData?.error?.data?.current || '?'}/${err.responseData?.error?.data?.max || 3} devices activated.`);
403
+ console.log('');
404
+ info('Run the following to manage your devices:');
405
+ console.log(` ${colorize('gray', 'npx antigravity-seo-kit devices')}`);
406
+ console.log(` ${colorize('gray', 'npx antigravity-seo-kit devices remove <deviceId>')}`);
407
+ } else if (err.isNotFound || errCode.includes('LicenseNotFound')) {
408
+ error('Invalid license key. Please check your key and try again.');
409
+ console.log(` Purchase: ${colorize('cyan', 'https://antigravitykit.solann.io/')}`);
410
+ } else if (errCode.includes('LicenseExpired')) {
411
+ error('License has expired. Please renew your license.');
412
+ console.log(` Renew: ${colorize('cyan', 'https://antigravitykit.solann.io/')}`);
413
+ } else if (errCode.includes('LicenseSuspended') || errCode.includes('LicenseRevoked')) {
414
+ error('License has been suspended or revoked. Contact support.');
415
+ } else {
416
+ error(err.message);
417
+ }
418
+ process.exit(1);
419
+ }
420
+
421
+ // Ensure global plugin directory exists
422
+ fs.mkdirSync(globalPluginDir, { recursive: true });
423
+
424
+ // Step 2: Download assets from server
425
+ console.log('');
426
+ const spinDl = spinner('Downloading SEO Kit assets...').start();
427
+ let downloadResult;
428
+ try {
429
+ const downloadUrl = api.getAssetsDownloadUrl(PACKAGE_VERSION);
430
+ downloadResult = await downloadAndExtract(downloadUrl, licenseKey, globalPluginDir);
431
+ spinDl.succeed(`Downloaded and installed ${downloadResult.count} files`);
432
+ } catch (err) {
433
+ spinDl.fail('Asset download failed');
434
+ error(err.message);
435
+ info('If this persists, contact support@solann.io');
436
+ process.exit(1);
437
+ }
438
+
439
+ // Step 3: Move files/folders inside the `.agent/` subdirectory up to the plugin root
440
+ const agentDir = path.join(globalPluginDir, '.agent');
441
+ if (fs.existsSync(agentDir)) {
442
+ const entries = fs.readdirSync(agentDir);
443
+ for (const entry of entries) {
444
+ const srcPath = path.join(agentDir, entry);
445
+ const destPath = path.join(globalPluginDir, entry);
446
+ if (fs.existsSync(destPath)) {
447
+ removeRecursive(destPath);
448
+ }
449
+ fs.renameSync(srcPath, destPath);
450
+ }
451
+ removeRecursive(agentDir);
452
+ }
453
+
454
+ // Step 4: Generate plugin.json and installed_version.json
455
+ const pluginJson = {
456
+ name: "antigravity-seo-kit",
457
+ version: PACKAGE_VERSION,
458
+ description: "Professional SEO Analysis Toolkit for Google Antigravity AI Agent — 44 specialized skills covering technical audit, E-E-A-T, schema, GEO, local SEO & more",
459
+ author: {
460
+ name: "Antigravity SEO Kit"
461
+ },
462
+ license: "SEE LICENSE IN LICENSE",
463
+ keywords: [
464
+ "seo",
465
+ "universal-seo-kit",
466
+ "ai-agent",
467
+ "antigravity",
468
+ "seo-geo",
469
+ "seo-audit",
470
+ "seo-toolkit",
471
+ "agent-skills",
472
+ "seo-analysis",
473
+ "technical-seo",
474
+ "eeat"
475
+ ]
476
+ };
477
+ fs.writeFileSync(path.join(globalPluginDir, 'plugin.json'), JSON.stringify(pluginJson, null, 2), 'utf-8');
478
+ fs.writeFileSync(path.join(globalPluginDir, 'installed_version.json'), JSON.stringify({ version: PACKAGE_VERSION }), 'utf-8');
479
+
480
+ // Step 5: Save global license info + manifest
481
+ const LICENSE_FILE = '.seo-kit-license';
482
+ writeLicenseFile(globalPluginDir, {
483
+ key: licenseKey,
484
+ deviceId,
485
+ deviceName,
486
+ version: PACKAGE_VERSION,
487
+ installedAt: new Date().toISOString(),
488
+ plan: verifyResult?.license?.planName || verifyResult?.license?.plan || 'unknown',
489
+ installedFiles: downloadResult.files.map(f => f.replace(/^\.agent\//, '')),
490
+ });
491
+
492
+ // Done!
493
+ console.log('');
494
+ console.log(colorize('green', '═══════════════════════════════════════════════════════'));
495
+ success(`SEO Kit v${PACKAGE_VERSION} installed successfully as a global Antigravity plugin!`);
496
+ console.log(colorize('green', '═══════════════════════════════════════════════════════'));
497
+ console.log('');
498
+
499
+ info('Next steps:');
500
+ console.log(` ${colorize('yellow', '1.')} Restart or open any workspace in ${colorize('bold', 'Google Antigravity')}`);
501
+ console.log(` ${colorize('yellow', '2.')} The SEO Kit plugin will automatically load globally.`);
502
+ console.log('');
503
+
504
+ if (verifyResult?.devicesUsed != null) {
505
+ info(`Devices: ${verifyResult.devicesUsed}/${verifyResult.maxDevices} activated`);
506
+ }
507
+ console.log('');
508
+ }
509
+
510
+ function setupWorkspace(cwd) {
511
+ const globalDir = getGlobalPluginDir();
512
+ const localAgentDir = path.join(cwd, '.agent');
513
+
514
+ if (fs.existsSync(localAgentDir)) {
515
+ return;
516
+ }
517
+
518
+ if (!fs.existsSync(globalDir)) {
519
+ error('Global SEO Kit plugin is not installed.');
520
+ info('Please run: npx antigravity-seo-kit install-plugin --key=YOUR_KEY');
521
+ process.exit(1);
522
+ }
523
+
524
+ const spin = spinner('Replicating .agent directory to local workspace...').start();
525
+ try {
526
+ fs.mkdirSync(localAgentDir, { recursive: true });
527
+
528
+ // Only copy necessary directories. agents and rules are loaded globally via the plugin.
529
+ // skills are copied but filtered to exclude .md files to save space and avoid duplicate prompts/instructions.
530
+ const directoriesToCopy = ['.shared', 'config', 'dashboard', 'docs', 'scripts', 'workflows'];
531
+ const filesToCopy = ['ARCHITECTURE.md'];
532
+
533
+ let count = 0;
534
+ for (const dir of directoriesToCopy) {
535
+ const src = path.join(globalDir, dir);
536
+ const dest = path.join(localAgentDir, dir);
537
+ if (fs.existsSync(src)) {
538
+ if (dir === 'skills') {
539
+ // Keep scripts and assets, but exclude instruction/documentation markdown files
540
+ count += copyRecursive(src, dest, {
541
+ overwrite: true,
542
+ filter: (filePath) => !filePath.endsWith('.md')
543
+ });
544
+ } else {
545
+ count += copyRecursive(src, dest, { overwrite: true });
546
+ }
547
+ }
548
+ }
549
+ for (const file of filesToCopy) {
550
+ const src = path.join(globalDir, file);
551
+ const dest = path.join(localAgentDir, file);
552
+ if (fs.existsSync(src)) {
553
+ fs.copyFileSync(src, dest);
554
+ count++;
555
+ }
556
+ }
557
+
558
+ const LICENSE_FILE = '.seo-kit-license';
559
+ const globalLicense = path.join(globalDir, LICENSE_FILE);
560
+ const localLicense = path.join(cwd, LICENSE_FILE);
561
+ if (fs.existsSync(globalLicense) && !fs.existsSync(localLicense)) {
562
+ fs.copyFileSync(globalLicense, localLicense);
563
+ }
564
+
565
+ spin.succeed(`Workspace setup complete! Copied ${count} files to .agent/`);
566
+ } catch (err) {
567
+ spin.fail('Workspace setup failed');
568
+ error(err.message);
569
+ process.exit(1);
570
+ }
571
+ }
572
+
386
573
  // ─── Exports ────────────────────────────────────────────────────────────────
387
574
 
388
575
  module.exports = {
@@ -392,4 +579,6 @@ module.exports = {
392
579
  status,
393
580
  devices,
394
581
  deviceRemove,
582
+ installPlugin,
583
+ setupWorkspace,
395
584
  };
package/lib/utils.js CHANGED
@@ -92,8 +92,9 @@ function spinner(message) {
92
92
  * Recursively copy a directory, merging with existing content.
93
93
  * Does not overwrite existing files unless `overwrite` is true.
94
94
  */
95
- function copyRecursive(src, dest, { overwrite = false, fileCallback = null } = {}) {
95
+ function copyRecursive(src, dest, { overwrite = false, fileCallback = null, filter = null } = {}) {
96
96
  if (!fs.existsSync(src)) return 0;
97
+ if (filter && !filter(src)) return 0;
97
98
 
98
99
  let count = 0;
99
100
 
@@ -104,7 +105,7 @@ function copyRecursive(src, dest, { overwrite = false, fileCallback = null } = {
104
105
  count += copyRecursive(
105
106
  path.join(src, entry),
106
107
  path.join(dest, entry),
107
- { overwrite, fileCallback }
108
+ { overwrite, fileCallback, filter }
108
109
  );
109
110
  }
110
111
  } else {
@@ -185,15 +186,19 @@ function showHelp() {
185
186
  console.log(`${colorize('bold', 'Usage:')} npx antigravity-seo-kit <command> [options]`);
186
187
  console.log('');
187
188
  console.log(`${colorize('bold', 'Commands:')}`);
188
- console.log(` ${colorize('green', 'install')} --key=SK-XXXX-XXXX-XXXX Install SEO Kit into current workspace`);
189
- console.log(` ${colorize('green', 'update')} Update to latest version`);
190
- console.log(` ${colorize('green', 'uninstall')} Remove SEO Kit from workspace`);
191
- console.log(` ${colorize('green', 'status')} Show license & installation info`);
192
- console.log(` ${colorize('green', 'devices')} List activated devices`);
193
- console.log(` ${colorize('green', 'devices remove')} <deviceId> Remove a device from license`);
189
+ console.log(` ${colorize('green', 'install')} --key=SK-XXXX-XXXX-XXXX Install SEO Kit into current workspace`);
190
+ console.log(` ${colorize('green', 'install-plugin')} --key=SK-XXXX-XXXX-XXXX Install SEO Kit globally as an Antigravity plugin`);
191
+ console.log(` ${colorize('green', 'setup-workspace')} Replicate .agent config directory to current workspace`);
192
+ console.log(` ${colorize('green', 'update')} Update to latest version`);
193
+ console.log(` ${colorize('green', 'uninstall')} Remove SEO Kit from workspace`);
194
+ console.log(` ${colorize('green', 'status')} Show license & installation info`);
195
+ console.log(` ${colorize('green', 'devices')} List activated devices`);
196
+ console.log(` ${colorize('green', 'devices remove')} <deviceId> Remove a device from license`);
194
197
  console.log('');
195
198
  console.log(`${colorize('bold', 'Examples:')}`);
196
199
  console.log(` ${colorize('gray', 'npx antigravity-seo-kit install --key=SK-A1B2-C3D4-E5F6')}`);
200
+ console.log(` ${colorize('gray', 'npx antigravity-seo-kit install-plugin --key=SK-A1B2-C3D4-E5F6')}`);
201
+ console.log(` ${colorize('gray', 'npx antigravity-seo-kit setup-workspace')}`);
197
202
  console.log(` ${colorize('gray', 'npx antigravity-seo-kit devices')}`);
198
203
  console.log(` ${colorize('gray', 'npx antigravity-seo-kit devices remove a1b2c3d4')}`);
199
204
  console.log('');
@@ -205,8 +210,15 @@ function showHelp() {
205
210
 
206
211
  const LICENSE_FILE = '.seo-kit-license';
207
212
 
213
+ function getGlobalPluginDir() {
214
+ return path.join(require('os').homedir(), '.gemini', 'config', 'plugins', 'antigravity-seo-kit');
215
+ }
216
+
208
217
  function readLicenseFile(cwd) {
209
- const licensePath = path.join(cwd, LICENSE_FILE);
218
+ let licensePath = path.join(cwd, LICENSE_FILE);
219
+ if (!fs.existsSync(licensePath)) {
220
+ licensePath = path.join(getGlobalPluginDir(), LICENSE_FILE);
221
+ }
210
222
  if (!fs.existsSync(licensePath)) return null;
211
223
  try {
212
224
  const data = JSON.parse(fs.readFileSync(licensePath, 'utf-8'));
@@ -282,9 +294,10 @@ module.exports = {
282
294
  isValidKeyFormat,
283
295
  showBanner,
284
296
  showHelp,
297
+ getGlobalPluginDir,
285
298
  readLicenseFile,
286
299
  writeLicenseFile,
287
300
  removeLicenseFile,
288
301
  LICENSE_FILE,
289
- PACKAGE_VERSION: '1.2.6',
302
+ PACKAGE_VERSION: '1.2.8',
290
303
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antigravity-seo-kit",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "Professional SEO Analysis Toolkit for Google Antigravity AI Agent — 44 specialized skills covering technical audit, E-E-A-T, schema, GEO, local SEO & more",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {
@@ -18,6 +18,8 @@
18
18
  "cursor",
19
19
  "gemini-cli",
20
20
  "antigravity",
21
+ "antigravity-plugin",
22
+ "antigravity-seo-kit-plugin",
21
23
  "seo-geo",
22
24
  "seo-audit",
23
25
  "seo-toolkit",
@@ -31,5 +33,10 @@
31
33
  "homepage": "https://antigravityseokit.solann.io/",
32
34
  "engines": {
33
35
  "node": ">=18"
36
+ },
37
+ "scripts": {
38
+ "pack-assets": "node scripts/pack-assets.js",
39
+ "pack-npm": "npm pack --pack-destination dist",
40
+ "pack-all": "node scripts/pack-assets.js && npm pack --pack-destination dist"
34
41
  }
35
42
  }