@rankcli/cli 0.0.13 → 0.0.15
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 +103 -207
- package/dist/index.js +239 -81
- package/dist/index.mjs +201 -43
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -288,16 +288,18 @@ function getSupabaseAnonKey() {
|
|
|
288
288
|
}
|
|
289
289
|
var _supabaseUrl = null;
|
|
290
290
|
var _supabaseAnonKey = null;
|
|
291
|
-
|
|
291
|
+
function getSupabaseUrlLazy() {
|
|
292
292
|
if (!_supabaseUrl) _supabaseUrl = getSupabaseUrl();
|
|
293
293
|
return _supabaseUrl;
|
|
294
|
-
}
|
|
295
|
-
|
|
294
|
+
}
|
|
295
|
+
function getSupabaseAnonKeyLazy() {
|
|
296
296
|
if (!_supabaseAnonKey) _supabaseAnonKey = getSupabaseAnonKey();
|
|
297
297
|
return _supabaseAnonKey;
|
|
298
|
-
}
|
|
298
|
+
}
|
|
299
299
|
var WEB_APP_URL = process.env.RANKCLI_WEB_URL || INJECTED_CONFIG?.webAppUrl || "https://rankcli.dev";
|
|
300
|
-
|
|
300
|
+
function getApiUrl() {
|
|
301
|
+
return process.env.RANKCLI_API_URL || getSupabaseUrlLazy();
|
|
302
|
+
}
|
|
301
303
|
var config = new Conf({
|
|
302
304
|
projectName: "rankcli",
|
|
303
305
|
schema: {
|
|
@@ -316,7 +318,7 @@ var config = new Conf({
|
|
|
316
318
|
var supabaseClient = null;
|
|
317
319
|
function getSupabaseClient() {
|
|
318
320
|
if (!supabaseClient) {
|
|
319
|
-
supabaseClient = createClient(
|
|
321
|
+
supabaseClient = createClient(getSupabaseUrlLazy(), getSupabaseAnonKeyLazy(), {
|
|
320
322
|
auth: {
|
|
321
323
|
persistSession: false,
|
|
322
324
|
// We manage session ourselves via conf
|
|
@@ -354,7 +356,7 @@ function saveApiKey(apiKey, keyName) {
|
|
|
354
356
|
}
|
|
355
357
|
async function validateApiKey(apiKey) {
|
|
356
358
|
try {
|
|
357
|
-
const response = await fetch(`${
|
|
359
|
+
const response = await fetch(`${getApiUrl()}/functions/v1/validate-api-key`, {
|
|
358
360
|
method: "POST",
|
|
359
361
|
headers: {
|
|
360
362
|
"Content-Type": "application/json",
|
|
@@ -1033,6 +1035,161 @@ async function apply(options) {
|
|
|
1033
1035
|
console.log("");
|
|
1034
1036
|
}
|
|
1035
1037
|
|
|
1038
|
+
// src/commands/geo.ts
|
|
1039
|
+
import { analyzers } from "@rankcli/agent-runtime";
|
|
1040
|
+
import ora2 from "ora";
|
|
1041
|
+
import chalk2 from "chalk";
|
|
1042
|
+
async function fetchResources(url) {
|
|
1043
|
+
const headers = {};
|
|
1044
|
+
let html = null;
|
|
1045
|
+
let robotsTxt = null;
|
|
1046
|
+
try {
|
|
1047
|
+
const response = await fetch(url, {
|
|
1048
|
+
headers: {
|
|
1049
|
+
"User-Agent": "RankCLI-Bot/1.0 (+https://rankcli.dev/bot)",
|
|
1050
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
if (response.ok) {
|
|
1054
|
+
html = await response.text();
|
|
1055
|
+
response.headers.forEach((value, key) => {
|
|
1056
|
+
headers[key.toLowerCase()] = value;
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
} catch {
|
|
1060
|
+
}
|
|
1061
|
+
try {
|
|
1062
|
+
const robotsUrl = new URL("/robots.txt", url).href;
|
|
1063
|
+
const robotsResponse = await fetch(robotsUrl);
|
|
1064
|
+
if (robotsResponse.ok) {
|
|
1065
|
+
robotsTxt = await robotsResponse.text();
|
|
1066
|
+
}
|
|
1067
|
+
} catch {
|
|
1068
|
+
}
|
|
1069
|
+
return { html, robotsTxt, headers };
|
|
1070
|
+
}
|
|
1071
|
+
async function geo(options) {
|
|
1072
|
+
const url = options.url;
|
|
1073
|
+
if (!url) {
|
|
1074
|
+
console.log("\n\u26A0\uFE0F Please provide a URL to analyze:");
|
|
1075
|
+
console.log(" rankcli geo --url https://your-site.com");
|
|
1076
|
+
console.log("\nOptions:");
|
|
1077
|
+
console.log(" --output json Output as JSON");
|
|
1078
|
+
console.log("");
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
try {
|
|
1082
|
+
new URL(url);
|
|
1083
|
+
} catch {
|
|
1084
|
+
console.log("\n\u274C Invalid URL format. Please provide a valid URL.\n");
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const spinner = ora2("Fetching page and robots.txt...").start();
|
|
1088
|
+
const { html, robotsTxt, headers } = await fetchResources(url);
|
|
1089
|
+
if (!html) {
|
|
1090
|
+
spinner.fail("Could not fetch page content");
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
spinner.text = "Running GEO analysis...";
|
|
1094
|
+
try {
|
|
1095
|
+
const geoResult = await analyzers.analyzeGEO(html, url, robotsTxt || void 0);
|
|
1096
|
+
const cwvResult = analyzers.analyzeCoreWebVitals(html, url, headers);
|
|
1097
|
+
const securityResult = analyzers.analyzeSecurityHeaders(headers, url);
|
|
1098
|
+
const schemaResult = analyzers.analyzeStructuredData(html, url);
|
|
1099
|
+
const mobileResult = analyzers.analyzeMobileSEO(html, url);
|
|
1100
|
+
spinner.succeed("GEO analysis complete");
|
|
1101
|
+
if (options.output === "json") {
|
|
1102
|
+
const jsonOutput = {
|
|
1103
|
+
url,
|
|
1104
|
+
geo: geoResult,
|
|
1105
|
+
coreWebVitals: cwvResult,
|
|
1106
|
+
security: securityResult,
|
|
1107
|
+
structuredData: schemaResult,
|
|
1108
|
+
mobile: mobileResult
|
|
1109
|
+
};
|
|
1110
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
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)));
|
|
1116
|
+
const geoScore = geoResult.score;
|
|
1117
|
+
const scoreColor = geoScore >= 80 ? chalk2.green : geoScore >= 60 ? chalk2.yellow : chalk2.red;
|
|
1118
|
+
console.log(`
|
|
1119
|
+
${chalk2.bold("GEO Score:")} ${scoreColor.bold(geoScore + "/100")}`);
|
|
1120
|
+
console.log("\n" + chalk2.bold(" AI Crawler Access:"));
|
|
1121
|
+
const { aiCrawlerAccess } = geoResult;
|
|
1122
|
+
if (aiCrawlerAccess.blockedCrawlers.length > 0) {
|
|
1123
|
+
console.log(chalk2.red(` \u274C Blocked: ${aiCrawlerAccess.blockedCrawlers.join(", ")}`));
|
|
1124
|
+
}
|
|
1125
|
+
if (aiCrawlerAccess.allowedCrawlers.length > 0) {
|
|
1126
|
+
const topAllowed = aiCrawlerAccess.allowedCrawlers.slice(0, 5);
|
|
1127
|
+
console.log(chalk2.green(` \u2705 Allowed: ${topAllowed.join(", ")}${aiCrawlerAccess.allowedCrawlers.length > 5 ? "..." : ""}`));
|
|
1128
|
+
}
|
|
1129
|
+
console.log("\n" + chalk2.bold(" Rendering:"));
|
|
1130
|
+
if (aiCrawlerAccess.serverSideRendered) {
|
|
1131
|
+
console.log(chalk2.green(" \u2705 Server-side rendered (AI crawlers can see content)"));
|
|
1132
|
+
} else if (aiCrawlerAccess.jsRenderingRequired) {
|
|
1133
|
+
console.log(chalk2.red(" \u274C JavaScript required (AI crawlers may see blank page)"));
|
|
1134
|
+
}
|
|
1135
|
+
console.log("\n" + chalk2.bold(" LLM Friendliness:"));
|
|
1136
|
+
const { llmSignals } = geoResult;
|
|
1137
|
+
const bar = (score) => {
|
|
1138
|
+
const filled = Math.round(score / 5);
|
|
1139
|
+
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));
|
|
1142
|
+
};
|
|
1143
|
+
console.log(` Content Clarity: ${bar(llmSignals.contentClarity)} ${llmSignals.contentClarity}%`);
|
|
1144
|
+
console.log(` Fact Density: ${bar(llmSignals.factDensity)} ${llmSignals.factDensity}%`);
|
|
1145
|
+
console.log(` Structure: ${bar(llmSignals.structureQuality)} ${llmSignals.structureQuality}%`);
|
|
1146
|
+
console.log(` Citations: ${bar(llmSignals.citationQuality)} ${llmSignals.citationQuality}%`);
|
|
1147
|
+
console.log("\n" + chalk2.bold(" Content Structure:"));
|
|
1148
|
+
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:"));
|
|
1155
|
+
const { citationReadiness } = geoResult;
|
|
1156
|
+
if (citationReadiness.trustSignals.length > 0) {
|
|
1157
|
+
console.log(chalk2.green(` \u2705 ${citationReadiness.trustSignals.join(", ")}`));
|
|
1158
|
+
} else {
|
|
1159
|
+
console.log(chalk2.yellow(" \u25CB No trust signals detected"));
|
|
1160
|
+
}
|
|
1161
|
+
console.log("\n" + chalk2.bold(" Other Scores:"));
|
|
1162
|
+
console.log(` Core Web Vitals: ${bar(cwvResult.overallScore)} ${cwvResult.overallScore}%`);
|
|
1163
|
+
console.log(` Security Headers: ${securityResult.grade} (${securityResult.score}%)`);
|
|
1164
|
+
console.log(` Structured Data: ${bar(schemaResult.score)} ${schemaResult.score}%`);
|
|
1165
|
+
console.log(` Mobile SEO: ${bar(mobileResult.score)} ${mobileResult.score}%`);
|
|
1166
|
+
const allIssues = [
|
|
1167
|
+
...geoResult.issues,
|
|
1168
|
+
...cwvResult.issues,
|
|
1169
|
+
...securityResult.issues,
|
|
1170
|
+
...schemaResult.issues,
|
|
1171
|
+
...mobileResult.issues
|
|
1172
|
+
];
|
|
1173
|
+
const criticalIssues = allIssues.filter((i) => i.severity === "critical" || i.severity === "error");
|
|
1174
|
+
if (criticalIssues.length > 0) {
|
|
1175
|
+
console.log("\n" + chalk2.bold.red(" Critical Issues:"));
|
|
1176
|
+
criticalIssues.slice(0, 5).forEach((issue, i) => {
|
|
1177
|
+
console.log(chalk2.red(` ${i + 1}. ${issue.title}`));
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
if (geoResult.recommendations.length > 0) {
|
|
1181
|
+
console.log("\n" + chalk2.bold(" Recommendations:"));
|
|
1182
|
+
geoResult.recommendations.slice(0, 5).forEach((rec, i) => {
|
|
1183
|
+
console.log(` ${i + 1}. ${rec}`);
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
console.log("\n" + chalk2.bold("\u2550".repeat(60)));
|
|
1187
|
+
console.log("");
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
spinner.fail(`Analysis failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1036
1193
|
// src/commands/keywords.ts
|
|
1037
1194
|
import * as readline from "readline";
|
|
1038
1195
|
import {
|
|
@@ -1206,7 +1363,7 @@ async function keywordsQuick(url, providedSeeds) {
|
|
|
1206
1363
|
`);
|
|
1207
1364
|
}
|
|
1208
1365
|
}
|
|
1209
|
-
var
|
|
1366
|
+
var SUPABASE_URL = "https://eqzlmjbvrtrglknphdai.supabase.co";
|
|
1210
1367
|
async function keywordsAI(url, options = {}) {
|
|
1211
1368
|
console.log("\n\u{1F916} AI-Powered Keyword Research\n");
|
|
1212
1369
|
console.log("This uses GPT-4 to analyze your site and generate keyword recommendations");
|
|
@@ -1220,7 +1377,7 @@ async function keywordsAI(url, options = {}) {
|
|
|
1220
1377
|
if (!options.forceLocal) {
|
|
1221
1378
|
console.log("\u{1F517} Connecting to RankCLI cloud...");
|
|
1222
1379
|
try {
|
|
1223
|
-
const response = await fetch(`${
|
|
1380
|
+
const response = await fetch(`${SUPABASE_URL}/functions/v1/ai-keywords`, {
|
|
1224
1381
|
method: "POST",
|
|
1225
1382
|
headers: {
|
|
1226
1383
|
"Content-Type": "application/json"
|
|
@@ -1705,30 +1862,30 @@ function showGSCInstructions() {
|
|
|
1705
1862
|
}
|
|
1706
1863
|
|
|
1707
1864
|
// src/commands/auth.ts
|
|
1708
|
-
import
|
|
1709
|
-
import
|
|
1865
|
+
import chalk3 from "chalk";
|
|
1866
|
+
import ora3 from "ora";
|
|
1710
1867
|
import inquirer from "inquirer";
|
|
1711
1868
|
import open from "open";
|
|
1712
1869
|
async function login(options) {
|
|
1713
1870
|
if (isLoggedIn()) {
|
|
1714
1871
|
const { email } = getUserInfo();
|
|
1715
|
-
console.log(
|
|
1716
|
-
console.log(
|
|
1872
|
+
console.log(chalk3.yellow(`Already logged in as ${email}`));
|
|
1873
|
+
console.log(chalk3.dim('Run "rankcli logout" to sign out first.'));
|
|
1717
1874
|
return;
|
|
1718
1875
|
}
|
|
1719
|
-
const spinner =
|
|
1876
|
+
const spinner = ora3();
|
|
1720
1877
|
try {
|
|
1721
1878
|
if (options.token) {
|
|
1722
1879
|
const apiKey = options.token;
|
|
1723
1880
|
if (!apiKey.startsWith("rankcli_")) {
|
|
1724
|
-
console.log(
|
|
1881
|
+
console.log(chalk3.red('Invalid API key format. Keys should start with "rankcli_"'));
|
|
1725
1882
|
return;
|
|
1726
1883
|
}
|
|
1727
1884
|
spinner.start("Validating API key...");
|
|
1728
1885
|
const result = await validateApiKey(apiKey);
|
|
1729
1886
|
if (!result.valid) {
|
|
1730
1887
|
spinner.fail("Invalid API key");
|
|
1731
|
-
console.log(
|
|
1888
|
+
console.log(chalk3.red(`
|
|
1732
1889
|
${result.error}`));
|
|
1733
1890
|
return;
|
|
1734
1891
|
}
|
|
@@ -1741,14 +1898,14 @@ ${result.error}`));
|
|
|
1741
1898
|
});
|
|
1742
1899
|
}
|
|
1743
1900
|
spinner.succeed(`Authenticated via API key`);
|
|
1744
|
-
console.log(
|
|
1901
|
+
console.log(chalk3.dim(`
|
|
1745
1902
|
Email: ${result.user?.email}`));
|
|
1746
|
-
console.log(
|
|
1903
|
+
console.log(chalk3.dim(`Plan: ${result.subscription?.planName || "Free"}`));
|
|
1747
1904
|
return;
|
|
1748
1905
|
}
|
|
1749
1906
|
if (options.browser) {
|
|
1750
|
-
console.log(
|
|
1751
|
-
console.log(
|
|
1907
|
+
console.log(chalk3.cyan("\nOpening browser for login..."));
|
|
1908
|
+
console.log(chalk3.dim("Complete the login in your browser, then return here.\n"));
|
|
1752
1909
|
const loginUrl = `${WEB_APP_URL}/login?cli=true`;
|
|
1753
1910
|
await open(loginUrl);
|
|
1754
1911
|
const { token } = await inquirer.prompt([
|
|
@@ -1764,7 +1921,7 @@ Email: ${result.user?.email}`));
|
|
|
1764
1921
|
const { data, error } = await supabase.auth.getUser(token);
|
|
1765
1922
|
if (error || !data.user) {
|
|
1766
1923
|
spinner.fail("Invalid token");
|
|
1767
|
-
console.log(
|
|
1924
|
+
console.log(chalk3.red("The token is invalid or expired. Please try again."));
|
|
1768
1925
|
return;
|
|
1769
1926
|
}
|
|
1770
1927
|
const { data: sessionData, error: sessionError } = await supabase.auth.setSession({
|
|
@@ -1778,7 +1935,7 @@ Email: ${result.user?.email}`));
|
|
|
1778
1935
|
}
|
|
1779
1936
|
saveSession(sessionData.session);
|
|
1780
1937
|
await fetchAndSaveSubscription();
|
|
1781
|
-
spinner.succeed(`Logged in as ${
|
|
1938
|
+
spinner.succeed(`Logged in as ${chalk3.green(data.user.email)}`);
|
|
1782
1939
|
} else {
|
|
1783
1940
|
let email = options.email;
|
|
1784
1941
|
if (!email) {
|
|
@@ -1811,11 +1968,11 @@ Email: ${result.user?.email}`));
|
|
|
1811
1968
|
});
|
|
1812
1969
|
if (error) {
|
|
1813
1970
|
spinner.fail("Login failed");
|
|
1814
|
-
console.log(
|
|
1971
|
+
console.log(chalk3.red(`
|
|
1815
1972
|
${error.message}`));
|
|
1816
1973
|
if (error.message.includes("Invalid login")) {
|
|
1817
|
-
console.log(
|
|
1818
|
-
console.log(
|
|
1974
|
+
console.log(chalk3.dim("\nDon't have an account? Sign up at:"));
|
|
1975
|
+
console.log(chalk3.cyan(` ${WEB_APP_URL}/signup`));
|
|
1819
1976
|
}
|
|
1820
1977
|
return;
|
|
1821
1978
|
}
|
|
@@ -1825,41 +1982,41 @@ ${error.message}`));
|
|
|
1825
1982
|
}
|
|
1826
1983
|
saveSession(data.session);
|
|
1827
1984
|
await fetchAndSaveSubscription();
|
|
1828
|
-
spinner.succeed(`Logged in as ${
|
|
1985
|
+
spinner.succeed(`Logged in as ${chalk3.green(data.user?.email)}`);
|
|
1829
1986
|
const { planName, sitesLimit } = getUserInfo();
|
|
1830
|
-
console.log(
|
|
1987
|
+
console.log(chalk3.dim(`
|
|
1831
1988
|
Plan: ${planName} (${sitesLimit} site${sitesLimit !== 1 ? "s" : ""})`));
|
|
1832
1989
|
}
|
|
1833
1990
|
} catch (err) {
|
|
1834
1991
|
spinner.fail("Login failed");
|
|
1835
|
-
console.error(
|
|
1992
|
+
console.error(chalk3.red(err instanceof Error ? err.message : "Unknown error"));
|
|
1836
1993
|
}
|
|
1837
1994
|
}
|
|
1838
1995
|
async function logout() {
|
|
1839
1996
|
if (!isLoggedIn()) {
|
|
1840
|
-
console.log(
|
|
1997
|
+
console.log(chalk3.yellow("Not logged in"));
|
|
1841
1998
|
return;
|
|
1842
1999
|
}
|
|
1843
2000
|
const { email } = getUserInfo();
|
|
1844
|
-
const spinner =
|
|
2001
|
+
const spinner = ora3("Signing out...").start();
|
|
1845
2002
|
try {
|
|
1846
2003
|
const supabase = getSupabaseClient();
|
|
1847
2004
|
await supabase.auth.signOut();
|
|
1848
2005
|
clearCredentials();
|
|
1849
|
-
spinner.succeed(`Logged out from ${
|
|
2006
|
+
spinner.succeed(`Logged out from ${chalk3.dim(email)}`);
|
|
1850
2007
|
} catch (err) {
|
|
1851
2008
|
spinner.fail("Logout failed");
|
|
1852
|
-
console.error(
|
|
2009
|
+
console.error(chalk3.red(err instanceof Error ? err.message : "Unknown error"));
|
|
1853
2010
|
}
|
|
1854
2011
|
}
|
|
1855
2012
|
async function whoami() {
|
|
1856
2013
|
if (!isLoggedIn()) {
|
|
1857
|
-
console.log(
|
|
1858
|
-
console.log(
|
|
1859
|
-
console.log(
|
|
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`));
|
|
1860
2017
|
return;
|
|
1861
2018
|
}
|
|
1862
|
-
const spinner =
|
|
2019
|
+
const spinner = ora3("Checking account...").start();
|
|
1863
2020
|
try {
|
|
1864
2021
|
if (isApiKeyAuth()) {
|
|
1865
2022
|
const apiKey = getApiKey();
|
|
@@ -1879,18 +2036,18 @@ async function whoami() {
|
|
|
1879
2036
|
const { email, planName, sitesLimit } = getUserInfo();
|
|
1880
2037
|
const authMethod = isApiKeyAuth() ? "API Key" : "Session";
|
|
1881
2038
|
spinner.stop();
|
|
1882
|
-
console.log(
|
|
2039
|
+
console.log(chalk3.bold("\nRankCLI Account"));
|
|
1883
2040
|
console.log("\u2500".repeat(40));
|
|
1884
|
-
console.log(` Email: ${
|
|
1885
|
-
console.log(` Plan: ${
|
|
2041
|
+
console.log(` Email: ${chalk3.cyan(email || "N/A")}`);
|
|
2042
|
+
console.log(` Plan: ${chalk3.green(planName)}`);
|
|
1886
2043
|
console.log(` Sites: ${sitesLimit} allowed`);
|
|
1887
|
-
console.log(` Auth: ${
|
|
2044
|
+
console.log(` Auth: ${chalk3.dim(authMethod)}`);
|
|
1888
2045
|
console.log("\u2500".repeat(40));
|
|
1889
|
-
console.log(
|
|
2046
|
+
console.log(chalk3.dim(`
|
|
1890
2047
|
Manage at: ${WEB_APP_URL}/account`));
|
|
1891
2048
|
} catch (err) {
|
|
1892
2049
|
spinner.fail("Failed to fetch account info");
|
|
1893
|
-
console.error(
|
|
2050
|
+
console.error(chalk3.red(err instanceof Error ? err.message : "Unknown error"));
|
|
1894
2051
|
}
|
|
1895
2052
|
}
|
|
1896
2053
|
async function fetchAndSaveSubscription() {
|
|
@@ -1922,7 +2079,7 @@ async function fetchAndSaveSubscription() {
|
|
|
1922
2079
|
}
|
|
1923
2080
|
|
|
1924
2081
|
// src/version.ts
|
|
1925
|
-
var VERSION = "0.0.
|
|
2082
|
+
var VERSION = "0.0.15";
|
|
1926
2083
|
|
|
1927
2084
|
// src/index.ts
|
|
1928
2085
|
var program = new Command();
|
|
@@ -1934,6 +2091,7 @@ program.command("init").description("Initialize SEO Autopilot in your project").
|
|
|
1934
2091
|
program.command("analyze").description("AI analyzes your codebase for SEO opportunities").option("-v, --verbose", "Show detailed output").action(analyze);
|
|
1935
2092
|
program.command("audit").description("Run a comprehensive SEO audit (170+ checks, Ahrefs-level)").option("-u, --url <url>", "URL to audit").option("-o, --output <format>", "Output format (json, console)", "console").option("--check-links", "Check for broken internal/external links (slower)").option("--max-pages <n>", "Max pages to crawl (default: 5)").option("--ai", "Enable AI analysis").option("--ai-provider <provider>", "AI provider: openai or anthropic").option("--ai-key <key>", "API key (or set OPENAI_API_KEY/ANTHROPIC_API_KEY)").action(audit);
|
|
1936
2093
|
program.command("apply").description("Analyze a live URL and apply SEO fixes to your codebase").option("-u, --url <url>", "URL to analyze").option("--auto", "Auto-apply fixes without confirmation").option("--dry-run", "Preview changes without applying").action(apply);
|
|
2094
|
+
program.command("geo").description("GEO analysis - Check AI search visibility (ChatGPT, Perplexity, Claude)").option("-u, --url <url>", "URL to analyze").option("-o, --output <format>", "Output format (json, console)", "console").action(geo);
|
|
1937
2095
|
program.command("keywords").description("AI-powered keyword research with actionable suggestions").option("-u, --url <url>", "Your website URL").option("-s, --seed <keywords>", "Seed keywords (comma-separated)").option("--auto", "Auto-extract seed keywords from your page").option("--quick", "Quick mode - skip questions, use defaults").option("--ai", "Use GPT-4 for enhanced analysis with free tool ideas").option("--local", "Force local processing (skip cloud worker)").option("--competitor", "Competitor gap analysis mode (SpyFu Kombat-style)").option("-c, --competitors <domains>", "Competitor domains (comma-separated)").action(async (options) => {
|
|
1938
2096
|
if (options.ai && options.url) {
|
|
1939
2097
|
await keywordsAI(options.url, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rankcli/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "RankCLI - Ship code, get ranked. SEO meets CI/CD.",
|
|
5
5
|
"homepage": "https://rankcli.dev",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"update-version": "node scripts/update-version.js"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@rankcli/agent-runtime": "^0.0.
|
|
47
|
+
"@rankcli/agent-runtime": "^0.0.11",
|
|
48
48
|
"@supabase/supabase-js": "^2.45.0",
|
|
49
49
|
"chalk": "^5.3.0",
|
|
50
50
|
"commander": "^12.0.0",
|