@rankcli/agent-runtime 0.0.25 → 0.0.27

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  analyzeUrl
3
- } from "./chunk-NATXE6B3.mjs";
3
+ } from "./chunk-O7B2LBKG.mjs";
4
4
  import "./chunk-6BWS3CLP.mjs";
5
5
  export {
6
6
  analyzeUrl
@@ -13,6 +13,12 @@ function headersToObject(headers) {
13
13
  });
14
14
  return obj;
15
15
  }
16
+ function looksLikeHtmlFallback(body, contentType) {
17
+ if (contentType && /text\/html/i.test(contentType)) return true;
18
+ if (typeof body !== "string") return false;
19
+ const head = body.trimStart().slice(0, 200).toLowerCase();
20
+ return head.startsWith("<!doctype html") || head.startsWith("<html");
21
+ }
16
22
  async function httpGet(url, config = {}) {
17
23
  const {
18
24
  headers = {},
@@ -745,6 +751,7 @@ function generateRecommendations(issues) {
745
751
  }
746
752
 
747
753
  export {
754
+ looksLikeHtmlFallback,
748
755
  httpGet,
749
756
  httpHead,
750
757
  httpPost,
package/dist/index.d.mts CHANGED
@@ -2846,6 +2846,7 @@ declare function generateGitHubActionSetup(config: WorkflowConfig): {
2846
2846
  secretsDoc: string;
2847
2847
  readmeBadge: string;
2848
2848
  };
2849
+
2849
2850
  /**
2850
2851
  * Write GitHub Action files to project
2851
2852
  */
@@ -2982,6 +2983,46 @@ declare function generateEleventySEO(options: MetaFixOptions): GeneratedCode;
2982
2983
  declare function generateRemixSEO(options: MetaFixOptions): GeneratedCode;
2983
2984
  declare function getFrameworkSpecificFixExtended(framework: FrameworkInfo$1, options: MetaFixOptions): GeneratedCode;
2984
2985
 
2986
+ interface DeterministicFixInput {
2987
+ siteUrl: string;
2988
+ siteName: string;
2989
+ /** Monorepo app root, '' at the repository root. Every emitted path starts with it. */
2990
+ appRoot: string;
2991
+ /** Issue codes open on the audit; a fix is only offered for something actually reported. */
2992
+ issueCodes: Set<string> | string[];
2993
+ /** Current file contents, null when the file does not exist. */
2994
+ existing: {
2995
+ indexHtml?: string | null;
2996
+ robotsTxt?: string | null;
2997
+ sitemapXml?: string | null;
2998
+ llmsTxt?: string | null;
2999
+ };
3000
+ /** URLs the audit saw, for the sitemap. The site URL alone is a valid sitemap. */
3001
+ pages?: string[];
3002
+ /** Meta description already on the page, if any. Never invented when absent. */
3003
+ description?: string | null;
3004
+ }
3005
+ interface DeterministicFile {
3006
+ path: string;
3007
+ content: string;
3008
+ message: string;
3009
+ /** 'created' for a new file, 'edited' for an insertion into an existing one. */
3010
+ action: 'created' | 'edited';
3011
+ }
3012
+ interface DeterministicFixes {
3013
+ files: DeterministicFile[];
3014
+ /** Issue codes these files address, for the PR body and issue bookkeeping. */
3015
+ addressed: string[];
3016
+ summary: string;
3017
+ }
3018
+ declare function buildDeterministicFixes(input: DeterministicFixInput): DeterministicFixes;
3019
+
3020
+ /**
3021
+ * @param files every path in the repository, as returned by the git tree API
3022
+ * @returns the app root with a trailing slash, or '' for the repository root
3023
+ */
3024
+ declare function findAppRoot(files: string[]): string;
3025
+
2985
3026
  /**
2986
3027
  * World-Class JSON-LD Schema Generators
2987
3028
  *
@@ -5542,4 +5583,4 @@ declare namespace index {
5542
5583
  export { type index_Framework as Framework, type index_FrameworkCheck as FrameworkCheck, type index_FrameworkInfo as FrameworkInfo, type index_FrameworkRecipe as FrameworkRecipe, index_SUPPORTED_FRAMEWORKS as SUPPORTED_FRAMEWORKS, index_detectFramework as detectFramework, index_detectFromHeaders as detectFromHeaders, index_detectFromHtml as detectFromHtml, index_detectFromPackageJson as detectFromPackageJson, index_embedRecipe as embedRecipe, index_getFrameworkChecks as getFrameworkChecks, index_getFrameworkDisplayName as getFrameworkDisplayName, index_getFrameworkIssues as getFrameworkIssues, index_getFrameworkSuggestions as getFrameworkSuggestions, index_getFrameworkSummary as getFrameworkSummary, index_getMigrationPaths as getMigrationPaths, index_getPerformanceTips as getPerformanceTips, index_getSeoChallenges as getSeoChallenges, index_getTotalFrameworkChecks as getTotalFrameworkChecks, index_loadRecipe as loadRecipe };
5543
5584
  }
5544
5585
 
5545
- export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, detectDuplicates, detectFramework$1 as detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
5586
+ export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DeterministicFixInput, type DeterministicFixes, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, buildDeterministicFixes, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, detectDuplicates, detectFramework$1 as detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findAppRoot, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
package/dist/index.d.ts CHANGED
@@ -2846,6 +2846,7 @@ declare function generateGitHubActionSetup(config: WorkflowConfig): {
2846
2846
  secretsDoc: string;
2847
2847
  readmeBadge: string;
2848
2848
  };
2849
+
2849
2850
  /**
2850
2851
  * Write GitHub Action files to project
2851
2852
  */
@@ -2982,6 +2983,46 @@ declare function generateEleventySEO(options: MetaFixOptions): GeneratedCode;
2982
2983
  declare function generateRemixSEO(options: MetaFixOptions): GeneratedCode;
2983
2984
  declare function getFrameworkSpecificFixExtended(framework: FrameworkInfo$1, options: MetaFixOptions): GeneratedCode;
2984
2985
 
2986
+ interface DeterministicFixInput {
2987
+ siteUrl: string;
2988
+ siteName: string;
2989
+ /** Monorepo app root, '' at the repository root. Every emitted path starts with it. */
2990
+ appRoot: string;
2991
+ /** Issue codes open on the audit; a fix is only offered for something actually reported. */
2992
+ issueCodes: Set<string> | string[];
2993
+ /** Current file contents, null when the file does not exist. */
2994
+ existing: {
2995
+ indexHtml?: string | null;
2996
+ robotsTxt?: string | null;
2997
+ sitemapXml?: string | null;
2998
+ llmsTxt?: string | null;
2999
+ };
3000
+ /** URLs the audit saw, for the sitemap. The site URL alone is a valid sitemap. */
3001
+ pages?: string[];
3002
+ /** Meta description already on the page, if any. Never invented when absent. */
3003
+ description?: string | null;
3004
+ }
3005
+ interface DeterministicFile {
3006
+ path: string;
3007
+ content: string;
3008
+ message: string;
3009
+ /** 'created' for a new file, 'edited' for an insertion into an existing one. */
3010
+ action: 'created' | 'edited';
3011
+ }
3012
+ interface DeterministicFixes {
3013
+ files: DeterministicFile[];
3014
+ /** Issue codes these files address, for the PR body and issue bookkeeping. */
3015
+ addressed: string[];
3016
+ summary: string;
3017
+ }
3018
+ declare function buildDeterministicFixes(input: DeterministicFixInput): DeterministicFixes;
3019
+
3020
+ /**
3021
+ * @param files every path in the repository, as returned by the git tree API
3022
+ * @returns the app root with a trailing slash, or '' for the repository root
3023
+ */
3024
+ declare function findAppRoot(files: string[]): string;
3025
+
2985
3026
  /**
2986
3027
  * World-Class JSON-LD Schema Generators
2987
3028
  *
@@ -5542,4 +5583,4 @@ declare namespace index {
5542
5583
  export { type index_Framework as Framework, type index_FrameworkCheck as FrameworkCheck, type index_FrameworkInfo as FrameworkInfo, type index_FrameworkRecipe as FrameworkRecipe, index_SUPPORTED_FRAMEWORKS as SUPPORTED_FRAMEWORKS, index_detectFramework as detectFramework, index_detectFromHeaders as detectFromHeaders, index_detectFromHtml as detectFromHtml, index_detectFromPackageJson as detectFromPackageJson, index_embedRecipe as embedRecipe, index_getFrameworkChecks as getFrameworkChecks, index_getFrameworkDisplayName as getFrameworkDisplayName, index_getFrameworkIssues as getFrameworkIssues, index_getFrameworkSuggestions as getFrameworkSuggestions, index_getFrameworkSummary as getFrameworkSummary, index_getMigrationPaths as getMigrationPaths, index_getPerformanceTips as getPerformanceTips, index_getSeoChallenges as getSeoChallenges, index_getTotalFrameworkChecks as getTotalFrameworkChecks, index_loadRecipe as loadRecipe };
5543
5584
  }
5544
5585
 
5545
- export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, detectDuplicates, detectFramework$1 as detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
5586
+ export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DeterministicFixInput, type DeterministicFixes, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, buildDeterministicFixes, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, detectDuplicates, detectFramework$1 as detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findAppRoot, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
package/dist/index.js CHANGED
@@ -43,6 +43,12 @@ function headersToObject(headers) {
43
43
  });
44
44
  return obj;
45
45
  }
46
+ function looksLikeHtmlFallback(body, contentType) {
47
+ if (contentType && /text\/html/i.test(contentType)) return true;
48
+ if (typeof body !== "string") return false;
49
+ const head = body.trimStart().slice(0, 200).toLowerCase();
50
+ return head.startsWith("<!doctype html") || head.startsWith("<html");
51
+ }
46
52
  async function httpGet(url, config = {}) {
47
53
  const {
48
54
  headers = {},
@@ -535,8 +541,8 @@ async function analyzeUrl(params) {
535
541
  }
536
542
  const sitemapResult = await checkSitemap2({ url });
537
543
  if (sitemapResult.success) {
538
- const sitemap = sitemapResult.data;
539
- if (!sitemap.exists) {
544
+ const sitemap2 = sitemapResult.data;
545
+ if (!sitemap2.exists) {
540
546
  issues.push({
541
547
  severity: "warning",
542
548
  category: "technical",
@@ -3346,6 +3352,7 @@ __export(index_exports, {
3346
3352
  analyzeUrlSafety: () => analyzeUrlSafety,
3347
3353
  analyzers: () => analyzers_exports,
3348
3354
  applyFixes: () => applyFixes,
3355
+ buildDeterministicFixes: () => buildDeterministicFixes,
3349
3356
  buildGSCApiRequest: () => buildGSCApiRequest,
3350
3357
  buildGSCRequest: () => buildGSCRequest,
3351
3358
  calculateAIVisibilityScore: () => calculateAIVisibilityScore,
@@ -3415,6 +3422,7 @@ __export(index_exports, {
3415
3422
  extractTopics: () => extractTopics,
3416
3423
  fetchCoreWebVitals: () => fetchCoreWebVitals,
3417
3424
  findAlmostPage1Keywords: () => findAlmostPage1Keywords,
3425
+ findAppRoot: () => findAppRoot,
3418
3426
  findCTROpportunities: () => findCTROpportunities,
3419
3427
  findHtmlEntry: () => findHtmlEntry,
3420
3428
  findLSIKeywords: () => findLSIKeywords,
@@ -7779,28 +7787,28 @@ var HTML_SIGNATURES = [
7779
7787
  {
7780
7788
  framework: "remix",
7781
7789
  language: "typescript",
7782
- patterns: [/__remix/, /remix-/, /data-remix/],
7790
+ patterns: [/__remix/, /data-remix/],
7783
7791
  confidence: "high"
7784
7792
  },
7785
7793
  // Solid.js
7786
7794
  {
7787
7795
  framework: "solidjs",
7788
7796
  language: "typescript",
7789
- patterns: [/solid-/, /data-solid/, /_solid/],
7797
+ patterns: [/data-solid/, /_solid/],
7790
7798
  confidence: "high"
7791
7799
  },
7792
7800
  // Qwik
7793
7801
  {
7794
7802
  framework: "qwik",
7795
7803
  language: "typescript",
7796
- patterns: [/q:/, /qwik/, /q-/],
7804
+ patterns: [/q:container/, /q:base/, /q:version/, /q:id=/, /qwik/],
7797
7805
  confidence: "high"
7798
7806
  },
7799
7807
  // Fresh (Deno)
7800
7808
  {
7801
7809
  framework: "fresh",
7802
7810
  language: "typescript",
7803
- patterns: [/fresh-/, /_frsh\//, /data-fresh/],
7811
+ patterns: [/_frsh\//, /data-fresh/],
7804
7812
  confidence: "high"
7805
7813
  },
7806
7814
  // React (generic - lower priority)
@@ -7839,7 +7847,7 @@ var HTML_SIGNATURES = [
7839
7847
  {
7840
7848
  framework: "hotwire",
7841
7849
  language: "ruby",
7842
- patterns: [/turbo-frame/, /turbo-stream/, /data-turbo/, /stimulus/],
7850
+ patterns: [/turbo-frame/, /turbo-stream/, /data-turbo/],
7843
7851
  confidence: "high"
7844
7852
  },
7845
7853
  // ============ PHP Frameworks ============
@@ -7906,7 +7914,10 @@ var HTML_SIGNATURES = [
7906
7914
  {
7907
7915
  framework: "spring",
7908
7916
  language: "java",
7909
- patterns: [/spring/, /thymeleaf/, /th:/, /_csrf/],
7917
+ // /th:/ matched "width:" and "height:" in inline CSS, which made almost every styled
7918
+ // page on the web a Spring Boot app - rankcli.dev included, once WordPress stopped
7919
+ // matching first. Thymeleaf attributes are what th: actually means.
7920
+ patterns: [/\sth:[a-z-]+=/, /thymeleaf/, /_csrf/],
7910
7921
  confidence: "medium"
7911
7922
  },
7912
7923
  // ============ C# Frameworks ============
@@ -7914,7 +7925,7 @@ var HTML_SIGNATURES = [
7914
7925
  {
7915
7926
  framework: "aspnet",
7916
7927
  language: "csharp",
7917
- patterns: [/aspnetcore/, /__RequestVerificationToken/, /blazor/, /_framework\/blazor/],
7928
+ patterns: [/aspnetcore/, /__RequestVerificationToken/, /_framework\/blazor/],
7918
7929
  confidence: "medium"
7919
7930
  },
7920
7931
  // ============ Elixir Frameworks ============
@@ -7922,7 +7933,7 @@ var HTML_SIGNATURES = [
7922
7933
  {
7923
7934
  framework: "phoenix",
7924
7935
  language: "elixir",
7925
- patterns: [/phx-/, /phoenix/, /live-socket/, /data-phx/],
7936
+ patterns: [/phx-/, /live-socket/, /data-phx/],
7926
7937
  confidence: "high"
7927
7938
  },
7928
7939
  // ============ Static Site Generators ============
@@ -7930,14 +7941,14 @@ var HTML_SIGNATURES = [
7930
7941
  {
7931
7942
  framework: "hugo",
7932
7943
  language: "go",
7933
- patterns: [/hugo/, /generator.*hugo/i],
7944
+ patterns: [/name=["']generator["'][^>]*content=["']Hugo/i],
7934
7945
  confidence: "medium"
7935
7946
  },
7936
7947
  // Jekyll
7937
7948
  {
7938
7949
  framework: "jekyll",
7939
7950
  language: "ruby",
7940
- patterns: [/jekyll/, /generator.*jekyll/i],
7951
+ patterns: [/name=["']generator["'][^>]*content=["']Jekyll/i],
7941
7952
  confidence: "medium"
7942
7953
  },
7943
7954
  // Eleventy
@@ -8403,6 +8414,17 @@ async function checkRobotsTxt(baseUrl) {
8403
8414
  };
8404
8415
  }
8405
8416
  const content = response.data;
8417
+ if (looksLikeHtmlFallback(content, response.headers?.["content-type"])) {
8418
+ issues.push({
8419
+ ...ISSUE_DEFINITIONS.ROBOTS_TXT_MISSING,
8420
+ affectedUrls: [url],
8421
+ details: { servedHtml: true }
8422
+ });
8423
+ return {
8424
+ issues,
8425
+ data: { exists: false, blocksAll: false, hasSitemap: false, sitemapUrls: [] }
8426
+ };
8427
+ }
8406
8428
  const lines = content.toLowerCase().split("\n");
8407
8429
  let blocksAll = false;
8408
8430
  let currentUserAgent = "";
@@ -10544,6 +10566,13 @@ async function checkLlmsTxt(baseUrl) {
10544
10566
  };
10545
10567
  }
10546
10568
  const content = response.data;
10569
+ if (looksLikeHtmlFallback(content, response.headers?.["content-type"])) {
10570
+ issues.push({
10571
+ ...ISSUE_DEFINITIONS.LLMS_TXT_MISSING,
10572
+ affectedUrls: [url]
10573
+ });
10574
+ return { issues, data: { exists: false, valid: false, errors: ["File not found (the site served HTML)"] } };
10575
+ }
10547
10576
  const lines = content.split("\n").filter((line) => line.trim());
10548
10577
  const errors = [];
10549
10578
  let valid = true;
@@ -25333,7 +25362,7 @@ This will open a browser for OAuth consent and save tokens locally.
25333
25362
  `;
25334
25363
  }
25335
25364
 
25336
- // src/tracking/github-action.ts
25365
+ // src/tracking/github-action-template.ts
25337
25366
  function generateWorkflow(config) {
25338
25367
  const cronSchedule = getCronSchedule(config.schedule);
25339
25368
  const sync = !!config.syncToDashboard;
@@ -25564,6 +25593,8 @@ function generateGitHubActionSetup(config) {
25564
25593
  readmeBadge: `[![SEO Status](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/seo.yml/badge.svg)](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/seo.yml)`
25565
25594
  };
25566
25595
  }
25596
+
25597
+ // src/tracking/github-action.ts
25567
25598
  function writeGitHubActionFiles(projectPath, config) {
25568
25599
  const fs3 = require("fs");
25569
25600
  const path3 = require("path");
@@ -32094,7 +32125,7 @@ Sitemap: ${url}/sitemap.xml`;
32094
32125
  };
32095
32126
  }
32096
32127
  function generateSitemapFix(context, url) {
32097
- const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
32128
+ const sitemap2 = `<?xml version="1.0" encoding="UTF-8"?>
32098
32129
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
32099
32130
  <url>
32100
32131
  <loc>${url}</loc>
@@ -32106,7 +32137,7 @@ function generateSitemapFix(context, url) {
32106
32137
  issue: { code: "MISSING_SITEMAP", message: "No sitemap.xml found", severity: "warning" },
32107
32138
  file: "public/sitemap.xml",
32108
32139
  before: null,
32109
- after: sitemap,
32140
+ after: sitemap2,
32110
32141
  explanation: "Created sitemap.xml to help search engines discover all pages"
32111
32142
  };
32112
32143
  }
@@ -32203,7 +32234,7 @@ function generateLangFix(context) {
32203
32234
  };
32204
32235
  }
32205
32236
  function generateLlmsTxtFix(context, siteName, url) {
32206
- const llmsTxt = `# ${siteName}
32237
+ const llmsTxt2 = `# ${siteName}
32207
32238
  > ${siteName} - A brief description of your product/service
32208
32239
 
32209
32240
  ## About
@@ -32226,7 +32257,7 @@ ${siteName} is... [Add your description here]
32226
32257
  issue: { code: "AI_NO_LLMS_TXT", message: "No llms.txt file for AI crawlers", severity: "info" },
32227
32258
  file: "public/llms.txt",
32228
32259
  before: null,
32229
- after: llmsTxt,
32260
+ after: llmsTxt2,
32230
32261
  explanation: "Created llms.txt to help AI systems understand your site. Customize the content."
32231
32262
  };
32232
32263
  }
@@ -32276,6 +32307,9 @@ async function applyFixes(fixes, options) {
32276
32307
  return { applied, skipped };
32277
32308
  }
32278
32309
 
32310
+ // src/fixer/deterministic.ts
32311
+ init_geo_analyzer();
32312
+
32279
32313
  // src/fixer/schemas.ts
32280
32314
  function generateOrganizationSchema(data) {
32281
32315
  return {
@@ -32858,6 +32892,155 @@ var Schemas = {
32858
32892
  sitelinksSearchbox: generateSitelinksSearchboxSchema
32859
32893
  };
32860
32894
 
32895
+ // src/fixer/deterministic.ts
32896
+ function xmlEscape(value) {
32897
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
32898
+ }
32899
+ function sitemap(urls) {
32900
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
32901
+ const entries = urls.map(
32902
+ (url) => ` <url>
32903
+ <loc>${xmlEscape(url)}</loc>
32904
+ <lastmod>${today}</lastmod>
32905
+ </url>`
32906
+ ).join("\n");
32907
+ return `<?xml version="1.0" encoding="UTF-8"?>
32908
+ <!-- Generated by RankCLI - https://rankcli.dev -->
32909
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
32910
+ ${entries}
32911
+ </urlset>
32912
+ `;
32913
+ }
32914
+ function llmsTxt(siteName, siteUrl, description, pages) {
32915
+ const base = siteUrl.replace(/\/$/, "");
32916
+ const trim = (url) => url.replace(/\/$/, "");
32917
+ const links = pages.filter((url) => trim(url) !== base).map((url) => ({ url, path: url.replace(base, "").replace(/^$/, "/") })).filter((page) => page.path !== "/").slice(0, 20).map((page) => `- [${page.path}](${page.url})`).join("\n");
32918
+ return `# ${siteName}
32919
+
32920
+ ${description ? `> ${description}
32921
+
32922
+ ` : ""}${base}
32923
+
32924
+ ## Pages
32925
+ - [Home](${base}/)
32926
+ ${links}
32927
+
32928
+ <!-- Generated by RankCLI - https://rankcli.dev -->
32929
+ `;
32930
+ }
32931
+ function jsonLdBlock(siteName, siteUrl, description) {
32932
+ const base = siteUrl.replace(/\/$/, "");
32933
+ const graph = [
32934
+ generateWebSiteSchema({ name: siteName, url: base, ...description ? { description } : {} }),
32935
+ generateOrganizationSchema({ name: siteName, url: base, ...description ? { description } : {} })
32936
+ ];
32937
+ return ` <script type="application/ld+json">
32938
+ ${JSON.stringify(graph, null, 2).split("\n").map((line) => ` ${line}`).join("\n")}
32939
+ </script>
32940
+ `;
32941
+ }
32942
+ var SCHEMA_CODES = [
32943
+ "SCHEMA_MISSING",
32944
+ "SCHEMA_ORG_MISSING",
32945
+ "ORGANIZATION_SCHEMA_MISSING",
32946
+ "NO_ORGANIZATION_SCHEMA",
32947
+ "NO_ENTITY_SCHEMA",
32948
+ "STRUCTURED_DATA_MISSING"
32949
+ ];
32950
+ function metaDescriptionOf(html) {
32951
+ if (!html) return null;
32952
+ const match = html.match(/<meta[^>]+name=["']description["'][^>]+content=["']([^"']+)["']/i) || html.match(/<meta[^>]+content=["']([^"']+)["'][^>]+name=["']description["']/i);
32953
+ return match?.[1]?.trim() || null;
32954
+ }
32955
+ function buildDeterministicFixes(input) {
32956
+ const codes = new Set(input.issueCodes);
32957
+ const has = (...wanted) => wanted.some((code) => codes.has(code));
32958
+ const root = input.appRoot || "";
32959
+ const files = [];
32960
+ const addressed = [];
32961
+ const base = input.siteUrl.replace(/\/$/, "");
32962
+ const pages = input.pages?.length ? input.pages : [`${base}/`];
32963
+ const description = input.description ?? metaDescriptionOf(input.existing.indexHtml);
32964
+ if (has("ROBOTS_TXT_MISSING", "AI_BOTS_BLOCKED", "AI_CRAWLERS_BLOCKED") && !input.existing.robotsTxt) {
32965
+ files.push({
32966
+ path: `${root}public/robots.txt`,
32967
+ content: generateAIFriendlyRobotsTxt(base),
32968
+ message: "fix(seo): add robots.txt allowing search and AI crawlers",
32969
+ action: "created"
32970
+ });
32971
+ addressed.push("ROBOTS_TXT_MISSING");
32972
+ }
32973
+ if (has("SITEMAP_MISSING") && !input.existing.sitemapXml) {
32974
+ files.push({
32975
+ path: `${root}public/sitemap.xml`,
32976
+ content: sitemap(pages),
32977
+ message: "fix(seo): add sitemap.xml",
32978
+ action: "created"
32979
+ });
32980
+ addressed.push("SITEMAP_MISSING");
32981
+ }
32982
+ if (has("LLMS_TXT_MISSING") && !input.existing.llmsTxt) {
32983
+ files.push({
32984
+ path: `${root}public/llms.txt`,
32985
+ content: llmsTxt(input.siteName, base, description, pages),
32986
+ message: "fix(seo): add llms.txt for AI assistants",
32987
+ action: "created"
32988
+ });
32989
+ addressed.push("LLMS_TXT_MISSING");
32990
+ }
32991
+ const indexHtml = input.existing.indexHtml;
32992
+ if (has(...SCHEMA_CODES) && indexHtml && !indexHtml.includes("application/ld+json") && indexHtml.includes("</head>")) {
32993
+ files.push({
32994
+ path: `${root}index.html`,
32995
+ content: indexHtml.replace("</head>", `${jsonLdBlock(input.siteName, base, description)} </head>`),
32996
+ message: "fix(seo): add WebSite and Organization JSON-LD",
32997
+ action: "edited"
32998
+ });
32999
+ addressed.push(...SCHEMA_CODES.filter((code) => codes.has(code)));
33000
+ }
33001
+ const name = (file) => file.path.split("/").pop();
33002
+ const created = files.filter((f) => f.action === "created").map(name);
33003
+ const edited = files.filter((f) => f.action === "edited").map(name);
33004
+ const parts = [
33005
+ created.length ? `Added ${created.join(", ")}` : null,
33006
+ edited.length ? `updated ${edited.join(", ")}` : null
33007
+ ].filter(Boolean);
33008
+ const summary = files.length ? `${parts.join("; ")} from RankCLI's templates - generated from the site's URL and name, with no AI and nothing invented.` : "No template-generatable issues were open on this report.";
33009
+ return { files, addressed, summary };
33010
+ }
33011
+
33012
+ // src/fixer/app-root.ts
33013
+ var PREFERRED_NAMES = ["web", "app", "site", "www", "frontend", "client", "ui", "saas", "marketing", "docs"];
33014
+ function directoryOf(path3) {
33015
+ const cut = path3.lastIndexOf("/");
33016
+ return cut === -1 ? "" : path3.slice(0, cut);
33017
+ }
33018
+ function findAppRoot(files) {
33019
+ const real = files.filter((f) => !f.includes("node_modules/") && !f.startsWith("."));
33020
+ const packageDirs = new Set(
33021
+ real.filter((f) => f === "package.json" || f.endsWith("/package.json")).map(directoryOf)
33022
+ );
33023
+ const candidates = real.filter((f) => f === "index.html" || f.endsWith("/index.html")).map(directoryOf).map((dir) => dir.endsWith("/public") ? dir.slice(0, -"/public".length) : dir === "public" ? "" : dir).filter((dir) => packageDirs.has(dir));
33024
+ if (candidates.length > 0) {
33025
+ const depth = (dir) => dir === "" ? 0 : dir.split("/").length;
33026
+ candidates.sort((a, b) => {
33027
+ if (depth(a) !== depth(b)) return depth(a) - depth(b);
33028
+ const rank = (dir) => {
33029
+ const name = dir.split("/").pop() ?? "";
33030
+ const index = PREFERRED_NAMES.indexOf(name);
33031
+ return index === -1 ? PREFERRED_NAMES.length : index;
33032
+ };
33033
+ return rank(a) - rank(b) || a.localeCompare(b);
33034
+ });
33035
+ const best = candidates[0];
33036
+ return best === "" ? "" : `${best}/`;
33037
+ }
33038
+ const nonRoot = [...packageDirs].filter((dir) => dir !== "");
33039
+ if (packageDirs.has("") || nonRoot.length === 0) return "";
33040
+ nonRoot.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
33041
+ return `${nonRoot[0]}/`;
33042
+ }
33043
+
32861
33044
  // src/git/commit-helper.ts
32862
33045
  var import_child_process2 = require("child_process");
32863
33046
  var import_util2 = require("util");
@@ -35465,6 +35648,7 @@ var SUPPORTED_FRAMEWORKS = [
35465
35648
  analyzeUrlSafety,
35466
35649
  analyzers,
35467
35650
  applyFixes,
35651
+ buildDeterministicFixes,
35468
35652
  buildGSCApiRequest,
35469
35653
  buildGSCRequest,
35470
35654
  calculateAIVisibilityScore,
@@ -35534,6 +35718,7 @@ var SUPPORTED_FRAMEWORKS = [
35534
35718
  extractTopics,
35535
35719
  fetchCoreWebVitals,
35536
35720
  findAlmostPage1Keywords,
35721
+ findAppRoot,
35537
35722
  findCTROpportunities,
35538
35723
  findHtmlEntry,
35539
35724
  findLSIKeywords,
package/dist/index.mjs CHANGED
@@ -13,8 +13,9 @@ import {
13
13
  extractSchema,
14
14
  httpGet,
15
15
  httpHead,
16
- httpPost
17
- } from "./chunk-NATXE6B3.mjs";
16
+ httpPost,
17
+ looksLikeHtmlFallback
18
+ } from "./chunk-O7B2LBKG.mjs";
18
19
  import {
19
20
  AI_CRAWLERS_INFO,
20
21
  analyzeCitationReadiness,
@@ -4258,28 +4259,28 @@ var HTML_SIGNATURES = [
4258
4259
  {
4259
4260
  framework: "remix",
4260
4261
  language: "typescript",
4261
- patterns: [/__remix/, /remix-/, /data-remix/],
4262
+ patterns: [/__remix/, /data-remix/],
4262
4263
  confidence: "high"
4263
4264
  },
4264
4265
  // Solid.js
4265
4266
  {
4266
4267
  framework: "solidjs",
4267
4268
  language: "typescript",
4268
- patterns: [/solid-/, /data-solid/, /_solid/],
4269
+ patterns: [/data-solid/, /_solid/],
4269
4270
  confidence: "high"
4270
4271
  },
4271
4272
  // Qwik
4272
4273
  {
4273
4274
  framework: "qwik",
4274
4275
  language: "typescript",
4275
- patterns: [/q:/, /qwik/, /q-/],
4276
+ patterns: [/q:container/, /q:base/, /q:version/, /q:id=/, /qwik/],
4276
4277
  confidence: "high"
4277
4278
  },
4278
4279
  // Fresh (Deno)
4279
4280
  {
4280
4281
  framework: "fresh",
4281
4282
  language: "typescript",
4282
- patterns: [/fresh-/, /_frsh\//, /data-fresh/],
4283
+ patterns: [/_frsh\//, /data-fresh/],
4283
4284
  confidence: "high"
4284
4285
  },
4285
4286
  // React (generic - lower priority)
@@ -4318,7 +4319,7 @@ var HTML_SIGNATURES = [
4318
4319
  {
4319
4320
  framework: "hotwire",
4320
4321
  language: "ruby",
4321
- patterns: [/turbo-frame/, /turbo-stream/, /data-turbo/, /stimulus/],
4322
+ patterns: [/turbo-frame/, /turbo-stream/, /data-turbo/],
4322
4323
  confidence: "high"
4323
4324
  },
4324
4325
  // ============ PHP Frameworks ============
@@ -4385,7 +4386,10 @@ var HTML_SIGNATURES = [
4385
4386
  {
4386
4387
  framework: "spring",
4387
4388
  language: "java",
4388
- patterns: [/spring/, /thymeleaf/, /th:/, /_csrf/],
4389
+ // /th:/ matched "width:" and "height:" in inline CSS, which made almost every styled
4390
+ // page on the web a Spring Boot app - rankcli.dev included, once WordPress stopped
4391
+ // matching first. Thymeleaf attributes are what th: actually means.
4392
+ patterns: [/\sth:[a-z-]+=/, /thymeleaf/, /_csrf/],
4389
4393
  confidence: "medium"
4390
4394
  },
4391
4395
  // ============ C# Frameworks ============
@@ -4393,7 +4397,7 @@ var HTML_SIGNATURES = [
4393
4397
  {
4394
4398
  framework: "aspnet",
4395
4399
  language: "csharp",
4396
- patterns: [/aspnetcore/, /__RequestVerificationToken/, /blazor/, /_framework\/blazor/],
4400
+ patterns: [/aspnetcore/, /__RequestVerificationToken/, /_framework\/blazor/],
4397
4401
  confidence: "medium"
4398
4402
  },
4399
4403
  // ============ Elixir Frameworks ============
@@ -4401,7 +4405,7 @@ var HTML_SIGNATURES = [
4401
4405
  {
4402
4406
  framework: "phoenix",
4403
4407
  language: "elixir",
4404
- patterns: [/phx-/, /phoenix/, /live-socket/, /data-phx/],
4408
+ patterns: [/phx-/, /live-socket/, /data-phx/],
4405
4409
  confidence: "high"
4406
4410
  },
4407
4411
  // ============ Static Site Generators ============
@@ -4409,14 +4413,14 @@ var HTML_SIGNATURES = [
4409
4413
  {
4410
4414
  framework: "hugo",
4411
4415
  language: "go",
4412
- patterns: [/hugo/, /generator.*hugo/i],
4416
+ patterns: [/name=["']generator["'][^>]*content=["']Hugo/i],
4413
4417
  confidence: "medium"
4414
4418
  },
4415
4419
  // Jekyll
4416
4420
  {
4417
4421
  framework: "jekyll",
4418
4422
  language: "ruby",
4419
- patterns: [/jekyll/, /generator.*jekyll/i],
4423
+ patterns: [/name=["']generator["'][^>]*content=["']Jekyll/i],
4420
4424
  confidence: "medium"
4421
4425
  },
4422
4426
  // Eleventy
@@ -4881,6 +4885,17 @@ async function checkRobotsTxt(baseUrl) {
4881
4885
  };
4882
4886
  }
4883
4887
  const content = response.data;
4888
+ if (looksLikeHtmlFallback(content, response.headers?.["content-type"])) {
4889
+ issues.push({
4890
+ ...ISSUE_DEFINITIONS.ROBOTS_TXT_MISSING,
4891
+ affectedUrls: [url],
4892
+ details: { servedHtml: true }
4893
+ });
4894
+ return {
4895
+ issues,
4896
+ data: { exists: false, blocksAll: false, hasSitemap: false, sitemapUrls: [] }
4897
+ };
4898
+ }
4884
4899
  const lines = content.toLowerCase().split("\n");
4885
4900
  let blocksAll = false;
4886
4901
  let currentUserAgent = "";
@@ -7014,6 +7029,13 @@ async function checkLlmsTxt(baseUrl) {
7014
7029
  };
7015
7030
  }
7016
7031
  const content = response.data;
7032
+ if (looksLikeHtmlFallback(content, response.headers?.["content-type"])) {
7033
+ issues.push({
7034
+ ...ISSUE_DEFINITIONS.LLMS_TXT_MISSING,
7035
+ affectedUrls: [url]
7036
+ });
7037
+ return { issues, data: { exists: false, valid: false, errors: ["File not found (the site served HTML)"] } };
7038
+ }
7017
7039
  const lines = content.split("\n").filter((line) => line.trim());
7018
7040
  const errors = [];
7019
7041
  let valid = true;
@@ -21781,7 +21803,7 @@ This will open a browser for OAuth consent and save tokens locally.
21781
21803
  `;
21782
21804
  }
21783
21805
 
21784
- // src/tracking/github-action.ts
21806
+ // src/tracking/github-action-template.ts
21785
21807
  function generateWorkflow(config) {
21786
21808
  const cronSchedule = getCronSchedule(config.schedule);
21787
21809
  const sync = !!config.syncToDashboard;
@@ -22012,6 +22034,8 @@ function generateGitHubActionSetup(config) {
22012
22034
  readmeBadge: `[![SEO Status](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/seo.yml/badge.svg)](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/seo.yml)`
22013
22035
  };
22014
22036
  }
22037
+
22038
+ // src/tracking/github-action.ts
22015
22039
  function writeGitHubActionFiles(projectPath, config) {
22016
22040
  const fs3 = __require("fs");
22017
22041
  const path3 = __require("path");
@@ -23068,7 +23092,7 @@ async function executeAgent(agent, options = {}) {
23068
23092
  }
23069
23093
  }
23070
23094
  async function runDirectAnalysis(url) {
23071
- const { analyzeUrl: analyzeUrl2 } = await import("./analyzer-MCBRQHB7.mjs");
23095
+ const { analyzeUrl: analyzeUrl2 } = await import("./analyzer-BYAFIO33.mjs");
23072
23096
  const result = await analyzeUrl2({ url });
23073
23097
  if (!result.success || !result.data) {
23074
23098
  throw new Error(result.error || "Analysis failed");
@@ -28536,7 +28560,7 @@ Sitemap: ${url}/sitemap.xml`;
28536
28560
  };
28537
28561
  }
28538
28562
  function generateSitemapFix(context, url) {
28539
- const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
28563
+ const sitemap2 = `<?xml version="1.0" encoding="UTF-8"?>
28540
28564
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
28541
28565
  <url>
28542
28566
  <loc>${url}</loc>
@@ -28548,7 +28572,7 @@ function generateSitemapFix(context, url) {
28548
28572
  issue: { code: "MISSING_SITEMAP", message: "No sitemap.xml found", severity: "warning" },
28549
28573
  file: "public/sitemap.xml",
28550
28574
  before: null,
28551
- after: sitemap,
28575
+ after: sitemap2,
28552
28576
  explanation: "Created sitemap.xml to help search engines discover all pages"
28553
28577
  };
28554
28578
  }
@@ -28645,7 +28669,7 @@ function generateLangFix(context) {
28645
28669
  };
28646
28670
  }
28647
28671
  function generateLlmsTxtFix(context, siteName, url) {
28648
- const llmsTxt = `# ${siteName}
28672
+ const llmsTxt2 = `# ${siteName}
28649
28673
  > ${siteName} - A brief description of your product/service
28650
28674
 
28651
28675
  ## About
@@ -28668,7 +28692,7 @@ ${siteName} is... [Add your description here]
28668
28692
  issue: { code: "AI_NO_LLMS_TXT", message: "No llms.txt file for AI crawlers", severity: "info" },
28669
28693
  file: "public/llms.txt",
28670
28694
  before: null,
28671
- after: llmsTxt,
28695
+ after: llmsTxt2,
28672
28696
  explanation: "Created llms.txt to help AI systems understand your site. Customize the content."
28673
28697
  };
28674
28698
  }
@@ -29300,6 +29324,155 @@ var Schemas = {
29300
29324
  sitelinksSearchbox: generateSitelinksSearchboxSchema
29301
29325
  };
29302
29326
 
29327
+ // src/fixer/deterministic.ts
29328
+ function xmlEscape(value) {
29329
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
29330
+ }
29331
+ function sitemap(urls) {
29332
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
29333
+ const entries = urls.map(
29334
+ (url) => ` <url>
29335
+ <loc>${xmlEscape(url)}</loc>
29336
+ <lastmod>${today}</lastmod>
29337
+ </url>`
29338
+ ).join("\n");
29339
+ return `<?xml version="1.0" encoding="UTF-8"?>
29340
+ <!-- Generated by RankCLI - https://rankcli.dev -->
29341
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
29342
+ ${entries}
29343
+ </urlset>
29344
+ `;
29345
+ }
29346
+ function llmsTxt(siteName, siteUrl, description, pages) {
29347
+ const base = siteUrl.replace(/\/$/, "");
29348
+ const trim = (url) => url.replace(/\/$/, "");
29349
+ const links = pages.filter((url) => trim(url) !== base).map((url) => ({ url, path: url.replace(base, "").replace(/^$/, "/") })).filter((page) => page.path !== "/").slice(0, 20).map((page) => `- [${page.path}](${page.url})`).join("\n");
29350
+ return `# ${siteName}
29351
+
29352
+ ${description ? `> ${description}
29353
+
29354
+ ` : ""}${base}
29355
+
29356
+ ## Pages
29357
+ - [Home](${base}/)
29358
+ ${links}
29359
+
29360
+ <!-- Generated by RankCLI - https://rankcli.dev -->
29361
+ `;
29362
+ }
29363
+ function jsonLdBlock(siteName, siteUrl, description) {
29364
+ const base = siteUrl.replace(/\/$/, "");
29365
+ const graph = [
29366
+ generateWebSiteSchema({ name: siteName, url: base, ...description ? { description } : {} }),
29367
+ generateOrganizationSchema({ name: siteName, url: base, ...description ? { description } : {} })
29368
+ ];
29369
+ return ` <script type="application/ld+json">
29370
+ ${JSON.stringify(graph, null, 2).split("\n").map((line) => ` ${line}`).join("\n")}
29371
+ </script>
29372
+ `;
29373
+ }
29374
+ var SCHEMA_CODES = [
29375
+ "SCHEMA_MISSING",
29376
+ "SCHEMA_ORG_MISSING",
29377
+ "ORGANIZATION_SCHEMA_MISSING",
29378
+ "NO_ORGANIZATION_SCHEMA",
29379
+ "NO_ENTITY_SCHEMA",
29380
+ "STRUCTURED_DATA_MISSING"
29381
+ ];
29382
+ function metaDescriptionOf(html) {
29383
+ if (!html) return null;
29384
+ const match = html.match(/<meta[^>]+name=["']description["'][^>]+content=["']([^"']+)["']/i) || html.match(/<meta[^>]+content=["']([^"']+)["'][^>]+name=["']description["']/i);
29385
+ return match?.[1]?.trim() || null;
29386
+ }
29387
+ function buildDeterministicFixes(input) {
29388
+ const codes = new Set(input.issueCodes);
29389
+ const has = (...wanted) => wanted.some((code) => codes.has(code));
29390
+ const root = input.appRoot || "";
29391
+ const files = [];
29392
+ const addressed = [];
29393
+ const base = input.siteUrl.replace(/\/$/, "");
29394
+ const pages = input.pages?.length ? input.pages : [`${base}/`];
29395
+ const description = input.description ?? metaDescriptionOf(input.existing.indexHtml);
29396
+ if (has("ROBOTS_TXT_MISSING", "AI_BOTS_BLOCKED", "AI_CRAWLERS_BLOCKED") && !input.existing.robotsTxt) {
29397
+ files.push({
29398
+ path: `${root}public/robots.txt`,
29399
+ content: generateAIFriendlyRobotsTxt(base),
29400
+ message: "fix(seo): add robots.txt allowing search and AI crawlers",
29401
+ action: "created"
29402
+ });
29403
+ addressed.push("ROBOTS_TXT_MISSING");
29404
+ }
29405
+ if (has("SITEMAP_MISSING") && !input.existing.sitemapXml) {
29406
+ files.push({
29407
+ path: `${root}public/sitemap.xml`,
29408
+ content: sitemap(pages),
29409
+ message: "fix(seo): add sitemap.xml",
29410
+ action: "created"
29411
+ });
29412
+ addressed.push("SITEMAP_MISSING");
29413
+ }
29414
+ if (has("LLMS_TXT_MISSING") && !input.existing.llmsTxt) {
29415
+ files.push({
29416
+ path: `${root}public/llms.txt`,
29417
+ content: llmsTxt(input.siteName, base, description, pages),
29418
+ message: "fix(seo): add llms.txt for AI assistants",
29419
+ action: "created"
29420
+ });
29421
+ addressed.push("LLMS_TXT_MISSING");
29422
+ }
29423
+ const indexHtml = input.existing.indexHtml;
29424
+ if (has(...SCHEMA_CODES) && indexHtml && !indexHtml.includes("application/ld+json") && indexHtml.includes("</head>")) {
29425
+ files.push({
29426
+ path: `${root}index.html`,
29427
+ content: indexHtml.replace("</head>", `${jsonLdBlock(input.siteName, base, description)} </head>`),
29428
+ message: "fix(seo): add WebSite and Organization JSON-LD",
29429
+ action: "edited"
29430
+ });
29431
+ addressed.push(...SCHEMA_CODES.filter((code) => codes.has(code)));
29432
+ }
29433
+ const name = (file) => file.path.split("/").pop();
29434
+ const created = files.filter((f) => f.action === "created").map(name);
29435
+ const edited = files.filter((f) => f.action === "edited").map(name);
29436
+ const parts = [
29437
+ created.length ? `Added ${created.join(", ")}` : null,
29438
+ edited.length ? `updated ${edited.join(", ")}` : null
29439
+ ].filter(Boolean);
29440
+ const summary = files.length ? `${parts.join("; ")} from RankCLI's templates - generated from the site's URL and name, with no AI and nothing invented.` : "No template-generatable issues were open on this report.";
29441
+ return { files, addressed, summary };
29442
+ }
29443
+
29444
+ // src/fixer/app-root.ts
29445
+ var PREFERRED_NAMES = ["web", "app", "site", "www", "frontend", "client", "ui", "saas", "marketing", "docs"];
29446
+ function directoryOf(path3) {
29447
+ const cut = path3.lastIndexOf("/");
29448
+ return cut === -1 ? "" : path3.slice(0, cut);
29449
+ }
29450
+ function findAppRoot(files) {
29451
+ const real = files.filter((f) => !f.includes("node_modules/") && !f.startsWith("."));
29452
+ const packageDirs = new Set(
29453
+ real.filter((f) => f === "package.json" || f.endsWith("/package.json")).map(directoryOf)
29454
+ );
29455
+ const candidates = real.filter((f) => f === "index.html" || f.endsWith("/index.html")).map(directoryOf).map((dir) => dir.endsWith("/public") ? dir.slice(0, -"/public".length) : dir === "public" ? "" : dir).filter((dir) => packageDirs.has(dir));
29456
+ if (candidates.length > 0) {
29457
+ const depth = (dir) => dir === "" ? 0 : dir.split("/").length;
29458
+ candidates.sort((a, b) => {
29459
+ if (depth(a) !== depth(b)) return depth(a) - depth(b);
29460
+ const rank = (dir) => {
29461
+ const name = dir.split("/").pop() ?? "";
29462
+ const index = PREFERRED_NAMES.indexOf(name);
29463
+ return index === -1 ? PREFERRED_NAMES.length : index;
29464
+ };
29465
+ return rank(a) - rank(b) || a.localeCompare(b);
29466
+ });
29467
+ const best = candidates[0];
29468
+ return best === "" ? "" : `${best}/`;
29469
+ }
29470
+ const nonRoot = [...packageDirs].filter((dir) => dir !== "");
29471
+ if (packageDirs.has("") || nonRoot.length === 0) return "";
29472
+ nonRoot.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
29473
+ return `${nonRoot[0]}/`;
29474
+ }
29475
+
29303
29476
  // src/git/commit-helper.ts
29304
29477
  import { execSync, exec as exec2 } from "child_process";
29305
29478
  import { promisify as promisify2 } from "util";
@@ -31899,6 +32072,7 @@ export {
31899
32072
  analyzeUrlSafety,
31900
32073
  analyzers_exports as analyzers,
31901
32074
  applyFixes,
32075
+ buildDeterministicFixes,
31902
32076
  buildGSCApiRequest,
31903
32077
  buildGSCRequest,
31904
32078
  calculateAIVisibilityScore,
@@ -31968,6 +32142,7 @@ export {
31968
32142
  extractTopics,
31969
32143
  fetchCoreWebVitals,
31970
32144
  findAlmostPage1Keywords,
32145
+ findAppRoot,
31971
32146
  findCTROpportunities,
31972
32147
  findHtmlEntry,
31973
32148
  findLSIKeywords,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rankcli/agent-runtime",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
4
4
  "description": "RankCLI agent runtime - executes SEO audits and fixes with AI",
5
5
  "homepage": "https://rankcli.dev",
6
6
  "main": "dist/index.js",