@rankcli/agent-runtime 0.0.1
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 +242 -0
- package/dist/analyzer-2CSWIQGD.mjs +6 -0
- package/dist/chunk-YNZYHEYM.mjs +774 -0
- package/dist/index.d.mts +4012 -0
- package/dist/index.d.ts +4012 -0
- package/dist/index.js +29672 -0
- package/dist/index.mjs +28602 -0
- package/package.json +53 -0
- package/scripts/build-deno.ts +134 -0
- package/src/audit/ai/analyzer.ts +347 -0
- package/src/audit/ai/index.ts +29 -0
- package/src/audit/ai/prompts/content-analysis.ts +271 -0
- package/src/audit/ai/types.ts +179 -0
- package/src/audit/checks/additional-checks.ts +439 -0
- package/src/audit/checks/ai-citation-worthiness.ts +399 -0
- package/src/audit/checks/ai-content-structure.ts +325 -0
- package/src/audit/checks/ai-readiness.ts +339 -0
- package/src/audit/checks/anchor-text.ts +179 -0
- package/src/audit/checks/answer-conciseness.ts +322 -0
- package/src/audit/checks/asset-minification.ts +270 -0
- package/src/audit/checks/bing-optimization.ts +206 -0
- package/src/audit/checks/brand-mention-optimization.ts +349 -0
- package/src/audit/checks/caching-headers.ts +305 -0
- package/src/audit/checks/canonical-advanced.ts +150 -0
- package/src/audit/checks/canonical-domain.ts +196 -0
- package/src/audit/checks/citation-quality.ts +358 -0
- package/src/audit/checks/client-rendering.ts +542 -0
- package/src/audit/checks/color-contrast.ts +342 -0
- package/src/audit/checks/content-freshness.ts +170 -0
- package/src/audit/checks/content-science.ts +589 -0
- package/src/audit/checks/conversion-elements.ts +526 -0
- package/src/audit/checks/crawlability.ts +220 -0
- package/src/audit/checks/directory-listing.ts +172 -0
- package/src/audit/checks/dom-analysis.ts +191 -0
- package/src/audit/checks/dom-size.ts +246 -0
- package/src/audit/checks/duplicate-content.ts +194 -0
- package/src/audit/checks/eeat-signals.ts +990 -0
- package/src/audit/checks/entity-seo.ts +396 -0
- package/src/audit/checks/featured-snippet.ts +473 -0
- package/src/audit/checks/freshness-signals.ts +443 -0
- package/src/audit/checks/funnel-intent.ts +463 -0
- package/src/audit/checks/hreflang.ts +174 -0
- package/src/audit/checks/html-compliance.ts +302 -0
- package/src/audit/checks/image-dimensions.ts +167 -0
- package/src/audit/checks/images.ts +160 -0
- package/src/audit/checks/indexnow.ts +275 -0
- package/src/audit/checks/interactive-tools.ts +475 -0
- package/src/audit/checks/internal-link-graph.ts +436 -0
- package/src/audit/checks/keyword-analysis.ts +239 -0
- package/src/audit/checks/keyword-cannibalization.ts +385 -0
- package/src/audit/checks/keyword-placement.ts +471 -0
- package/src/audit/checks/links.ts +203 -0
- package/src/audit/checks/llms-txt.ts +224 -0
- package/src/audit/checks/local-seo.ts +296 -0
- package/src/audit/checks/mobile.ts +167 -0
- package/src/audit/checks/modern-images.ts +226 -0
- package/src/audit/checks/navboost-signals.ts +395 -0
- package/src/audit/checks/on-page.ts +209 -0
- package/src/audit/checks/page-resources.ts +285 -0
- package/src/audit/checks/pagination.ts +180 -0
- package/src/audit/checks/performance.ts +153 -0
- package/src/audit/checks/platform-presence.ts +580 -0
- package/src/audit/checks/redirect-analysis.ts +153 -0
- package/src/audit/checks/redirect-chain.ts +389 -0
- package/src/audit/checks/resource-hints.ts +420 -0
- package/src/audit/checks/responsive-css.ts +247 -0
- package/src/audit/checks/responsive-images.ts +396 -0
- package/src/audit/checks/review-ecosystem.ts +415 -0
- package/src/audit/checks/robots-validation.ts +373 -0
- package/src/audit/checks/security-headers.ts +172 -0
- package/src/audit/checks/security.ts +144 -0
- package/src/audit/checks/serp-preview.ts +251 -0
- package/src/audit/checks/site-maturity.ts +444 -0
- package/src/audit/checks/social-meta.test.ts +275 -0
- package/src/audit/checks/social-meta.ts +134 -0
- package/src/audit/checks/soft-404.ts +151 -0
- package/src/audit/checks/structured-data.ts +238 -0
- package/src/audit/checks/tech-detection.ts +496 -0
- package/src/audit/checks/topical-clusters.ts +435 -0
- package/src/audit/checks/tracker-bloat.ts +462 -0
- package/src/audit/checks/tracking-verification.test.ts +371 -0
- package/src/audit/checks/tracking-verification.ts +636 -0
- package/src/audit/checks/url-safety.ts +682 -0
- package/src/audit/deno-entry.ts +66 -0
- package/src/audit/discovery/index.ts +15 -0
- package/src/audit/discovery/link-crawler.ts +232 -0
- package/src/audit/discovery/repo-routes.ts +347 -0
- package/src/audit/engine.ts +620 -0
- package/src/audit/fixes/index.ts +209 -0
- package/src/audit/fixes/social-meta-fixes.test.ts +329 -0
- package/src/audit/fixes/social-meta-fixes.ts +463 -0
- package/src/audit/index.ts +74 -0
- package/src/audit/runner.test.ts +299 -0
- package/src/audit/runner.ts +130 -0
- package/src/audit/types.ts +1953 -0
- package/src/content/featured-snippet.ts +367 -0
- package/src/content/generator.test.ts +534 -0
- package/src/content/generator.ts +501 -0
- package/src/content/headline.ts +317 -0
- package/src/content/index.ts +62 -0
- package/src/content/intent.ts +258 -0
- package/src/content/keyword-density.ts +349 -0
- package/src/content/readability.ts +262 -0
- package/src/executor.ts +336 -0
- package/src/fixer.ts +416 -0
- package/src/frameworks/detector.test.ts +248 -0
- package/src/frameworks/detector.ts +371 -0
- package/src/frameworks/index.ts +68 -0
- package/src/frameworks/recipes/angular.yaml +171 -0
- package/src/frameworks/recipes/astro.yaml +206 -0
- package/src/frameworks/recipes/django.yaml +180 -0
- package/src/frameworks/recipes/laravel.yaml +137 -0
- package/src/frameworks/recipes/nextjs.yaml +268 -0
- package/src/frameworks/recipes/nuxt.yaml +175 -0
- package/src/frameworks/recipes/rails.yaml +188 -0
- package/src/frameworks/recipes/react.yaml +202 -0
- package/src/frameworks/recipes/sveltekit.yaml +154 -0
- package/src/frameworks/recipes/vue.yaml +137 -0
- package/src/frameworks/recipes/wordpress.yaml +209 -0
- package/src/frameworks/suggestion-engine.ts +320 -0
- package/src/geo/geo-content.test.ts +305 -0
- package/src/geo/geo-content.ts +266 -0
- package/src/geo/geo-history.test.ts +473 -0
- package/src/geo/geo-history.ts +433 -0
- package/src/geo/geo-tracker.test.ts +359 -0
- package/src/geo/geo-tracker.ts +411 -0
- package/src/geo/index.ts +10 -0
- package/src/git/commit-helper.test.ts +261 -0
- package/src/git/commit-helper.ts +329 -0
- package/src/git/index.ts +12 -0
- package/src/git/pr-helper.test.ts +284 -0
- package/src/git/pr-helper.ts +307 -0
- package/src/index.ts +66 -0
- package/src/keywords/ai-keyword-engine.ts +1062 -0
- package/src/keywords/ai-summarizer.ts +387 -0
- package/src/keywords/ci-mode.ts +555 -0
- package/src/keywords/engine.ts +359 -0
- package/src/keywords/index.ts +151 -0
- package/src/keywords/llm-judge.ts +357 -0
- package/src/keywords/nlp-analysis.ts +706 -0
- package/src/keywords/prioritizer.ts +295 -0
- package/src/keywords/site-crawler.ts +342 -0
- package/src/keywords/sources/autocomplete.ts +139 -0
- package/src/keywords/sources/competitive-search.ts +450 -0
- package/src/keywords/sources/competitor-analysis.ts +374 -0
- package/src/keywords/sources/dataforseo.ts +206 -0
- package/src/keywords/sources/free-sources.ts +294 -0
- package/src/keywords/sources/gsc.ts +123 -0
- package/src/keywords/topic-grouping.ts +327 -0
- package/src/keywords/types.ts +144 -0
- package/src/keywords/wizard.ts +457 -0
- package/src/loader.ts +40 -0
- package/src/reports/index.ts +7 -0
- package/src/reports/report-generator.test.ts +293 -0
- package/src/reports/report-generator.ts +713 -0
- package/src/scheduler/alerts.test.ts +458 -0
- package/src/scheduler/alerts.ts +328 -0
- package/src/scheduler/index.ts +8 -0
- package/src/scheduler/scheduled-audit.test.ts +377 -0
- package/src/scheduler/scheduled-audit.ts +149 -0
- package/src/test/integration-test.ts +325 -0
- package/src/tools/analyzer.ts +373 -0
- package/src/tools/crawl.ts +293 -0
- package/src/tools/files.ts +301 -0
- package/src/tools/h1-fixer.ts +249 -0
- package/src/tools/index.ts +67 -0
- package/src/tracking/github-action.ts +326 -0
- package/src/tracking/google-analytics.ts +265 -0
- package/src/tracking/index.ts +45 -0
- package/src/tracking/report-generator.ts +386 -0
- package/src/tracking/search-console.ts +335 -0
- package/src/types.ts +134 -0
- package/src/utils/http.ts +302 -0
- package/src/wasm-adapter.ts +297 -0
- package/src/wasm-entry.ts +14 -0
- package/tsconfig.json +17 -0
- package/tsup.wasm.config.ts +26 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
// Featured Snippet Optimization
|
|
2
|
+
// Analyzes and optimizes content for Google's featured snippets
|
|
3
|
+
|
|
4
|
+
export type SnippetType = 'paragraph' | 'list' | 'table' | 'video' | 'none';
|
|
5
|
+
|
|
6
|
+
export interface FeaturedSnippetAnalysis {
|
|
7
|
+
// Current snippet potential
|
|
8
|
+
snippetPotential: number; // 0-100
|
|
9
|
+
bestSnippetType: SnippetType;
|
|
10
|
+
// Analysis
|
|
11
|
+
hasDirectAnswer: boolean;
|
|
12
|
+
hasNumberedSteps: boolean;
|
|
13
|
+
hasBulletList: boolean;
|
|
14
|
+
hasTable: boolean;
|
|
15
|
+
hasDefinition: boolean;
|
|
16
|
+
// Content recommendations
|
|
17
|
+
recommendations: SnippetRecommendation[];
|
|
18
|
+
// Optimized content suggestions
|
|
19
|
+
suggestedParagraph?: string;
|
|
20
|
+
suggestedListItems?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SnippetRecommendation {
|
|
24
|
+
priority: 'high' | 'medium' | 'low';
|
|
25
|
+
action: string;
|
|
26
|
+
example?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Question patterns that often trigger featured snippets
|
|
30
|
+
const SNIPPET_TRIGGER_PATTERNS = [
|
|
31
|
+
/^what (is|are)\b/i,
|
|
32
|
+
/^how (to|do|does|can|much|many|long)\b/i,
|
|
33
|
+
/^why (is|are|do|does)\b/i,
|
|
34
|
+
/^when (to|should|do|does)\b/i,
|
|
35
|
+
/^where (to|can|do|is)\b/i,
|
|
36
|
+
/^who (is|are|was|were)\b/i,
|
|
37
|
+
/\b(definition|meaning|example|vs|versus)\b/i,
|
|
38
|
+
/\b(best|top|steps|ways|tips|types)\b/i,
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Analyze content for featured snippet optimization
|
|
43
|
+
*/
|
|
44
|
+
export function analyzeFeaturedSnippetPotential(
|
|
45
|
+
query: string,
|
|
46
|
+
content: string,
|
|
47
|
+
headings: string[]
|
|
48
|
+
): FeaturedSnippetAnalysis {
|
|
49
|
+
const recommendations: SnippetRecommendation[] = [];
|
|
50
|
+
let snippetPotential = 0;
|
|
51
|
+
|
|
52
|
+
// Check if query is snippet-worthy
|
|
53
|
+
const isSnippetQuery = SNIPPET_TRIGGER_PATTERNS.some(p => p.test(query));
|
|
54
|
+
if (isSnippetQuery) {
|
|
55
|
+
snippetPotential += 20;
|
|
56
|
+
} else {
|
|
57
|
+
recommendations.push({
|
|
58
|
+
priority: 'medium',
|
|
59
|
+
action: 'This query may not trigger featured snippets. Consider targeting question-based keywords.',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Analyze content structure
|
|
64
|
+
const hasDirectAnswer = checkForDirectAnswer(query, content);
|
|
65
|
+
const hasNumberedSteps = /(\d+\.\s|\d+\)|\bstep\s+\d+)/i.test(content);
|
|
66
|
+
const hasBulletList = /^[\s]*[-•*]\s/m.test(content) || /<li>/i.test(content);
|
|
67
|
+
const hasTable = /<table/i.test(content) || /\|.*\|.*\|/m.test(content);
|
|
68
|
+
const hasDefinition = checkForDefinition(query, content);
|
|
69
|
+
|
|
70
|
+
// Score based on content elements
|
|
71
|
+
if (hasDirectAnswer) {
|
|
72
|
+
snippetPotential += 25;
|
|
73
|
+
} else {
|
|
74
|
+
recommendations.push({
|
|
75
|
+
priority: 'high',
|
|
76
|
+
action: 'Add a direct answer to the query in the first paragraph',
|
|
77
|
+
example: generateDirectAnswer(query),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (hasNumberedSteps) {
|
|
82
|
+
snippetPotential += 15;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (hasBulletList) {
|
|
86
|
+
snippetPotential += 10;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (hasTable) {
|
|
90
|
+
snippetPotential += 10;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (hasDefinition) {
|
|
94
|
+
snippetPotential += 15;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check heading structure
|
|
98
|
+
const hasQueryInHeading = headings.some(h =>
|
|
99
|
+
h.toLowerCase().includes(query.toLowerCase()) ||
|
|
100
|
+
query.toLowerCase().includes(h.toLowerCase())
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
if (hasQueryInHeading) {
|
|
104
|
+
snippetPotential += 10;
|
|
105
|
+
} else {
|
|
106
|
+
recommendations.push({
|
|
107
|
+
priority: 'high',
|
|
108
|
+
action: 'Include the target query in an H2 or H3 heading',
|
|
109
|
+
example: `<h2>${capitalizeFirst(query)}</h2>`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Determine best snippet type
|
|
114
|
+
const bestSnippetType = determineBestSnippetType(query, content);
|
|
115
|
+
|
|
116
|
+
// Add type-specific recommendations
|
|
117
|
+
if (bestSnippetType === 'paragraph' && !hasDirectAnswer) {
|
|
118
|
+
recommendations.push({
|
|
119
|
+
priority: 'high',
|
|
120
|
+
action: 'Write a 40-60 word paragraph that directly answers the query',
|
|
121
|
+
example: generateParagraphSnippet(query),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (bestSnippetType === 'list' && !hasNumberedSteps && !hasBulletList) {
|
|
126
|
+
recommendations.push({
|
|
127
|
+
priority: 'high',
|
|
128
|
+
action: 'Add a numbered or bulleted list with 5-8 items',
|
|
129
|
+
example: 'Structure as: 1. Step one\n2. Step two\n3. Step three',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (bestSnippetType === 'table' && !hasTable) {
|
|
134
|
+
recommendations.push({
|
|
135
|
+
priority: 'medium',
|
|
136
|
+
action: 'Consider adding a comparison table for this topic',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Content length check
|
|
141
|
+
const wordCount = content.split(/\s+/).length;
|
|
142
|
+
if (wordCount < 300) {
|
|
143
|
+
snippetPotential -= 10;
|
|
144
|
+
recommendations.push({
|
|
145
|
+
priority: 'medium',
|
|
146
|
+
action: 'Expand content to at least 300 words for better snippet eligibility',
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Generate suggested content
|
|
151
|
+
const suggestedParagraph = generateParagraphSnippet(query);
|
|
152
|
+
const suggestedListItems = generateListSnippet(query);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
snippetPotential: Math.max(0, Math.min(100, snippetPotential)),
|
|
156
|
+
bestSnippetType,
|
|
157
|
+
hasDirectAnswer,
|
|
158
|
+
hasNumberedSteps,
|
|
159
|
+
hasBulletList,
|
|
160
|
+
hasTable,
|
|
161
|
+
hasDefinition,
|
|
162
|
+
recommendations,
|
|
163
|
+
suggestedParagraph,
|
|
164
|
+
suggestedListItems,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function checkForDirectAnswer(query: string, content: string): boolean {
|
|
169
|
+
// Look for patterns like "X is Y" or "X are Y" near the beginning
|
|
170
|
+
const firstParagraph = content.split(/\n\n/)[0] || content.slice(0, 500);
|
|
171
|
+
const queryWords = query.toLowerCase().split(/\s+/).filter(w => w.length > 3);
|
|
172
|
+
|
|
173
|
+
// Check if query words appear in first paragraph with "is" or "are"
|
|
174
|
+
const hasDefinitionPattern = queryWords.some(word =>
|
|
175
|
+
new RegExp(`\\b${word}\\b.{0,50}\\b(is|are|refers to|means|defined as)\\b`, 'i').test(firstParagraph)
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
return hasDefinitionPattern;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function checkForDefinition(query: string, content: string): boolean {
|
|
182
|
+
const definitionPatterns = [
|
|
183
|
+
/\b(is defined as|refers to|is a|are a|means that|is the process of)\b/i,
|
|
184
|
+
/\bdefinition:\s/i,
|
|
185
|
+
/\bin other words,\s/i,
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
return definitionPatterns.some(p => p.test(content));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function determineBestSnippetType(query: string, content: string): SnippetType {
|
|
192
|
+
const q = query.toLowerCase();
|
|
193
|
+
|
|
194
|
+
// How-to queries → List snippet
|
|
195
|
+
if (/^how to\b/i.test(q) || /\b(steps|ways|tips|methods)\b/i.test(q)) {
|
|
196
|
+
return 'list';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Comparison queries → Table snippet
|
|
200
|
+
if (/\b(vs|versus|compared to|difference between)\b/i.test(q)) {
|
|
201
|
+
return 'table';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// What/definition queries → Paragraph snippet
|
|
205
|
+
if (/^what (is|are)\b/i.test(q) || /\b(definition|meaning)\b/i.test(q)) {
|
|
206
|
+
return 'paragraph';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// List-style queries
|
|
210
|
+
if (/^(best|top)\s+\d+\b/i.test(q) || /\b(types of|examples of|list of)\b/i.test(q)) {
|
|
211
|
+
return 'list';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Default to paragraph
|
|
215
|
+
return 'paragraph';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function generateDirectAnswer(query: string): string {
|
|
219
|
+
const q = query.toLowerCase();
|
|
220
|
+
|
|
221
|
+
if (q.startsWith('what is ')) {
|
|
222
|
+
const topic = query.slice(8);
|
|
223
|
+
return `${capitalizeFirst(topic)} is [brief definition]. It [key characteristic or function].`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (q.startsWith('how to ')) {
|
|
227
|
+
const topic = query.slice(7);
|
|
228
|
+
return `To ${topic}, follow these steps: 1) [First step], 2) [Second step], 3) [Third step].`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return `[Direct answer to "${query}"] in 40-60 words.`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function generateParagraphSnippet(query: string): string {
|
|
235
|
+
const q = query.toLowerCase();
|
|
236
|
+
|
|
237
|
+
if (q.startsWith('what is ')) {
|
|
238
|
+
const topic = query.slice(8);
|
|
239
|
+
return `${capitalizeFirst(topic)} is a [category] that [main function/purpose]. ` +
|
|
240
|
+
`It is commonly used for [use case] and helps [benefit]. ` +
|
|
241
|
+
`The key features include [feature 1], [feature 2], and [feature 3].`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (q.startsWith('why ')) {
|
|
245
|
+
return `The main reason [topic] is because [primary reason]. ` +
|
|
246
|
+
`Additionally, [secondary reason]. This leads to [outcome/benefit].`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return `[Topic] refers to [definition]. It [main characteristic]. ` +
|
|
250
|
+
`The most important aspects are [aspect 1] and [aspect 2].`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function generateListSnippet(query: string): string[] {
|
|
254
|
+
const q = query.toLowerCase();
|
|
255
|
+
|
|
256
|
+
if (q.startsWith('how to ')) {
|
|
257
|
+
return [
|
|
258
|
+
'Identify your goal and requirements',
|
|
259
|
+
'Research available options',
|
|
260
|
+
'Prepare necessary tools or resources',
|
|
261
|
+
'Follow the step-by-step process',
|
|
262
|
+
'Test and verify results',
|
|
263
|
+
'Make adjustments as needed',
|
|
264
|
+
'Document your process for future reference',
|
|
265
|
+
];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (/\b(best|top)\b/i.test(q)) {
|
|
269
|
+
return [
|
|
270
|
+
'[Option 1] - Best for [use case]',
|
|
271
|
+
'[Option 2] - Best for [use case]',
|
|
272
|
+
'[Option 3] - Best for [use case]',
|
|
273
|
+
'[Option 4] - Best for [use case]',
|
|
274
|
+
'[Option 5] - Best for [use case]',
|
|
275
|
+
];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return [
|
|
279
|
+
'Key point or step 1',
|
|
280
|
+
'Key point or step 2',
|
|
281
|
+
'Key point or step 3',
|
|
282
|
+
'Key point or step 4',
|
|
283
|
+
'Key point or step 5',
|
|
284
|
+
];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function capitalizeFirst(str: string): string {
|
|
288
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Format featured snippet analysis report
|
|
293
|
+
*/
|
|
294
|
+
export function formatFeaturedSnippetReport(analysis: FeaturedSnippetAnalysis): string {
|
|
295
|
+
const lines: string[] = [];
|
|
296
|
+
|
|
297
|
+
lines.push('');
|
|
298
|
+
lines.push('═'.repeat(60));
|
|
299
|
+
lines.push(' FEATURED SNIPPET ANALYSIS');
|
|
300
|
+
lines.push('═'.repeat(60));
|
|
301
|
+
lines.push('');
|
|
302
|
+
|
|
303
|
+
// Potential score
|
|
304
|
+
const potentialIcon = analysis.snippetPotential >= 70 ? '🟢' :
|
|
305
|
+
analysis.snippetPotential >= 40 ? '🟡' : '🔴';
|
|
306
|
+
lines.push(`${potentialIcon} Snippet Potential: ${analysis.snippetPotential}/100`);
|
|
307
|
+
lines.push(` Best Snippet Type: ${analysis.bestSnippetType}`);
|
|
308
|
+
lines.push('');
|
|
309
|
+
|
|
310
|
+
// Content elements
|
|
311
|
+
lines.push('📋 CONTENT ELEMENTS');
|
|
312
|
+
lines.push('─'.repeat(60));
|
|
313
|
+
lines.push(` Direct Answer: ${analysis.hasDirectAnswer ? '✅ Yes' : '❌ No'}`);
|
|
314
|
+
lines.push(` Definition: ${analysis.hasDefinition ? '✅ Yes' : '❌ No'}`);
|
|
315
|
+
lines.push(` Numbered Steps: ${analysis.hasNumberedSteps ? '✅ Yes' : '❌ No'}`);
|
|
316
|
+
lines.push(` Bullet List: ${analysis.hasBulletList ? '✅ Yes' : '❌ No'}`);
|
|
317
|
+
lines.push(` Table: ${analysis.hasTable ? '✅ Yes' : '❌ No'}`);
|
|
318
|
+
lines.push('');
|
|
319
|
+
|
|
320
|
+
// Recommendations
|
|
321
|
+
if (analysis.recommendations.length > 0) {
|
|
322
|
+
lines.push('💡 RECOMMENDATIONS');
|
|
323
|
+
lines.push('─'.repeat(60));
|
|
324
|
+
|
|
325
|
+
const highPriority = analysis.recommendations.filter(r => r.priority === 'high');
|
|
326
|
+
const mediumPriority = analysis.recommendations.filter(r => r.priority === 'medium');
|
|
327
|
+
|
|
328
|
+
if (highPriority.length > 0) {
|
|
329
|
+
lines.push(' HIGH PRIORITY:');
|
|
330
|
+
for (const rec of highPriority) {
|
|
331
|
+
lines.push(` 🔴 ${rec.action}`);
|
|
332
|
+
if (rec.example) {
|
|
333
|
+
lines.push(` Example: ${rec.example}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (mediumPriority.length > 0) {
|
|
339
|
+
lines.push(' MEDIUM PRIORITY:');
|
|
340
|
+
for (const rec of mediumPriority) {
|
|
341
|
+
lines.push(` 🟡 ${rec.action}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
lines.push('');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Suggested content
|
|
348
|
+
if (analysis.suggestedParagraph) {
|
|
349
|
+
lines.push('📝 SUGGESTED PARAGRAPH SNIPPET');
|
|
350
|
+
lines.push('─'.repeat(60));
|
|
351
|
+
lines.push(` ${analysis.suggestedParagraph}`);
|
|
352
|
+
lines.push('');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (analysis.suggestedListItems && analysis.suggestedListItems.length > 0) {
|
|
356
|
+
lines.push('📝 SUGGESTED LIST SNIPPET');
|
|
357
|
+
lines.push('─'.repeat(60));
|
|
358
|
+
for (let i = 0; i < analysis.suggestedListItems.length; i++) {
|
|
359
|
+
lines.push(` ${i + 1}. ${analysis.suggestedListItems[i]}`);
|
|
360
|
+
}
|
|
361
|
+
lines.push('');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
lines.push('═'.repeat(60));
|
|
365
|
+
|
|
366
|
+
return lines.join('\n');
|
|
367
|
+
}
|