gsc-cli 2.1.0 → 2.2.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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/AUTH.md +4 -1
  3. data/README.md +448 -408
  4. data/bin/gsc +28067 -5661
  5. data/dist/gsc +29121 -5046
  6. data/lib/gsc/aio_hunter.rb +343 -0
  7. data/lib/gsc/answer_synthesizer.rb +157 -0
  8. data/lib/gsc/api.rb +53 -1
  9. data/lib/gsc/auth.rb +26 -0
  10. data/lib/gsc/brand_segmenter.rb +140 -0
  11. data/lib/gsc/cache_manager.rb +806 -0
  12. data/lib/gsc/cannibalization_analyzer.rb +141 -0
  13. data/lib/gsc/canonical_chains.rb +367 -0
  14. data/lib/gsc/citation_simulator.rb +339 -0
  15. data/lib/gsc/cli/aio_hunter.rb +154 -0
  16. data/lib/gsc/cli/analytics.rb +788 -0
  17. data/lib/gsc/cli/audit.rb +1976 -0
  18. data/lib/gsc/cli/base.rb +384 -0
  19. data/lib/gsc/cli/cache.rb +266 -0
  20. data/lib/gsc/cli/canonical.rb +223 -0
  21. data/lib/gsc/cli/citation_simulator.rb +152 -0
  22. data/lib/gsc/cli/dashboard.rb +354 -0
  23. data/lib/gsc/cli/doctor.rb +129 -0
  24. data/lib/gsc/cli/eeat.rb +125 -0
  25. data/lib/gsc/cli/ga4.rb +852 -0
  26. data/lib/gsc/cli/growth.rb +650 -0
  27. data/lib/gsc/cli/hreflang.rb +164 -0
  28. data/lib/gsc/cli/image_seo.rb +162 -0
  29. data/lib/gsc/cli/indexing.rb +458 -0
  30. data/lib/gsc/cli/intent_shift.rb +125 -0
  31. data/lib/gsc/cli/keyword_value.rb +134 -0
  32. data/lib/gsc/cli/keywords.rb +795 -0
  33. data/lib/gsc/cli/landing_roi.rb +308 -0
  34. data/lib/gsc/cli/low_ctr.rb +213 -0
  35. data/lib/gsc/cli/mobile_parity.rb +150 -0
  36. data/lib/gsc/cli/report.rb +100 -0
  37. data/lib/gsc/cli/rich_results.rb +172 -0
  38. data/lib/gsc/cli/schema_generate.rb +149 -0
  39. data/lib/gsc/cli/seasonal.rb +232 -0
  40. data/lib/gsc/cli/security.rb +153 -0
  41. data/lib/gsc/cli/setup.rb +1291 -0
  42. data/lib/gsc/cli/sitemap_tree.rb +143 -0
  43. data/lib/gsc/cli/skill_pack.rb +62 -0
  44. data/lib/gsc/cli/soft_404.rb +199 -0
  45. data/lib/gsc/cli/sparkline.rb +227 -0
  46. data/lib/gsc/cli/watchdog.rb +150 -0
  47. data/lib/gsc/cli/zombie_purger.rb +208 -0
  48. data/lib/gsc/cli.rb +707 -5265
  49. data/lib/gsc/cli_advanced.rb +987 -44
  50. data/lib/gsc/client.rb +17 -2
  51. data/lib/gsc/color.rb +16 -1
  52. data/lib/gsc/command_registry.rb +47 -9
  53. data/lib/gsc/config.rb +2 -2
  54. data/lib/gsc/ctr_curve.rb +115 -0
  55. data/lib/gsc/decay_predictor.rb +322 -0
  56. data/lib/gsc/doctor.rb +434 -0
  57. data/lib/gsc/eeat_auditor.rb +428 -0
  58. data/lib/gsc/entity_auditor.rb +229 -0
  59. data/lib/gsc/firewall_scanner.rb +733 -0
  60. data/lib/gsc/geo_auditor.rb +368 -0
  61. data/lib/gsc/google_trends.rb +8 -1
  62. data/lib/gsc/heading_validator.rb +283 -0
  63. data/lib/gsc/hreflang_validator.rb +412 -0
  64. data/lib/gsc/image_seo.rb +286 -0
  65. data/lib/gsc/indexing_queue.rb +179 -0
  66. data/lib/gsc/indexnow.rb +93 -0
  67. data/lib/gsc/intent_shift.rb +188 -0
  68. data/lib/gsc/internal_links.rb +153 -36
  69. data/lib/gsc/keyword_value.rb +191 -0
  70. data/lib/gsc/landing_roi.rb +195 -0
  71. data/lib/gsc/llms_generator.rb +343 -22
  72. data/lib/gsc/low_ctr_rewriter.rb +408 -0
  73. data/lib/gsc/mobile_parity.rb +222 -0
  74. data/lib/gsc/network_tracer.rb +8 -1
  75. data/lib/gsc/page_analyzer.rb +47 -7
  76. data/lib/gsc/prompts.rb +38 -29
  77. data/lib/gsc/questions_harvester.rb +178 -0
  78. data/lib/gsc/report_generator.rb +461 -0
  79. data/lib/gsc/rich_results.rb +388 -0
  80. data/lib/gsc/robots_checker.rb +46 -15
  81. data/lib/gsc/schema_generator.rb +788 -0
  82. data/lib/gsc/schema_validator.rb +36 -38
  83. data/lib/gsc/seasonal_predictor.rb +381 -0
  84. data/lib/gsc/security_scanner.rb +496 -0
  85. data/lib/gsc/serp_feature_detector.rb +359 -0
  86. data/lib/gsc/serp_preview.rb +108 -22
  87. data/lib/gsc/site_crawler.rb +113 -21
  88. data/lib/gsc/sitemap_loader.rb +15 -4
  89. data/lib/gsc/sitemap_tree.rb +301 -0
  90. data/lib/gsc/skill_pack.rb +195 -0
  91. data/lib/gsc/soft_404_analyzer.rb +385 -0
  92. data/lib/gsc/sparkline.rb +171 -0
  93. data/lib/gsc/speed_correlator.rb +416 -0
  94. data/lib/gsc/striking_playbook.rb +190 -0
  95. data/lib/gsc/title_optimizer.rb +420 -0
  96. data/lib/gsc/vault.rb +260 -0
  97. data/lib/gsc/version.rb +1 -1
  98. data/lib/gsc/watchdog.rb +235 -0
  99. data/lib/gsc/zombie_purger.rb +366 -0
  100. data/lib/gsc.rb +118 -0
  101. metadata +75 -1
@@ -0,0 +1,134 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'json'
5
+ require_relative 'base'
6
+ require_relative '../keyword_value'
7
+ require_relative '../color'
8
+
9
+ module GSC
10
+ class CLI
11
+ module KeywordValue
12
+ module_function
13
+
14
+ def run(command, target, extra, options, api = nil, site_url = nil, hostname = nil)
15
+ target_domain = target || hostname || (Config.default_domain rescue nil)
16
+ if target_domain.nil? || target_domain.to_s.strip.empty?
17
+ if options[:json]
18
+ puts JSON.pretty_generate({ error: 'Target domain required. Example: gsc kw-value mysite.com or gsc use mysite.com' })
19
+ else
20
+ puts Color.c("❌ Error: Target domain required. Example: gsc kw-value mysite.com or gsc use mysite.com", Color::RED, Color::BOLD)
21
+ end
22
+ return
23
+ end
24
+ target_domain = target_domain.sub(%r{^https?://}, '').sub(%r{/.*$}, '')
25
+
26
+ puts "💰 Modeling conversion-weighted keyword revenue matrix for #{Color.cyan(target_domain)}..." unless options[:json]
27
+
28
+ res = GSC::KeywordValue.analyze(options, api, target_domain)
29
+
30
+ if options[:json]
31
+ puts JSON.pretty_generate(res)
32
+ return
33
+ end
34
+
35
+ render_terminal(res, options)
36
+
37
+ if options[:csv]
38
+ export_csv(res, options[:csv])
39
+ end
40
+ end
41
+
42
+ def render_terminal(res, options = {})
43
+ f = res[:financials]
44
+ p = res[:parameters]
45
+
46
+ puts "\n#{Color.bold("═══════════════════════════════════════════════════════════════════════════")}"
47
+ puts "#{Color.bold("💰 CONVERSION-WEIGHTED KEYWORD VALUE & REVENUE UPSIDE MATRIX")}"
48
+ puts "#{Color.bold("═══════════════════════════════════════════════════════════════════════════")}"
49
+
50
+ puts " â€ĸ Monitored Domain: #{Color.bold(res[:domain])}"
51
+ customized = options[:aov] || options[:conv_rate] || options[:margin]
52
+ param_label = customized ? Color.green("(Custom Model)") : Color.gray("(Defaults: override with --aov, --conv-rate, --margin)")
53
+ puts " â€ĸ Economic Parameters: AOV: $#{p[:aov]} | Conv Rate: #{p[:conversion_rate_pct]}% | Margin: #{p[:profit_margin_pct]}% #{param_label}"
54
+ puts " â€ĸ Current Monthly Organic Rev: #{Color.bold("$" + format_num(f[:current_monthly_revenue]))} ($#{format_num(f[:current_annual_run_rate])}/yr run rate)"
55
+ puts " â€ĸ Potential Top #{p[:target_position]} Monthly Revenue: #{Color.green("$" + format_num(f[:potential_monthly_revenue]))}"
56
+ puts " â€ĸ Unlocked Monthly Revenue Gap: #{Color.bold(Color.green("+$" + format_num(f[:unlocked_monthly_upside])))}/mo"
57
+ puts " â€ĸ Unlocked Annual Pipeline Upside: #{Color.bold(Color.green("+$" + format_num(f[:unlocked_annual_pipeline_upside])))}/yr"
58
+ puts "#{Color.bold("───────────────────────────────────────────────────────────────────────────")}\n"
59
+
60
+ if res[:keywords].empty?
61
+ puts " â„šī¸ No Search Console query performance data found for #{res[:domain]}."
62
+ puts " Connect Google Search Console credentials via `gsc setup` or specify a verified property with `--domain <domain>`."
63
+ puts "\n#{Color.bold("═══════════════════════════════════════════════════════════════════════════")}\n"
64
+ return
65
+ end
66
+
67
+ puts "#{Color.bold("TOP HIGH-ROI KEYWORD REVENUE OPPORTUNITIES:")}\n\n"
68
+ printf " %-30s %-7s %-8s %-12s %-12s %-14s %s\n", "Search Query", "Intent", "Pos", "Current Rev", "Top 3 Rev", "Monthly Upside", "Score"
69
+ puts " ---------------------------------------------------------------------------------------------------"
70
+
71
+ res[:keywords].each do |k|
72
+ q_str = k[:query].length > 28 ? "#{k[:query][0..25]}..." : k[:query]
73
+ intent_str = case k[:intent]
74
+ when :transactional then Color.green("BUY")
75
+ when :commercial then Color.cyan("COMM")
76
+ when :navigational then Color.yellow("NAV")
77
+ else "INFO"
78
+ end
79
+
80
+ upside_str = k[:monthly_revenue_upside] > 0 ? Color.green("+$" + format_num(k[:monthly_revenue_upside])) : "$0"
81
+ score_str = k[:value_score] >= 70 ? Color.green(k[:value_score].to_s) : k[:value_score].to_s
82
+
83
+ printf " %-30s %-7s Pos %-4s $%-11s $%-11s %-14s [ %s ]\n",
84
+ q_str,
85
+ intent_str,
86
+ k[:position],
87
+ format_num(k[:current_monthly_revenue]),
88
+ format_num(k[:potential_monthly_revenue]),
89
+ upside_str,
90
+ score_str
91
+
92
+ if k[:action] && k[:monthly_revenue_upside] > 50.0
93
+ puts " #{Color.yellow("â†ŗ")} #{k[:action]}"
94
+ end
95
+ end
96
+
97
+ puts "\n#{Color.bold("───────────────────────────────────────────────────────────────────────────")}"
98
+ puts "#{Color.bold("💡 STRATEGIC EXECUTION ADVICE:")}"
99
+ puts " Focus engineering on Page-1 and Page-2 keywords with BUY intent. Even modest rank improvements"
100
+ puts " deliver compounding recurring pipeline without increasing paid ad acquisition costs."
101
+ puts "#{Color.bold("═══════════════════════════════════════════════════════════════════════════")}\n"
102
+ end
103
+
104
+ def export_csv(res, file_path)
105
+ headers = %w[Query Intent Position Impressions Clicks CTR ConvRate CurrentOrders CurrentRevenue PotentialClicks PotentialRevenue MonthlyUpside AnnualUpside ValueScore StrategicAction]
106
+ rows = res[:keywords].map do |k|
107
+ [
108
+ k[:query],
109
+ k[:intent].to_s,
110
+ k[:position],
111
+ k[:impressions],
112
+ k[:clicks],
113
+ k[:ctr],
114
+ k[:effective_conv_rate],
115
+ k[:current_orders],
116
+ k[:current_monthly_revenue],
117
+ k[:potential_clicks],
118
+ k[:potential_monthly_revenue],
119
+ k[:monthly_revenue_upside],
120
+ k[:annual_revenue_upside],
121
+ k[:value_score],
122
+ k[:action]
123
+ ]
124
+ end
125
+ Base.write_csv(file_path, headers, rows)
126
+ puts Color.cyan("📁 Successfully exported keyword value matrix to #{file_path}")
127
+ end
128
+
129
+ def format_num(val)
130
+ val.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
131
+ end
132
+ end
133
+ end
134
+ end