@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,336 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import type { AgentDefinition, ToolResult, SEOAnalysisResult } from './types.js';
3
+ import { tools } from './tools/index.js';
4
+ import { interpolatePrompt } from './loader.js';
5
+
6
+ export interface ExecuteOptions {
7
+ variables?: Record<string, string>;
8
+ onToolCall?: (name: string, params: Record<string, unknown>) => void;
9
+ onToolResult?: (name: string, result: ToolResult) => void;
10
+ cwd?: string;
11
+ }
12
+
13
+ export interface ExecutionResult {
14
+ success: boolean;
15
+ data?: unknown;
16
+ error?: string;
17
+ toolCalls?: { name: string; params: unknown; result: ToolResult }[];
18
+ }
19
+
20
+ // Map agent tool definitions to Anthropic API tool format
21
+ function mapToolsToAnthropic(agentTools: AgentDefinition['tools']): Anthropic.Tool[] {
22
+ const toolSchemas: Record<string, Anthropic.Tool> = {
23
+ crawl_url: {
24
+ name: 'crawl_url',
25
+ description: 'Fetch and parse HTML from a URL',
26
+ input_schema: {
27
+ type: 'object' as const,
28
+ properties: {
29
+ url: { type: 'string', description: 'URL to crawl' }
30
+ },
31
+ required: ['url']
32
+ }
33
+ },
34
+ extract_meta: {
35
+ name: 'extract_meta',
36
+ description: 'Extract all meta tags from HTML',
37
+ input_schema: {
38
+ type: 'object' as const,
39
+ properties: {
40
+ html: { type: 'string', description: 'HTML content' },
41
+ url: { type: 'string', description: 'Base URL' }
42
+ },
43
+ required: ['html', 'url']
44
+ }
45
+ },
46
+ analyze_headings: {
47
+ name: 'analyze_headings',
48
+ description: 'Extract and analyze heading hierarchy',
49
+ input_schema: {
50
+ type: 'object' as const,
51
+ properties: {
52
+ html: { type: 'string', description: 'HTML content' }
53
+ },
54
+ required: ['html']
55
+ }
56
+ },
57
+ extract_images: {
58
+ name: 'extract_images',
59
+ description: 'Extract all images with their attributes',
60
+ input_schema: {
61
+ type: 'object' as const,
62
+ properties: {
63
+ html: { type: 'string', description: 'HTML content' }
64
+ },
65
+ required: ['html']
66
+ }
67
+ },
68
+ extract_links: {
69
+ name: 'extract_links',
70
+ description: 'Extract all links from the page',
71
+ input_schema: {
72
+ type: 'object' as const,
73
+ properties: {
74
+ html: { type: 'string', description: 'HTML content' },
75
+ baseUrl: { type: 'string', description: 'Base URL for determining internal/external' }
76
+ },
77
+ required: ['html', 'baseUrl']
78
+ }
79
+ },
80
+ extract_schema: {
81
+ name: 'extract_schema',
82
+ description: 'Extract JSON-LD structured data',
83
+ input_schema: {
84
+ type: 'object' as const,
85
+ properties: {
86
+ html: { type: 'string', description: 'HTML content' }
87
+ },
88
+ required: ['html']
89
+ }
90
+ },
91
+ check_robots: {
92
+ name: 'check_robots',
93
+ description: 'Fetch and parse robots.txt',
94
+ input_schema: {
95
+ type: 'object' as const,
96
+ properties: {
97
+ url: { type: 'string', description: 'Base URL of the site' }
98
+ },
99
+ required: ['url']
100
+ }
101
+ },
102
+ check_sitemap: {
103
+ name: 'check_sitemap',
104
+ description: 'Fetch and parse sitemap.xml',
105
+ input_schema: {
106
+ type: 'object' as const,
107
+ properties: {
108
+ url: { type: 'string', description: 'Base URL of the site' }
109
+ },
110
+ required: ['url']
111
+ }
112
+ },
113
+ read_file: {
114
+ name: 'read_file',
115
+ description: 'Read a file from the codebase',
116
+ input_schema: {
117
+ type: 'object' as const,
118
+ properties: {
119
+ path: { type: 'string', description: 'File path to read' },
120
+ cwd: { type: 'string', description: 'Working directory' }
121
+ },
122
+ required: ['path']
123
+ }
124
+ },
125
+ write_file: {
126
+ name: 'write_file',
127
+ description: 'Write content to a file',
128
+ input_schema: {
129
+ type: 'object' as const,
130
+ properties: {
131
+ path: { type: 'string', description: 'File path to write' },
132
+ content: { type: 'string', description: 'Content to write' },
133
+ cwd: { type: 'string', description: 'Working directory' }
134
+ },
135
+ required: ['path', 'content']
136
+ }
137
+ },
138
+ list_files: {
139
+ name: 'list_files',
140
+ description: 'List files in a directory',
141
+ input_schema: {
142
+ type: 'object' as const,
143
+ properties: {
144
+ path: { type: 'string', description: 'Directory path' },
145
+ pattern: { type: 'string', description: 'File pattern to match' },
146
+ recursive: { type: 'boolean', description: 'Search recursively' },
147
+ cwd: { type: 'string', description: 'Working directory' }
148
+ },
149
+ required: ['path']
150
+ }
151
+ },
152
+ detect_framework: {
153
+ name: 'detect_framework',
154
+ description: 'Detect the web framework used in a project',
155
+ input_schema: {
156
+ type: 'object' as const,
157
+ properties: {
158
+ cwd: { type: 'string', description: 'Project directory' }
159
+ },
160
+ required: []
161
+ }
162
+ },
163
+ find_html_entry: {
164
+ name: 'find_html_entry',
165
+ description: 'Find the HTML entry point file',
166
+ input_schema: {
167
+ type: 'object' as const,
168
+ properties: {
169
+ cwd: { type: 'string', description: 'Project directory' }
170
+ },
171
+ required: []
172
+ }
173
+ },
174
+ find_page_files: {
175
+ name: 'find_page_files',
176
+ description: 'Find all page/route files in the project',
177
+ input_schema: {
178
+ type: 'object' as const,
179
+ properties: {
180
+ cwd: { type: 'string', description: 'Project directory' },
181
+ framework: { type: 'string', description: 'Framework name' }
182
+ },
183
+ required: []
184
+ }
185
+ },
186
+ analyze_url: {
187
+ name: 'analyze_url',
188
+ description: 'Run a complete SEO analysis on a URL',
189
+ input_schema: {
190
+ type: 'object' as const,
191
+ properties: {
192
+ url: { type: 'string', description: 'URL to analyze' }
193
+ },
194
+ required: ['url']
195
+ }
196
+ },
197
+ };
198
+
199
+ return agentTools
200
+ .filter(t => toolSchemas[t.name])
201
+ .map(t => toolSchemas[t.name]);
202
+ }
203
+
204
+ export async function executeAgent(
205
+ agent: AgentDefinition,
206
+ options: ExecuteOptions = {}
207
+ ): Promise<ExecutionResult> {
208
+ const apiKey = process.env.ANTHROPIC_API_KEY;
209
+ if (!apiKey) {
210
+ return { success: false, error: 'ANTHROPIC_API_KEY environment variable not set' };
211
+ }
212
+
213
+ const client = new Anthropic({ apiKey });
214
+ const { variables = {}, onToolCall, onToolResult, cwd } = options;
215
+
216
+ // Interpolate the prompt with variables
217
+ const userPrompt = interpolatePrompt(agent.prompt, variables);
218
+
219
+ // Map agent tools to Anthropic format
220
+ const anthropicTools = mapToolsToAnthropic(agent.tools);
221
+
222
+ const toolCalls: { name: string; params: unknown; result: ToolResult }[] = [];
223
+
224
+ try {
225
+ // Initial message
226
+ const messages: Anthropic.MessageParam[] = [
227
+ { role: 'user', content: userPrompt }
228
+ ];
229
+
230
+ let response = await client.messages.create({
231
+ model: agent.model,
232
+ max_tokens: agent.max_tokens,
233
+ temperature: agent.temperature,
234
+ system: agent.system,
235
+ tools: anthropicTools.length > 0 ? anthropicTools : undefined,
236
+ messages,
237
+ });
238
+
239
+ // Process tool calls in a loop
240
+ while (response.stop_reason === 'tool_use') {
241
+ const toolUseBlocks = response.content.filter(
242
+ (block): block is Anthropic.ToolUseBlock => block.type === 'tool_use'
243
+ );
244
+
245
+ const toolResults: Anthropic.ToolResultBlockParam[] = [];
246
+
247
+ for (const toolUse of toolUseBlocks) {
248
+ const toolName = toolUse.name;
249
+ const toolParams = toolUse.input as Record<string, unknown>;
250
+
251
+ // Add cwd to file-related tools
252
+ if (['read_file', 'write_file', 'list_files', 'detect_framework', 'find_html_entry', 'find_page_files'].includes(toolName)) {
253
+ if (cwd && !toolParams.cwd) {
254
+ toolParams.cwd = cwd;
255
+ }
256
+ }
257
+
258
+ onToolCall?.(toolName, toolParams);
259
+
260
+ const toolFn = tools[toolName];
261
+ let result: ToolResult;
262
+
263
+ if (toolFn) {
264
+ result = await toolFn(toolParams);
265
+ } else {
266
+ result = { success: false, error: `Unknown tool: ${toolName}` };
267
+ }
268
+
269
+ onToolResult?.(toolName, result);
270
+ toolCalls.push({ name: toolName, params: toolParams, result });
271
+
272
+ toolResults.push({
273
+ type: 'tool_result',
274
+ tool_use_id: toolUse.id,
275
+ content: JSON.stringify(result),
276
+ });
277
+ }
278
+
279
+ // Continue conversation with tool results
280
+ messages.push({ role: 'assistant', content: response.content });
281
+ messages.push({ role: 'user', content: toolResults });
282
+
283
+ response = await client.messages.create({
284
+ model: agent.model,
285
+ max_tokens: agent.max_tokens,
286
+ temperature: agent.temperature,
287
+ system: agent.system,
288
+ tools: anthropicTools,
289
+ messages,
290
+ });
291
+ }
292
+
293
+ // Extract final text response
294
+ const textBlocks = response.content.filter(
295
+ (block): block is Anthropic.TextBlock => block.type === 'text'
296
+ );
297
+
298
+ const responseText = textBlocks.map(b => b.text).join('\n');
299
+
300
+ // Try to parse JSON from the response
301
+ let data: unknown;
302
+ try {
303
+ // Look for JSON in the response
304
+ const jsonMatch = responseText.match(/```json\n?([\s\S]*?)\n?```/) ||
305
+ responseText.match(/\{[\s\S]*\}/);
306
+ if (jsonMatch) {
307
+ const jsonStr = jsonMatch[1] || jsonMatch[0];
308
+ data = JSON.parse(jsonStr);
309
+ } else {
310
+ data = responseText;
311
+ }
312
+ } catch {
313
+ data = responseText;
314
+ }
315
+
316
+ return { success: true, data, toolCalls };
317
+ } catch (error) {
318
+ return {
319
+ success: false,
320
+ error: error instanceof Error ? error.message : 'Agent execution failed',
321
+ toolCalls,
322
+ };
323
+ }
324
+ }
325
+
326
+ // Simplified direct analysis function that doesn't need Claude API
327
+ export async function runDirectAnalysis(url: string): Promise<SEOAnalysisResult> {
328
+ const { analyzeUrl } = await import('./tools/analyzer.js');
329
+ const result = await analyzeUrl({ url });
330
+
331
+ if (!result.success || !result.data) {
332
+ throw new Error(result.error || 'Analysis failed');
333
+ }
334
+
335
+ return result.data as SEOAnalysisResult;
336
+ }