@lynxflow/seo-engine 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/above-fold-cro-auditor.d.ts +41 -0
- package/dist/context-templates-generator.d.ts +27 -0
- package/dist/humanity-ai-scrubber.d.ts +29 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +502 -1
- package/dist/index.mjs +502 -1
- package/dist/passive-voice-complexity.d.ts +31 -0
- package/dist/serp-length-comparator.d.ts +34 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ High-Performance Universal Programmatic SEO & Structured Data Engine for Modern
|
|
|
4
4
|
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
[](LICENSE)
|
|
7
|
-
[](https://www.npmjs.com/package/@lynxflow/seo-engine)
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🎯 Above-The-Fold & Landing Page CRO Auditor
|
|
3
|
+
* Evaluates hero sections and landing page conversion architecture (0-100 Score).
|
|
4
|
+
* Analyzes Value Proposition clarity, CTA strength, Trust signals, and Friction points.
|
|
5
|
+
*/
|
|
6
|
+
export interface AboveFoldAuditInput {
|
|
7
|
+
h1: string;
|
|
8
|
+
subheadline?: string;
|
|
9
|
+
primaryCtaText: string;
|
|
10
|
+
primaryCtaUrl?: string;
|
|
11
|
+
hasTestimonialsOrStars?: boolean;
|
|
12
|
+
hasRiskReversalOrGuarantee?: boolean;
|
|
13
|
+
hasCustomerLogos?: boolean;
|
|
14
|
+
pageContentSnippet?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CroCriterionResult {
|
|
17
|
+
name: string;
|
|
18
|
+
passed: boolean;
|
|
19
|
+
score: number;
|
|
20
|
+
feedback: string;
|
|
21
|
+
}
|
|
22
|
+
export interface AboveFoldCroReport {
|
|
23
|
+
croScore: number;
|
|
24
|
+
rating: "high_converting" | "solid" | "high_friction";
|
|
25
|
+
criteria: CroCriterionResult[];
|
|
26
|
+
recommendedFixes: string[];
|
|
27
|
+
suggestedHeroVariant: {
|
|
28
|
+
h1: string;
|
|
29
|
+
subheadline: string;
|
|
30
|
+
ctaText: string;
|
|
31
|
+
reassuranceBadge: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare class AboveFoldCroAuditor {
|
|
35
|
+
private static readonly STRONG_ACTION_VERBS;
|
|
36
|
+
private static readonly WEAK_ACTION_VERBS;
|
|
37
|
+
/**
|
|
38
|
+
* Audits hero section and returns a comprehensive CRO report.
|
|
39
|
+
*/
|
|
40
|
+
static auditHero(input: AboveFoldAuditInput): AboveFoldCroReport;
|
|
41
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 📁 Context Templates Generator for AI Coding Agents
|
|
3
|
+
* Automatically generates production-ready markdown configuration templates
|
|
4
|
+
* (.claude/ and context/ directories) for Claude Code, Cursor, and Antigravity.
|
|
5
|
+
*/
|
|
6
|
+
export interface BrandContextInput {
|
|
7
|
+
brandName: string;
|
|
8
|
+
domain: string;
|
|
9
|
+
industry: string;
|
|
10
|
+
coreFeatures: string[];
|
|
11
|
+
targetAudience: string;
|
|
12
|
+
primaryKeywords: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare class ContextTemplatesGenerator {
|
|
15
|
+
/**
|
|
16
|
+
* Generates a complete bundle of 8 context markdown files tailored to a brand.
|
|
17
|
+
*/
|
|
18
|
+
static generateAllContextFiles(input: BrandContextInput): Record<string, string>;
|
|
19
|
+
static generateBrandVoice(input: BrandContextInput): string;
|
|
20
|
+
static generateFeatures(input: BrandContextInput): string;
|
|
21
|
+
static generateInternalLinksMap(input: BrandContextInput): string;
|
|
22
|
+
static generateStyleGuide(input: BrandContextInput): string;
|
|
23
|
+
static generateTargetKeywords(input: BrandContextInput): string;
|
|
24
|
+
static generateCompetitorAnalysis(input: BrandContextInput): string;
|
|
25
|
+
static generateSeoGuidelines(input: BrandContextInput): string;
|
|
26
|
+
static generateCroBestPractices(input: BrandContextInput): string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🧹 Humanity AI Scrubber & Anti-AI Watermark Engine
|
|
3
|
+
* Detects and removes robotic LLM patterns, clichés, em-dash abuses, and filler phrases.
|
|
4
|
+
* Computes a rigorous Humanity Score (0-100) and produces humanized copy.
|
|
5
|
+
*/
|
|
6
|
+
export interface AiClichéMatch {
|
|
7
|
+
pattern: string;
|
|
8
|
+
count: number;
|
|
9
|
+
category: "filler_word" | "robotic_intro" | "excessive_punctuation" | "overused_transition";
|
|
10
|
+
suggestion: string;
|
|
11
|
+
}
|
|
12
|
+
export interface HumanityScrubReport {
|
|
13
|
+
humanityScore: number;
|
|
14
|
+
aiProbabilityScore: number;
|
|
15
|
+
totalWords: number;
|
|
16
|
+
clichésDetectedCount: number;
|
|
17
|
+
clichés: AiClichéMatch[];
|
|
18
|
+
isHumanSounding: boolean;
|
|
19
|
+
scrubbedText: string;
|
|
20
|
+
improvementRecommendations: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare class HumanityAiScrubber {
|
|
23
|
+
private static readonly BANNED_AI_WORDS;
|
|
24
|
+
private static readonly ROBOTIC_INTROS;
|
|
25
|
+
/**
|
|
26
|
+
* Analyzes text and returns a comprehensive Humanity Score and scrubbed output.
|
|
27
|
+
*/
|
|
28
|
+
static analyzeAndScrub(text: string, language?: string): HumanityScrubReport;
|
|
29
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -114,6 +114,11 @@ import { PageRegenerationTracker } from "./generation-tracker";
|
|
|
114
114
|
import { GoogleIndexingClient } from "./google-indexing-client";
|
|
115
115
|
import { AeoSnippetSynthesizer } from "./aeo-snippet-synthesizer";
|
|
116
116
|
import { SemanticCannibalizationDetector } from "./semantic-cannibalization";
|
|
117
|
+
import { HumanityAiScrubber } from "./humanity-ai-scrubber";
|
|
118
|
+
import { SerpLengthComparator } from "./serp-length-comparator";
|
|
119
|
+
import { PassiveVoiceComplexityAnalyzer } from "./passive-voice-complexity";
|
|
120
|
+
import { AboveFoldCroAuditor } from "./above-fold-cro-auditor";
|
|
121
|
+
import { ContextTemplatesGenerator } from "./context-templates-generator";
|
|
117
122
|
export declare function createLynxSeoEngine(config: EngineConfig): LynxSeoEngine;
|
|
118
123
|
export declare const LynxSeo: {
|
|
119
124
|
createEngine: typeof createLynxSeoEngine;
|
|
@@ -125,6 +130,11 @@ export declare const LynxSeo: {
|
|
|
125
130
|
googleIndexing: typeof GoogleIndexingClient;
|
|
126
131
|
aeoSnippet: typeof AeoSnippetSynthesizer;
|
|
127
132
|
cannibalization: typeof SemanticCannibalizationDetector;
|
|
133
|
+
humanityScrubber: typeof HumanityAiScrubber;
|
|
134
|
+
serpLength: typeof SerpLengthComparator;
|
|
135
|
+
readabilityComplexity: typeof PassiveVoiceComplexityAnalyzer;
|
|
136
|
+
aboveFoldCro: typeof AboveFoldCroAuditor;
|
|
137
|
+
contextTemplates: typeof ContextTemplatesGenerator;
|
|
128
138
|
inspectMeta: typeof SiteAuditor.inspectMeta;
|
|
129
139
|
crawlDomain: typeof DeepCrawlerAuditor.crawlAndAuditDomain;
|
|
130
140
|
inspectHtmlSnapshot: typeof DeepCrawlerAuditor.inspectHtmlSnapshot;
|
|
@@ -166,6 +176,11 @@ export declare const LynxSeo: {
|
|
|
166
176
|
marketingSkills: typeof MarketingSkillsEngine;
|
|
167
177
|
regenerationTracker: typeof PageRegenerationTracker;
|
|
168
178
|
};
|
|
179
|
+
export * from "./humanity-ai-scrubber";
|
|
180
|
+
export * from "./serp-length-comparator";
|
|
181
|
+
export * from "./passive-voice-complexity";
|
|
182
|
+
export * from "./above-fold-cro-auditor";
|
|
183
|
+
export * from "./context-templates-generator";
|
|
169
184
|
export * from "./google-indexing-client";
|
|
170
185
|
export * from "./aeo-snippet-synthesizer";
|
|
171
186
|
export * from "./semantic-cannibalization";
|
package/dist/index.js
CHANGED
|
@@ -9902,6 +9902,497 @@ class SemanticCannibalizationDetector {
|
|
|
9902
9902
|
}
|
|
9903
9903
|
}
|
|
9904
9904
|
|
|
9905
|
+
// src/humanity-ai-scrubber.ts
|
|
9906
|
+
class HumanityAiScrubber {
|
|
9907
|
+
static BANNED_AI_WORDS = {
|
|
9908
|
+
delve: "explore / look into",
|
|
9909
|
+
delving: "exploring",
|
|
9910
|
+
tapestry: "structure / landscape",
|
|
9911
|
+
testament: "proof / demonstration",
|
|
9912
|
+
pivotal: "key / important",
|
|
9913
|
+
crucial: "essential / vital",
|
|
9914
|
+
foster: "encourage / build",
|
|
9915
|
+
paramount: "primary / top priority",
|
|
9916
|
+
beacon: "leader / reference",
|
|
9917
|
+
unwavering: "solid / steady",
|
|
9918
|
+
leverage: "use / apply",
|
|
9919
|
+
streamline: "simplify / speed up",
|
|
9920
|
+
revolutionize: "transform",
|
|
9921
|
+
groundbreaking: "innovative",
|
|
9922
|
+
moreover: "also / additionally",
|
|
9923
|
+
furthermore: "also",
|
|
9924
|
+
"in summary": "to wrap up",
|
|
9925
|
+
"in conclusion": "finally / in short"
|
|
9926
|
+
};
|
|
9927
|
+
static ROBOTIC_INTROS = [
|
|
9928
|
+
/in today['’]s fast-paced (digital )?(world|landscape|era)/i,
|
|
9929
|
+
/in an era where (technology|digital|ai)/i,
|
|
9930
|
+
/it is important to remember that/i,
|
|
9931
|
+
/when it comes to (managing|growing|scaling)/i,
|
|
9932
|
+
/it is crucial to understand that/i,
|
|
9933
|
+
/look no further than/i
|
|
9934
|
+
];
|
|
9935
|
+
static analyzeAndScrub(text, language = "en") {
|
|
9936
|
+
if (!text || text.trim().length === 0) {
|
|
9937
|
+
return {
|
|
9938
|
+
humanityScore: 100,
|
|
9939
|
+
aiProbabilityScore: 0,
|
|
9940
|
+
totalWords: 0,
|
|
9941
|
+
clichésDetectedCount: 0,
|
|
9942
|
+
clichés: [],
|
|
9943
|
+
isHumanSounding: true,
|
|
9944
|
+
scrubbedText: "",
|
|
9945
|
+
improvementRecommendations: []
|
|
9946
|
+
};
|
|
9947
|
+
}
|
|
9948
|
+
let scrubbed = text;
|
|
9949
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
9950
|
+
const totalWords = words.length;
|
|
9951
|
+
const clich_s = [];
|
|
9952
|
+
const recommendations = [];
|
|
9953
|
+
for (const [word, replacement] of Object.entries(this.BANNED_AI_WORDS)) {
|
|
9954
|
+
const regex = new RegExp(`\\b${word}\\b`, "gi");
|
|
9955
|
+
const matches = text.match(regex);
|
|
9956
|
+
if (matches && matches.length > 0) {
|
|
9957
|
+
clich_s.push({
|
|
9958
|
+
pattern: word,
|
|
9959
|
+
count: matches.length,
|
|
9960
|
+
category: "filler_word",
|
|
9961
|
+
suggestion: `Replace with "${replacement}"`
|
|
9962
|
+
});
|
|
9963
|
+
scrubbed = scrubbed.replace(regex, replacement.split(" / ")[0]);
|
|
9964
|
+
}
|
|
9965
|
+
}
|
|
9966
|
+
for (const introRegex of this.ROBOTIC_INTROS) {
|
|
9967
|
+
if (introRegex.test(text)) {
|
|
9968
|
+
clich_s.push({
|
|
9969
|
+
pattern: introRegex.source,
|
|
9970
|
+
count: 1,
|
|
9971
|
+
category: "robotic_intro",
|
|
9972
|
+
suggestion: "Delete cliché opening sentence. Start immediately with the core problem or direct value."
|
|
9973
|
+
});
|
|
9974
|
+
scrubbed = scrubbed.replace(introRegex, "");
|
|
9975
|
+
}
|
|
9976
|
+
}
|
|
9977
|
+
const emDashCount = (text.match(/—|--/g) || []).length;
|
|
9978
|
+
if (emDashCount > Math.max(2, Math.floor(totalWords / 250))) {
|
|
9979
|
+
clich_s.push({
|
|
9980
|
+
pattern: "Em-dash (—)",
|
|
9981
|
+
count: emDashCount,
|
|
9982
|
+
category: "excessive_punctuation",
|
|
9983
|
+
suggestion: "Reduce em-dashes (—). Replace with simple commas, colons, or clean periods."
|
|
9984
|
+
});
|
|
9985
|
+
scrubbed = scrubbed.replace(/\s*—\s*/g, ", ").replace(/\s*--\s*/g, ", ");
|
|
9986
|
+
}
|
|
9987
|
+
const totalViolations = clich_s.reduce((acc, c) => acc + c.count, 0);
|
|
9988
|
+
const penalty = Math.min(80, totalViolations * 8);
|
|
9989
|
+
const humanityScore = Math.max(15, 100 - penalty);
|
|
9990
|
+
const aiProbabilityScore = 100 - humanityScore;
|
|
9991
|
+
const isHumanSounding = humanityScore >= 80;
|
|
9992
|
+
if (humanityScore < 80) {
|
|
9993
|
+
recommendations.push("Replace detected filler words with conversational, direct vocabulary.");
|
|
9994
|
+
}
|
|
9995
|
+
if (emDashCount > 2) {
|
|
9996
|
+
recommendations.push("Lower punctuation density (em-dashes and semicolons) to sound more natural.");
|
|
9997
|
+
}
|
|
9998
|
+
if (clich_s.some((c) => c.category === "robotic_intro")) {
|
|
9999
|
+
recommendations.push("Hook the reader immediately in the first 5 words without generic preamble.");
|
|
10000
|
+
}
|
|
10001
|
+
return {
|
|
10002
|
+
humanityScore,
|
|
10003
|
+
aiProbabilityScore,
|
|
10004
|
+
totalWords,
|
|
10005
|
+
clichésDetectedCount: totalViolations,
|
|
10006
|
+
clichés: clich_s,
|
|
10007
|
+
isHumanSounding,
|
|
10008
|
+
scrubbedText: scrubbed.trim(),
|
|
10009
|
+
improvementRecommendations: recommendations
|
|
10010
|
+
};
|
|
10011
|
+
}
|
|
10012
|
+
}
|
|
10013
|
+
|
|
10014
|
+
// src/serp-length-comparator.ts
|
|
10015
|
+
class SerpLengthComparator {
|
|
10016
|
+
static compareLength(userWordCount, targetQuery, competitors) {
|
|
10017
|
+
const comps = competitors && competitors.length > 0 ? competitors : [
|
|
10018
|
+
{ url: "https://competitor1.com/guide", rank: 1, wordCount: 2850 },
|
|
10019
|
+
{ url: "https://competitor2.com/article", rank: 2, wordCount: 2420 },
|
|
10020
|
+
{ url: "https://competitor3.com/blog", rank: 3, wordCount: 3100 },
|
|
10021
|
+
{ url: "https://competitor4.com/overview", rank: 4, wordCount: 1950 },
|
|
10022
|
+
{ url: "https://competitor5.com/tutorial", rank: 5, wordCount: 2200 },
|
|
10023
|
+
{ url: "https://competitor6.com/review", rank: 6, wordCount: 1800 },
|
|
10024
|
+
{ url: "https://competitor7.com/best-tools", rank: 7, wordCount: 2600 },
|
|
10025
|
+
{ url: "https://competitor8.com/strategies", rank: 8, wordCount: 1750 },
|
|
10026
|
+
{ url: "https://competitor9.com/case-study", rank: 9, wordCount: 1600 },
|
|
10027
|
+
{ url: "https://competitor10.com/list", rank: 10, wordCount: 2100 }
|
|
10028
|
+
];
|
|
10029
|
+
const sortedCounts = comps.map((c) => c.wordCount).sort((a, b) => a - b);
|
|
10030
|
+
const count = sortedCounts.length;
|
|
10031
|
+
const min = sortedCounts[0];
|
|
10032
|
+
const max = sortedCounts[count - 1];
|
|
10033
|
+
const median = sortedCounts[Math.floor(count / 2)];
|
|
10034
|
+
const p75 = sortedCounts[Math.floor(count * 0.75)];
|
|
10035
|
+
const top3 = comps.filter((c) => c.rank <= 3);
|
|
10036
|
+
const top3Average = Math.round(top3.reduce((acc, c) => acc + c.wordCount, 0) / Math.max(1, top3.length));
|
|
10037
|
+
const recommendedTargetWords = Math.round(Math.max(median, top3Average) * 1.1);
|
|
10038
|
+
const gapToTarget = recommendedTargetWords - userWordCount;
|
|
10039
|
+
let status = "optimal";
|
|
10040
|
+
let actionPlan = `Content length is aligned with top-ranking competitors (Recommended: ~${recommendedTargetWords} words).`;
|
|
10041
|
+
if (gapToTarget > 300) {
|
|
10042
|
+
status = "insufficient";
|
|
10043
|
+
actionPlan = `Content has a deficit of ${gapToTarget} words compared to top 3 SERP average (~${top3Average} words). Expand with 2-3 detailed sub-sections, case studies, or step-by-step FAQs.`;
|
|
10044
|
+
} else if (gapToTarget < -1500) {
|
|
10045
|
+
status = "excessive";
|
|
10046
|
+
actionPlan = `Content exceeds the 75th percentile by ${Math.abs(gapToTarget)} words. Ensure there is no fluff; split into a pillar hub or prune repetitive paragraphs.`;
|
|
10047
|
+
}
|
|
10048
|
+
return {
|
|
10049
|
+
userWordCount,
|
|
10050
|
+
targetQuery,
|
|
10051
|
+
competitorCount: comps.length,
|
|
10052
|
+
competitors: comps,
|
|
10053
|
+
stats: {
|
|
10054
|
+
min,
|
|
10055
|
+
max,
|
|
10056
|
+
median,
|
|
10057
|
+
p75,
|
|
10058
|
+
top3Average,
|
|
10059
|
+
recommendedTargetWords
|
|
10060
|
+
},
|
|
10061
|
+
gapToTarget,
|
|
10062
|
+
status,
|
|
10063
|
+
actionPlan
|
|
10064
|
+
};
|
|
10065
|
+
}
|
|
10066
|
+
}
|
|
10067
|
+
|
|
10068
|
+
// src/passive-voice-complexity.ts
|
|
10069
|
+
class PassiveVoiceComplexityAnalyzer {
|
|
10070
|
+
static PASSIVE_PATTERNS = {
|
|
10071
|
+
en: [
|
|
10072
|
+
/\b(is|are|was|were|been|being|be)\s+([a-z]+ed|built|written|made|done|seen|found|given|taken|chosen)\b/i,
|
|
10073
|
+
/\bby\s+(the|a|an|our|their|users|google)\b/i
|
|
10074
|
+
],
|
|
10075
|
+
fr: [
|
|
10076
|
+
/\b(est|sont|a été|ont été|étant|fut|seront)\s+([a-z]+é|[a-z]+ée|[a-z]+és|[a-z]+ées|fait|pris|écrit|construit)\b/i,
|
|
10077
|
+
/\bpar\s+(le|la|les|un|une|des|notre)\b/i
|
|
10078
|
+
],
|
|
10079
|
+
es: [
|
|
10080
|
+
/\b(es|son|fue|fueron|sido|siendo)\s+([a-z]+ado|[a-z]+ados|[a-z]+ada|[a-z]+adas|hecho|escrito|visto)\b/i,
|
|
10081
|
+
/\bpor\s+(el|la|los|las|un|una)\b/i
|
|
10082
|
+
],
|
|
10083
|
+
de: [
|
|
10084
|
+
/\b(wird|wurden|wurde|worden|geworden)\s+([a-z]+t|[a-z]+en)\b/i,
|
|
10085
|
+
/\bvon\s+(dem|der|den|einem|einer)\b/i
|
|
10086
|
+
]
|
|
10087
|
+
};
|
|
10088
|
+
static analyze(text, language = "en") {
|
|
10089
|
+
if (!text || text.trim().length === 0) {
|
|
10090
|
+
return {
|
|
10091
|
+
totalSentences: 0,
|
|
10092
|
+
totalWords: 0,
|
|
10093
|
+
averageWordsPerSentence: 0,
|
|
10094
|
+
passiveVoiceRatio: 0,
|
|
10095
|
+
passiveSentencesCount: 0,
|
|
10096
|
+
complexSentencesCount: 0,
|
|
10097
|
+
gradeLevelEstimate: 0,
|
|
10098
|
+
readabilityEaseScore: 100,
|
|
10099
|
+
status: "clear_and_punchy",
|
|
10100
|
+
keyWarnings: [],
|
|
10101
|
+
sentences: []
|
|
10102
|
+
};
|
|
10103
|
+
}
|
|
10104
|
+
const rawSentences = text.replace(/([.?!])\s*(?=[A-Z0-9À-ÖØ-ß])/g, "$1|").split("|").map((s) => s.trim()).filter((s) => s.length > 3);
|
|
10105
|
+
const patterns = this.PASSIVE_PATTERNS[language] || this.PASSIVE_PATTERNS.en;
|
|
10106
|
+
const sentenceDetails = [];
|
|
10107
|
+
let totalWords = 0;
|
|
10108
|
+
let passiveCount = 0;
|
|
10109
|
+
let complexCount = 0;
|
|
10110
|
+
for (const sentence of rawSentences) {
|
|
10111
|
+
const words = sentence.split(/\s+/).filter(Boolean);
|
|
10112
|
+
const wCount = words.length;
|
|
10113
|
+
totalWords += wCount;
|
|
10114
|
+
const isPassive = patterns.some((regex) => regex.test(sentence));
|
|
10115
|
+
const isTooLong = wCount > 22;
|
|
10116
|
+
if (isPassive)
|
|
10117
|
+
passiveCount++;
|
|
10118
|
+
if (isTooLong)
|
|
10119
|
+
complexCount++;
|
|
10120
|
+
sentenceDetails.push({
|
|
10121
|
+
text: sentence,
|
|
10122
|
+
wordCount: wCount,
|
|
10123
|
+
isPassive,
|
|
10124
|
+
isTooLong
|
|
10125
|
+
});
|
|
10126
|
+
}
|
|
10127
|
+
const totalSentences = Math.max(1, rawSentences.length);
|
|
10128
|
+
const avgWordsPerSentence = Math.round(totalWords / totalSentences * 10) / 10;
|
|
10129
|
+
const passiveVoiceRatio = Math.round(passiveCount / totalSentences * 100) / 100;
|
|
10130
|
+
const syllableEstimate = totalWords * 1.4;
|
|
10131
|
+
const ease = Math.max(10, Math.min(100, Math.round(206.835 - 1.015 * avgWordsPerSentence - 84.6 * (syllableEstimate / totalWords))));
|
|
10132
|
+
const gradeLevel = Math.max(4, Math.round(0.39 * avgWordsPerSentence + 11.8 * (syllableEstimate / totalWords) - 15.59));
|
|
10133
|
+
const warnings = [];
|
|
10134
|
+
if (passiveVoiceRatio > 0.15) {
|
|
10135
|
+
warnings.push(`Passive voice is ${Math.round(passiveVoiceRatio * 100)}% (Target: < 10%). Convert passive constructions to active voice.`);
|
|
10136
|
+
}
|
|
10137
|
+
if (complexCount > totalSentences * 0.25) {
|
|
10138
|
+
warnings.push(`${complexCount} sentences exceed 22 words. Break long compound sentences into shorter, punchier statements.`);
|
|
10139
|
+
}
|
|
10140
|
+
if (avgWordsPerSentence > 18) {
|
|
10141
|
+
warnings.push(`Average sentence length is ${avgWordsPerSentence} words (Target: 12-16 words).`);
|
|
10142
|
+
}
|
|
10143
|
+
let status = "clear_and_punchy";
|
|
10144
|
+
if (ease < 50 || passiveVoiceRatio > 0.2) {
|
|
10145
|
+
status = "hard_to_read";
|
|
10146
|
+
} else if (ease < 65 || passiveVoiceRatio > 0.12) {
|
|
10147
|
+
status = "acceptable";
|
|
10148
|
+
}
|
|
10149
|
+
return {
|
|
10150
|
+
totalSentences,
|
|
10151
|
+
totalWords,
|
|
10152
|
+
averageWordsPerSentence: avgWordsPerSentence,
|
|
10153
|
+
passiveVoiceRatio,
|
|
10154
|
+
passiveSentencesCount: passiveCount,
|
|
10155
|
+
complexSentencesCount: complexCount,
|
|
10156
|
+
gradeLevelEstimate: gradeLevel,
|
|
10157
|
+
readabilityEaseScore: ease,
|
|
10158
|
+
status,
|
|
10159
|
+
keyWarnings: warnings,
|
|
10160
|
+
sentences: sentenceDetails
|
|
10161
|
+
};
|
|
10162
|
+
}
|
|
10163
|
+
}
|
|
10164
|
+
|
|
10165
|
+
// src/above-fold-cro-auditor.ts
|
|
10166
|
+
class AboveFoldCroAuditor {
|
|
10167
|
+
static STRONG_ACTION_VERBS = [
|
|
10168
|
+
/start/i,
|
|
10169
|
+
/get/i,
|
|
10170
|
+
/try/i,
|
|
10171
|
+
/claim/i,
|
|
10172
|
+
/launch/i,
|
|
10173
|
+
/boost/i,
|
|
10174
|
+
/automate/i,
|
|
10175
|
+
/démarrer/i,
|
|
10176
|
+
/essayer/i,
|
|
10177
|
+
/obtenir/i,
|
|
10178
|
+
/profiter/i,
|
|
10179
|
+
/iniciar/i,
|
|
10180
|
+
/probar/i,
|
|
10181
|
+
/jetzt/i,
|
|
10182
|
+
/kostenlos/i
|
|
10183
|
+
];
|
|
10184
|
+
static WEAK_ACTION_VERBS = [
|
|
10185
|
+
/^submit$/i,
|
|
10186
|
+
/^click here$/i,
|
|
10187
|
+
/^learn more$/i,
|
|
10188
|
+
/^envoyer$/i,
|
|
10189
|
+
/^cliquez ici$/i,
|
|
10190
|
+
/^en savoir plus$/i,
|
|
10191
|
+
/^weiter$/i,
|
|
10192
|
+
/^leer más$/i
|
|
10193
|
+
];
|
|
10194
|
+
static auditHero(input) {
|
|
10195
|
+
const criteria = [];
|
|
10196
|
+
const fixes = [];
|
|
10197
|
+
const h1Words = input.h1.trim().split(/\s+/).length;
|
|
10198
|
+
const isH1Clear = h1Words >= 4 && h1Words <= 14;
|
|
10199
|
+
let h1Score = isH1Clear ? 25 : 12;
|
|
10200
|
+
if (!isH1Clear) {
|
|
10201
|
+
fixes.push("Optimize H1 length between 4 and 12 words. Make the specific outcome and audience explicit.");
|
|
10202
|
+
}
|
|
10203
|
+
criteria.push({
|
|
10204
|
+
name: "Headline Clarity & Value Proposition",
|
|
10205
|
+
passed: isH1Clear,
|
|
10206
|
+
score: h1Score,
|
|
10207
|
+
feedback: isH1Clear ? "Headline is concise, outcome-oriented, and immediately readable in < 3 seconds." : "Headline is either too vague (< 4 words) or too dense (> 14 words)."
|
|
10208
|
+
});
|
|
10209
|
+
const hasStrongVerb = this.STRONG_ACTION_VERBS.some((r) => r.test(input.primaryCtaText));
|
|
10210
|
+
const hasWeakVerb = this.WEAK_ACTION_VERBS.some((r) => r.test(input.primaryCtaText.trim()));
|
|
10211
|
+
const isCtaEffective = hasStrongVerb && !hasWeakVerb;
|
|
10212
|
+
let ctaScore = isCtaEffective ? 25 : hasWeakVerb ? 5 : 15;
|
|
10213
|
+
if (!isCtaEffective) {
|
|
10214
|
+
fixes.push(`Replace generic CTA "${input.primaryCtaText}" with an outcome-focused action verb (e.g. "Start Free Trial", "Get Instant Access", "Démarrer Gratuitement").`);
|
|
10215
|
+
}
|
|
10216
|
+
criteria.push({
|
|
10217
|
+
name: "Primary CTA Contrast & Action Verb",
|
|
10218
|
+
passed: isCtaEffective,
|
|
10219
|
+
score: ctaScore,
|
|
10220
|
+
feedback: isCtaEffective ? `CTA "${input.primaryCtaText}" uses a strong, high-conversion action trigger.` : `CTA "${input.primaryCtaText}" lacks urgency or is too generic.`
|
|
10221
|
+
});
|
|
10222
|
+
const hasProof = Boolean(input.hasTestimonialsOrStars || input.hasCustomerLogos);
|
|
10223
|
+
let proofScore = hasProof ? 20 : 0;
|
|
10224
|
+
if (!hasProof) {
|
|
10225
|
+
fixes.push("Add social proof above the fold (e.g. 'Rated 4.9/5 by 1,200+ teams' or client logos).");
|
|
10226
|
+
}
|
|
10227
|
+
criteria.push({
|
|
10228
|
+
name: "Above-the-Fold Social Proof & Ratings",
|
|
10229
|
+
passed: hasProof,
|
|
10230
|
+
score: proofScore,
|
|
10231
|
+
feedback: hasProof ? "Trust signals and social proof are visible immediately above the fold." : "Missing social proof above the fold. Increases visitor bounce risk."
|
|
10232
|
+
});
|
|
10233
|
+
const hasReversal = Boolean(input.hasRiskReversalOrGuarantee);
|
|
10234
|
+
let reversalScore = hasReversal ? 20 : 5;
|
|
10235
|
+
if (!hasReversal) {
|
|
10236
|
+
fixes.push("Add a friction-killer under the CTA (e.g. 'No credit card required • Cancel anytime • 14-day free trial').");
|
|
10237
|
+
}
|
|
10238
|
+
criteria.push({
|
|
10239
|
+
name: "Risk Reversal & Friction Elimination",
|
|
10240
|
+
passed: hasReversal,
|
|
10241
|
+
score: reversalScore,
|
|
10242
|
+
feedback: hasReversal ? "Zero-risk micro-copy present under the main action trigger." : "No risk reversal micro-copy found under CTA."
|
|
10243
|
+
});
|
|
10244
|
+
const subWords = (input.subheadline || "").trim().split(/\s+/).length;
|
|
10245
|
+
const isSubheadlineSolid = subWords >= 8 && subWords <= 30;
|
|
10246
|
+
let subScore = isSubheadlineSolid ? 10 : 5;
|
|
10247
|
+
if (!isSubheadlineSolid) {
|
|
10248
|
+
fixes.push("Add a supporting subheadline (12-25 words) explaining HOW the product solves the pain point.");
|
|
10249
|
+
}
|
|
10250
|
+
criteria.push({
|
|
10251
|
+
name: "Subheadline Supporting Context",
|
|
10252
|
+
passed: isSubheadlineSolid,
|
|
10253
|
+
score: subScore,
|
|
10254
|
+
feedback: isSubheadlineSolid ? "Subheadline reinforces the value proposition with concrete details." : "Subheadline is missing or insufficient."
|
|
10255
|
+
});
|
|
10256
|
+
const croScore = h1Score + ctaScore + proofScore + reversalScore + subScore;
|
|
10257
|
+
const rating = croScore >= 80 ? "high_converting" : croScore >= 55 ? "solid" : "high_friction";
|
|
10258
|
+
return {
|
|
10259
|
+
croScore,
|
|
10260
|
+
rating,
|
|
10261
|
+
criteria,
|
|
10262
|
+
recommendedFixes: fixes,
|
|
10263
|
+
suggestedHeroVariant: {
|
|
10264
|
+
h1: input.h1.includes("—") ? input.h1 : `${input.h1} — Built for Fast Growing Teams`,
|
|
10265
|
+
subheadline: input.subheadline || "Automate operational workflows, boost organic search traffic, and cut manual workload in half with instant setup.",
|
|
10266
|
+
ctaText: "Start Free Trial — Instant Setup",
|
|
10267
|
+
reassuranceBadge: "No credit card required • 14-day trial • 100% Free migration"
|
|
10268
|
+
}
|
|
10269
|
+
};
|
|
10270
|
+
}
|
|
10271
|
+
}
|
|
10272
|
+
|
|
10273
|
+
// src/context-templates-generator.ts
|
|
10274
|
+
class ContextTemplatesGenerator {
|
|
10275
|
+
static generateAllContextFiles(input) {
|
|
10276
|
+
return {
|
|
10277
|
+
"context/brand-voice.md": this.generateBrandVoice(input),
|
|
10278
|
+
"context/features.md": this.generateFeatures(input),
|
|
10279
|
+
"context/internal-links-map.md": this.generateInternalLinksMap(input),
|
|
10280
|
+
"context/style-guide.md": this.generateStyleGuide(input),
|
|
10281
|
+
"context/target-keywords.md": this.generateTargetKeywords(input),
|
|
10282
|
+
"context/competitor-analysis.md": this.generateCompetitorAnalysis(input),
|
|
10283
|
+
"context/seo-guidelines.md": this.generateSeoGuidelines(input),
|
|
10284
|
+
"context/cro-best-practices.md": this.generateCroBestPractices(input)
|
|
10285
|
+
};
|
|
10286
|
+
}
|
|
10287
|
+
static generateBrandVoice(input) {
|
|
10288
|
+
return `# Brand Voice & Messaging Framework — ${input.brandName}
|
|
10289
|
+
|
|
10290
|
+
## 1. Core Voice Pillars
|
|
10291
|
+
- **Authoritative yet Approachable:** Speak with deep domain expertise in ${input.industry}, without academic jargon.
|
|
10292
|
+
- **Direct & Action-Oriented:** Get straight to the solution in the first 5 words. No fluff or generic preambles.
|
|
10293
|
+
- **Outcome-Focused:** Emphasize time saved, revenue recovered, and operational ease for ${input.targetAudience}.
|
|
10294
|
+
|
|
10295
|
+
## 2. Terminology & Word Preferences
|
|
10296
|
+
- **Preferred Words:** Streamlined, automated, verified, instant, high-performance, precision.
|
|
10297
|
+
- **Banned Words:** Delve, tapestry, testament, crucial, groundbreaking, revolutionize, in today's fast-paced world.
|
|
10298
|
+
|
|
10299
|
+
## 3. Core Audience
|
|
10300
|
+
- **Primary Persona:** ${input.targetAudience}
|
|
10301
|
+
- **Primary Domain:** ${input.domain}
|
|
10302
|
+
`.trim();
|
|
10303
|
+
}
|
|
10304
|
+
static generateFeatures(input) {
|
|
10305
|
+
const list = input.coreFeatures.map((f) => `- **${f}:** Enterprise-grade workflow automation and real-time synchronization.`).join(`
|
|
10306
|
+
`);
|
|
10307
|
+
return `# Product Features & Capabilities — ${input.brandName}
|
|
10308
|
+
|
|
10309
|
+
## 1. Feature Catalog
|
|
10310
|
+
${list}
|
|
10311
|
+
|
|
10312
|
+
## 2. Competitive Value Proposition
|
|
10313
|
+
- Instant setup (< 3 minutes).
|
|
10314
|
+
- Zero complex infrastructure requirements.
|
|
10315
|
+
- 100% scalable in-memory architecture.
|
|
10316
|
+
`.trim();
|
|
10317
|
+
}
|
|
10318
|
+
static generateInternalLinksMap(input) {
|
|
10319
|
+
return `# Internal Links Architecture Map — ${input.domain}
|
|
10320
|
+
|
|
10321
|
+
## 1. Core Pillar Hubs
|
|
10322
|
+
- **Homepage:** \`${input.domain}/\` (Anchor: "${input.brandName}", "${input.industry} Software")
|
|
10323
|
+
- **Solutions & PSEO Hub:** \`${input.domain}/solutions\` (Anchor: "Explore all solutions", "Platform features")
|
|
10324
|
+
- **Pricing & Plans:** \`${input.domain}/pricing\` (Anchor: "View pricing plans", "Compare tiers")
|
|
10325
|
+
- **Free Tools:** \`${input.domain}/tools\` (Anchor: "Free interactive calculators", "SEO tools")
|
|
10326
|
+
|
|
10327
|
+
## 2. Linking Best Practices
|
|
10328
|
+
- Add 3-5 relevant internal links per 2,000 words.
|
|
10329
|
+
- Use descriptive, keyword-rich anchor text rather than "click here".
|
|
10330
|
+
`.trim();
|
|
10331
|
+
}
|
|
10332
|
+
static generateStyleGuide(input) {
|
|
10333
|
+
return `# Editorial & Style Guide — ${input.brandName}
|
|
10334
|
+
|
|
10335
|
+
## 1. Formatting Rules
|
|
10336
|
+
- **Sentence Length:** 12 to 18 words on average. Never exceed 24 words in a single sentence.
|
|
10337
|
+
- **Paragraphs:** 2 to 4 sentences maximum for high mobile readability.
|
|
10338
|
+
- **Subheadings:** Introduce an H2 or H3 every 250-350 words.
|
|
10339
|
+
- **Punctuation:** Limit em-dashes (—) to 1 per 500 words. Use clear periods and commas.
|
|
10340
|
+
|
|
10341
|
+
## 2. Readability Target
|
|
10342
|
+
- **Flesch Reading Ease:** 65 to 80.
|
|
10343
|
+
- **Reading Level:** 7th to 9th grade.
|
|
10344
|
+
`.trim();
|
|
10345
|
+
}
|
|
10346
|
+
static generateTargetKeywords(input) {
|
|
10347
|
+
const kwList = input.primaryKeywords.map((k) => `- \`${k}\` (Intent: Commercial/Transactional)`).join(`
|
|
10348
|
+
`);
|
|
10349
|
+
return `# Target Keywords & Topic Clusters — ${input.brandName}
|
|
10350
|
+
|
|
10351
|
+
## 1. Primary Keywords
|
|
10352
|
+
${kwList}
|
|
10353
|
+
|
|
10354
|
+
## 2. Secondary Modifiers & Long-Tail
|
|
10355
|
+
- Best ${input.industry} software for ${input.targetAudience}
|
|
10356
|
+
- How to automate ${input.industry} workflows in 2026
|
|
10357
|
+
- ${input.brandName} pricing and alternatives
|
|
10358
|
+
`.trim();
|
|
10359
|
+
}
|
|
10360
|
+
static generateCompetitorAnalysis(input) {
|
|
10361
|
+
return `# Competitor Analysis & Differentiation — ${input.brandName}
|
|
10362
|
+
|
|
10363
|
+
## 1. Market Overview
|
|
10364
|
+
- **Our Positioning:** The fastest, lightest, in-memory automation platform for ${input.industry}.
|
|
10365
|
+
- **Key Differentiator:** 100% cloud-native, sub-millisecond response time, no heavy legacy bloat.
|
|
10366
|
+
|
|
10367
|
+
## 2. Comparison Angles
|
|
10368
|
+
- Transparent pricing with zero hidden add-on fees.
|
|
10369
|
+
- Modern API & MCP integrations (Claude Code, Cursor, Antigravity).
|
|
10370
|
+
`.trim();
|
|
10371
|
+
}
|
|
10372
|
+
static generateSeoGuidelines(input) {
|
|
10373
|
+
return `# SEO Technical & On-Page Requirements — ${input.brandName}
|
|
10374
|
+
|
|
10375
|
+
## 1. Title & Meta Descriptions
|
|
10376
|
+
- **Title Tag:** 50-60 characters, primary keyword in front.
|
|
10377
|
+
- **Meta Description:** 140-155 characters, includes value prop and soft CTA.
|
|
10378
|
+
|
|
10379
|
+
## 2. Schema.org Graphs
|
|
10380
|
+
- Inject valid JSON-LD graph on all programmatic pages (SoftwareApplication, LocalBusiness, FAQPage, Breadcrumbs).
|
|
10381
|
+
- Zero syntax errors or missing required fields.
|
|
10382
|
+
`.trim();
|
|
10383
|
+
}
|
|
10384
|
+
static generateCroBestPractices(input) {
|
|
10385
|
+
return `# Conversion Rate Optimization (CRO) Standards — ${input.brandName}
|
|
10386
|
+
|
|
10387
|
+
## 1. Above-The-Fold Checklist
|
|
10388
|
+
- [ ] Clear H1 communicating the specific outcome in < 3 seconds.
|
|
10389
|
+
- [ ] High-contrast primary CTA with strong action verb.
|
|
10390
|
+
- [ ] Social proof visible without scrolling (star ratings, reviews, client logos).
|
|
10391
|
+
- [ ] Risk reversal micro-copy directly beneath the main button ("No credit card required").
|
|
10392
|
+
`.trim();
|
|
10393
|
+
}
|
|
10394
|
+
}
|
|
10395
|
+
|
|
9905
10396
|
// src/index.ts
|
|
9906
10397
|
function createLynxSeoEngine(config) {
|
|
9907
10398
|
return new LynxSeoEngine(config);
|
|
@@ -9916,6 +10407,11 @@ var LynxSeo = {
|
|
|
9916
10407
|
googleIndexing: GoogleIndexingClient,
|
|
9917
10408
|
aeoSnippet: AeoSnippetSynthesizer,
|
|
9918
10409
|
cannibalization: SemanticCannibalizationDetector,
|
|
10410
|
+
humanityScrubber: HumanityAiScrubber,
|
|
10411
|
+
serpLength: SerpLengthComparator,
|
|
10412
|
+
readabilityComplexity: PassiveVoiceComplexityAnalyzer,
|
|
10413
|
+
aboveFoldCro: AboveFoldCroAuditor,
|
|
10414
|
+
contextTemplates: ContextTemplatesGenerator,
|
|
9919
10415
|
inspectMeta: SiteAuditor.inspectMeta,
|
|
9920
10416
|
crawlDomain: DeepCrawlerAuditor.crawlAndAuditDomain,
|
|
9921
10417
|
inspectHtmlSnapshot: DeepCrawlerAuditor.inspectHtmlSnapshot,
|
|
@@ -9984,6 +10480,7 @@ export {
|
|
|
9984
10480
|
SocialAdsSocialSeoEngine,
|
|
9985
10481
|
SiteAuditor,
|
|
9986
10482
|
SerpRankHistoryEngine,
|
|
10483
|
+
SerpLengthComparator,
|
|
9987
10484
|
SerpClient,
|
|
9988
10485
|
SeoOpportunitiesDecayDetector,
|
|
9989
10486
|
SemanticCannibalizationDetector,
|
|
@@ -9996,6 +10493,7 @@ export {
|
|
|
9996
10493
|
PublicRoutesManifestEngine,
|
|
9997
10494
|
PseoMatrixEngine,
|
|
9998
10495
|
PowerWordsPsychologyEngine,
|
|
10496
|
+
PassiveVoiceComplexityAnalyzer,
|
|
9999
10497
|
PageRegenerationTracker,
|
|
10000
10498
|
PSEO_AGENT_SYSTEM_PROMPT,
|
|
10001
10499
|
OgImageGenerator,
|
|
@@ -10024,6 +10522,7 @@ export {
|
|
|
10024
10522
|
IndexNowClient,
|
|
10025
10523
|
I18nDetector,
|
|
10026
10524
|
HyperswitchGateway,
|
|
10525
|
+
HumanityAiScrubber,
|
|
10027
10526
|
GoogleIndexingClient,
|
|
10028
10527
|
GoogleBusinessProfileEngine,
|
|
10029
10528
|
GeoMeshLinkingEngine,
|
|
@@ -10037,6 +10536,7 @@ export {
|
|
|
10037
10536
|
CrosslinkScorerEngine,
|
|
10038
10537
|
CroCopywritingEngine,
|
|
10039
10538
|
CopywritingFrameworksMaster,
|
|
10539
|
+
ContextTemplatesGenerator,
|
|
10040
10540
|
CONTENT_AI_40_TOOLS,
|
|
10041
10541
|
BrandDnaCalendarEngine,
|
|
10042
10542
|
BacklinksClient,
|
|
@@ -10046,5 +10546,6 @@ export {
|
|
|
10046
10546
|
AiCopilotClient,
|
|
10047
10547
|
AiBotsLogAnalyzer,
|
|
10048
10548
|
AeoSnippetSynthesizer,
|
|
10049
|
-
AdIntelligenceCroEngine
|
|
10549
|
+
AdIntelligenceCroEngine,
|
|
10550
|
+
AboveFoldCroAuditor
|
|
10050
10551
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -9902,6 +9902,497 @@ class SemanticCannibalizationDetector {
|
|
|
9902
9902
|
}
|
|
9903
9903
|
}
|
|
9904
9904
|
|
|
9905
|
+
// src/humanity-ai-scrubber.ts
|
|
9906
|
+
class HumanityAiScrubber {
|
|
9907
|
+
static BANNED_AI_WORDS = {
|
|
9908
|
+
delve: "explore / look into",
|
|
9909
|
+
delving: "exploring",
|
|
9910
|
+
tapestry: "structure / landscape",
|
|
9911
|
+
testament: "proof / demonstration",
|
|
9912
|
+
pivotal: "key / important",
|
|
9913
|
+
crucial: "essential / vital",
|
|
9914
|
+
foster: "encourage / build",
|
|
9915
|
+
paramount: "primary / top priority",
|
|
9916
|
+
beacon: "leader / reference",
|
|
9917
|
+
unwavering: "solid / steady",
|
|
9918
|
+
leverage: "use / apply",
|
|
9919
|
+
streamline: "simplify / speed up",
|
|
9920
|
+
revolutionize: "transform",
|
|
9921
|
+
groundbreaking: "innovative",
|
|
9922
|
+
moreover: "also / additionally",
|
|
9923
|
+
furthermore: "also",
|
|
9924
|
+
"in summary": "to wrap up",
|
|
9925
|
+
"in conclusion": "finally / in short"
|
|
9926
|
+
};
|
|
9927
|
+
static ROBOTIC_INTROS = [
|
|
9928
|
+
/in today['’]s fast-paced (digital )?(world|landscape|era)/i,
|
|
9929
|
+
/in an era where (technology|digital|ai)/i,
|
|
9930
|
+
/it is important to remember that/i,
|
|
9931
|
+
/when it comes to (managing|growing|scaling)/i,
|
|
9932
|
+
/it is crucial to understand that/i,
|
|
9933
|
+
/look no further than/i
|
|
9934
|
+
];
|
|
9935
|
+
static analyzeAndScrub(text, language = "en") {
|
|
9936
|
+
if (!text || text.trim().length === 0) {
|
|
9937
|
+
return {
|
|
9938
|
+
humanityScore: 100,
|
|
9939
|
+
aiProbabilityScore: 0,
|
|
9940
|
+
totalWords: 0,
|
|
9941
|
+
clichésDetectedCount: 0,
|
|
9942
|
+
clichés: [],
|
|
9943
|
+
isHumanSounding: true,
|
|
9944
|
+
scrubbedText: "",
|
|
9945
|
+
improvementRecommendations: []
|
|
9946
|
+
};
|
|
9947
|
+
}
|
|
9948
|
+
let scrubbed = text;
|
|
9949
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
9950
|
+
const totalWords = words.length;
|
|
9951
|
+
const clich_s = [];
|
|
9952
|
+
const recommendations = [];
|
|
9953
|
+
for (const [word, replacement] of Object.entries(this.BANNED_AI_WORDS)) {
|
|
9954
|
+
const regex = new RegExp(`\\b${word}\\b`, "gi");
|
|
9955
|
+
const matches = text.match(regex);
|
|
9956
|
+
if (matches && matches.length > 0) {
|
|
9957
|
+
clich_s.push({
|
|
9958
|
+
pattern: word,
|
|
9959
|
+
count: matches.length,
|
|
9960
|
+
category: "filler_word",
|
|
9961
|
+
suggestion: `Replace with "${replacement}"`
|
|
9962
|
+
});
|
|
9963
|
+
scrubbed = scrubbed.replace(regex, replacement.split(" / ")[0]);
|
|
9964
|
+
}
|
|
9965
|
+
}
|
|
9966
|
+
for (const introRegex of this.ROBOTIC_INTROS) {
|
|
9967
|
+
if (introRegex.test(text)) {
|
|
9968
|
+
clich_s.push({
|
|
9969
|
+
pattern: introRegex.source,
|
|
9970
|
+
count: 1,
|
|
9971
|
+
category: "robotic_intro",
|
|
9972
|
+
suggestion: "Delete cliché opening sentence. Start immediately with the core problem or direct value."
|
|
9973
|
+
});
|
|
9974
|
+
scrubbed = scrubbed.replace(introRegex, "");
|
|
9975
|
+
}
|
|
9976
|
+
}
|
|
9977
|
+
const emDashCount = (text.match(/—|--/g) || []).length;
|
|
9978
|
+
if (emDashCount > Math.max(2, Math.floor(totalWords / 250))) {
|
|
9979
|
+
clich_s.push({
|
|
9980
|
+
pattern: "Em-dash (—)",
|
|
9981
|
+
count: emDashCount,
|
|
9982
|
+
category: "excessive_punctuation",
|
|
9983
|
+
suggestion: "Reduce em-dashes (—). Replace with simple commas, colons, or clean periods."
|
|
9984
|
+
});
|
|
9985
|
+
scrubbed = scrubbed.replace(/\s*—\s*/g, ", ").replace(/\s*--\s*/g, ", ");
|
|
9986
|
+
}
|
|
9987
|
+
const totalViolations = clich_s.reduce((acc, c) => acc + c.count, 0);
|
|
9988
|
+
const penalty = Math.min(80, totalViolations * 8);
|
|
9989
|
+
const humanityScore = Math.max(15, 100 - penalty);
|
|
9990
|
+
const aiProbabilityScore = 100 - humanityScore;
|
|
9991
|
+
const isHumanSounding = humanityScore >= 80;
|
|
9992
|
+
if (humanityScore < 80) {
|
|
9993
|
+
recommendations.push("Replace detected filler words with conversational, direct vocabulary.");
|
|
9994
|
+
}
|
|
9995
|
+
if (emDashCount > 2) {
|
|
9996
|
+
recommendations.push("Lower punctuation density (em-dashes and semicolons) to sound more natural.");
|
|
9997
|
+
}
|
|
9998
|
+
if (clich_s.some((c) => c.category === "robotic_intro")) {
|
|
9999
|
+
recommendations.push("Hook the reader immediately in the first 5 words without generic preamble.");
|
|
10000
|
+
}
|
|
10001
|
+
return {
|
|
10002
|
+
humanityScore,
|
|
10003
|
+
aiProbabilityScore,
|
|
10004
|
+
totalWords,
|
|
10005
|
+
clichésDetectedCount: totalViolations,
|
|
10006
|
+
clichés: clich_s,
|
|
10007
|
+
isHumanSounding,
|
|
10008
|
+
scrubbedText: scrubbed.trim(),
|
|
10009
|
+
improvementRecommendations: recommendations
|
|
10010
|
+
};
|
|
10011
|
+
}
|
|
10012
|
+
}
|
|
10013
|
+
|
|
10014
|
+
// src/serp-length-comparator.ts
|
|
10015
|
+
class SerpLengthComparator {
|
|
10016
|
+
static compareLength(userWordCount, targetQuery, competitors) {
|
|
10017
|
+
const comps = competitors && competitors.length > 0 ? competitors : [
|
|
10018
|
+
{ url: "https://competitor1.com/guide", rank: 1, wordCount: 2850 },
|
|
10019
|
+
{ url: "https://competitor2.com/article", rank: 2, wordCount: 2420 },
|
|
10020
|
+
{ url: "https://competitor3.com/blog", rank: 3, wordCount: 3100 },
|
|
10021
|
+
{ url: "https://competitor4.com/overview", rank: 4, wordCount: 1950 },
|
|
10022
|
+
{ url: "https://competitor5.com/tutorial", rank: 5, wordCount: 2200 },
|
|
10023
|
+
{ url: "https://competitor6.com/review", rank: 6, wordCount: 1800 },
|
|
10024
|
+
{ url: "https://competitor7.com/best-tools", rank: 7, wordCount: 2600 },
|
|
10025
|
+
{ url: "https://competitor8.com/strategies", rank: 8, wordCount: 1750 },
|
|
10026
|
+
{ url: "https://competitor9.com/case-study", rank: 9, wordCount: 1600 },
|
|
10027
|
+
{ url: "https://competitor10.com/list", rank: 10, wordCount: 2100 }
|
|
10028
|
+
];
|
|
10029
|
+
const sortedCounts = comps.map((c) => c.wordCount).sort((a, b) => a - b);
|
|
10030
|
+
const count = sortedCounts.length;
|
|
10031
|
+
const min = sortedCounts[0];
|
|
10032
|
+
const max = sortedCounts[count - 1];
|
|
10033
|
+
const median = sortedCounts[Math.floor(count / 2)];
|
|
10034
|
+
const p75 = sortedCounts[Math.floor(count * 0.75)];
|
|
10035
|
+
const top3 = comps.filter((c) => c.rank <= 3);
|
|
10036
|
+
const top3Average = Math.round(top3.reduce((acc, c) => acc + c.wordCount, 0) / Math.max(1, top3.length));
|
|
10037
|
+
const recommendedTargetWords = Math.round(Math.max(median, top3Average) * 1.1);
|
|
10038
|
+
const gapToTarget = recommendedTargetWords - userWordCount;
|
|
10039
|
+
let status = "optimal";
|
|
10040
|
+
let actionPlan = `Content length is aligned with top-ranking competitors (Recommended: ~${recommendedTargetWords} words).`;
|
|
10041
|
+
if (gapToTarget > 300) {
|
|
10042
|
+
status = "insufficient";
|
|
10043
|
+
actionPlan = `Content has a deficit of ${gapToTarget} words compared to top 3 SERP average (~${top3Average} words). Expand with 2-3 detailed sub-sections, case studies, or step-by-step FAQs.`;
|
|
10044
|
+
} else if (gapToTarget < -1500) {
|
|
10045
|
+
status = "excessive";
|
|
10046
|
+
actionPlan = `Content exceeds the 75th percentile by ${Math.abs(gapToTarget)} words. Ensure there is no fluff; split into a pillar hub or prune repetitive paragraphs.`;
|
|
10047
|
+
}
|
|
10048
|
+
return {
|
|
10049
|
+
userWordCount,
|
|
10050
|
+
targetQuery,
|
|
10051
|
+
competitorCount: comps.length,
|
|
10052
|
+
competitors: comps,
|
|
10053
|
+
stats: {
|
|
10054
|
+
min,
|
|
10055
|
+
max,
|
|
10056
|
+
median,
|
|
10057
|
+
p75,
|
|
10058
|
+
top3Average,
|
|
10059
|
+
recommendedTargetWords
|
|
10060
|
+
},
|
|
10061
|
+
gapToTarget,
|
|
10062
|
+
status,
|
|
10063
|
+
actionPlan
|
|
10064
|
+
};
|
|
10065
|
+
}
|
|
10066
|
+
}
|
|
10067
|
+
|
|
10068
|
+
// src/passive-voice-complexity.ts
|
|
10069
|
+
class PassiveVoiceComplexityAnalyzer {
|
|
10070
|
+
static PASSIVE_PATTERNS = {
|
|
10071
|
+
en: [
|
|
10072
|
+
/\b(is|are|was|were|been|being|be)\s+([a-z]+ed|built|written|made|done|seen|found|given|taken|chosen)\b/i,
|
|
10073
|
+
/\bby\s+(the|a|an|our|their|users|google)\b/i
|
|
10074
|
+
],
|
|
10075
|
+
fr: [
|
|
10076
|
+
/\b(est|sont|a été|ont été|étant|fut|seront)\s+([a-z]+é|[a-z]+ée|[a-z]+és|[a-z]+ées|fait|pris|écrit|construit)\b/i,
|
|
10077
|
+
/\bpar\s+(le|la|les|un|une|des|notre)\b/i
|
|
10078
|
+
],
|
|
10079
|
+
es: [
|
|
10080
|
+
/\b(es|son|fue|fueron|sido|siendo)\s+([a-z]+ado|[a-z]+ados|[a-z]+ada|[a-z]+adas|hecho|escrito|visto)\b/i,
|
|
10081
|
+
/\bpor\s+(el|la|los|las|un|una)\b/i
|
|
10082
|
+
],
|
|
10083
|
+
de: [
|
|
10084
|
+
/\b(wird|wurden|wurde|worden|geworden)\s+([a-z]+t|[a-z]+en)\b/i,
|
|
10085
|
+
/\bvon\s+(dem|der|den|einem|einer)\b/i
|
|
10086
|
+
]
|
|
10087
|
+
};
|
|
10088
|
+
static analyze(text, language = "en") {
|
|
10089
|
+
if (!text || text.trim().length === 0) {
|
|
10090
|
+
return {
|
|
10091
|
+
totalSentences: 0,
|
|
10092
|
+
totalWords: 0,
|
|
10093
|
+
averageWordsPerSentence: 0,
|
|
10094
|
+
passiveVoiceRatio: 0,
|
|
10095
|
+
passiveSentencesCount: 0,
|
|
10096
|
+
complexSentencesCount: 0,
|
|
10097
|
+
gradeLevelEstimate: 0,
|
|
10098
|
+
readabilityEaseScore: 100,
|
|
10099
|
+
status: "clear_and_punchy",
|
|
10100
|
+
keyWarnings: [],
|
|
10101
|
+
sentences: []
|
|
10102
|
+
};
|
|
10103
|
+
}
|
|
10104
|
+
const rawSentences = text.replace(/([.?!])\s*(?=[A-Z0-9À-ÖØ-ß])/g, "$1|").split("|").map((s) => s.trim()).filter((s) => s.length > 3);
|
|
10105
|
+
const patterns = this.PASSIVE_PATTERNS[language] || this.PASSIVE_PATTERNS.en;
|
|
10106
|
+
const sentenceDetails = [];
|
|
10107
|
+
let totalWords = 0;
|
|
10108
|
+
let passiveCount = 0;
|
|
10109
|
+
let complexCount = 0;
|
|
10110
|
+
for (const sentence of rawSentences) {
|
|
10111
|
+
const words = sentence.split(/\s+/).filter(Boolean);
|
|
10112
|
+
const wCount = words.length;
|
|
10113
|
+
totalWords += wCount;
|
|
10114
|
+
const isPassive = patterns.some((regex) => regex.test(sentence));
|
|
10115
|
+
const isTooLong = wCount > 22;
|
|
10116
|
+
if (isPassive)
|
|
10117
|
+
passiveCount++;
|
|
10118
|
+
if (isTooLong)
|
|
10119
|
+
complexCount++;
|
|
10120
|
+
sentenceDetails.push({
|
|
10121
|
+
text: sentence,
|
|
10122
|
+
wordCount: wCount,
|
|
10123
|
+
isPassive,
|
|
10124
|
+
isTooLong
|
|
10125
|
+
});
|
|
10126
|
+
}
|
|
10127
|
+
const totalSentences = Math.max(1, rawSentences.length);
|
|
10128
|
+
const avgWordsPerSentence = Math.round(totalWords / totalSentences * 10) / 10;
|
|
10129
|
+
const passiveVoiceRatio = Math.round(passiveCount / totalSentences * 100) / 100;
|
|
10130
|
+
const syllableEstimate = totalWords * 1.4;
|
|
10131
|
+
const ease = Math.max(10, Math.min(100, Math.round(206.835 - 1.015 * avgWordsPerSentence - 84.6 * (syllableEstimate / totalWords))));
|
|
10132
|
+
const gradeLevel = Math.max(4, Math.round(0.39 * avgWordsPerSentence + 11.8 * (syllableEstimate / totalWords) - 15.59));
|
|
10133
|
+
const warnings = [];
|
|
10134
|
+
if (passiveVoiceRatio > 0.15) {
|
|
10135
|
+
warnings.push(`Passive voice is ${Math.round(passiveVoiceRatio * 100)}% (Target: < 10%). Convert passive constructions to active voice.`);
|
|
10136
|
+
}
|
|
10137
|
+
if (complexCount > totalSentences * 0.25) {
|
|
10138
|
+
warnings.push(`${complexCount} sentences exceed 22 words. Break long compound sentences into shorter, punchier statements.`);
|
|
10139
|
+
}
|
|
10140
|
+
if (avgWordsPerSentence > 18) {
|
|
10141
|
+
warnings.push(`Average sentence length is ${avgWordsPerSentence} words (Target: 12-16 words).`);
|
|
10142
|
+
}
|
|
10143
|
+
let status = "clear_and_punchy";
|
|
10144
|
+
if (ease < 50 || passiveVoiceRatio > 0.2) {
|
|
10145
|
+
status = "hard_to_read";
|
|
10146
|
+
} else if (ease < 65 || passiveVoiceRatio > 0.12) {
|
|
10147
|
+
status = "acceptable";
|
|
10148
|
+
}
|
|
10149
|
+
return {
|
|
10150
|
+
totalSentences,
|
|
10151
|
+
totalWords,
|
|
10152
|
+
averageWordsPerSentence: avgWordsPerSentence,
|
|
10153
|
+
passiveVoiceRatio,
|
|
10154
|
+
passiveSentencesCount: passiveCount,
|
|
10155
|
+
complexSentencesCount: complexCount,
|
|
10156
|
+
gradeLevelEstimate: gradeLevel,
|
|
10157
|
+
readabilityEaseScore: ease,
|
|
10158
|
+
status,
|
|
10159
|
+
keyWarnings: warnings,
|
|
10160
|
+
sentences: sentenceDetails
|
|
10161
|
+
};
|
|
10162
|
+
}
|
|
10163
|
+
}
|
|
10164
|
+
|
|
10165
|
+
// src/above-fold-cro-auditor.ts
|
|
10166
|
+
class AboveFoldCroAuditor {
|
|
10167
|
+
static STRONG_ACTION_VERBS = [
|
|
10168
|
+
/start/i,
|
|
10169
|
+
/get/i,
|
|
10170
|
+
/try/i,
|
|
10171
|
+
/claim/i,
|
|
10172
|
+
/launch/i,
|
|
10173
|
+
/boost/i,
|
|
10174
|
+
/automate/i,
|
|
10175
|
+
/démarrer/i,
|
|
10176
|
+
/essayer/i,
|
|
10177
|
+
/obtenir/i,
|
|
10178
|
+
/profiter/i,
|
|
10179
|
+
/iniciar/i,
|
|
10180
|
+
/probar/i,
|
|
10181
|
+
/jetzt/i,
|
|
10182
|
+
/kostenlos/i
|
|
10183
|
+
];
|
|
10184
|
+
static WEAK_ACTION_VERBS = [
|
|
10185
|
+
/^submit$/i,
|
|
10186
|
+
/^click here$/i,
|
|
10187
|
+
/^learn more$/i,
|
|
10188
|
+
/^envoyer$/i,
|
|
10189
|
+
/^cliquez ici$/i,
|
|
10190
|
+
/^en savoir plus$/i,
|
|
10191
|
+
/^weiter$/i,
|
|
10192
|
+
/^leer más$/i
|
|
10193
|
+
];
|
|
10194
|
+
static auditHero(input) {
|
|
10195
|
+
const criteria = [];
|
|
10196
|
+
const fixes = [];
|
|
10197
|
+
const h1Words = input.h1.trim().split(/\s+/).length;
|
|
10198
|
+
const isH1Clear = h1Words >= 4 && h1Words <= 14;
|
|
10199
|
+
let h1Score = isH1Clear ? 25 : 12;
|
|
10200
|
+
if (!isH1Clear) {
|
|
10201
|
+
fixes.push("Optimize H1 length between 4 and 12 words. Make the specific outcome and audience explicit.");
|
|
10202
|
+
}
|
|
10203
|
+
criteria.push({
|
|
10204
|
+
name: "Headline Clarity & Value Proposition",
|
|
10205
|
+
passed: isH1Clear,
|
|
10206
|
+
score: h1Score,
|
|
10207
|
+
feedback: isH1Clear ? "Headline is concise, outcome-oriented, and immediately readable in < 3 seconds." : "Headline is either too vague (< 4 words) or too dense (> 14 words)."
|
|
10208
|
+
});
|
|
10209
|
+
const hasStrongVerb = this.STRONG_ACTION_VERBS.some((r) => r.test(input.primaryCtaText));
|
|
10210
|
+
const hasWeakVerb = this.WEAK_ACTION_VERBS.some((r) => r.test(input.primaryCtaText.trim()));
|
|
10211
|
+
const isCtaEffective = hasStrongVerb && !hasWeakVerb;
|
|
10212
|
+
let ctaScore = isCtaEffective ? 25 : hasWeakVerb ? 5 : 15;
|
|
10213
|
+
if (!isCtaEffective) {
|
|
10214
|
+
fixes.push(`Replace generic CTA "${input.primaryCtaText}" with an outcome-focused action verb (e.g. "Start Free Trial", "Get Instant Access", "Démarrer Gratuitement").`);
|
|
10215
|
+
}
|
|
10216
|
+
criteria.push({
|
|
10217
|
+
name: "Primary CTA Contrast & Action Verb",
|
|
10218
|
+
passed: isCtaEffective,
|
|
10219
|
+
score: ctaScore,
|
|
10220
|
+
feedback: isCtaEffective ? `CTA "${input.primaryCtaText}" uses a strong, high-conversion action trigger.` : `CTA "${input.primaryCtaText}" lacks urgency or is too generic.`
|
|
10221
|
+
});
|
|
10222
|
+
const hasProof = Boolean(input.hasTestimonialsOrStars || input.hasCustomerLogos);
|
|
10223
|
+
let proofScore = hasProof ? 20 : 0;
|
|
10224
|
+
if (!hasProof) {
|
|
10225
|
+
fixes.push("Add social proof above the fold (e.g. 'Rated 4.9/5 by 1,200+ teams' or client logos).");
|
|
10226
|
+
}
|
|
10227
|
+
criteria.push({
|
|
10228
|
+
name: "Above-the-Fold Social Proof & Ratings",
|
|
10229
|
+
passed: hasProof,
|
|
10230
|
+
score: proofScore,
|
|
10231
|
+
feedback: hasProof ? "Trust signals and social proof are visible immediately above the fold." : "Missing social proof above the fold. Increases visitor bounce risk."
|
|
10232
|
+
});
|
|
10233
|
+
const hasReversal = Boolean(input.hasRiskReversalOrGuarantee);
|
|
10234
|
+
let reversalScore = hasReversal ? 20 : 5;
|
|
10235
|
+
if (!hasReversal) {
|
|
10236
|
+
fixes.push("Add a friction-killer under the CTA (e.g. 'No credit card required • Cancel anytime • 14-day free trial').");
|
|
10237
|
+
}
|
|
10238
|
+
criteria.push({
|
|
10239
|
+
name: "Risk Reversal & Friction Elimination",
|
|
10240
|
+
passed: hasReversal,
|
|
10241
|
+
score: reversalScore,
|
|
10242
|
+
feedback: hasReversal ? "Zero-risk micro-copy present under the main action trigger." : "No risk reversal micro-copy found under CTA."
|
|
10243
|
+
});
|
|
10244
|
+
const subWords = (input.subheadline || "").trim().split(/\s+/).length;
|
|
10245
|
+
const isSubheadlineSolid = subWords >= 8 && subWords <= 30;
|
|
10246
|
+
let subScore = isSubheadlineSolid ? 10 : 5;
|
|
10247
|
+
if (!isSubheadlineSolid) {
|
|
10248
|
+
fixes.push("Add a supporting subheadline (12-25 words) explaining HOW the product solves the pain point.");
|
|
10249
|
+
}
|
|
10250
|
+
criteria.push({
|
|
10251
|
+
name: "Subheadline Supporting Context",
|
|
10252
|
+
passed: isSubheadlineSolid,
|
|
10253
|
+
score: subScore,
|
|
10254
|
+
feedback: isSubheadlineSolid ? "Subheadline reinforces the value proposition with concrete details." : "Subheadline is missing or insufficient."
|
|
10255
|
+
});
|
|
10256
|
+
const croScore = h1Score + ctaScore + proofScore + reversalScore + subScore;
|
|
10257
|
+
const rating = croScore >= 80 ? "high_converting" : croScore >= 55 ? "solid" : "high_friction";
|
|
10258
|
+
return {
|
|
10259
|
+
croScore,
|
|
10260
|
+
rating,
|
|
10261
|
+
criteria,
|
|
10262
|
+
recommendedFixes: fixes,
|
|
10263
|
+
suggestedHeroVariant: {
|
|
10264
|
+
h1: input.h1.includes("—") ? input.h1 : `${input.h1} — Built for Fast Growing Teams`,
|
|
10265
|
+
subheadline: input.subheadline || "Automate operational workflows, boost organic search traffic, and cut manual workload in half with instant setup.",
|
|
10266
|
+
ctaText: "Start Free Trial — Instant Setup",
|
|
10267
|
+
reassuranceBadge: "No credit card required • 14-day trial • 100% Free migration"
|
|
10268
|
+
}
|
|
10269
|
+
};
|
|
10270
|
+
}
|
|
10271
|
+
}
|
|
10272
|
+
|
|
10273
|
+
// src/context-templates-generator.ts
|
|
10274
|
+
class ContextTemplatesGenerator {
|
|
10275
|
+
static generateAllContextFiles(input) {
|
|
10276
|
+
return {
|
|
10277
|
+
"context/brand-voice.md": this.generateBrandVoice(input),
|
|
10278
|
+
"context/features.md": this.generateFeatures(input),
|
|
10279
|
+
"context/internal-links-map.md": this.generateInternalLinksMap(input),
|
|
10280
|
+
"context/style-guide.md": this.generateStyleGuide(input),
|
|
10281
|
+
"context/target-keywords.md": this.generateTargetKeywords(input),
|
|
10282
|
+
"context/competitor-analysis.md": this.generateCompetitorAnalysis(input),
|
|
10283
|
+
"context/seo-guidelines.md": this.generateSeoGuidelines(input),
|
|
10284
|
+
"context/cro-best-practices.md": this.generateCroBestPractices(input)
|
|
10285
|
+
};
|
|
10286
|
+
}
|
|
10287
|
+
static generateBrandVoice(input) {
|
|
10288
|
+
return `# Brand Voice & Messaging Framework — ${input.brandName}
|
|
10289
|
+
|
|
10290
|
+
## 1. Core Voice Pillars
|
|
10291
|
+
- **Authoritative yet Approachable:** Speak with deep domain expertise in ${input.industry}, without academic jargon.
|
|
10292
|
+
- **Direct & Action-Oriented:** Get straight to the solution in the first 5 words. No fluff or generic preambles.
|
|
10293
|
+
- **Outcome-Focused:** Emphasize time saved, revenue recovered, and operational ease for ${input.targetAudience}.
|
|
10294
|
+
|
|
10295
|
+
## 2. Terminology & Word Preferences
|
|
10296
|
+
- **Preferred Words:** Streamlined, automated, verified, instant, high-performance, precision.
|
|
10297
|
+
- **Banned Words:** Delve, tapestry, testament, crucial, groundbreaking, revolutionize, in today's fast-paced world.
|
|
10298
|
+
|
|
10299
|
+
## 3. Core Audience
|
|
10300
|
+
- **Primary Persona:** ${input.targetAudience}
|
|
10301
|
+
- **Primary Domain:** ${input.domain}
|
|
10302
|
+
`.trim();
|
|
10303
|
+
}
|
|
10304
|
+
static generateFeatures(input) {
|
|
10305
|
+
const list = input.coreFeatures.map((f) => `- **${f}:** Enterprise-grade workflow automation and real-time synchronization.`).join(`
|
|
10306
|
+
`);
|
|
10307
|
+
return `# Product Features & Capabilities — ${input.brandName}
|
|
10308
|
+
|
|
10309
|
+
## 1. Feature Catalog
|
|
10310
|
+
${list}
|
|
10311
|
+
|
|
10312
|
+
## 2. Competitive Value Proposition
|
|
10313
|
+
- Instant setup (< 3 minutes).
|
|
10314
|
+
- Zero complex infrastructure requirements.
|
|
10315
|
+
- 100% scalable in-memory architecture.
|
|
10316
|
+
`.trim();
|
|
10317
|
+
}
|
|
10318
|
+
static generateInternalLinksMap(input) {
|
|
10319
|
+
return `# Internal Links Architecture Map — ${input.domain}
|
|
10320
|
+
|
|
10321
|
+
## 1. Core Pillar Hubs
|
|
10322
|
+
- **Homepage:** \`${input.domain}/\` (Anchor: "${input.brandName}", "${input.industry} Software")
|
|
10323
|
+
- **Solutions & PSEO Hub:** \`${input.domain}/solutions\` (Anchor: "Explore all solutions", "Platform features")
|
|
10324
|
+
- **Pricing & Plans:** \`${input.domain}/pricing\` (Anchor: "View pricing plans", "Compare tiers")
|
|
10325
|
+
- **Free Tools:** \`${input.domain}/tools\` (Anchor: "Free interactive calculators", "SEO tools")
|
|
10326
|
+
|
|
10327
|
+
## 2. Linking Best Practices
|
|
10328
|
+
- Add 3-5 relevant internal links per 2,000 words.
|
|
10329
|
+
- Use descriptive, keyword-rich anchor text rather than "click here".
|
|
10330
|
+
`.trim();
|
|
10331
|
+
}
|
|
10332
|
+
static generateStyleGuide(input) {
|
|
10333
|
+
return `# Editorial & Style Guide — ${input.brandName}
|
|
10334
|
+
|
|
10335
|
+
## 1. Formatting Rules
|
|
10336
|
+
- **Sentence Length:** 12 to 18 words on average. Never exceed 24 words in a single sentence.
|
|
10337
|
+
- **Paragraphs:** 2 to 4 sentences maximum for high mobile readability.
|
|
10338
|
+
- **Subheadings:** Introduce an H2 or H3 every 250-350 words.
|
|
10339
|
+
- **Punctuation:** Limit em-dashes (—) to 1 per 500 words. Use clear periods and commas.
|
|
10340
|
+
|
|
10341
|
+
## 2. Readability Target
|
|
10342
|
+
- **Flesch Reading Ease:** 65 to 80.
|
|
10343
|
+
- **Reading Level:** 7th to 9th grade.
|
|
10344
|
+
`.trim();
|
|
10345
|
+
}
|
|
10346
|
+
static generateTargetKeywords(input) {
|
|
10347
|
+
const kwList = input.primaryKeywords.map((k) => `- \`${k}\` (Intent: Commercial/Transactional)`).join(`
|
|
10348
|
+
`);
|
|
10349
|
+
return `# Target Keywords & Topic Clusters — ${input.brandName}
|
|
10350
|
+
|
|
10351
|
+
## 1. Primary Keywords
|
|
10352
|
+
${kwList}
|
|
10353
|
+
|
|
10354
|
+
## 2. Secondary Modifiers & Long-Tail
|
|
10355
|
+
- Best ${input.industry} software for ${input.targetAudience}
|
|
10356
|
+
- How to automate ${input.industry} workflows in 2026
|
|
10357
|
+
- ${input.brandName} pricing and alternatives
|
|
10358
|
+
`.trim();
|
|
10359
|
+
}
|
|
10360
|
+
static generateCompetitorAnalysis(input) {
|
|
10361
|
+
return `# Competitor Analysis & Differentiation — ${input.brandName}
|
|
10362
|
+
|
|
10363
|
+
## 1. Market Overview
|
|
10364
|
+
- **Our Positioning:** The fastest, lightest, in-memory automation platform for ${input.industry}.
|
|
10365
|
+
- **Key Differentiator:** 100% cloud-native, sub-millisecond response time, no heavy legacy bloat.
|
|
10366
|
+
|
|
10367
|
+
## 2. Comparison Angles
|
|
10368
|
+
- Transparent pricing with zero hidden add-on fees.
|
|
10369
|
+
- Modern API & MCP integrations (Claude Code, Cursor, Antigravity).
|
|
10370
|
+
`.trim();
|
|
10371
|
+
}
|
|
10372
|
+
static generateSeoGuidelines(input) {
|
|
10373
|
+
return `# SEO Technical & On-Page Requirements — ${input.brandName}
|
|
10374
|
+
|
|
10375
|
+
## 1. Title & Meta Descriptions
|
|
10376
|
+
- **Title Tag:** 50-60 characters, primary keyword in front.
|
|
10377
|
+
- **Meta Description:** 140-155 characters, includes value prop and soft CTA.
|
|
10378
|
+
|
|
10379
|
+
## 2. Schema.org Graphs
|
|
10380
|
+
- Inject valid JSON-LD graph on all programmatic pages (SoftwareApplication, LocalBusiness, FAQPage, Breadcrumbs).
|
|
10381
|
+
- Zero syntax errors or missing required fields.
|
|
10382
|
+
`.trim();
|
|
10383
|
+
}
|
|
10384
|
+
static generateCroBestPractices(input) {
|
|
10385
|
+
return `# Conversion Rate Optimization (CRO) Standards — ${input.brandName}
|
|
10386
|
+
|
|
10387
|
+
## 1. Above-The-Fold Checklist
|
|
10388
|
+
- [ ] Clear H1 communicating the specific outcome in < 3 seconds.
|
|
10389
|
+
- [ ] High-contrast primary CTA with strong action verb.
|
|
10390
|
+
- [ ] Social proof visible without scrolling (star ratings, reviews, client logos).
|
|
10391
|
+
- [ ] Risk reversal micro-copy directly beneath the main button ("No credit card required").
|
|
10392
|
+
`.trim();
|
|
10393
|
+
}
|
|
10394
|
+
}
|
|
10395
|
+
|
|
9905
10396
|
// src/index.ts
|
|
9906
10397
|
function createLynxSeoEngine(config) {
|
|
9907
10398
|
return new LynxSeoEngine(config);
|
|
@@ -9916,6 +10407,11 @@ var LynxSeo = {
|
|
|
9916
10407
|
googleIndexing: GoogleIndexingClient,
|
|
9917
10408
|
aeoSnippet: AeoSnippetSynthesizer,
|
|
9918
10409
|
cannibalization: SemanticCannibalizationDetector,
|
|
10410
|
+
humanityScrubber: HumanityAiScrubber,
|
|
10411
|
+
serpLength: SerpLengthComparator,
|
|
10412
|
+
readabilityComplexity: PassiveVoiceComplexityAnalyzer,
|
|
10413
|
+
aboveFoldCro: AboveFoldCroAuditor,
|
|
10414
|
+
contextTemplates: ContextTemplatesGenerator,
|
|
9919
10415
|
inspectMeta: SiteAuditor.inspectMeta,
|
|
9920
10416
|
crawlDomain: DeepCrawlerAuditor.crawlAndAuditDomain,
|
|
9921
10417
|
inspectHtmlSnapshot: DeepCrawlerAuditor.inspectHtmlSnapshot,
|
|
@@ -9984,6 +10480,7 @@ export {
|
|
|
9984
10480
|
SocialAdsSocialSeoEngine,
|
|
9985
10481
|
SiteAuditor,
|
|
9986
10482
|
SerpRankHistoryEngine,
|
|
10483
|
+
SerpLengthComparator,
|
|
9987
10484
|
SerpClient,
|
|
9988
10485
|
SeoOpportunitiesDecayDetector,
|
|
9989
10486
|
SemanticCannibalizationDetector,
|
|
@@ -9996,6 +10493,7 @@ export {
|
|
|
9996
10493
|
PublicRoutesManifestEngine,
|
|
9997
10494
|
PseoMatrixEngine,
|
|
9998
10495
|
PowerWordsPsychologyEngine,
|
|
10496
|
+
PassiveVoiceComplexityAnalyzer,
|
|
9999
10497
|
PageRegenerationTracker,
|
|
10000
10498
|
PSEO_AGENT_SYSTEM_PROMPT,
|
|
10001
10499
|
OgImageGenerator,
|
|
@@ -10024,6 +10522,7 @@ export {
|
|
|
10024
10522
|
IndexNowClient,
|
|
10025
10523
|
I18nDetector,
|
|
10026
10524
|
HyperswitchGateway,
|
|
10525
|
+
HumanityAiScrubber,
|
|
10027
10526
|
GoogleIndexingClient,
|
|
10028
10527
|
GoogleBusinessProfileEngine,
|
|
10029
10528
|
GeoMeshLinkingEngine,
|
|
@@ -10037,6 +10536,7 @@ export {
|
|
|
10037
10536
|
CrosslinkScorerEngine,
|
|
10038
10537
|
CroCopywritingEngine,
|
|
10039
10538
|
CopywritingFrameworksMaster,
|
|
10539
|
+
ContextTemplatesGenerator,
|
|
10040
10540
|
CONTENT_AI_40_TOOLS,
|
|
10041
10541
|
BrandDnaCalendarEngine,
|
|
10042
10542
|
BacklinksClient,
|
|
@@ -10046,5 +10546,6 @@ export {
|
|
|
10046
10546
|
AiCopilotClient,
|
|
10047
10547
|
AiBotsLogAnalyzer,
|
|
10048
10548
|
AeoSnippetSynthesizer,
|
|
10049
|
-
AdIntelligenceCroEngine
|
|
10549
|
+
AdIntelligenceCroEngine,
|
|
10550
|
+
AboveFoldCroAuditor
|
|
10050
10551
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ✍️ Readability, Passive Voice & Sentence Complexity Analyzer
|
|
3
|
+
* Analyzes sentence length variance, passive voice frequency, and paragraph density
|
|
4
|
+
* across English, French, German, and Spanish.
|
|
5
|
+
*/
|
|
6
|
+
export interface SentenceDetail {
|
|
7
|
+
text: string;
|
|
8
|
+
wordCount: number;
|
|
9
|
+
isPassive: boolean;
|
|
10
|
+
isTooLong: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ReadabilityComplexityReport {
|
|
13
|
+
totalSentences: number;
|
|
14
|
+
totalWords: number;
|
|
15
|
+
averageWordsPerSentence: number;
|
|
16
|
+
passiveVoiceRatio: number;
|
|
17
|
+
passiveSentencesCount: number;
|
|
18
|
+
complexSentencesCount: number;
|
|
19
|
+
gradeLevelEstimate: number;
|
|
20
|
+
readabilityEaseScore: number;
|
|
21
|
+
status: "clear_and_punchy" | "acceptable" | "hard_to_read";
|
|
22
|
+
keyWarnings: string[];
|
|
23
|
+
sentences: SentenceDetail[];
|
|
24
|
+
}
|
|
25
|
+
export declare class PassiveVoiceComplexityAnalyzer {
|
|
26
|
+
private static readonly PASSIVE_PATTERNS;
|
|
27
|
+
/**
|
|
28
|
+
* Analyzes text for passive voice, sentence complexity, and readability.
|
|
29
|
+
*/
|
|
30
|
+
static analyze(text: string, language?: string): ReadabilityComplexityReport;
|
|
31
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 📏 SERP Content Length Comparator
|
|
3
|
+
* Analyzes top 10-20 SERP competitor word counts, computes statistical benchmarks
|
|
4
|
+
* (Median, 75th Percentile, Top 3 Average), and recommends the optimal content length.
|
|
5
|
+
*/
|
|
6
|
+
export interface CompetitorWordCount {
|
|
7
|
+
url: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
rank: number;
|
|
10
|
+
wordCount: number;
|
|
11
|
+
}
|
|
12
|
+
export interface SerpLengthAnalysis {
|
|
13
|
+
userWordCount: number;
|
|
14
|
+
targetQuery: string;
|
|
15
|
+
competitorCount: number;
|
|
16
|
+
competitors: CompetitorWordCount[];
|
|
17
|
+
stats: {
|
|
18
|
+
min: number;
|
|
19
|
+
max: number;
|
|
20
|
+
median: number;
|
|
21
|
+
p75: number;
|
|
22
|
+
top3Average: number;
|
|
23
|
+
recommendedTargetWords: number;
|
|
24
|
+
};
|
|
25
|
+
gapToTarget: number;
|
|
26
|
+
status: "insufficient" | "optimal" | "excessive";
|
|
27
|
+
actionPlan: string;
|
|
28
|
+
}
|
|
29
|
+
export declare class SerpLengthComparator {
|
|
30
|
+
/**
|
|
31
|
+
* Evaluates content length against real or simulated SERP competitor word counts.
|
|
32
|
+
*/
|
|
33
|
+
static compareLength(userWordCount: number, targetQuery: string, competitors?: CompetitorWordCount[]): SerpLengthAnalysis;
|
|
34
|
+
}
|