@rankcli/cli 0.0.18 → 0.0.19

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 (4) hide show
  1. package/README.md +15 -10
  2. package/dist/index.js +325 -131
  3. package/dist/index.mjs +325 -132
  4. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -262,7 +262,7 @@ async function analyze(options) {
262
262
  // src/commands/audit.ts
263
263
  import { runFullAudit, discoverPagesFromLinks, frameworks } from "@rankcli/agent-runtime";
264
264
  import ora from "ora";
265
- import chalk from "chalk";
265
+ import chalk2 from "chalk";
266
266
 
267
267
  // src/lib/config.ts
268
268
  import Conf from "conf";
@@ -457,6 +457,174 @@ async function getAuthenticatedClient() {
457
457
  return supabase;
458
458
  }
459
459
 
460
+ // src/lib/upsell.ts
461
+ import chalk from "chalk";
462
+ var WEB_APP_URL2 = "https://rankcli.dev";
463
+ var TIER_LIMITS = {
464
+ free_anon: {
465
+ checks: 50,
466
+ sites: 0,
467
+ domains_per_month: 1,
468
+ features: { geo: false, auto_fix: false, github: false, ai: false }
469
+ },
470
+ free: {
471
+ checks: 100,
472
+ sites: 1,
473
+ domains_per_month: 2,
474
+ features: { geo: "basic", auto_fix: false, github: false, ai: false }
475
+ },
476
+ solo: {
477
+ checks: 280,
478
+ sites: 3,
479
+ domains_per_month: 5,
480
+ features: { geo: true, auto_fix: true, github: false, ai: true }
481
+ },
482
+ pro: {
483
+ checks: 280,
484
+ sites: 10,
485
+ domains_per_month: 15,
486
+ features: { geo: true, auto_fix: true, github: true, ai: true }
487
+ },
488
+ agency: {
489
+ checks: 280,
490
+ sites: 50,
491
+ domains_per_month: null,
492
+ // unlimited
493
+ features: { geo: true, auto_fix: true, github: true, ai: true }
494
+ }
495
+ };
496
+ var UPSELL_MESSAGES = {
497
+ // After free audit completes
498
+ free_audit_complete: () => [
499
+ "",
500
+ chalk.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
501
+ chalk.yellow.bold(" \u{1F680} Get 2x more checks + audit history"),
502
+ chalk.dim(" Create a free account to unlock 100 checks and save your results."),
503
+ "",
504
+ chalk.cyan(` \u2192 rankcli login`),
505
+ chalk.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
506
+ ""
507
+ ],
508
+ // When checks limit is reached
509
+ checks_limit: (ctx) => [
510
+ "",
511
+ chalk.yellow(`\u26A0\uFE0F Running ${ctx.current || 50} of ${TIER_LIMITS.solo.checks} available checks`),
512
+ chalk.dim(` Unlock all ${TIER_LIMITS.solo.checks}+ checks with Solo ($9/mo)`),
513
+ chalk.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_checks`),
514
+ ""
515
+ ],
516
+ // When domain limit is reached
517
+ domain_limit: (ctx) => [
518
+ "",
519
+ chalk.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
520
+ chalk.red.bold(" \u2717 Domain limit reached"),
521
+ chalk.dim(` Your ${ctx.tier} plan allows ${ctx.max} domains per month.`),
522
+ "",
523
+ chalk.dim(" Upgrade to audit more sites:"),
524
+ chalk.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_domain_limit`),
525
+ chalk.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
526
+ ""
527
+ ],
528
+ // When GEO features are limited
529
+ geo_limited: (ctx) => [
530
+ "",
531
+ chalk.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
532
+ chalk.yellow.bold(" \u{1F916} Full GEO analysis requires Solo"),
533
+ chalk.dim(" You're seeing basic AI crawler access checks."),
534
+ chalk.dim(" Full analysis includes: LLM signals, citation readiness, content structure"),
535
+ "",
536
+ chalk.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_geo`),
537
+ chalk.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
538
+ ""
539
+ ],
540
+ // When auto-fix is attempted without access
541
+ auto_fix_blocked: () => [
542
+ "",
543
+ chalk.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
544
+ chalk.red.bold(" \u2717 Auto-fix requires Solo plan"),
545
+ chalk.dim(" Apply framework-specific SEO fixes directly to your codebase."),
546
+ "",
547
+ chalk.dim(" Solo ($9/mo) includes:"),
548
+ chalk.dim(" \u2022 Local auto-fix via CLI"),
549
+ chalk.dim(" \u2022 25+ framework support"),
550
+ chalk.dim(" \u2022 Full 280+ checks"),
551
+ "",
552
+ chalk.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_autofix`),
553
+ chalk.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
554
+ ""
555
+ ],
556
+ // When GitHub integration is attempted without access
557
+ github_blocked: () => [
558
+ "",
559
+ chalk.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
560
+ chalk.red.bold(" \u2717 GitHub auto-fix PRs require Pro"),
561
+ chalk.dim(" Automatically create fix PRs when SEO issues are detected."),
562
+ "",
563
+ chalk.dim(" Pro ($29/mo) includes:"),
564
+ chalk.dim(" \u2022 GitHub App integration"),
565
+ chalk.dim(" \u2022 Auto-fix PRs"),
566
+ chalk.dim(" \u2022 Daily scheduled audits"),
567
+ chalk.dim(" \u2022 Slack/Discord alerts"),
568
+ "",
569
+ chalk.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_github`),
570
+ chalk.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
571
+ ""
572
+ ],
573
+ // Rate limit exceeded
574
+ rate_limit: (ctx) => [
575
+ "",
576
+ chalk.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
577
+ chalk.red.bold(" \u2717 API rate limit exceeded"),
578
+ chalk.dim(` Your ${ctx.tier} plan allows ${ctx.max} requests/day.`),
579
+ "",
580
+ chalk.dim(" Need more? Upgrade for higher limits:"),
581
+ chalk.dim(" \u2022 Solo: 100 req/day"),
582
+ chalk.dim(" \u2022 Pro: 1,000 req/day"),
583
+ chalk.dim(" \u2022 Agency: 10,000 req/day"),
584
+ "",
585
+ chalk.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=cli_rate_limit`),
586
+ chalk.red("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
587
+ ""
588
+ ],
589
+ // Schedule exceeded (for GitHub Action)
590
+ schedule_exceeded: (ctx) => [
591
+ "",
592
+ chalk.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
593
+ chalk.yellow.bold(" \u23F0 Scheduled audit skipped"),
594
+ chalk.dim(` Your ${ctx.tier} plan allows ${ctx.limit} scheduled audits.`),
595
+ "",
596
+ chalk.dim(" Upgrade for more frequent audits:"),
597
+ chalk.dim(" \u2022 Solo: Weekly"),
598
+ chalk.dim(" \u2022 Pro: Daily"),
599
+ chalk.dim(" \u2022 Agency: Hourly"),
600
+ "",
601
+ chalk.cyan(` \u2192 ${WEB_APP_URL2}/pricing?from=schedule_exceeded`),
602
+ chalk.yellow("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),
603
+ ""
604
+ ],
605
+ // Login prompt (soft)
606
+ login_prompt: () => [
607
+ "",
608
+ chalk.dim(" \u{1F4A1} Tip: Log in to sync results and track progress over time:"),
609
+ chalk.dim(" rankcli login"),
610
+ ""
611
+ ]
612
+ };
613
+ function showUpsell(messageKey, context) {
614
+ const messageFn = UPSELL_MESSAGES[messageKey];
615
+ if (!messageFn) return;
616
+ const ctx = {
617
+ tier: "free",
618
+ ...context
619
+ };
620
+ const lines = messageFn(ctx);
621
+ lines.forEach((line) => console.log(line));
622
+ }
623
+ function hasFeature(tier, feature) {
624
+ const limits = TIER_LIMITS[tier];
625
+ return limits?.features?.[feature] ?? false;
626
+ }
627
+
460
628
  // src/commands/audit.ts
461
629
  var { detectFramework: detectFramework2, getFrameworkDisplayName, getFrameworkIssues } = frameworks;
462
630
  async function fetchHtml(url, timeout = 1e4) {
@@ -524,7 +692,7 @@ Return JSON with:
524
692
  }
525
693
  const response = await fetch(apiUrl, { method: "POST", headers, body });
526
694
  if (!response.ok) {
527
- console.error(chalk.yellow(`AI analysis failed: ${response.status}`));
695
+ console.error(chalk2.yellow(`AI analysis failed: ${response.status}`));
528
696
  return null;
529
697
  }
530
698
  const data = await response.json();
@@ -534,7 +702,7 @@ Return JSON with:
534
702
  if (!jsonMatch) return null;
535
703
  return JSON.parse(jsonMatch[0]);
536
704
  } catch (error) {
537
- console.error(chalk.yellow(`AI analysis error: ${error}`));
705
+ console.error(chalk2.yellow(`AI analysis error: ${error}`));
538
706
  return null;
539
707
  }
540
708
  }
@@ -553,7 +721,7 @@ function mergeIssues(issueArrays) {
553
721
  }
554
722
  return Array.from(issueMap.values());
555
723
  }
556
- async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis) {
724
+ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis, durationMs, checksRun) {
557
725
  const supabase = await getAuthenticatedClient();
558
726
  if (!supabase) return null;
559
727
  const { userId, sitesLimit } = getUserInfo();
@@ -569,9 +737,9 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
569
737
  const { count } = await supabase.from("projects").select("id", { count: "exact", head: true }).eq("user_id", userId).eq("is_active", true);
570
738
  const limit = sitesLimit ?? 1;
571
739
  if (count !== null && count >= limit) {
572
- console.log(chalk.yellow(`
740
+ console.log(chalk2.yellow(`
573
741
  \u26A0\uFE0F Project limit reached (${count}/${limit})`));
574
- console.log(chalk.dim(` Upgrade at: ${WEB_APP_URL}/account`));
742
+ console.log(chalk2.dim(` Upgrade at: ${WEB_APP_URL}/account`));
575
743
  return null;
576
744
  }
577
745
  const { data: newProject, error: projectError } = await supabase.from("projects").insert({
@@ -581,7 +749,7 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
581
749
  domain
582
750
  }).select("id").single();
583
751
  if (projectError || !newProject) {
584
- console.error(chalk.dim("Failed to create project"));
752
+ console.error(chalk2.dim("Failed to create project"));
585
753
  return null;
586
754
  }
587
755
  projectId = newProject.id;
@@ -595,6 +763,7 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
595
763
  performance: report.healthScore?.performance || 0,
596
764
  security: report.healthScore?.security || 0
597
765
  };
766
+ const now = (/* @__PURE__ */ new Date()).toISOString();
598
767
  const { data: audit2, error: auditError } = await supabase.from("audits").insert({
599
768
  project_id: projectId,
600
769
  triggered_by: "cli",
@@ -603,21 +772,23 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
603
772
  score,
604
773
  pages_crawled: pagesAudited,
605
774
  issues_found: mergedIssues.length,
606
- checks_run: mergedIssues.length > 0 ? 200 : 0,
607
- // Approximate
775
+ checks_run: checksRun,
608
776
  health_scores: healthScores,
609
- completed_at: (/* @__PURE__ */ new Date()).toISOString(),
777
+ started_at: new Date(Date.now() - durationMs).toISOString(),
778
+ completed_at: now,
779
+ duration_ms: durationMs,
610
780
  summary: {
611
781
  errors: mergedIssues.filter((i) => i.severity === "error").length,
612
782
  warnings: mergedIssues.filter((i) => i.severity === "warning").length,
613
783
  notices: mergedIssues.filter((i) => i.severity === "notice").length,
614
784
  pagesAudited,
785
+ checksRun,
615
786
  source: "cli"
616
787
  },
617
788
  ai_analysis: aiAnalysis
618
789
  }).select("id").single();
619
790
  if (auditError || !audit2) {
620
- console.error(chalk.dim("Failed to save audit"));
791
+ console.error(chalk2.dim("Failed to save audit"));
621
792
  return null;
622
793
  }
623
794
  if (mergedIssues.length > 0) {
@@ -638,12 +809,14 @@ async function syncToCloud(url, report, mergedIssues, pagesAudited, aiAnalysis)
638
809
  await supabase.from("audit_issues").insert(issueRecords);
639
810
  }
640
811
  await supabase.from("projects").update({
641
- last_audit_at: (/* @__PURE__ */ new Date()).toISOString(),
642
- last_audit_score: score
812
+ last_audit_at: now,
813
+ last_audit_score: score,
814
+ last_scheduled_audit_at: now,
815
+ scheduled_audit_source: "cli"
643
816
  }).eq("id", projectId);
644
817
  return { projectId, auditId: audit2.id };
645
818
  } catch (error) {
646
- console.error(chalk.dim("Cloud sync failed"));
819
+ console.error(chalk2.dim("Cloud sync failed"));
647
820
  return null;
648
821
  }
649
822
  }
@@ -664,8 +837,8 @@ async function audit(options) {
664
837
  console.log(" --ai-key <key> API key (or set OPENAI_API_KEY/ANTHROPIC_API_KEY)");
665
838
  console.log(" --output json Output as JSON");
666
839
  if (!loggedIn) {
667
- console.log(chalk.dim("\nTip: Log in to sync results to the web dashboard:"));
668
- console.log(chalk.dim(" rankcli login"));
840
+ console.log(chalk2.dim("\nTip: Log in to sync results to the web dashboard:"));
841
+ console.log(chalk2.dim(" rankcli login"));
669
842
  }
670
843
  console.log("");
671
844
  return;
@@ -749,12 +922,17 @@ async function audit(options) {
749
922
  aiSpinner.warn("AI analysis failed or returned no results");
750
923
  }
751
924
  } else if (enableAI && !aiKey) {
752
- console.log(chalk.yellow("\n\u26A0\uFE0F AI analysis requested but no API key provided"));
753
- console.log(chalk.dim(" Set OPENAI_API_KEY or ANTHROPIC_API_KEY, or use --ai-key"));
925
+ console.log(chalk2.yellow("\n\u26A0\uFE0F AI analysis requested but no API key provided"));
926
+ console.log(chalk2.dim(" Set OPENAI_API_KEY or ANTHROPIC_API_KEY, or use --ai-key"));
754
927
  }
755
928
  const totalTime = Date.now() - startTime;
756
929
  const frameworkName = detectedFramework.framework !== "unknown" ? getFrameworkDisplayName(detectedFramework.framework) : null;
930
+ const actualChecksRun = (primaryReport.summary?.passed || 0) + (primaryReport.summary?.errors || 0) + (primaryReport.summary?.warnings || 0) || 200;
757
931
  if (options.output === "json") {
932
+ let syncResult = null;
933
+ if (loggedIn) {
934
+ syncResult = await syncToCloud(url, primaryReport, mergedIssues, successCount, aiAnalysis, totalTime, actualChecksRun);
935
+ }
758
936
  const jsonOutput = {
759
937
  url,
760
938
  score: primaryReport.healthScore?.overall || 0,
@@ -763,31 +941,37 @@ async function audit(options) {
763
941
  framework: frameworkName,
764
942
  issues: mergedIssues,
765
943
  healthScores: primaryReport.healthScore,
766
- aiAnalysis
944
+ aiAnalysis,
945
+ // Include cloud sync info if synced
946
+ ...syncResult && {
947
+ projectId: syncResult.projectId,
948
+ auditId: syncResult.auditId,
949
+ dashboardUrl: `${WEB_APP_URL}/dashboard/projects/${syncResult.projectId}`
950
+ }
767
951
  };
768
952
  console.log(JSON.stringify(jsonOutput, null, 2));
769
953
  } else {
770
- console.log("\n" + chalk.bold("\u2550".repeat(60)));
771
- console.log(chalk.bold.cyan(` SEO AUDIT REPORT: ${url}`));
772
- console.log(chalk.bold("\u2550".repeat(60)));
954
+ console.log("\n" + chalk2.bold("\u2550".repeat(60)));
955
+ console.log(chalk2.bold.cyan(` SEO AUDIT REPORT: ${url}`));
956
+ console.log(chalk2.bold("\u2550".repeat(60)));
773
957
  const score = primaryReport.healthScore?.overall || 0;
774
- const scoreColor = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red;
958
+ const scoreColor = score >= 80 ? chalk2.green : score >= 60 ? chalk2.yellow : chalk2.red;
775
959
  console.log(`
776
- ${chalk.bold("Score:")} ${scoreColor.bold(score + "/100")}`);
777
- console.log(` ${chalk.bold("Pages audited:")} ${successCount}`);
778
- console.log(` ${chalk.bold("Issues found:")} ${mergedIssues.length}`);
779
- console.log(` ${chalk.bold("Time:")} ${totalTime}ms`);
960
+ ${chalk2.bold("Score:")} ${scoreColor.bold(score + "/100")}`);
961
+ console.log(` ${chalk2.bold("Pages audited:")} ${successCount}`);
962
+ console.log(` ${chalk2.bold("Issues found:")} ${mergedIssues.length}`);
963
+ console.log(` ${chalk2.bold("Time:")} ${totalTime}ms`);
780
964
  if (frameworkName) {
781
- console.log(` ${chalk.bold("Framework:")} ${chalk.cyan(frameworkName)}`);
965
+ console.log(` ${chalk2.bold("Framework:")} ${chalk2.cyan(frameworkName)}`);
782
966
  }
783
967
  if (primaryReport.healthScore) {
784
- console.log("\n" + chalk.bold(" Health Scores:"));
968
+ console.log("\n" + chalk2.bold(" Health Scores:"));
785
969
  const hs = primaryReport.healthScore;
786
970
  const bar = (score2) => {
787
971
  const filled = Math.round(score2 / 5);
788
972
  const empty = 20 - filled;
789
- const color = score2 >= 80 ? chalk.green : score2 >= 60 ? chalk.yellow : chalk.red;
790
- return color("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(empty));
973
+ const color = score2 >= 80 ? chalk2.green : score2 >= 60 ? chalk2.yellow : chalk2.red;
974
+ return color("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(empty));
791
975
  };
792
976
  console.log(` Crawlability: ${bar(hs.crawlability || 0)} ${hs.crawlability || 0}%`);
793
977
  console.log(` On-Page: ${bar(hs.onPage || 0)} ${hs.onPage || 0}%`);
@@ -798,50 +982,63 @@ async function audit(options) {
798
982
  const errors = mergedIssues.filter((i) => i.severity === "error");
799
983
  const warnings = mergedIssues.filter((i) => i.severity === "warning");
800
984
  const notices = mergedIssues.filter((i) => i.severity === "notice");
801
- console.log("\n" + chalk.bold(" Issues by Severity:"));
802
- console.log(` ${chalk.red("\u25CF")} Errors: ${errors.length}`);
803
- console.log(` ${chalk.yellow("\u25CF")} Warnings: ${warnings.length}`);
804
- console.log(` ${chalk.blue("\u25CF")} Notices: ${notices.length}`);
805
- console.log("\n" + chalk.bold(" Top Issues:"));
985
+ console.log("\n" + chalk2.bold(" Issues by Severity:"));
986
+ console.log(` ${chalk2.red("\u25CF")} Errors: ${errors.length}`);
987
+ console.log(` ${chalk2.yellow("\u25CF")} Warnings: ${warnings.length}`);
988
+ console.log(` ${chalk2.blue("\u25CF")} Notices: ${notices.length}`);
989
+ console.log("\n" + chalk2.bold(" Top Issues:"));
806
990
  const topIssues = [...errors, ...warnings, ...notices].slice(0, 10);
807
991
  topIssues.forEach((issue, i) => {
808
- const icon = issue.severity === "error" ? chalk.red("\u2717") : issue.severity === "warning" ? chalk.yellow("!") : chalk.blue("i");
992
+ const icon = issue.severity === "error" ? chalk2.red("\u2717") : issue.severity === "warning" ? chalk2.yellow("!") : chalk2.blue("i");
809
993
  console.log(` ${i + 1}. ${icon} ${issue.title}`);
810
994
  if (issue.affectedUrls && issue.affectedUrls.length > 1) {
811
- console.log(chalk.dim(` Affects ${issue.affectedUrls.length} pages`));
995
+ console.log(chalk2.dim(` Affects ${issue.affectedUrls.length} pages`));
812
996
  }
813
997
  });
814
998
  if (aiAnalysis) {
815
- console.log("\n" + chalk.bold(" AI Analysis:"));
999
+ console.log("\n" + chalk2.bold(" AI Analysis:"));
816
1000
  console.log(` Content Score: ${aiAnalysis.contentScore}/100`);
817
- console.log("\n " + chalk.bold("Suggestions:"));
1001
+ console.log("\n " + chalk2.bold("Suggestions:"));
818
1002
  aiAnalysis.suggestions.forEach((s, i) => {
819
1003
  console.log(` ${i + 1}. ${s}`);
820
1004
  });
821
1005
  if (aiAnalysis.prioritizedFixes?.length > 0) {
822
- console.log("\n " + chalk.bold("Priority Fixes:"));
1006
+ console.log("\n " + chalk2.bold("Priority Fixes:"));
823
1007
  aiAnalysis.prioritizedFixes.slice(0, 5).forEach((fix, i) => {
824
1008
  console.log(` ${i + 1}. [P${fix.priority}] ${fix.code}`);
825
- console.log(chalk.dim(` ${fix.rationale}`));
1009
+ console.log(chalk2.dim(` ${fix.rationale}`));
826
1010
  });
827
1011
  }
828
1012
  }
829
- console.log("\n" + chalk.bold("\u2550".repeat(60)));
1013
+ console.log("\n" + chalk2.bold("\u2550".repeat(60)));
1014
+ const userInfo = getUserInfo();
1015
+ const tier = userInfo.planName?.toLowerCase() || "free";
1016
+ const isFreeTier = !loggedIn || tier === "free";
1017
+ const isSoloOrBelow = isFreeTier || tier === "solo";
1018
+ if (isFreeTier) {
1019
+ const checksRun = mergedIssues.length > 0 ? loggedIn ? 100 : 50 : 0;
1020
+ showUpsell("checks_limit", {
1021
+ tier: loggedIn ? "free" : "free_anon",
1022
+ current: checksRun
1023
+ });
1024
+ }
1025
+ if (options.geo && isFreeTier) {
1026
+ showUpsell("geo_limited", { tier: loggedIn ? "free" : "free_anon" });
1027
+ }
830
1028
  if (loggedIn) {
831
1029
  const syncSpinner = ora("Syncing to cloud...").start();
832
- const syncResult = await syncToCloud(url, primaryReport, mergedIssues, successCount, aiAnalysis);
1030
+ const syncResult = await syncToCloud(url, primaryReport, mergedIssues, successCount, aiAnalysis, totalTime, actualChecksRun);
833
1031
  if (syncResult) {
834
1032
  syncSpinner.succeed("Results synced to cloud");
835
- console.log(chalk.cyan(`
1033
+ console.log(chalk2.cyan(`
836
1034
  View and manage issues in the web dashboard:`));
837
- console.log(chalk.bold(` ${WEB_APP_URL}/dashboard/projects/${syncResult.projectId}
1035
+ console.log(chalk2.bold(` ${WEB_APP_URL}/dashboard/projects/${syncResult.projectId}
838
1036
  `));
839
1037
  } else {
840
1038
  syncSpinner.warn("Could not sync to cloud");
841
1039
  }
842
1040
  } else {
843
- console.log(chalk.dim("\n Tip: Log in to sync results and manage issues in the web dashboard:"));
844
- console.log(chalk.dim(" rankcli login\n"));
1041
+ showUpsell("free_audit_complete");
845
1042
  }
846
1043
  }
847
1044
  } catch (error) {
@@ -855,7 +1052,6 @@ async function audit(options) {
855
1052
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
856
1053
  import { join as join3 } from "path";
857
1054
  import {
858
- runDirectAnalysis,
859
1055
  runAuditWithFixes,
860
1056
  generateFixes,
861
1057
  applyFixes,
@@ -914,40 +1110,37 @@ async function apply(options) {
914
1110
  }
915
1111
  console.log("\n\u{1F680} SEO Autopilot - Apply Fixes");
916
1112
  console.log("\u2550".repeat(50));
1113
+ const loggedIn = isLoggedIn();
1114
+ const userInfo = getUserInfo();
1115
+ const tier = userInfo.planName?.toLowerCase() || "free";
1116
+ const hasAutoFix = loggedIn && hasFeature(tier, "auto_fix");
1117
+ if (!hasAutoFix) {
1118
+ showUpsell("auto_fix_blocked");
1119
+ return;
1120
+ }
917
1121
  console.log("\n\u{1F50D} Detecting framework...");
918
1122
  const frameworkResult = await detectFramework3({ cwd });
919
1123
  const framework = frameworkResult.success ? frameworkResult.data : { name: "Unknown", metaPattern: "html-head" };
920
1124
  console.log(` Framework: ${framework.name}`);
921
- const isPaidUser = isLoggedIn();
922
1125
  console.log(`
923
1126
  \u{1F50D} Analyzing ${url}...`);
924
- if (isPaidUser) {
925
- console.log(" Running full audit (200+ checks)...");
926
- } else {
927
- console.log(" Running basic analysis (login for full 200+ checks)");
928
- }
1127
+ console.log(" Running full audit (200+ checks)...");
929
1128
  let issues;
930
1129
  let score;
931
1130
  let auditResult = null;
932
1131
  try {
933
- if (isPaidUser) {
934
- auditResult = await runAuditWithFixes({
935
- url,
936
- generateFixes: true
937
- });
938
- issues = auditResult.report.issues.map((issue) => ({
939
- severity: issue.severity,
940
- category: issue.category,
941
- code: issue.code,
942
- message: issue.title,
943
- impact: issue.description || void 0
944
- }));
945
- score = auditResult.score;
946
- } else {
947
- const analysis = await runDirectAnalysis(url);
948
- issues = analysis.issues;
949
- score = analysis.score;
950
- }
1132
+ auditResult = await runAuditWithFixes({
1133
+ url,
1134
+ generateFixes: true
1135
+ });
1136
+ issues = auditResult.report.issues.map((issue) => ({
1137
+ severity: issue.severity,
1138
+ category: issue.category,
1139
+ code: issue.code,
1140
+ message: issue.title,
1141
+ impact: issue.description || void 0
1142
+ }));
1143
+ score = auditResult.score;
951
1144
  } catch (error) {
952
1145
  console.log(`
953
1146
  \u274C Analysis failed: ${error instanceof Error ? error.message : "Unknown error"}
@@ -1038,7 +1231,7 @@ async function apply(options) {
1038
1231
  // src/commands/geo.ts
1039
1232
  import { analyzers } from "@rankcli/agent-runtime";
1040
1233
  import ora2 from "ora";
1041
- import chalk2 from "chalk";
1234
+ import chalk3 from "chalk";
1042
1235
  async function fetchResources(url) {
1043
1236
  const headers = {};
1044
1237
  let html = null;
@@ -1110,55 +1303,55 @@ async function geo(options) {
1110
1303
  console.log(JSON.stringify(jsonOutput, null, 2));
1111
1304
  return;
1112
1305
  }
1113
- console.log("\n" + chalk2.bold("\u2550".repeat(60)));
1114
- console.log(chalk2.bold.cyan(" GEO ANALYSIS: AI Search Optimization"));
1115
- console.log(chalk2.bold("\u2550".repeat(60)));
1306
+ console.log("\n" + chalk3.bold("\u2550".repeat(60)));
1307
+ console.log(chalk3.bold.cyan(" GEO ANALYSIS: AI Search Optimization"));
1308
+ console.log(chalk3.bold("\u2550".repeat(60)));
1116
1309
  const geoScore = geoResult.score;
1117
- const scoreColor = geoScore >= 80 ? chalk2.green : geoScore >= 60 ? chalk2.yellow : chalk2.red;
1310
+ const scoreColor = geoScore >= 80 ? chalk3.green : geoScore >= 60 ? chalk3.yellow : chalk3.red;
1118
1311
  console.log(`
1119
- ${chalk2.bold("GEO Score:")} ${scoreColor.bold(geoScore + "/100")}`);
1120
- console.log("\n" + chalk2.bold(" AI Crawler Access:"));
1312
+ ${chalk3.bold("GEO Score:")} ${scoreColor.bold(geoScore + "/100")}`);
1313
+ console.log("\n" + chalk3.bold(" AI Crawler Access:"));
1121
1314
  const { aiCrawlerAccess } = geoResult;
1122
1315
  if (aiCrawlerAccess.blockedCrawlers.length > 0) {
1123
- console.log(chalk2.red(` \u274C Blocked: ${aiCrawlerAccess.blockedCrawlers.join(", ")}`));
1316
+ console.log(chalk3.red(` \u274C Blocked: ${aiCrawlerAccess.blockedCrawlers.join(", ")}`));
1124
1317
  }
1125
1318
  if (aiCrawlerAccess.allowedCrawlers.length > 0) {
1126
1319
  const topAllowed = aiCrawlerAccess.allowedCrawlers.slice(0, 5);
1127
- console.log(chalk2.green(` \u2705 Allowed: ${topAllowed.join(", ")}${aiCrawlerAccess.allowedCrawlers.length > 5 ? "..." : ""}`));
1320
+ console.log(chalk3.green(` \u2705 Allowed: ${topAllowed.join(", ")}${aiCrawlerAccess.allowedCrawlers.length > 5 ? "..." : ""}`));
1128
1321
  }
1129
- console.log("\n" + chalk2.bold(" Rendering:"));
1322
+ console.log("\n" + chalk3.bold(" Rendering:"));
1130
1323
  if (aiCrawlerAccess.serverSideRendered) {
1131
- console.log(chalk2.green(" \u2705 Server-side rendered (AI crawlers can see content)"));
1324
+ console.log(chalk3.green(" \u2705 Server-side rendered (AI crawlers can see content)"));
1132
1325
  } else if (aiCrawlerAccess.jsRenderingRequired) {
1133
- console.log(chalk2.red(" \u274C JavaScript required (AI crawlers may see blank page)"));
1326
+ console.log(chalk3.red(" \u274C JavaScript required (AI crawlers may see blank page)"));
1134
1327
  }
1135
- console.log("\n" + chalk2.bold(" LLM Friendliness:"));
1328
+ console.log("\n" + chalk3.bold(" LLM Friendliness:"));
1136
1329
  const { llmSignals } = geoResult;
1137
1330
  const bar = (score) => {
1138
1331
  const filled = Math.round(score / 5);
1139
1332
  const empty = 20 - filled;
1140
- const color = score >= 70 ? chalk2.green : score >= 40 ? chalk2.yellow : chalk2.red;
1141
- return color("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(empty));
1333
+ const color = score >= 70 ? chalk3.green : score >= 40 ? chalk3.yellow : chalk3.red;
1334
+ return color("\u2588".repeat(filled)) + chalk3.gray("\u2591".repeat(empty));
1142
1335
  };
1143
1336
  console.log(` Content Clarity: ${bar(llmSignals.contentClarity)} ${llmSignals.contentClarity}%`);
1144
1337
  console.log(` Fact Density: ${bar(llmSignals.factDensity)} ${llmSignals.factDensity}%`);
1145
1338
  console.log(` Structure: ${bar(llmSignals.structureQuality)} ${llmSignals.structureQuality}%`);
1146
1339
  console.log(` Citations: ${bar(llmSignals.citationQuality)} ${llmSignals.citationQuality}%`);
1147
- console.log("\n" + chalk2.bold(" Content Structure:"));
1340
+ console.log("\n" + chalk3.bold(" Content Structure:"));
1148
1341
  const { contentStructure } = geoResult;
1149
- console.log(` ${contentStructure.hasStructuredData ? chalk2.green("\u2705") : chalk2.red("\u274C")} JSON-LD Structured Data`);
1150
- console.log(` ${contentStructure.hasFAQSchema ? chalk2.green("\u2705") : chalk2.yellow("\u25CB")} FAQ Schema`);
1151
- console.log(` ${contentStructure.hasArticleSchema ? chalk2.green("\u2705") : chalk2.yellow("\u25CB")} Article Schema`);
1152
- console.log(` ${contentStructure.hasBreadcrumbs ? chalk2.green("\u2705") : chalk2.yellow("\u25CB")} Breadcrumbs`);
1153
- console.log(` Heading Hierarchy: ${contentStructure.headingHierarchy === "good" ? chalk2.green("Good") : contentStructure.headingHierarchy === "needs-work" ? chalk2.yellow("Needs Work") : chalk2.red("Poor")}`);
1154
- console.log("\n" + chalk2.bold(" Citation Readiness:"));
1342
+ console.log(` ${contentStructure.hasStructuredData ? chalk3.green("\u2705") : chalk3.red("\u274C")} JSON-LD Structured Data`);
1343
+ console.log(` ${contentStructure.hasFAQSchema ? chalk3.green("\u2705") : chalk3.yellow("\u25CB")} FAQ Schema`);
1344
+ console.log(` ${contentStructure.hasArticleSchema ? chalk3.green("\u2705") : chalk3.yellow("\u25CB")} Article Schema`);
1345
+ console.log(` ${contentStructure.hasBreadcrumbs ? chalk3.green("\u2705") : chalk3.yellow("\u25CB")} Breadcrumbs`);
1346
+ console.log(` Heading Hierarchy: ${contentStructure.headingHierarchy === "good" ? chalk3.green("Good") : contentStructure.headingHierarchy === "needs-work" ? chalk3.yellow("Needs Work") : chalk3.red("Poor")}`);
1347
+ console.log("\n" + chalk3.bold(" Citation Readiness:"));
1155
1348
  const { citationReadiness } = geoResult;
1156
1349
  if (citationReadiness.trustSignals.length > 0) {
1157
- console.log(chalk2.green(` \u2705 ${citationReadiness.trustSignals.join(", ")}`));
1350
+ console.log(chalk3.green(` \u2705 ${citationReadiness.trustSignals.join(", ")}`));
1158
1351
  } else {
1159
- console.log(chalk2.yellow(" \u25CB No trust signals detected"));
1352
+ console.log(chalk3.yellow(" \u25CB No trust signals detected"));
1160
1353
  }
1161
- console.log("\n" + chalk2.bold(" Other Scores:"));
1354
+ console.log("\n" + chalk3.bold(" Other Scores:"));
1162
1355
  console.log(` Core Web Vitals: ${bar(cwvResult.overallScore)} ${cwvResult.overallScore}%`);
1163
1356
  console.log(` Security Headers: ${securityResult.grade} (${securityResult.score}%)`);
1164
1357
  console.log(` Structured Data: ${bar(schemaResult.score)} ${schemaResult.score}%`);
@@ -1172,18 +1365,18 @@ async function geo(options) {
1172
1365
  ];
1173
1366
  const criticalIssues = allIssues.filter((i) => i.severity === "critical" || i.severity === "error");
1174
1367
  if (criticalIssues.length > 0) {
1175
- console.log("\n" + chalk2.bold.red(" Critical Issues:"));
1368
+ console.log("\n" + chalk3.bold.red(" Critical Issues:"));
1176
1369
  criticalIssues.slice(0, 5).forEach((issue, i) => {
1177
- console.log(chalk2.red(` ${i + 1}. ${issue.title}`));
1370
+ console.log(chalk3.red(` ${i + 1}. ${issue.title}`));
1178
1371
  });
1179
1372
  }
1180
1373
  if (geoResult.recommendations.length > 0) {
1181
- console.log("\n" + chalk2.bold(" Recommendations:"));
1374
+ console.log("\n" + chalk3.bold(" Recommendations:"));
1182
1375
  geoResult.recommendations.slice(0, 5).forEach((rec, i) => {
1183
1376
  console.log(` ${i + 1}. ${rec}`);
1184
1377
  });
1185
1378
  }
1186
- console.log("\n" + chalk2.bold("\u2550".repeat(60)));
1379
+ console.log("\n" + chalk3.bold("\u2550".repeat(60)));
1187
1380
  console.log("");
1188
1381
  } catch (error) {
1189
1382
  spinner.fail(`Analysis failed: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -1862,15 +2055,15 @@ function showGSCInstructions() {
1862
2055
  }
1863
2056
 
1864
2057
  // src/commands/auth.ts
1865
- import chalk3 from "chalk";
2058
+ import chalk4 from "chalk";
1866
2059
  import ora3 from "ora";
1867
2060
  import inquirer from "inquirer";
1868
2061
  import open from "open";
1869
2062
  async function login(options) {
1870
2063
  if (isLoggedIn()) {
1871
2064
  const { email } = getUserInfo();
1872
- console.log(chalk3.yellow(`Already logged in as ${email}`));
1873
- console.log(chalk3.dim('Run "rankcli logout" to sign out first.'));
2065
+ console.log(chalk4.yellow(`Already logged in as ${email}`));
2066
+ console.log(chalk4.dim('Run "rankcli logout" to sign out first.'));
1874
2067
  return;
1875
2068
  }
1876
2069
  const spinner = ora3();
@@ -1878,14 +2071,14 @@ async function login(options) {
1878
2071
  if (options.token) {
1879
2072
  const apiKey = options.token;
1880
2073
  if (!apiKey.startsWith("rankcli_")) {
1881
- console.log(chalk3.red('Invalid API key format. Keys should start with "rankcli_"'));
2074
+ console.log(chalk4.red('Invalid API key format. Keys should start with "rankcli_"'));
1882
2075
  return;
1883
2076
  }
1884
2077
  spinner.start("Validating API key...");
1885
2078
  const result = await validateApiKey(apiKey);
1886
2079
  if (!result.valid) {
1887
2080
  spinner.fail("Invalid API key");
1888
- console.log(chalk3.red(`
2081
+ console.log(chalk4.red(`
1889
2082
  ${result.error}`));
1890
2083
  return;
1891
2084
  }
@@ -1898,14 +2091,14 @@ ${result.error}`));
1898
2091
  });
1899
2092
  }
1900
2093
  spinner.succeed(`Authenticated via API key`);
1901
- console.log(chalk3.dim(`
2094
+ console.log(chalk4.dim(`
1902
2095
  Email: ${result.user?.email}`));
1903
- console.log(chalk3.dim(`Plan: ${result.subscription?.planName || "Free"}`));
2096
+ console.log(chalk4.dim(`Plan: ${result.subscription?.planName || "Free"}`));
1904
2097
  return;
1905
2098
  }
1906
2099
  if (options.browser) {
1907
- console.log(chalk3.cyan("\nOpening browser for login..."));
1908
- console.log(chalk3.dim("Complete the login in your browser, then return here.\n"));
2100
+ console.log(chalk4.cyan("\nOpening browser for login..."));
2101
+ console.log(chalk4.dim("Complete the login in your browser, then return here.\n"));
1909
2102
  const loginUrl = `${WEB_APP_URL}/login?cli=true`;
1910
2103
  await open(loginUrl);
1911
2104
  const { token } = await inquirer.prompt([
@@ -1921,7 +2114,7 @@ Email: ${result.user?.email}`));
1921
2114
  const { data, error } = await supabase.auth.getUser(token);
1922
2115
  if (error || !data.user) {
1923
2116
  spinner.fail("Invalid token");
1924
- console.log(chalk3.red("The token is invalid or expired. Please try again."));
2117
+ console.log(chalk4.red("The token is invalid or expired. Please try again."));
1925
2118
  return;
1926
2119
  }
1927
2120
  const { data: sessionData, error: sessionError } = await supabase.auth.setSession({
@@ -1935,7 +2128,7 @@ Email: ${result.user?.email}`));
1935
2128
  }
1936
2129
  saveSession(sessionData.session);
1937
2130
  await fetchAndSaveSubscription();
1938
- spinner.succeed(`Logged in as ${chalk3.green(data.user.email)}`);
2131
+ spinner.succeed(`Logged in as ${chalk4.green(data.user.email)}`);
1939
2132
  } else {
1940
2133
  let email = options.email;
1941
2134
  if (!email) {
@@ -1968,11 +2161,11 @@ Email: ${result.user?.email}`));
1968
2161
  });
1969
2162
  if (error) {
1970
2163
  spinner.fail("Login failed");
1971
- console.log(chalk3.red(`
2164
+ console.log(chalk4.red(`
1972
2165
  ${error.message}`));
1973
2166
  if (error.message.includes("Invalid login")) {
1974
- console.log(chalk3.dim("\nDon't have an account? Sign up at:"));
1975
- console.log(chalk3.cyan(` ${WEB_APP_URL}/signup`));
2167
+ console.log(chalk4.dim("\nDon't have an account? Sign up at:"));
2168
+ console.log(chalk4.cyan(` ${WEB_APP_URL}/signup`));
1976
2169
  }
1977
2170
  return;
1978
2171
  }
@@ -1982,19 +2175,19 @@ ${error.message}`));
1982
2175
  }
1983
2176
  saveSession(data.session);
1984
2177
  await fetchAndSaveSubscription();
1985
- spinner.succeed(`Logged in as ${chalk3.green(data.user?.email)}`);
2178
+ spinner.succeed(`Logged in as ${chalk4.green(data.user?.email)}`);
1986
2179
  const { planName, sitesLimit } = getUserInfo();
1987
- console.log(chalk3.dim(`
2180
+ console.log(chalk4.dim(`
1988
2181
  Plan: ${planName} (${sitesLimit} site${sitesLimit !== 1 ? "s" : ""})`));
1989
2182
  }
1990
2183
  } catch (err) {
1991
2184
  spinner.fail("Login failed");
1992
- console.error(chalk3.red(err instanceof Error ? err.message : "Unknown error"));
2185
+ console.error(chalk4.red(err instanceof Error ? err.message : "Unknown error"));
1993
2186
  }
1994
2187
  }
1995
2188
  async function logout() {
1996
2189
  if (!isLoggedIn()) {
1997
- console.log(chalk3.yellow("Not logged in"));
2190
+ console.log(chalk4.yellow("Not logged in"));
1998
2191
  return;
1999
2192
  }
2000
2193
  const { email } = getUserInfo();
@@ -2003,17 +2196,17 @@ async function logout() {
2003
2196
  const supabase = getSupabaseClient();
2004
2197
  await supabase.auth.signOut();
2005
2198
  clearCredentials();
2006
- spinner.succeed(`Logged out from ${chalk3.dim(email)}`);
2199
+ spinner.succeed(`Logged out from ${chalk4.dim(email)}`);
2007
2200
  } catch (err) {
2008
2201
  spinner.fail("Logout failed");
2009
- console.error(chalk3.red(err instanceof Error ? err.message : "Unknown error"));
2202
+ console.error(chalk4.red(err instanceof Error ? err.message : "Unknown error"));
2010
2203
  }
2011
2204
  }
2012
2205
  async function whoami() {
2013
2206
  if (!isLoggedIn()) {
2014
- console.log(chalk3.yellow("Not logged in"));
2015
- console.log(chalk3.dim('\nRun "rankcli login" to sign in.'));
2016
- console.log(chalk3.dim(`Or sign up at: ${WEB_APP_URL}/signup`));
2207
+ console.log(chalk4.yellow("Not logged in"));
2208
+ console.log(chalk4.dim('\nRun "rankcli login" to sign in.'));
2209
+ console.log(chalk4.dim(`Or sign up at: ${WEB_APP_URL}/signup`));
2017
2210
  return;
2018
2211
  }
2019
2212
  const spinner = ora3("Checking account...").start();
@@ -2036,18 +2229,18 @@ async function whoami() {
2036
2229
  const { email, planName, sitesLimit } = getUserInfo();
2037
2230
  const authMethod = isApiKeyAuth() ? "API Key" : "Session";
2038
2231
  spinner.stop();
2039
- console.log(chalk3.bold("\nRankCLI Account"));
2232
+ console.log(chalk4.bold("\nRankCLI Account"));
2040
2233
  console.log("\u2500".repeat(40));
2041
- console.log(` Email: ${chalk3.cyan(email || "N/A")}`);
2042
- console.log(` Plan: ${chalk3.green(planName)}`);
2234
+ console.log(` Email: ${chalk4.cyan(email || "N/A")}`);
2235
+ console.log(` Plan: ${chalk4.green(planName)}`);
2043
2236
  console.log(` Sites: ${sitesLimit} allowed`);
2044
- console.log(` Auth: ${chalk3.dim(authMethod)}`);
2237
+ console.log(` Auth: ${chalk4.dim(authMethod)}`);
2045
2238
  console.log("\u2500".repeat(40));
2046
- console.log(chalk3.dim(`
2239
+ console.log(chalk4.dim(`
2047
2240
  Manage at: ${WEB_APP_URL}/account`));
2048
2241
  } catch (err) {
2049
2242
  spinner.fail("Failed to fetch account info");
2050
- console.error(chalk3.red(err instanceof Error ? err.message : "Unknown error"));
2243
+ console.error(chalk4.red(err instanceof Error ? err.message : "Unknown error"));
2051
2244
  }
2052
2245
  }
2053
2246
  async function fetchAndSaveSubscription() {
@@ -2079,7 +2272,7 @@ async function fetchAndSaveSubscription() {
2079
2272
  }
2080
2273
 
2081
2274
  // src/version.ts
2082
- var VERSION = "0.0.18";
2275
+ var VERSION = "0.0.19";
2083
2276
 
2084
2277
  // src/index.ts
2085
2278
  var program = new Command();