@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.
Files changed (178) hide show
  1. package/README.md +242 -0
  2. package/dist/analyzer-2CSWIQGD.mjs +6 -0
  3. package/dist/chunk-YNZYHEYM.mjs +774 -0
  4. package/dist/index.d.mts +4012 -0
  5. package/dist/index.d.ts +4012 -0
  6. package/dist/index.js +29672 -0
  7. package/dist/index.mjs +28602 -0
  8. package/package.json +53 -0
  9. package/scripts/build-deno.ts +134 -0
  10. package/src/audit/ai/analyzer.ts +347 -0
  11. package/src/audit/ai/index.ts +29 -0
  12. package/src/audit/ai/prompts/content-analysis.ts +271 -0
  13. package/src/audit/ai/types.ts +179 -0
  14. package/src/audit/checks/additional-checks.ts +439 -0
  15. package/src/audit/checks/ai-citation-worthiness.ts +399 -0
  16. package/src/audit/checks/ai-content-structure.ts +325 -0
  17. package/src/audit/checks/ai-readiness.ts +339 -0
  18. package/src/audit/checks/anchor-text.ts +179 -0
  19. package/src/audit/checks/answer-conciseness.ts +322 -0
  20. package/src/audit/checks/asset-minification.ts +270 -0
  21. package/src/audit/checks/bing-optimization.ts +206 -0
  22. package/src/audit/checks/brand-mention-optimization.ts +349 -0
  23. package/src/audit/checks/caching-headers.ts +305 -0
  24. package/src/audit/checks/canonical-advanced.ts +150 -0
  25. package/src/audit/checks/canonical-domain.ts +196 -0
  26. package/src/audit/checks/citation-quality.ts +358 -0
  27. package/src/audit/checks/client-rendering.ts +542 -0
  28. package/src/audit/checks/color-contrast.ts +342 -0
  29. package/src/audit/checks/content-freshness.ts +170 -0
  30. package/src/audit/checks/content-science.ts +589 -0
  31. package/src/audit/checks/conversion-elements.ts +526 -0
  32. package/src/audit/checks/crawlability.ts +220 -0
  33. package/src/audit/checks/directory-listing.ts +172 -0
  34. package/src/audit/checks/dom-analysis.ts +191 -0
  35. package/src/audit/checks/dom-size.ts +246 -0
  36. package/src/audit/checks/duplicate-content.ts +194 -0
  37. package/src/audit/checks/eeat-signals.ts +990 -0
  38. package/src/audit/checks/entity-seo.ts +396 -0
  39. package/src/audit/checks/featured-snippet.ts +473 -0
  40. package/src/audit/checks/freshness-signals.ts +443 -0
  41. package/src/audit/checks/funnel-intent.ts +463 -0
  42. package/src/audit/checks/hreflang.ts +174 -0
  43. package/src/audit/checks/html-compliance.ts +302 -0
  44. package/src/audit/checks/image-dimensions.ts +167 -0
  45. package/src/audit/checks/images.ts +160 -0
  46. package/src/audit/checks/indexnow.ts +275 -0
  47. package/src/audit/checks/interactive-tools.ts +475 -0
  48. package/src/audit/checks/internal-link-graph.ts +436 -0
  49. package/src/audit/checks/keyword-analysis.ts +239 -0
  50. package/src/audit/checks/keyword-cannibalization.ts +385 -0
  51. package/src/audit/checks/keyword-placement.ts +471 -0
  52. package/src/audit/checks/links.ts +203 -0
  53. package/src/audit/checks/llms-txt.ts +224 -0
  54. package/src/audit/checks/local-seo.ts +296 -0
  55. package/src/audit/checks/mobile.ts +167 -0
  56. package/src/audit/checks/modern-images.ts +226 -0
  57. package/src/audit/checks/navboost-signals.ts +395 -0
  58. package/src/audit/checks/on-page.ts +209 -0
  59. package/src/audit/checks/page-resources.ts +285 -0
  60. package/src/audit/checks/pagination.ts +180 -0
  61. package/src/audit/checks/performance.ts +153 -0
  62. package/src/audit/checks/platform-presence.ts +580 -0
  63. package/src/audit/checks/redirect-analysis.ts +153 -0
  64. package/src/audit/checks/redirect-chain.ts +389 -0
  65. package/src/audit/checks/resource-hints.ts +420 -0
  66. package/src/audit/checks/responsive-css.ts +247 -0
  67. package/src/audit/checks/responsive-images.ts +396 -0
  68. package/src/audit/checks/review-ecosystem.ts +415 -0
  69. package/src/audit/checks/robots-validation.ts +373 -0
  70. package/src/audit/checks/security-headers.ts +172 -0
  71. package/src/audit/checks/security.ts +144 -0
  72. package/src/audit/checks/serp-preview.ts +251 -0
  73. package/src/audit/checks/site-maturity.ts +444 -0
  74. package/src/audit/checks/social-meta.test.ts +275 -0
  75. package/src/audit/checks/social-meta.ts +134 -0
  76. package/src/audit/checks/soft-404.ts +151 -0
  77. package/src/audit/checks/structured-data.ts +238 -0
  78. package/src/audit/checks/tech-detection.ts +496 -0
  79. package/src/audit/checks/topical-clusters.ts +435 -0
  80. package/src/audit/checks/tracker-bloat.ts +462 -0
  81. package/src/audit/checks/tracking-verification.test.ts +371 -0
  82. package/src/audit/checks/tracking-verification.ts +636 -0
  83. package/src/audit/checks/url-safety.ts +682 -0
  84. package/src/audit/deno-entry.ts +66 -0
  85. package/src/audit/discovery/index.ts +15 -0
  86. package/src/audit/discovery/link-crawler.ts +232 -0
  87. package/src/audit/discovery/repo-routes.ts +347 -0
  88. package/src/audit/engine.ts +620 -0
  89. package/src/audit/fixes/index.ts +209 -0
  90. package/src/audit/fixes/social-meta-fixes.test.ts +329 -0
  91. package/src/audit/fixes/social-meta-fixes.ts +463 -0
  92. package/src/audit/index.ts +74 -0
  93. package/src/audit/runner.test.ts +299 -0
  94. package/src/audit/runner.ts +130 -0
  95. package/src/audit/types.ts +1953 -0
  96. package/src/content/featured-snippet.ts +367 -0
  97. package/src/content/generator.test.ts +534 -0
  98. package/src/content/generator.ts +501 -0
  99. package/src/content/headline.ts +317 -0
  100. package/src/content/index.ts +62 -0
  101. package/src/content/intent.ts +258 -0
  102. package/src/content/keyword-density.ts +349 -0
  103. package/src/content/readability.ts +262 -0
  104. package/src/executor.ts +336 -0
  105. package/src/fixer.ts +416 -0
  106. package/src/frameworks/detector.test.ts +248 -0
  107. package/src/frameworks/detector.ts +371 -0
  108. package/src/frameworks/index.ts +68 -0
  109. package/src/frameworks/recipes/angular.yaml +171 -0
  110. package/src/frameworks/recipes/astro.yaml +206 -0
  111. package/src/frameworks/recipes/django.yaml +180 -0
  112. package/src/frameworks/recipes/laravel.yaml +137 -0
  113. package/src/frameworks/recipes/nextjs.yaml +268 -0
  114. package/src/frameworks/recipes/nuxt.yaml +175 -0
  115. package/src/frameworks/recipes/rails.yaml +188 -0
  116. package/src/frameworks/recipes/react.yaml +202 -0
  117. package/src/frameworks/recipes/sveltekit.yaml +154 -0
  118. package/src/frameworks/recipes/vue.yaml +137 -0
  119. package/src/frameworks/recipes/wordpress.yaml +209 -0
  120. package/src/frameworks/suggestion-engine.ts +320 -0
  121. package/src/geo/geo-content.test.ts +305 -0
  122. package/src/geo/geo-content.ts +266 -0
  123. package/src/geo/geo-history.test.ts +473 -0
  124. package/src/geo/geo-history.ts +433 -0
  125. package/src/geo/geo-tracker.test.ts +359 -0
  126. package/src/geo/geo-tracker.ts +411 -0
  127. package/src/geo/index.ts +10 -0
  128. package/src/git/commit-helper.test.ts +261 -0
  129. package/src/git/commit-helper.ts +329 -0
  130. package/src/git/index.ts +12 -0
  131. package/src/git/pr-helper.test.ts +284 -0
  132. package/src/git/pr-helper.ts +307 -0
  133. package/src/index.ts +66 -0
  134. package/src/keywords/ai-keyword-engine.ts +1062 -0
  135. package/src/keywords/ai-summarizer.ts +387 -0
  136. package/src/keywords/ci-mode.ts +555 -0
  137. package/src/keywords/engine.ts +359 -0
  138. package/src/keywords/index.ts +151 -0
  139. package/src/keywords/llm-judge.ts +357 -0
  140. package/src/keywords/nlp-analysis.ts +706 -0
  141. package/src/keywords/prioritizer.ts +295 -0
  142. package/src/keywords/site-crawler.ts +342 -0
  143. package/src/keywords/sources/autocomplete.ts +139 -0
  144. package/src/keywords/sources/competitive-search.ts +450 -0
  145. package/src/keywords/sources/competitor-analysis.ts +374 -0
  146. package/src/keywords/sources/dataforseo.ts +206 -0
  147. package/src/keywords/sources/free-sources.ts +294 -0
  148. package/src/keywords/sources/gsc.ts +123 -0
  149. package/src/keywords/topic-grouping.ts +327 -0
  150. package/src/keywords/types.ts +144 -0
  151. package/src/keywords/wizard.ts +457 -0
  152. package/src/loader.ts +40 -0
  153. package/src/reports/index.ts +7 -0
  154. package/src/reports/report-generator.test.ts +293 -0
  155. package/src/reports/report-generator.ts +713 -0
  156. package/src/scheduler/alerts.test.ts +458 -0
  157. package/src/scheduler/alerts.ts +328 -0
  158. package/src/scheduler/index.ts +8 -0
  159. package/src/scheduler/scheduled-audit.test.ts +377 -0
  160. package/src/scheduler/scheduled-audit.ts +149 -0
  161. package/src/test/integration-test.ts +325 -0
  162. package/src/tools/analyzer.ts +373 -0
  163. package/src/tools/crawl.ts +293 -0
  164. package/src/tools/files.ts +301 -0
  165. package/src/tools/h1-fixer.ts +249 -0
  166. package/src/tools/index.ts +67 -0
  167. package/src/tracking/github-action.ts +326 -0
  168. package/src/tracking/google-analytics.ts +265 -0
  169. package/src/tracking/index.ts +45 -0
  170. package/src/tracking/report-generator.ts +386 -0
  171. package/src/tracking/search-console.ts +335 -0
  172. package/src/types.ts +134 -0
  173. package/src/utils/http.ts +302 -0
  174. package/src/wasm-adapter.ts +297 -0
  175. package/src/wasm-entry.ts +14 -0
  176. package/tsconfig.json +17 -0
  177. package/tsup.wasm.config.ts +26 -0
  178. package/vitest.config.ts +15 -0
@@ -0,0 +1,335 @@
1
+ // Google Search Console Integration
2
+ // Verification injection + API data fetching
3
+
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+
7
+ export interface GSCConfig {
8
+ verificationCode?: string; // For meta tag verification
9
+ siteUrl: string; // https://example.com or sc-domain:example.com
10
+ credentials?: GSCCredentials;
11
+ }
12
+
13
+ export interface GSCCredentials {
14
+ type: 'service_account' | 'oauth';
15
+ // Service account JSON or OAuth tokens
16
+ clientEmail?: string;
17
+ privateKey?: string;
18
+ accessToken?: string;
19
+ refreshToken?: string;
20
+ }
21
+
22
+ export interface GSCQueryResult {
23
+ query: string;
24
+ clicks: number;
25
+ impressions: number;
26
+ ctr: number;
27
+ position: number;
28
+ page?: string;
29
+ }
30
+
31
+ export interface GSCPerformanceData {
32
+ startDate: string;
33
+ endDate: string;
34
+ queries: GSCQueryResult[];
35
+ totalClicks: number;
36
+ totalImpressions: number;
37
+ avgCtr: number;
38
+ avgPosition: number;
39
+ }
40
+
41
+ export interface QuickWin {
42
+ query: string;
43
+ currentPosition: number;
44
+ impressions: number;
45
+ clicks: number;
46
+ ctr: number;
47
+ opportunity: 'page-1-close' | 'low-ctr' | 'high-impression';
48
+ suggestedAction: string;
49
+ potentialGain: number; // Estimated additional clicks
50
+ }
51
+
52
+ /**
53
+ * Generate Search Console verification meta tag
54
+ */
55
+ export function generateGSCVerificationTag(verificationCode: string): string {
56
+ return `<meta name="google-site-verification" content="${verificationCode}" />`;
57
+ }
58
+
59
+ /**
60
+ * Inject GSC verification into project
61
+ */
62
+ export async function injectGSCVerification(
63
+ projectPath: string,
64
+ verificationCode: string
65
+ ): Promise<{ success: boolean; file?: string; message: string }> {
66
+ // Find index.html or equivalent
67
+ const indexPaths = [
68
+ path.join(projectPath, 'index.html'),
69
+ path.join(projectPath, 'public', 'index.html'),
70
+ path.join(projectPath, 'app', 'layout.tsx'),
71
+ path.join(projectPath, 'src', 'app', 'layout.tsx'),
72
+ ];
73
+
74
+ for (const indexPath of indexPaths) {
75
+ if (fs.existsSync(indexPath)) {
76
+ let content = fs.readFileSync(indexPath, 'utf-8');
77
+
78
+ // Check if already verified
79
+ if (content.includes('google-site-verification')) {
80
+ return { success: true, file: indexPath, message: 'GSC verification already present' };
81
+ }
82
+
83
+ const verificationTag = generateGSCVerificationTag(verificationCode);
84
+
85
+ // For HTML files
86
+ if (indexPath.endsWith('.html')) {
87
+ content = content.replace('<head>', `<head>\n ${verificationTag}`);
88
+ fs.writeFileSync(indexPath, content);
89
+ return { success: true, file: indexPath, message: 'GSC verification tag added' };
90
+ }
91
+
92
+ // For React/Next.js layout files
93
+ if (indexPath.endsWith('.tsx') || indexPath.endsWith('.jsx')) {
94
+ // Add to <head> section
95
+ if (content.includes('<head>')) {
96
+ content = content.replace(
97
+ '<head>',
98
+ `<head>\n <meta name="google-site-verification" content="${verificationCode}" />`
99
+ );
100
+ fs.writeFileSync(indexPath, content);
101
+ return { success: true, file: indexPath, message: 'GSC verification tag added to layout' };
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ return {
108
+ success: false,
109
+ message: 'Could not find index.html or layout file. Add manually: ' + generateGSCVerificationTag(verificationCode),
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Build GSC API request (for use with Google APIs client)
115
+ * User needs to set up OAuth or service account credentials
116
+ */
117
+ export function buildGSCApiRequest(
118
+ siteUrl: string,
119
+ startDate: string,
120
+ endDate: string,
121
+ options: {
122
+ dimensions?: ('query' | 'page' | 'country' | 'device' | 'date')[];
123
+ rowLimit?: number;
124
+ startRow?: number;
125
+ } = {}
126
+ ): object {
127
+ return {
128
+ siteUrl,
129
+ requestBody: {
130
+ startDate,
131
+ endDate,
132
+ dimensions: options.dimensions || ['query'],
133
+ rowLimit: options.rowLimit || 1000,
134
+ startRow: options.startRow || 0,
135
+ },
136
+ };
137
+ }
138
+
139
+ /**
140
+ * Parse GSC API response into our format
141
+ */
142
+ export function parseGSCResponse(response: any): GSCPerformanceData {
143
+ const rows = response.rows || [];
144
+
145
+ const queries: GSCQueryResult[] = rows.map((row: any) => ({
146
+ query: row.keys?.[0] || '',
147
+ page: row.keys?.[1],
148
+ clicks: row.clicks || 0,
149
+ impressions: row.impressions || 0,
150
+ ctr: row.ctr || 0,
151
+ position: row.position || 0,
152
+ }));
153
+
154
+ const totalClicks = queries.reduce((sum, q) => sum + q.clicks, 0);
155
+ const totalImpressions = queries.reduce((sum, q) => sum + q.impressions, 0);
156
+ const avgCtr = totalImpressions > 0 ? totalClicks / totalImpressions : 0;
157
+ const avgPosition = queries.length > 0
158
+ ? queries.reduce((sum, q) => sum + q.position, 0) / queries.length
159
+ : 0;
160
+
161
+ return {
162
+ startDate: response.startDate || '',
163
+ endDate: response.endDate || '',
164
+ queries,
165
+ totalClicks,
166
+ totalImpressions,
167
+ avgCtr,
168
+ avgPosition,
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Identify quick wins from GSC data
174
+ */
175
+ export function identifyQuickWins(data: GSCPerformanceData): QuickWin[] {
176
+ const quickWins: QuickWin[] = [];
177
+
178
+ for (const query of data.queries) {
179
+ // Skip very low impression queries
180
+ if (query.impressions < 10) continue;
181
+
182
+ // Quick Win 1: Position 11-20 (almost page 1)
183
+ if (query.position >= 11 && query.position <= 20) {
184
+ const potentialGain = Math.round(query.impressions * 0.1); // ~10% CTR if on page 1
185
+ quickWins.push({
186
+ ...query,
187
+ currentPosition: query.position,
188
+ opportunity: 'page-1-close',
189
+ suggestedAction: `Improve content for "${query.query}" - you're at position ${Math.round(query.position)}, just off page 1!`,
190
+ potentialGain,
191
+ });
192
+ }
193
+
194
+ // Quick Win 2: High impressions but low CTR (position 1-10)
195
+ if (query.position <= 10 && query.impressions > 100 && query.ctr < 0.02) {
196
+ const expectedCtr = getExpectedCtr(query.position);
197
+ const potentialGain = Math.round(query.impressions * (expectedCtr - query.ctr));
198
+ if (potentialGain > 5) {
199
+ quickWins.push({
200
+ ...query,
201
+ currentPosition: query.position,
202
+ opportunity: 'low-ctr',
203
+ suggestedAction: `Improve title/description for "${query.query}" - CTR is ${(query.ctr * 100).toFixed(1)}% vs expected ${(expectedCtr * 100).toFixed(1)}%`,
204
+ potentialGain,
205
+ });
206
+ }
207
+ }
208
+
209
+ // Quick Win 3: High impressions, position 4-10 (could be #1)
210
+ if (query.position >= 4 && query.position <= 10 && query.impressions > 500) {
211
+ const potentialGain = Math.round(query.impressions * 0.15); // Gain from reaching #1
212
+ quickWins.push({
213
+ ...query,
214
+ currentPosition: query.position,
215
+ opportunity: 'high-impression',
216
+ suggestedAction: `Push "${query.query}" to top 3 - high traffic potential at ${query.impressions} impressions/month`,
217
+ potentialGain,
218
+ });
219
+ }
220
+ }
221
+
222
+ // Sort by potential gain
223
+ quickWins.sort((a, b) => b.potentialGain - a.potentialGain);
224
+
225
+ return quickWins.slice(0, 20); // Top 20 quick wins
226
+ }
227
+
228
+ /**
229
+ * Expected CTR by position (industry average)
230
+ */
231
+ function getExpectedCtr(position: number): number {
232
+ const ctrByPosition: Record<number, number> = {
233
+ 1: 0.28,
234
+ 2: 0.15,
235
+ 3: 0.11,
236
+ 4: 0.08,
237
+ 5: 0.06,
238
+ 6: 0.05,
239
+ 7: 0.04,
240
+ 8: 0.03,
241
+ 9: 0.03,
242
+ 10: 0.02,
243
+ };
244
+ return ctrByPosition[Math.round(position)] || 0.01;
245
+ }
246
+
247
+ /**
248
+ * Compare two periods to identify trends
249
+ */
250
+ export function comparePeriods(
251
+ current: GSCPerformanceData,
252
+ previous: GSCPerformanceData
253
+ ): {
254
+ improved: GSCQueryResult[];
255
+ declined: GSCQueryResult[];
256
+ newKeywords: GSCQueryResult[];
257
+ lostKeywords: GSCQueryResult[];
258
+ summary: {
259
+ clicksChange: number;
260
+ impressionsChange: number;
261
+ avgPositionChange: number;
262
+ };
263
+ } {
264
+ const previousMap = new Map(previous.queries.map(q => [q.query, q]));
265
+ const currentMap = new Map(current.queries.map(q => [q.query, q]));
266
+
267
+ const improved: GSCQueryResult[] = [];
268
+ const declined: GSCQueryResult[] = [];
269
+ const newKeywords: GSCQueryResult[] = [];
270
+ const lostKeywords: GSCQueryResult[] = [];
271
+
272
+ // Check current queries
273
+ for (const query of current.queries) {
274
+ const prev = previousMap.get(query.query);
275
+ if (!prev) {
276
+ newKeywords.push(query);
277
+ } else if (query.position < prev.position - 2) {
278
+ improved.push(query);
279
+ } else if (query.position > prev.position + 2) {
280
+ declined.push(query);
281
+ }
282
+ }
283
+
284
+ // Check for lost keywords
285
+ for (const query of previous.queries) {
286
+ if (!currentMap.has(query.query) && query.impressions > 50) {
287
+ lostKeywords.push(query);
288
+ }
289
+ }
290
+
291
+ return {
292
+ improved: improved.slice(0, 10),
293
+ declined: declined.slice(0, 10),
294
+ newKeywords: newKeywords.slice(0, 10),
295
+ lostKeywords: lostKeywords.slice(0, 10),
296
+ summary: {
297
+ clicksChange: current.totalClicks - previous.totalClicks,
298
+ impressionsChange: current.totalImpressions - previous.totalImpressions,
299
+ avgPositionChange: current.avgPosition - previous.avgPosition,
300
+ },
301
+ };
302
+ }
303
+
304
+ /**
305
+ * Generate credentials setup instructions
306
+ */
307
+ export function getGSCSetupInstructions(): string {
308
+ return `
309
+ # Google Search Console API Setup
310
+
311
+ ## Option 1: Service Account (Recommended for GitHub Actions)
312
+
313
+ 1. Go to Google Cloud Console: https://console.cloud.google.com
314
+ 2. Create a new project or select existing
315
+ 3. Enable "Search Console API"
316
+ 4. Go to "IAM & Admin" > "Service Accounts"
317
+ 5. Create a service account
318
+ 6. Create a JSON key and download it
319
+ 7. In Search Console, add the service account email as a user
320
+
321
+ Add to GitHub Secrets:
322
+ - GSC_SERVICE_ACCOUNT_EMAIL: your-service-account@project.iam.gserviceaccount.com
323
+ - GSC_PRIVATE_KEY: (the private_key from JSON, including -----BEGIN/END-----)
324
+
325
+ ## Option 2: OAuth (For user-specific access)
326
+
327
+ 1. Go to Google Cloud Console
328
+ 2. Enable "Search Console API"
329
+ 3. Create OAuth 2.0 credentials
330
+ 4. Download client_secret.json
331
+ 5. Run: seo auth --google
332
+
333
+ This will open a browser for OAuth consent and save tokens locally.
334
+ `;
335
+ }
package/src/types.ts ADDED
@@ -0,0 +1,134 @@
1
+ export interface AgentDefinition {
2
+ name: string;
3
+ description: string;
4
+ model: string;
5
+ temperature: number;
6
+ max_tokens: number;
7
+ tools: AgentTool[];
8
+ system: string;
9
+ prompt: string;
10
+ }
11
+
12
+ export interface AgentTool {
13
+ name: string;
14
+ description: string;
15
+ }
16
+
17
+ export interface SEOIssue {
18
+ severity: 'critical' | 'warning' | 'info';
19
+ category: 'meta' | 'schema' | 'content' | 'technical' | 'performance' | 'social' | 'accessibility';
20
+ code: string;
21
+ message: string;
22
+ impact: string;
23
+ element?: string;
24
+ fix?: {
25
+ file: string;
26
+ before: string | null;
27
+ after: string;
28
+ };
29
+ }
30
+
31
+ export interface SEOAnalysisResult {
32
+ url: string;
33
+ score: number;
34
+ core_web_vitals?: {
35
+ lcp?: { value: string; status: 'good' | 'needs-improvement' | 'poor' };
36
+ fid?: { value: string; status: 'good' | 'needs-improvement' | 'poor' };
37
+ cls?: { value: string; status: 'good' | 'needs-improvement' | 'poor' };
38
+ };
39
+ issues: SEOIssue[];
40
+ recommendations: SEORecommendation[];
41
+ }
42
+
43
+ export interface SEORecommendation {
44
+ priority: 'high' | 'medium' | 'low';
45
+ category: string;
46
+ message: string;
47
+ impact: string;
48
+ }
49
+
50
+ export interface CrawlResult {
51
+ url: string;
52
+ html: string;
53
+ statusCode: number;
54
+ headers: Record<string, string>;
55
+ loadTime: number;
56
+ }
57
+
58
+ export interface MetaData {
59
+ title?: string;
60
+ description?: string;
61
+ canonical?: string;
62
+ robots?: string;
63
+ viewport?: string;
64
+ charset?: string;
65
+ openGraph: {
66
+ title?: string;
67
+ description?: string;
68
+ image?: string;
69
+ url?: string;
70
+ type?: string;
71
+ siteName?: string;
72
+ };
73
+ twitter: {
74
+ card?: string;
75
+ title?: string;
76
+ description?: string;
77
+ image?: string;
78
+ site?: string;
79
+ };
80
+ other: Record<string, string>;
81
+ }
82
+
83
+ export interface HeadingStructure {
84
+ tag: string;
85
+ text: string;
86
+ level: number;
87
+ }
88
+
89
+ export interface ImageInfo {
90
+ src: string;
91
+ alt: string | null;
92
+ width?: string;
93
+ height?: string;
94
+ loading?: string;
95
+ }
96
+
97
+ export interface LinkInfo {
98
+ href: string;
99
+ text: string;
100
+ isInternal: boolean;
101
+ isNofollow: boolean;
102
+ }
103
+
104
+ export interface SchemaData {
105
+ type: string;
106
+ data: Record<string, unknown>;
107
+ }
108
+
109
+ export interface FrameworkInfo {
110
+ name: string;
111
+ version?: string;
112
+ router?: 'app' | 'pages' | 'file-based' | 'custom';
113
+ metaPattern: 'metadata-export' | 'head-component' | 'frontmatter' | 'meta-function' | 'html-head';
114
+ }
115
+
116
+ export interface Fix {
117
+ issue: {
118
+ code: string;
119
+ message: string;
120
+ severity: string;
121
+ };
122
+ file: string;
123
+ before: string | null;
124
+ after: string;
125
+ explanation: string;
126
+ }
127
+
128
+ export interface ToolResult {
129
+ success: boolean;
130
+ data?: unknown;
131
+ error?: string;
132
+ }
133
+
134
+ export type ToolFunction = (params: Record<string, unknown>) => Promise<ToolResult>;