@rankcli/agent-runtime 0.0.1 → 0.0.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.
- package/dist/index.d.mts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +373 -1
- package/dist/index.mjs +369 -1
- package/package.json +1 -1
- package/src/audit/checks/color-contrast.ts +1 -1
- package/src/audit/checks/dom-size.ts +1 -1
- package/src/audit/checks/html-compliance.ts +1 -1
- package/src/audit/checks/image-dimensions.ts +1 -1
- package/src/audit/checks/links.ts +70 -0
- package/src/audit/checks/site-maturity.ts +9 -0
- package/src/audit/runner.test.ts +7 -7
- package/src/audit/types.ts +9 -0
- package/src/fixer.ts +220 -0
- package/src/geo/index.ts +1 -0
- package/src/geo/llm-citation-checker.ts +188 -0
- package/src/keywords/sources/free-sources.ts +1 -1
- package/src/scheduler/scheduled-audit.test.ts +6 -6
package/dist/index.js
CHANGED
|
@@ -853,6 +853,7 @@ __export(index_exports, {
|
|
|
853
853
|
applyFixes: () => applyFixes,
|
|
854
854
|
buildGSCApiRequest: () => buildGSCApiRequest,
|
|
855
855
|
buildGSCRequest: () => buildGSCRequest,
|
|
856
|
+
calculateAIVisibilityScore: () => calculateAIVisibilityScore,
|
|
856
857
|
calculateBM25: () => calculateBM252,
|
|
857
858
|
calculateKeywordTFIDF: () => calculateTFIDF,
|
|
858
859
|
calculateNextRun: () => calculateNextRun,
|
|
@@ -867,6 +868,7 @@ __export(index_exports, {
|
|
|
867
868
|
checkGitHubCLI: () => checkGitHubCLI,
|
|
868
869
|
checkInternalRedirects: () => checkInternalRedirects,
|
|
869
870
|
checkJSRenderingRatio: () => checkJSRenderingRatio,
|
|
871
|
+
checkLLMCitations: () => checkLLMCitations,
|
|
870
872
|
checkLlmsTxt: () => checkLlmsTxt,
|
|
871
873
|
checkMobileResources: () => checkMobileResources,
|
|
872
874
|
checkPlaintextEmails: () => checkPlaintextEmails,
|
|
@@ -970,6 +972,7 @@ __export(index_exports, {
|
|
|
970
972
|
generatePDFReport: () => generatePDFReport,
|
|
971
973
|
generatePRDescription: () => generatePRDescription,
|
|
972
974
|
generateReactHelmetSocialMeta: () => generateReactHelmetSocialMeta,
|
|
975
|
+
generateRecommendationQueries: () => generateRecommendationQueries,
|
|
973
976
|
generateRemixMeta: () => generateRemixMeta,
|
|
974
977
|
generateSecretsDoc: () => generateSecretsDoc,
|
|
975
978
|
generateSocialMetaFix: () => generateSocialMetaFix,
|
|
@@ -977,6 +980,7 @@ __export(index_exports, {
|
|
|
977
980
|
generateUncertaintyQuestions: () => generateUncertaintyQuestions,
|
|
978
981
|
generateWizardQuestions: () => generateWizardQuestions,
|
|
979
982
|
generateWorkflow: () => generateWorkflow,
|
|
983
|
+
getAIVisibilitySummary: () => getAIVisibilitySummary,
|
|
980
984
|
getAutocompleteSuggestions: () => getAutocompleteSuggestions,
|
|
981
985
|
getDateRange: () => getDateRange,
|
|
982
986
|
getExpandedSuggestions: () => getExpandedSuggestions,
|
|
@@ -1321,6 +1325,15 @@ var ISSUE_DEFINITIONS = {
|
|
|
1321
1325
|
impact: "Poor user experience and potential trust issues.",
|
|
1322
1326
|
howToFix: "Update or remove the broken external link."
|
|
1323
1327
|
},
|
|
1328
|
+
JS_ONLY_NAVIGATION: {
|
|
1329
|
+
code: "JS_ONLY_NAVIGATION",
|
|
1330
|
+
severity: "warning",
|
|
1331
|
+
category: "links",
|
|
1332
|
+
title: "JavaScript-only navigation detected",
|
|
1333
|
+
description: "Navigation elements use onClick handlers without proper <a href> links.",
|
|
1334
|
+
impact: "Search engine crawlers cannot follow JavaScript-only navigation. These links are invisible to crawlers, preventing page discovery and indexing.",
|
|
1335
|
+
howToFix: "Replace onClick navigation with proper <a href> or <Link> components. In React, use react-router Link instead of navigate() in onClick handlers. Ensure all navigation renders as real anchor tags with href attributes."
|
|
1336
|
+
},
|
|
1324
1337
|
TOO_MANY_LINKS: {
|
|
1325
1338
|
code: "TOO_MANY_LINKS",
|
|
1326
1339
|
severity: "notice",
|
|
@@ -3183,6 +3196,7 @@ async function analyzeLinks(html, baseUrl, checkBroken = false) {
|
|
|
3183
3196
|
const external = [];
|
|
3184
3197
|
const brokenInternal = [];
|
|
3185
3198
|
const brokenExternal = [];
|
|
3199
|
+
const jsOnlyNavigation = [];
|
|
3186
3200
|
$("a[href]").each((_, el) => {
|
|
3187
3201
|
const href = $(el).attr("href") || "";
|
|
3188
3202
|
const text = $(el).text().trim();
|
|
@@ -3211,6 +3225,56 @@ async function analyzeLinks(html, baseUrl, checkBroken = false) {
|
|
|
3211
3225
|
}
|
|
3212
3226
|
});
|
|
3213
3227
|
const totalLinks = internal.length + external.length;
|
|
3228
|
+
const navPatterns = /navigate|router|history\.push|location\.href|window\.location|goto|redirect/i;
|
|
3229
|
+
const navTextPatterns = /^(home|about|contact|pricing|blog|docs|features|products?|services?|sign\s*up|log\s*in|register|dashboard|account|settings|help|faq|support|menu|nav)$/i;
|
|
3230
|
+
$("button[onclick], div[onclick], span[onclick], li[onclick]").each((_, el) => {
|
|
3231
|
+
const $el = $(el);
|
|
3232
|
+
const onclick = $el.attr("onclick") || "";
|
|
3233
|
+
const text = $el.text().trim().substring(0, 50);
|
|
3234
|
+
if (navPatterns.test(onclick) || navTextPatterns.test(text)) {
|
|
3235
|
+
jsOnlyNavigation.push({
|
|
3236
|
+
element: el.tagName.toLowerCase(),
|
|
3237
|
+
text: text || "(no text)",
|
|
3238
|
+
reason: "Element uses onClick for navigation instead of <a href>"
|
|
3239
|
+
});
|
|
3240
|
+
}
|
|
3241
|
+
});
|
|
3242
|
+
$("a[onclick]").each((_, el) => {
|
|
3243
|
+
const $el = $(el);
|
|
3244
|
+
const href = $el.attr("href") || "";
|
|
3245
|
+
const onclick = $el.attr("onclick") || "";
|
|
3246
|
+
const text = $el.text().trim().substring(0, 50);
|
|
3247
|
+
if ((href === "#" || href === "" || href.startsWith("javascript:")) && onclick) {
|
|
3248
|
+
jsOnlyNavigation.push({
|
|
3249
|
+
element: "a",
|
|
3250
|
+
text: text || "(no text)",
|
|
3251
|
+
reason: `Link has href="${href}" with onClick - crawlers cannot follow this`
|
|
3252
|
+
});
|
|
3253
|
+
}
|
|
3254
|
+
});
|
|
3255
|
+
$("[data-href], [data-to], [data-link], [data-route]").each((_, el) => {
|
|
3256
|
+
const $el = $(el);
|
|
3257
|
+
const tagName = el.tagName.toLowerCase();
|
|
3258
|
+
const text = $el.text().trim().substring(0, 50);
|
|
3259
|
+
if (tagName !== "a" || !$el.attr("href") || $el.attr("href") === "#") {
|
|
3260
|
+
jsOnlyNavigation.push({
|
|
3261
|
+
element: tagName,
|
|
3262
|
+
text: text || "(no text)",
|
|
3263
|
+
reason: "Uses data attribute for routing instead of real href"
|
|
3264
|
+
});
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3267
|
+
if (jsOnlyNavigation.length > 0) {
|
|
3268
|
+
const uniqueNav = jsOnlyNavigation.slice(0, 10);
|
|
3269
|
+
issues.push({
|
|
3270
|
+
...ISSUE_DEFINITIONS.JS_ONLY_NAVIGATION,
|
|
3271
|
+
affectedUrls: [baseUrl],
|
|
3272
|
+
details: {
|
|
3273
|
+
count: jsOnlyNavigation.length,
|
|
3274
|
+
examples: uniqueNav
|
|
3275
|
+
}
|
|
3276
|
+
});
|
|
3277
|
+
}
|
|
3214
3278
|
const internalCount = internal.length;
|
|
3215
3279
|
const externalCount = external.length;
|
|
3216
3280
|
const internalToExternalRatio = externalCount > 0 ? internalCount / externalCount : null;
|
|
@@ -3318,6 +3382,7 @@ async function analyzeLinks(html, baseUrl, checkBroken = false) {
|
|
|
3318
3382
|
totalLinks,
|
|
3319
3383
|
brokenInternal,
|
|
3320
3384
|
brokenExternal,
|
|
3385
|
+
jsOnlyNavigation,
|
|
3321
3386
|
ratio: {
|
|
3322
3387
|
internal: internalCount,
|
|
3323
3388
|
external: externalCount,
|
|
@@ -8693,6 +8758,12 @@ function analyzeContentScience(html, url, targetKeywords = []) {
|
|
|
8693
8758
|
init_http();
|
|
8694
8759
|
var cheerio28 = __toESM(require("cheerio"));
|
|
8695
8760
|
async function getSSLCertificateAge(url) {
|
|
8761
|
+
let https;
|
|
8762
|
+
try {
|
|
8763
|
+
https = await import("https");
|
|
8764
|
+
} catch {
|
|
8765
|
+
return null;
|
|
8766
|
+
}
|
|
8696
8767
|
return new Promise((resolve) => {
|
|
8697
8768
|
try {
|
|
8698
8769
|
const parsedUrl = new URL(url);
|
|
@@ -15210,7 +15281,7 @@ var cheerio49 = __toESM(require("cheerio"));
|
|
|
15210
15281
|
init_http();
|
|
15211
15282
|
async function analyzeHtmlCompliance(html, url, headers) {
|
|
15212
15283
|
const issues = [];
|
|
15213
|
-
const $ = cheerio49.load(html
|
|
15284
|
+
const $ = cheerio49.load(html);
|
|
15214
15285
|
const parsedUrl = new URL(url);
|
|
15215
15286
|
const doctypeMatch = html.match(/<!DOCTYPE\s+([^>]+)>/i);
|
|
15216
15287
|
const hasDoctype = doctypeMatch !== null;
|
|
@@ -26679,26 +26750,71 @@ async function generateFixForIssue(issue, context) {
|
|
|
26679
26750
|
const fullUrl = url || "https://example.com";
|
|
26680
26751
|
const siteName = new URL(fullUrl).hostname.replace("www.", "");
|
|
26681
26752
|
switch (issue.code) {
|
|
26753
|
+
// Title issues
|
|
26682
26754
|
case "MISSING_TITLE":
|
|
26755
|
+
case "TITLE_KEYWORD_MISMATCH":
|
|
26756
|
+
case "TITLE_H1_KEYWORD_MISMATCH":
|
|
26757
|
+
case "OUTDATED_YEAR_IN_TITLE":
|
|
26683
26758
|
return generateTitleFix(context, siteName);
|
|
26759
|
+
// Meta description issues
|
|
26684
26760
|
case "MISSING_META_DESC":
|
|
26685
26761
|
return generateMetaDescFix(context, siteName);
|
|
26762
|
+
// Canonical issues
|
|
26686
26763
|
case "MISSING_CANONICAL":
|
|
26764
|
+
case "CANONICAL_NO_HTTPS_REDIRECT":
|
|
26687
26765
|
return generateCanonicalFix(context, fullUrl);
|
|
26766
|
+
// Viewport issues
|
|
26688
26767
|
case "MISSING_VIEWPORT":
|
|
26768
|
+
case "HTML_NO_VIEWPORT":
|
|
26769
|
+
case "RESPONSIVE_NO_VIEWPORT":
|
|
26689
26770
|
return generateViewportFix(context);
|
|
26771
|
+
// Open Graph issues
|
|
26690
26772
|
case "MISSING_OG_TAGS":
|
|
26691
26773
|
return generateOGFix(context, siteName, fullUrl);
|
|
26774
|
+
// Twitter Card issues
|
|
26692
26775
|
case "MISSING_TWITTER_CARD":
|
|
26693
26776
|
return generateTwitterFix(context, siteName);
|
|
26777
|
+
// Schema/structured data issues
|
|
26694
26778
|
case "MISSING_SCHEMA":
|
|
26779
|
+
case "SCHEMA_ORG_MISSING":
|
|
26780
|
+
case "NO_ORGANIZATION_SCHEMA":
|
|
26781
|
+
case "NO_ENTITY_SCHEMA":
|
|
26782
|
+
case "FAQ_SCHEMA_MISSING":
|
|
26695
26783
|
return generateSchemaFix(context, siteName, fullUrl);
|
|
26784
|
+
// Robots.txt issues
|
|
26696
26785
|
case "MISSING_ROBOTS":
|
|
26786
|
+
case "ROBOTS_TXT_WARNINGS":
|
|
26787
|
+
case "ROBOTS_TXT_INVALID_SYNTAX":
|
|
26697
26788
|
return generateRobotsFix(context, fullUrl);
|
|
26789
|
+
// Sitemap issues
|
|
26698
26790
|
case "MISSING_SITEMAP":
|
|
26791
|
+
case "BING_SITEMAP_MISSING":
|
|
26699
26792
|
return generateSitemapFix(context, fullUrl);
|
|
26793
|
+
// H1 issues
|
|
26700
26794
|
case "MISSING_H1":
|
|
26795
|
+
case "NO_VISIBLE_HEADLINE":
|
|
26796
|
+
case "H1_MISSING_KEYWORD":
|
|
26701
26797
|
return await generateH1Fix({ cwd });
|
|
26798
|
+
// SPA-specific: add meta management library recommendation
|
|
26799
|
+
case "SPA_NO_META_MANAGEMENT":
|
|
26800
|
+
return generateSPAMetaFix(context, framework);
|
|
26801
|
+
// Preconnect issues
|
|
26802
|
+
case "GOOGLE_FONTS_NO_PRECONNECT":
|
|
26803
|
+
case "MISSING_PRECONNECT":
|
|
26804
|
+
return generatePreconnectFix(context);
|
|
26805
|
+
// Favicon/icons
|
|
26806
|
+
case "HTML_NO_FAVICON":
|
|
26807
|
+
case "HTML_NO_APPLE_TOUCH_ICON":
|
|
26808
|
+
return generateFaviconFix(context);
|
|
26809
|
+
// Charset/lang
|
|
26810
|
+
case "HTML_NO_CHARSET":
|
|
26811
|
+
case "HTML_NOT_UTF8":
|
|
26812
|
+
return generateCharsetFix(context);
|
|
26813
|
+
case "HTML_NO_LANG":
|
|
26814
|
+
return generateLangFix(context);
|
|
26815
|
+
// AI/LLMs.txt
|
|
26816
|
+
case "AI_NO_LLMS_TXT":
|
|
26817
|
+
return generateLlmsTxtFix(context, siteName, fullUrl);
|
|
26702
26818
|
default:
|
|
26703
26819
|
return null;
|
|
26704
26820
|
}
|
|
@@ -26927,6 +27043,159 @@ function generateSitemapFix(context, url) {
|
|
|
26927
27043
|
explanation: "Created sitemap.xml to help search engines discover all pages"
|
|
26928
27044
|
};
|
|
26929
27045
|
}
|
|
27046
|
+
function generateSPAMetaFix(context, framework) {
|
|
27047
|
+
const { htmlPath } = context;
|
|
27048
|
+
if (framework.name.toLowerCase().includes("react") || framework.name === "Unknown") {
|
|
27049
|
+
return {
|
|
27050
|
+
issue: { code: "SPA_NO_META_MANAGEMENT", message: "SPA without dynamic meta tag management", severity: "warning" },
|
|
27051
|
+
file: "src/components/SEOHead.tsx",
|
|
27052
|
+
before: null,
|
|
27053
|
+
after: `import { Helmet } from 'react-helmet-async';
|
|
27054
|
+
|
|
27055
|
+
interface SEOHeadProps {
|
|
27056
|
+
title?: string;
|
|
27057
|
+
description?: string;
|
|
27058
|
+
image?: string;
|
|
27059
|
+
url?: string;
|
|
27060
|
+
}
|
|
27061
|
+
|
|
27062
|
+
export function SEOHead({
|
|
27063
|
+
title = 'Your Site Name',
|
|
27064
|
+
description = 'Your site description',
|
|
27065
|
+
image = '/og-image.png',
|
|
27066
|
+
url = window.location.href,
|
|
27067
|
+
}: SEOHeadProps) {
|
|
27068
|
+
return (
|
|
27069
|
+
<Helmet>
|
|
27070
|
+
<title>{title}</title>
|
|
27071
|
+
<meta name="description" content={description} />
|
|
27072
|
+
<link rel="canonical" href={url} />
|
|
27073
|
+
|
|
27074
|
+
{/* Open Graph */}
|
|
27075
|
+
<meta property="og:title" content={title} />
|
|
27076
|
+
<meta property="og:description" content={description} />
|
|
27077
|
+
<meta property="og:image" content={image} />
|
|
27078
|
+
<meta property="og:url" content={url} />
|
|
27079
|
+
<meta property="og:type" content="website" />
|
|
27080
|
+
|
|
27081
|
+
{/* Twitter */}
|
|
27082
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
27083
|
+
<meta name="twitter:title" content={title} />
|
|
27084
|
+
<meta name="twitter:description" content={description} />
|
|
27085
|
+
<meta name="twitter:image" content={image} />
|
|
27086
|
+
</Helmet>
|
|
27087
|
+
);
|
|
27088
|
+
}`,
|
|
27089
|
+
explanation: "Created SEOHead component using react-helmet-async for dynamic meta tags. Install: npm install react-helmet-async"
|
|
27090
|
+
};
|
|
27091
|
+
}
|
|
27092
|
+
return {
|
|
27093
|
+
issue: { code: "SPA_NO_META_MANAGEMENT", message: "SPA without meta management", severity: "warning" },
|
|
27094
|
+
file: htmlPath,
|
|
27095
|
+
before: null,
|
|
27096
|
+
after: "<!-- Add a meta management library for your framework -->",
|
|
27097
|
+
explanation: `Add dynamic meta tag management for ${framework.name}`,
|
|
27098
|
+
skipped: true,
|
|
27099
|
+
skipReason: `Framework-specific solution needed for ${framework.name}`
|
|
27100
|
+
};
|
|
27101
|
+
}
|
|
27102
|
+
function generatePreconnectFix(context) {
|
|
27103
|
+
const { htmlPath, htmlContent } = context;
|
|
27104
|
+
const preconnects = `<!-- Preconnect to external origins -->
|
|
27105
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
27106
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />`;
|
|
27107
|
+
return {
|
|
27108
|
+
issue: { code: "MISSING_PRECONNECT", message: "Missing preconnect hints", severity: "info" },
|
|
27109
|
+
file: htmlPath,
|
|
27110
|
+
before: "<head>",
|
|
27111
|
+
after: `<head>
|
|
27112
|
+
${preconnects}`,
|
|
27113
|
+
explanation: "Added preconnect hints to speed up loading of external resources"
|
|
27114
|
+
};
|
|
27115
|
+
}
|
|
27116
|
+
function generateFaviconFix(context) {
|
|
27117
|
+
const { htmlPath } = context;
|
|
27118
|
+
const faviconTags = `<!-- Favicons -->
|
|
27119
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
27120
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
27121
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />`;
|
|
27122
|
+
return {
|
|
27123
|
+
issue: { code: "HTML_NO_FAVICON", message: "Missing favicon", severity: "info" },
|
|
27124
|
+
file: htmlPath,
|
|
27125
|
+
before: "<head>",
|
|
27126
|
+
after: `<head>
|
|
27127
|
+
${faviconTags}`,
|
|
27128
|
+
explanation: "Added favicon links. Create favicon files in public/ directory."
|
|
27129
|
+
};
|
|
27130
|
+
}
|
|
27131
|
+
function generateCharsetFix(context) {
|
|
27132
|
+
const { htmlPath, htmlContent } = context;
|
|
27133
|
+
if (htmlContent.includes("charset")) {
|
|
27134
|
+
return {
|
|
27135
|
+
issue: { code: "HTML_NO_CHARSET", message: "Missing charset", severity: "warning" },
|
|
27136
|
+
file: htmlPath,
|
|
27137
|
+
before: null,
|
|
27138
|
+
after: '<meta charset="UTF-8" />',
|
|
27139
|
+
skipped: true,
|
|
27140
|
+
skipReason: "Charset already defined",
|
|
27141
|
+
explanation: "Charset is already present"
|
|
27142
|
+
};
|
|
27143
|
+
}
|
|
27144
|
+
return {
|
|
27145
|
+
issue: { code: "HTML_NO_CHARSET", message: "Missing charset declaration", severity: "warning" },
|
|
27146
|
+
file: htmlPath,
|
|
27147
|
+
before: "<head>",
|
|
27148
|
+
after: '<head>\n <meta charset="UTF-8" />',
|
|
27149
|
+
explanation: "Added UTF-8 charset declaration as first element in head"
|
|
27150
|
+
};
|
|
27151
|
+
}
|
|
27152
|
+
function generateLangFix(context) {
|
|
27153
|
+
const { htmlPath, htmlContent } = context;
|
|
27154
|
+
if (htmlContent.includes("<html") && !htmlContent.includes("lang=")) {
|
|
27155
|
+
return {
|
|
27156
|
+
issue: { code: "HTML_NO_LANG", message: "Missing lang attribute", severity: "warning" },
|
|
27157
|
+
file: htmlPath,
|
|
27158
|
+
before: "<html>",
|
|
27159
|
+
after: '<html lang="en">',
|
|
27160
|
+
explanation: "Added lang attribute for accessibility and SEO"
|
|
27161
|
+
};
|
|
27162
|
+
}
|
|
27163
|
+
return {
|
|
27164
|
+
issue: { code: "HTML_NO_LANG", message: "Missing lang attribute", severity: "warning" },
|
|
27165
|
+
file: htmlPath,
|
|
27166
|
+
before: "<html",
|
|
27167
|
+
after: '<html lang="en"',
|
|
27168
|
+
explanation: "Added lang attribute for accessibility and SEO"
|
|
27169
|
+
};
|
|
27170
|
+
}
|
|
27171
|
+
function generateLlmsTxtFix(context, siteName, url) {
|
|
27172
|
+
const llmsTxt = `# ${siteName}
|
|
27173
|
+
> ${siteName} - A brief description of your product/service
|
|
27174
|
+
|
|
27175
|
+
## About
|
|
27176
|
+
${siteName} is... [Add your description here]
|
|
27177
|
+
|
|
27178
|
+
## Features
|
|
27179
|
+
- Feature 1
|
|
27180
|
+
- Feature 2
|
|
27181
|
+
- Feature 3
|
|
27182
|
+
|
|
27183
|
+
## Links
|
|
27184
|
+
- Homepage: ${url}
|
|
27185
|
+
- Documentation: ${url}/docs
|
|
27186
|
+
- API: ${url}/api
|
|
27187
|
+
|
|
27188
|
+
## Contact
|
|
27189
|
+
- Email: hello@${new URL(url).hostname}
|
|
27190
|
+
`;
|
|
27191
|
+
return {
|
|
27192
|
+
issue: { code: "AI_NO_LLMS_TXT", message: "No llms.txt file for AI crawlers", severity: "info" },
|
|
27193
|
+
file: "public/llms.txt",
|
|
27194
|
+
before: null,
|
|
27195
|
+
after: llmsTxt,
|
|
27196
|
+
explanation: "Created llms.txt to help AI systems understand your site. Customize the content."
|
|
27197
|
+
};
|
|
27198
|
+
}
|
|
26930
27199
|
async function applyFixes(fixes, options) {
|
|
26931
27200
|
const { cwd, dryRun = false } = options;
|
|
26932
27201
|
const applied = [];
|
|
@@ -28725,6 +28994,105 @@ function escapeRegex2(str) {
|
|
|
28725
28994
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
28726
28995
|
}
|
|
28727
28996
|
|
|
28997
|
+
// src/geo/llm-citation-checker.ts
|
|
28998
|
+
var PROVIDER_NAMES = {
|
|
28999
|
+
openai: "ChatGPT",
|
|
29000
|
+
anthropic: "Claude",
|
|
29001
|
+
google: "Gemini",
|
|
29002
|
+
perplexity: "Perplexity"
|
|
29003
|
+
};
|
|
29004
|
+
var DEFAULT_PROVIDERS = ["openai", "anthropic", "google", "perplexity"];
|
|
29005
|
+
function generateRecommendationQueries(brand, industry) {
|
|
29006
|
+
const queries = [
|
|
29007
|
+
`What are the best ${brand.toLowerCase()} alternatives?`,
|
|
29008
|
+
`Can you recommend tools similar to ${brand}?`,
|
|
29009
|
+
`What do you think about ${brand}?`
|
|
29010
|
+
];
|
|
29011
|
+
if (industry) {
|
|
29012
|
+
queries.push(`What are the best ${industry} tools?`);
|
|
29013
|
+
queries.push(`Recommend a good ${industry} solution`);
|
|
29014
|
+
}
|
|
29015
|
+
return queries;
|
|
29016
|
+
}
|
|
29017
|
+
async function checkLLMCitations(brand, domain, options = {}) {
|
|
29018
|
+
const {
|
|
29019
|
+
providers = DEFAULT_PROVIDERS,
|
|
29020
|
+
queries = generateRecommendationQueries(brand),
|
|
29021
|
+
...trackingOptions
|
|
29022
|
+
} = options;
|
|
29023
|
+
const brandConfig = {
|
|
29024
|
+
brandName: brand,
|
|
29025
|
+
domains: [domain],
|
|
29026
|
+
alternativeNames: [brand.toLowerCase(), brand.toUpperCase()]
|
|
29027
|
+
};
|
|
29028
|
+
const query = queries[0];
|
|
29029
|
+
const results = await trackLLMVisibility(
|
|
29030
|
+
{
|
|
29031
|
+
keyword: query,
|
|
29032
|
+
brand: brandConfig,
|
|
29033
|
+
providers
|
|
29034
|
+
},
|
|
29035
|
+
trackingOptions
|
|
29036
|
+
);
|
|
29037
|
+
return results.map((result) => ({
|
|
29038
|
+
provider: result.provider,
|
|
29039
|
+
providerName: PROVIDER_NAMES[result.provider],
|
|
29040
|
+
mentioned: result.mentioned,
|
|
29041
|
+
position: result.position,
|
|
29042
|
+
sentiment: result.sentiment,
|
|
29043
|
+
context: result.contextSnippet || null,
|
|
29044
|
+
score: result.score,
|
|
29045
|
+
error: result.error
|
|
29046
|
+
}));
|
|
29047
|
+
}
|
|
29048
|
+
function calculateAIVisibilityScore(results) {
|
|
29049
|
+
if (results.length === 0) return 0;
|
|
29050
|
+
const mentionedCount = results.filter((r) => r.mentioned && !r.error).length;
|
|
29051
|
+
const validResults = results.filter((r) => !r.error);
|
|
29052
|
+
if (validResults.length === 0) return 0;
|
|
29053
|
+
let score = mentionedCount / validResults.length * 100;
|
|
29054
|
+
const topPositions = results.filter(
|
|
29055
|
+
(r) => r.mentioned && r.position !== null && r.position <= 3
|
|
29056
|
+
);
|
|
29057
|
+
if (topPositions.length > 0) {
|
|
29058
|
+
score = Math.min(100, score + 10);
|
|
29059
|
+
}
|
|
29060
|
+
const positiveResults = results.filter((r) => r.sentiment === "positive");
|
|
29061
|
+
if (positiveResults.length > mentionedCount / 2) {
|
|
29062
|
+
score = Math.min(100, score + 5);
|
|
29063
|
+
}
|
|
29064
|
+
return Math.round(score);
|
|
29065
|
+
}
|
|
29066
|
+
function getAIVisibilitySummary(results) {
|
|
29067
|
+
const score = calculateAIVisibilityScore(results);
|
|
29068
|
+
const mentionedIn = results.filter((r) => r.mentioned).map((r) => r.providerName);
|
|
29069
|
+
const notMentionedIn = results.filter((r) => !r.mentioned && !r.error).map((r) => r.providerName);
|
|
29070
|
+
const positions = results.filter((r) => r.position !== null).map((r) => r.position);
|
|
29071
|
+
const bestPosition = positions.length > 0 ? Math.min(...positions) : null;
|
|
29072
|
+
const sentiments = results.filter((r) => r.sentiment !== null).map((r) => r.sentiment);
|
|
29073
|
+
let overallSentiment = null;
|
|
29074
|
+
if (sentiments.length > 0) {
|
|
29075
|
+
const positiveCount = sentiments.filter((s) => s === "positive").length;
|
|
29076
|
+
const negativeCount = sentiments.filter((s) => s === "negative").length;
|
|
29077
|
+
if (positiveCount > 0 && negativeCount > 0) {
|
|
29078
|
+
overallSentiment = "mixed";
|
|
29079
|
+
} else if (positiveCount > negativeCount) {
|
|
29080
|
+
overallSentiment = "positive";
|
|
29081
|
+
} else if (negativeCount > positiveCount) {
|
|
29082
|
+
overallSentiment = "negative";
|
|
29083
|
+
} else {
|
|
29084
|
+
overallSentiment = "neutral";
|
|
29085
|
+
}
|
|
29086
|
+
}
|
|
29087
|
+
return {
|
|
29088
|
+
score,
|
|
29089
|
+
mentionedIn,
|
|
29090
|
+
notMentionedIn,
|
|
29091
|
+
bestPosition,
|
|
29092
|
+
overallSentiment
|
|
29093
|
+
};
|
|
29094
|
+
}
|
|
29095
|
+
|
|
28728
29096
|
// src/frameworks/index.ts
|
|
28729
29097
|
var frameworks_exports = {};
|
|
28730
29098
|
__export(frameworks_exports, {
|
|
@@ -29481,6 +29849,7 @@ if (typeof globalThis !== "undefined") {
|
|
|
29481
29849
|
applyFixes,
|
|
29482
29850
|
buildGSCApiRequest,
|
|
29483
29851
|
buildGSCRequest,
|
|
29852
|
+
calculateAIVisibilityScore,
|
|
29484
29853
|
calculateBM25,
|
|
29485
29854
|
calculateKeywordTFIDF,
|
|
29486
29855
|
calculateNextRun,
|
|
@@ -29495,6 +29864,7 @@ if (typeof globalThis !== "undefined") {
|
|
|
29495
29864
|
checkGitHubCLI,
|
|
29496
29865
|
checkInternalRedirects,
|
|
29497
29866
|
checkJSRenderingRatio,
|
|
29867
|
+
checkLLMCitations,
|
|
29498
29868
|
checkLlmsTxt,
|
|
29499
29869
|
checkMobileResources,
|
|
29500
29870
|
checkPlaintextEmails,
|
|
@@ -29598,6 +29968,7 @@ if (typeof globalThis !== "undefined") {
|
|
|
29598
29968
|
generatePDFReport,
|
|
29599
29969
|
generatePRDescription,
|
|
29600
29970
|
generateReactHelmetSocialMeta,
|
|
29971
|
+
generateRecommendationQueries,
|
|
29601
29972
|
generateRemixMeta,
|
|
29602
29973
|
generateSecretsDoc,
|
|
29603
29974
|
generateSocialMetaFix,
|
|
@@ -29605,6 +29976,7 @@ if (typeof globalThis !== "undefined") {
|
|
|
29605
29976
|
generateUncertaintyQuestions,
|
|
29606
29977
|
generateWizardQuestions,
|
|
29607
29978
|
generateWorkflow,
|
|
29979
|
+
getAIVisibilitySummary,
|
|
29608
29980
|
getAutocompleteSuggestions,
|
|
29609
29981
|
getDateRange,
|
|
29610
29982
|
getExpandedSuggestions,
|