antigravity-seo-kit 2.7.3 → 2.9.7

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
@@ -14,7 +14,7 @@ npx antigravity-seo-kit install-plugin --key=SK-XXXX-XXXX-XXXX
14
14
  # OR: npx antigravity-seo-kit install --global --key=SK-XXXX-XXXX-XXXX
15
15
  ```
16
16
 
17
- When you start work in a new workspace, run the following command to sync the agent rules and folders locally:
17
+ When you start work in a new workspace, run the following command to sync the `.agents/` structure locally:
18
18
  ```bash
19
19
  npx antigravity-seo-kit setup-workspace
20
20
  ```
@@ -45,12 +45,15 @@ npx antigravity-seo-kit install --key=SK-XXXX-XXXX-XXXX
45
45
  # Install globally as a plugin (recommended)
46
46
  npx antigravity-seo-kit install-plugin --key=SK-XXXX-XXXX-XXXX
47
47
 
48
- # Replicate .agent config directory from global plugin to local workspace
48
+ # Replicate .agents/ workspace structure from global plugin
49
49
  npx antigravity-seo-kit setup-workspace
50
50
 
51
51
  # Install locally into current workspace
52
52
  npx antigravity-seo-kit install --key=SK-XXXX-XXXX-XXXX
53
53
 
54
+ # Migrate from v2.x (.agent/) to v3.0 (.agents/)
55
+ npx antigravity-seo-kit migrate
56
+
54
57
  # Update to latest version
55
58
  npx antigravity-seo-kit update
56
59
 
package/bin/cli.js CHANGED
@@ -75,6 +75,12 @@ async function main() {
75
75
  break;
76
76
  }
77
77
 
78
+ case 'migrate': {
79
+ showBanner();
80
+ installer.migrate(cwd);
81
+ break;
82
+ }
83
+
78
84
  case 'update': {
79
85
  showBanner();
80
86
  await installer.update(cwd);
package/lib/downloader.js CHANGED
@@ -149,6 +149,7 @@ function extractTarGz(gzBuffer, targetDir) {
149
149
  const isConfigJson = fullName.endsWith('.json') &&
150
150
  (fullName.startsWith('config/') ||
151
151
  fullName.startsWith('.agent/config/') ||
152
+ fullName.startsWith('.agents/config/') ||
152
153
  fullName.includes('/config/'));
153
154
 
154
155
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
package/lib/installer.js CHANGED
@@ -69,7 +69,70 @@ async function install(licenseKey, cwd) {
69
69
  process.exit(1);
70
70
  }
71
71
 
72
- // Step 3: Save license info + manifest
72
+ // Step 3: Restructure into .agents/ format if server sent legacy .agent/
73
+ const legacyAgentDir = path.join(cwd, '.agent');
74
+ const newAgentsDir = path.join(cwd, '.agents');
75
+
76
+ if (fs.existsSync(legacyAgentDir) && !fs.existsSync(newAgentsDir)) {
77
+ const spinMigrate = spinner('Restructuring to .agents/ format...').start();
78
+ try {
79
+ fs.mkdirSync(newAgentsDir, { recursive: true });
80
+
81
+ // Workspace-level dirs → .agents/
82
+ const workspaceDirs = ['agents', 'workflows', 'scripts', 'config', 'dashboard', 'docs', '.shared'];
83
+ let migrateCount = 0;
84
+ for (const dir of workspaceDirs) {
85
+ const src = path.join(legacyAgentDir, dir);
86
+ const dest = path.join(newAgentsDir, dir);
87
+ if (fs.existsSync(src)) {
88
+ migrateCount += copyRecursive(src, dest, { overwrite: true });
89
+ }
90
+ }
91
+
92
+ // Root-level .md files → .agents/
93
+ for (const f of fs.readdirSync(legacyAgentDir)) {
94
+ const src = path.join(legacyAgentDir, f);
95
+ if (fs.statSync(src).isFile() && f.endsWith('.md')) {
96
+ fs.copyFileSync(src, path.join(newAgentsDir, f));
97
+ migrateCount++;
98
+ }
99
+ }
100
+
101
+ // skills + rules → .agents/plugins/antigravity-seo-kit/
102
+ const pluginDir = path.join(newAgentsDir, 'plugins', 'antigravity-seo-kit');
103
+ fs.mkdirSync(pluginDir, { recursive: true });
104
+
105
+ const skillsSrc = path.join(legacyAgentDir, 'skills');
106
+ if (fs.existsSync(skillsSrc)) {
107
+ migrateCount += copyRecursive(skillsSrc, path.join(pluginDir, 'skills'), { overwrite: true });
108
+ }
109
+ const rulesSrc = path.join(legacyAgentDir, 'rules');
110
+ if (fs.existsSync(rulesSrc)) {
111
+ migrateCount += copyRecursive(rulesSrc, path.join(pluginDir, 'rules'), { overwrite: true });
112
+ }
113
+
114
+ // Create plugin.json
115
+ const pluginJsonPath = path.join(pluginDir, 'plugin.json');
116
+ if (!fs.existsSync(pluginJsonPath)) {
117
+ fs.writeFileSync(pluginJsonPath, JSON.stringify({
118
+ name: 'antigravity-seo-kit',
119
+ version: PACKAGE_VERSION,
120
+ description: 'Professional Agentic SEO Platform — 44+ specialized skills',
121
+ author: { name: 'Solann', email: 'admin@solann.io' },
122
+ }, null, 2), 'utf-8');
123
+ }
124
+
125
+ // Remove legacy .agent/ (downloaded files only, not user data)
126
+ removeRecursive(legacyAgentDir);
127
+
128
+ spinMigrate.succeed(`Restructured to .agents/ format (${migrateCount} files)`);
129
+ } catch (err) {
130
+ spinMigrate.fail('Restructuring failed, keeping .agent/ as-is');
131
+ warn(`You can manually run: npx antigravity-seo-kit migrate`);
132
+ }
133
+ }
134
+
135
+ // Step 4: Save license info + manifest
73
136
  writeLicenseFile(cwd, {
74
137
  key: licenseKey,
75
138
  deviceId,
@@ -122,6 +185,10 @@ async function update(cwd) {
122
185
  process.exit(1);
123
186
  }
124
187
 
188
+ // Detect structure: v3.0 (.agents/) or v2.x (.agent/)
189
+ const isV3 = fs.existsSync(path.join(cwd, '.agents'));
190
+ const isV2 = fs.existsSync(path.join(cwd, '.agent'));
191
+
125
192
  // Download latest assets from server
126
193
  console.log('');
127
194
  const spinDl = spinner('Downloading latest SEO Kit assets...').start();
@@ -136,6 +203,13 @@ async function update(cwd) {
136
203
  process.exit(1);
137
204
  }
138
205
 
206
+ // If user was on v2 (.agent/) and download brings v3 (.agents/), inform about migrate
207
+ if (isV2 && !isV3 && fs.existsSync(path.join(cwd, '.agents'))) {
208
+ console.log('');
209
+ info('v3.0 structure detected in update. Run migrate to complete:');
210
+ console.log(` ${colorize('cyan', 'npx antigravity-seo-kit migrate')}`);
211
+ }
212
+
139
213
  // Update license file
140
214
  config.version = PACKAGE_VERSION;
141
215
  config.updatedAt = new Date().toISOString();
@@ -160,7 +234,7 @@ async function uninstall(cwd) {
160
234
  let totalRemoved = 0;
161
235
 
162
236
  if (config.installedFiles && config.installedFiles.length > 0) {
163
- // New approach: use manifest from license file
237
+ // Manifest-based removal
164
238
  for (const filePath of config.installedFiles) {
165
239
  const target = path.join(cwd, filePath);
166
240
  if (fs.existsSync(target)) {
@@ -168,13 +242,20 @@ async function uninstall(cwd) {
168
242
  totalRemoved++;
169
243
  }
170
244
  }
171
- // Clean up empty directories
245
+ // Clean up empty directories for both v2 and v3 structures
172
246
  cleanEmptyDirsUp(path.join(cwd, '.agent'));
247
+ cleanEmptyDirsUp(path.join(cwd, '.agents'));
173
248
  } else {
174
- // Legacy fallback: use hardcoded arrays
249
+ // Legacy fallback: use prefix scan
175
250
  totalRemoved += legacyUninstall(cwd);
176
251
  }
177
252
 
253
+ // Also clean .agents/plugins/antigravity-seo-kit/ if it exists (v3)
254
+ const v3PluginDir = path.join(cwd, '.agents', 'plugins', 'antigravity-seo-kit');
255
+ if (fs.existsSync(v3PluginDir)) {
256
+ totalRemoved += removeRecursive(v3PluginDir);
257
+ }
258
+
178
259
  // Remove license file
179
260
  removeLicenseFile(cwd);
180
261
 
@@ -253,24 +334,41 @@ async function status(cwd) {
253
334
 
254
335
  // Count installed files
255
336
  if (config.installedFiles && config.installedFiles.length > 0) {
256
- // New: count from manifest
337
+ // Manifest-based count
257
338
  let existingFiles = 0;
258
339
  for (const f of config.installedFiles) {
259
340
  if (fs.existsSync(path.join(cwd, f))) existingFiles++;
260
341
  }
261
342
  console.log(` ${colorize('cyan', 'Files:')} ${existingFiles}/${config.installedFiles.length} present`);
262
343
  } else {
263
- // Legacy: scan for seo-* prefixed entries
264
- const agentDir = path.join(cwd, '.agent');
344
+ // Legacy: scan for seo-* prefixed entries in both .agent/ and .agents/
265
345
  let totalLegacy = 0;
266
- for (const subDir of LEGACY_DIRS) {
267
- const dirPath = path.join(agentDir, subDir);
268
- if (!fs.existsSync(dirPath)) continue;
269
- for (const entry of fs.readdirSync(dirPath)) {
270
- if (entry.startsWith(LEGACY_SEO_PREFIX) || entry === 'seo') totalLegacy++;
346
+ for (const baseDir of ['.agent', '.agents']) {
347
+ const agentDir = path.join(cwd, baseDir);
348
+ if (!fs.existsSync(agentDir)) continue;
349
+ for (const subDir of LEGACY_DIRS) {
350
+ const dirPath = path.join(agentDir, subDir);
351
+ if (!fs.existsSync(dirPath)) continue;
352
+ for (const entry of fs.readdirSync(dirPath)) {
353
+ if (entry.startsWith(LEGACY_SEO_PREFIX) || entry === 'seo') totalLegacy++;
354
+ }
271
355
  }
272
356
  }
273
- console.log(` ${colorize('cyan', 'Components:')} ${totalLegacy} installed (legacy)`);
357
+ // Also count plugin dir if v3
358
+ const pluginSkillsDir = path.join(cwd, '.agents', 'plugins', 'antigravity-seo-kit', 'skills');
359
+ if (fs.existsSync(pluginSkillsDir)) {
360
+ totalLegacy += fs.readdirSync(pluginSkillsDir).length;
361
+ }
362
+ console.log(` ${colorize('cyan', 'Components:')} ${totalLegacy} installed`);
363
+ }
364
+
365
+ // Detect structure version
366
+ const hasV3 = fs.existsSync(path.join(cwd, '.agents'));
367
+ const hasV2 = fs.existsSync(path.join(cwd, '.agent'));
368
+ const structureVersion = hasV3 ? 'v3.0 (.agents/)' : hasV2 ? 'v2.x (.agent/)' : 'unknown';
369
+ console.log(` ${colorize('cyan', 'Structure:')} ${structureVersion}`);
370
+ if (hasV2 && !hasV3) {
371
+ warn('Using legacy v2.x structure. Run: npx antigravity-seo-kit migrate');
274
372
  }
275
373
  console.log('');
276
374
 
@@ -436,51 +534,72 @@ async function installPlugin(licenseKey) {
436
534
  process.exit(1);
437
535
  }
438
536
 
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);
537
+ // Step 3: Restructure into .agents/ plugin format
538
+ // v3.0: Downloaded assets arrive in .agents/ structure already
539
+ // Handle legacy .agent/ if server still serves old format
540
+ const legacyAgentDir = path.join(globalPluginDir, '.agent');
541
+ const newAgentsDir = path.join(globalPluginDir, '.agents');
542
+
543
+ if (fs.existsSync(legacyAgentDir) && !fs.existsSync(newAgentsDir)) {
544
+ // Legacy format: flatten .agent/ into plugin root
545
+ const entries = fs.readdirSync(legacyAgentDir);
443
546
  for (const entry of entries) {
444
- const srcPath = path.join(agentDir, entry);
547
+ const srcPath = path.join(legacyAgentDir, entry);
445
548
  const destPath = path.join(globalPluginDir, entry);
446
549
  if (fs.existsSync(destPath)) {
447
550
  removeRecursive(destPath);
448
551
  }
449
552
  fs.renameSync(srcPath, destPath);
450
553
  }
451
- removeRecursive(agentDir);
554
+ removeRecursive(legacyAgentDir);
555
+ } else if (fs.existsSync(newAgentsDir)) {
556
+ // v3.0 format: move plugin contents (skills, rules) to plugin root
557
+ const pluginSrcDir = path.join(newAgentsDir, 'plugins', 'antigravity-seo-kit');
558
+ if (fs.existsSync(pluginSrcDir)) {
559
+ for (const entry of fs.readdirSync(pluginSrcDir)) {
560
+ const srcPath = path.join(pluginSrcDir, entry);
561
+ const destPath = path.join(globalPluginDir, entry);
562
+ if (fs.existsSync(destPath)) {
563
+ removeRecursive(destPath);
564
+ }
565
+ fs.renameSync(srcPath, destPath);
566
+ }
567
+ }
568
+ // Keep workspace-level dirs (agents, workflows) for setupWorkspace
569
+ const workspaceDirs = ['agents', 'workflows', 'scripts', 'config', 'dashboard', 'docs', '.shared'];
570
+ for (const dir of workspaceDirs) {
571
+ const srcDir = path.join(newAgentsDir, dir);
572
+ const destDir = path.join(globalPluginDir, `_workspace_${dir}`);
573
+ if (fs.existsSync(srcDir)) {
574
+ if (fs.existsSync(destDir)) removeRecursive(destDir);
575
+ fs.renameSync(srcDir, destDir);
576
+ }
577
+ }
578
+ // Copy seo-architecture.md
579
+ const archFile = path.join(newAgentsDir, 'seo-architecture.md');
580
+ if (fs.existsSync(archFile)) {
581
+ fs.copyFileSync(archFile, path.join(globalPluginDir, '_workspace_seo-architecture.md'));
582
+ }
583
+ removeRecursive(newAgentsDir);
452
584
  }
453
585
 
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 Agentic SEO Platform for Google Antigravity 2 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
- email: "admin@solann.io",
462
- },
463
- repository: "https://solann.io/antigravity-seo-kit",
464
- license: "SEE LICENSE IN LICENSE",
465
- keywords: [
466
- "seo",
467
- "agentic-seo",
468
- "ai-agent",
469
- "antigravity",
470
- "seo-geo",
471
- "seo-audit",
472
- "seo-toolkit",
473
- "agent-skills",
474
- "seo-analysis",
475
- "technical-seo",
476
- "eeat"
477
- ]
478
- };
479
- fs.writeFileSync(path.join(globalPluginDir, 'plugin.json'), JSON.stringify(pluginJson, null, 2), 'utf-8');
586
+ // Step 4: Ensure plugin.json and installed_version.json
587
+ const pluginJsonPath = path.join(globalPluginDir, 'plugin.json');
588
+ if (!fs.existsSync(pluginJsonPath)) {
589
+ const pluginJson = {
590
+ name: "antigravity-seo-kit",
591
+ version: PACKAGE_VERSION,
592
+ description: "Professional Agentic SEO Platform — 44+ specialized skills",
593
+ author: { name: "Solann", email: "admin@solann.io" },
594
+ repository: "https://solann.io/antigravity-seo-kit",
595
+ license: "SEE LICENSE IN LICENSE",
596
+ keywords: ["seo", "agentic-seo", "ai-agent", "antigravity"]
597
+ };
598
+ fs.writeFileSync(pluginJsonPath, JSON.stringify(pluginJson, null, 2), 'utf-8');
599
+ }
480
600
  fs.writeFileSync(path.join(globalPluginDir, 'installed_version.json'), JSON.stringify({ version: PACKAGE_VERSION }), 'utf-8');
481
601
 
482
602
  // Step 5: Save global license info + manifest
483
- const LICENSE_FILE = '.seo-kit-license';
484
603
  writeLicenseFile(globalPluginDir, {
485
604
  key: licenseKey,
486
605
  deviceId,
@@ -488,19 +607,19 @@ async function installPlugin(licenseKey) {
488
607
  version: PACKAGE_VERSION,
489
608
  installedAt: new Date().toISOString(),
490
609
  plan: verifyResult?.license?.planName || verifyResult?.license?.plan || 'unknown',
491
- installedFiles: downloadResult.files.map(f => f.replace(/^\.agent\//, '')),
610
+ installedFiles: downloadResult.files.map(f => f.replace(/^\.agents?\//, '')),
492
611
  });
493
612
 
494
613
  // Done!
495
614
  console.log('');
496
615
  console.log(colorize('green', '═══════════════════════════════════════════════════════'));
497
- success(`SEO Kit v${PACKAGE_VERSION} installed successfully as a global Antigravity plugin!`);
616
+ success(`SEO Kit v${PACKAGE_VERSION} installed as global Antigravity plugin!`);
498
617
  console.log(colorize('green', '═══════════════════════════════════════════════════════'));
499
618
  console.log('');
500
619
 
501
620
  info('Next steps:');
502
621
  console.log(` ${colorize('yellow', '1.')} Restart or open any workspace in ${colorize('bold', 'Google Antigravity')}`);
503
- console.log(` ${colorize('yellow', '2.')} The SEO Kit plugin will automatically load globally.`);
622
+ console.log(` ${colorize('yellow', '2.')} Plugin loads globally. Run ${colorize('cyan', 'npx antigravity-seo-kit setup-workspace')} in a project.`);
504
623
  console.log('');
505
624
 
506
625
  if (verifyResult?.devicesUsed != null) {
@@ -511,10 +630,10 @@ async function installPlugin(licenseKey) {
511
630
 
512
631
  function setupWorkspace(cwd) {
513
632
  const globalDir = getGlobalPluginDir();
514
- const localAgentDir = path.join(cwd, '.agent');
633
+ const localAgentsDir = path.join(cwd, '.agents');
515
634
 
516
- if (fs.existsSync(localAgentDir)) {
517
- info('Existing .agent directory found. Re-synchronizing and updating files...');
635
+ if (fs.existsSync(localAgentsDir)) {
636
+ info('Existing .agents directory found. Re-synchronizing...');
518
637
  }
519
638
 
520
639
  if (!fs.existsSync(globalDir)) {
@@ -523,40 +642,60 @@ function setupWorkspace(cwd) {
523
642
  process.exit(1);
524
643
  }
525
644
 
526
- const spin = spinner('Replicating .agent directory to local workspace...').start();
645
+ const spin = spinner('Setting up .agents/ workspace structure...').start();
527
646
  try {
528
- fs.mkdirSync(localAgentDir, { recursive: true });
647
+ fs.mkdirSync(localAgentsDir, { recursive: true });
529
648
 
530
- // Only copy necessary directories. agents and rules are loaded globally via the plugin.
531
- // skills are copied but filtered to exclude .md files to save space and avoid duplicate prompts/instructions.
532
- const directoriesToCopy = ['.shared', 'config', 'dashboard', 'docs', 'scripts', 'workflows'];
649
+ // v3.0: Copy workspace-level dirs (agents, workflows, scripts, etc.)
650
+ // Plugin contents (skills, rules) stay in ~/.gemini/config/plugins/
651
+ const workspaceDirs = ['agents', 'workflows', 'scripts', 'config', 'dashboard', 'docs', '.shared'];
533
652
  const filesToCopy = ['seo-architecture.md'];
534
653
 
535
654
  let count = 0;
536
- for (const dir of directoriesToCopy) {
537
- const src = path.join(globalDir, dir);
538
- const dest = path.join(localAgentDir, dir);
655
+ for (const dir of workspaceDirs) {
656
+ // Try _workspace_ prefixed dirs first (v3.0 install format)
657
+ let src = path.join(globalDir, `_workspace_${dir}`);
658
+ if (!fs.existsSync(src)) {
659
+ // Fallback to flat dir (v2.x compat)
660
+ src = path.join(globalDir, dir);
661
+ }
662
+ const dest = path.join(localAgentsDir, dir);
539
663
  if (fs.existsSync(src)) {
540
664
  count += copyRecursive(src, dest, { overwrite: true });
541
665
  }
542
666
  }
543
- for (const file of filesToCopy) {
544
- const src = path.join(globalDir, file);
545
- const dest = path.join(localAgentDir, file);
667
+ for (const f of filesToCopy) {
668
+ let src = path.join(globalDir, `_workspace_${f}`);
669
+ if (!fs.existsSync(src)) {
670
+ src = path.join(globalDir, f);
671
+ }
672
+ const dest = path.join(localAgentsDir, f);
546
673
  if (fs.existsSync(src)) {
547
674
  fs.copyFileSync(src, dest);
548
675
  count++;
549
676
  }
550
677
  }
551
678
 
552
- const LICENSE_FILE = '.seo-kit-license';
553
- const globalLicense = path.join(globalDir, LICENSE_FILE);
554
- const localLicense = path.join(cwd, LICENSE_FILE);
679
+ // Create plugin symlink/reference in workspace
680
+ const pluginsDir = path.join(localAgentsDir, 'plugins', 'antigravity-seo-kit');
681
+ if (!fs.existsSync(pluginsDir)) {
682
+ fs.mkdirSync(pluginsDir, { recursive: true });
683
+ // Write a pointer file so workspace knows where the plugin lives
684
+ fs.writeFileSync(
685
+ path.join(pluginsDir, '_global_ref.json'),
686
+ JSON.stringify({ globalDir, version: PACKAGE_VERSION }, null, 2),
687
+ 'utf-8'
688
+ );
689
+ }
690
+
691
+ const LICENSE_FILE_NAME = '.seo-kit-license';
692
+ const globalLicense = path.join(globalDir, LICENSE_FILE_NAME);
693
+ const localLicense = path.join(cwd, LICENSE_FILE_NAME);
555
694
  if (fs.existsSync(globalLicense) && !fs.existsSync(localLicense)) {
556
695
  fs.copyFileSync(globalLicense, localLicense);
557
696
  }
558
697
 
559
- spin.succeed(`Workspace setup complete! Copied ${count} files to .agent/`);
698
+ spin.succeed(`Workspace setup complete! Copied ${count} files to .agents/`);
560
699
  } catch (err) {
561
700
  spin.fail('Workspace setup failed');
562
701
  error(err.message);
@@ -564,6 +703,89 @@ function setupWorkspace(cwd) {
564
703
  }
565
704
  }
566
705
 
706
+ // ─── Migrate Command (v2.x → v3.0) ─────────────────────────────────────────
707
+
708
+ function migrate(cwd) {
709
+ const oldAgentDir = path.join(cwd, '.agent');
710
+ const newAgentsDir = path.join(cwd, '.agents');
711
+
712
+ if (!fs.existsSync(oldAgentDir)) {
713
+ if (fs.existsSync(newAgentsDir)) {
714
+ info('Already using v3.0 .agents/ structure. No migration needed.');
715
+ return;
716
+ }
717
+ error('No .agent/ directory found. Nothing to migrate.');
718
+ process.exit(1);
719
+ }
720
+
721
+ if (fs.existsSync(newAgentsDir)) {
722
+ warn('.agents/ already exists. Migration will merge/overwrite.');
723
+ }
724
+
725
+ const spin = spinner('Migrating .agent/ → .agents/ (v2.x → v3.0)...').start();
726
+ try {
727
+ let count = 0;
728
+
729
+ // 1. Copy workspace-level dirs
730
+ const workspaceDirs = ['agents', 'workflows', 'scripts', 'config', 'dashboard', 'docs', '.shared'];
731
+ for (const dir of workspaceDirs) {
732
+ const src = path.join(oldAgentDir, dir);
733
+ const dest = path.join(newAgentsDir, dir);
734
+ if (fs.existsSync(src)) {
735
+ count += copyRecursive(src, dest, { overwrite: true, mergeJson: true });
736
+ }
737
+ }
738
+
739
+ // Copy root-level md files
740
+ const rootFiles = fs.readdirSync(oldAgentDir).filter(f => f.endsWith('.md'));
741
+ for (const f of rootFiles) {
742
+ const src = path.join(oldAgentDir, f);
743
+ const dest = path.join(newAgentsDir, f);
744
+ if (fs.statSync(src).isFile()) {
745
+ fs.copyFileSync(src, dest);
746
+ count++;
747
+ }
748
+ }
749
+
750
+ // 2. Copy skills + rules into plugin structure
751
+ const pluginDir = path.join(newAgentsDir, 'plugins', 'antigravity-seo-kit');
752
+ fs.mkdirSync(pluginDir, { recursive: true });
753
+
754
+ const skillsSrc = path.join(oldAgentDir, 'skills');
755
+ const skillsDest = path.join(pluginDir, 'skills');
756
+ if (fs.existsSync(skillsSrc)) {
757
+ count += copyRecursive(skillsSrc, skillsDest, { overwrite: true, mergeJson: true });
758
+ }
759
+
760
+ const rulesSrc = path.join(oldAgentDir, 'rules');
761
+ const rulesDest = path.join(pluginDir, 'rules');
762
+ if (fs.existsSync(rulesSrc)) {
763
+ count += copyRecursive(rulesSrc, rulesDest, { overwrite: true });
764
+ }
765
+
766
+ // 3. Create plugin.json if missing
767
+ const pluginJsonPath = path.join(pluginDir, 'plugin.json');
768
+ if (!fs.existsSync(pluginJsonPath)) {
769
+ fs.writeFileSync(pluginJsonPath, JSON.stringify({
770
+ name: 'antigravity-seo-kit',
771
+ version: PACKAGE_VERSION,
772
+ description: 'Migrated from v2.x .agent/ structure',
773
+ }, null, 2), 'utf-8');
774
+ }
775
+
776
+ spin.succeed(`Migration complete! ${count} files moved to .agents/`);
777
+ console.log('');
778
+ info('The old .agent/ directory has been preserved as backup.');
779
+ info('After verifying everything works, you can remove it:');
780
+ console.log(` ${colorize('gray', 'rm -rf .agent/')}`);
781
+ console.log('');
782
+ } catch (err) {
783
+ spin.fail('Migration failed');
784
+ error(err.message);
785
+ process.exit(1);
786
+ }
787
+ }
788
+
567
789
  // ─── Exports ────────────────────────────────────────────────────────────────
568
790
 
569
791
  module.exports = {
@@ -575,4 +797,5 @@ module.exports = {
575
797
  deviceRemove,
576
798
  installPlugin,
577
799
  setupWorkspace,
800
+ migrate,
578
801
  };
package/lib/utils.js CHANGED
@@ -307,7 +307,8 @@ function showHelp() {
307
307
  console.log(`${colorize('bold', 'Commands:')}`);
308
308
  console.log(` ${colorize('green', 'install')} --key=SK-XXXX-XXXX-XXXX Install SEO Kit into current workspace`);
309
309
  console.log(` ${colorize('green', 'install-plugin')} --key=SK-XXXX-XXXX-XXXX Install SEO Kit globally as an Antigravity plugin`);
310
- console.log(` ${colorize('green', 'setup-workspace')} Replicate .agent config directory to current workspace`);
310
+ console.log(` ${colorize('green', 'setup-workspace')} Replicate .agents config directory to current workspace`);
311
+ console.log(` ${colorize('green', 'migrate')} Migrate .agent/ to .agents/ (v2.x → v3.0)`);
311
312
  console.log(` ${colorize('green', 'update')} Update to latest version`);
312
313
  console.log(` ${colorize('green', 'uninstall')} Remove SEO Kit from workspace`);
313
314
  console.log(` ${colorize('green', 'status')} Show license & installation info`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antigravity-seo-kit",
3
- "version": "2.7.3",
3
+ "version": "2.9.7",
4
4
  "description": "Professional Agentic SEO Platform for Google Antigravity 2 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": {