@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,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-as-Judge Module
|
|
3
|
+
*
|
|
4
|
+
* Uses AI to evaluate tool feasibility, specificity, and product fit.
|
|
5
|
+
* Prevents vague or unbuildable tool suggestions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import OpenAI from 'openai';
|
|
9
|
+
import type { CompetitorTool, CompetitiveSearchResult } from './sources/competitive-search.js';
|
|
10
|
+
import type { SiteSummary } from './ai-summarizer.js';
|
|
11
|
+
|
|
12
|
+
export interface ToolFeasibilityScore {
|
|
13
|
+
/** Overall feasibility score 1-5 */
|
|
14
|
+
feasibility: number;
|
|
15
|
+
/** Specificity score 1-5 (how detailed/actionable is the idea?) */
|
|
16
|
+
specificity: number;
|
|
17
|
+
/** Product fit score 1-5 (how well does it tie into the main product?) */
|
|
18
|
+
productFit: number;
|
|
19
|
+
/** Market opportunity score 1-5 */
|
|
20
|
+
marketOpportunity: number;
|
|
21
|
+
/** Combined weighted score */
|
|
22
|
+
overallScore: number;
|
|
23
|
+
/** Pass/fail based on minimum threshold */
|
|
24
|
+
passes: boolean;
|
|
25
|
+
/** Explanation for the scores */
|
|
26
|
+
reasoning: string;
|
|
27
|
+
/** Issues that would prevent building */
|
|
28
|
+
blockers: string[];
|
|
29
|
+
/** Improvements to make the tool more specific */
|
|
30
|
+
improvements: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface EnhancedToolIdea {
|
|
34
|
+
keyword: string;
|
|
35
|
+
toolName: string;
|
|
36
|
+
description: string;
|
|
37
|
+
/** Specific input format (e.g., "PlantUML text syntax") */
|
|
38
|
+
inputFormat: string;
|
|
39
|
+
/** Specific output format (e.g., "Mermaid diagram code") */
|
|
40
|
+
outputFormat: string;
|
|
41
|
+
/** How this tool connects back to the main product */
|
|
42
|
+
productTieIn: string;
|
|
43
|
+
/** Specific CTA text */
|
|
44
|
+
ctaText: string;
|
|
45
|
+
/** Deep link into product if possible */
|
|
46
|
+
deepLinkPattern?: string;
|
|
47
|
+
/** Competitor analysis */
|
|
48
|
+
competitors: {
|
|
49
|
+
hasFreeAlternative: boolean;
|
|
50
|
+
topCompetitor?: string;
|
|
51
|
+
ourAdvantage: string;
|
|
52
|
+
};
|
|
53
|
+
/** Technical implementation hints */
|
|
54
|
+
implementationHints: {
|
|
55
|
+
suggestedLibraries: string[];
|
|
56
|
+
estimatedComplexity: 'trivial' | 'simple' | 'moderate' | 'complex';
|
|
57
|
+
canUseExistingOSS: boolean;
|
|
58
|
+
ossLibrary?: string;
|
|
59
|
+
};
|
|
60
|
+
/** Feasibility assessment */
|
|
61
|
+
feasibilityScore: ToolFeasibilityScore;
|
|
62
|
+
/** Search volume and difficulty from keyword data */
|
|
63
|
+
volume: number;
|
|
64
|
+
difficulty: number;
|
|
65
|
+
priority: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface LLMJudgeOptions {
|
|
69
|
+
openaiApiKey: string;
|
|
70
|
+
minFeasibilityScore?: number; // Default: 3
|
|
71
|
+
minSpecificityScore?: number; // Default: 3
|
|
72
|
+
minProductFitScore?: number; // Default: 2
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Evaluate a tool idea for feasibility, specificity, and product fit
|
|
77
|
+
*/
|
|
78
|
+
export async function evaluateToolFeasibility(
|
|
79
|
+
toolIdea: {
|
|
80
|
+
keyword: string;
|
|
81
|
+
toolName: string;
|
|
82
|
+
toolDescription: string;
|
|
83
|
+
},
|
|
84
|
+
siteSummary: SiteSummary,
|
|
85
|
+
competitiveData: CompetitiveSearchResult | null,
|
|
86
|
+
options: LLMJudgeOptions
|
|
87
|
+
): Promise<ToolFeasibilityScore> {
|
|
88
|
+
const openai = new OpenAI({ apiKey: options.openaiApiKey });
|
|
89
|
+
|
|
90
|
+
const minFeasibility = options.minFeasibilityScore ?? 3;
|
|
91
|
+
const minSpecificity = options.minSpecificityScore ?? 3;
|
|
92
|
+
const minProductFit = options.minProductFitScore ?? 2;
|
|
93
|
+
|
|
94
|
+
const competitorInfo = competitiveData
|
|
95
|
+
? `
|
|
96
|
+
Existing competitors found:
|
|
97
|
+
${competitiveData.existingTools.slice(0, 5).map(t => `- ${t.name}: ${t.isPaid ? 'Paid' : 'Free'} - ${t.description}`).join('\n')}
|
|
98
|
+
|
|
99
|
+
Open source options:
|
|
100
|
+
${competitiveData.openSourceOptions.slice(0, 5).map(t => `- ${t.name} (${t.stars || 0} stars): ${t.description}`).join('\n')}
|
|
101
|
+
|
|
102
|
+
Market insights:
|
|
103
|
+
- Has free solutions: ${competitiveData.marketInsights.hasFreeSolutions}
|
|
104
|
+
- Has paid solutions: ${competitiveData.marketInsights.hasPaidSolutions}
|
|
105
|
+
- Dominant player: ${competitiveData.marketInsights.dominantPlayer || 'None identified'}
|
|
106
|
+
- Market gap: ${competitiveData.marketInsights.marketGap || 'None identified'}
|
|
107
|
+
`
|
|
108
|
+
: 'No competitive data available.';
|
|
109
|
+
|
|
110
|
+
const prompt = `You are evaluating a free tool idea for feasibility and quality. Be STRICT - reject vague or unbuildable ideas.
|
|
111
|
+
|
|
112
|
+
## Main Product Context
|
|
113
|
+
- Product: ${siteSummary.productName}
|
|
114
|
+
- Description: ${siteSummary.productDescription}
|
|
115
|
+
- Industry: ${siteSummary.industry}
|
|
116
|
+
- Key features: ${siteSummary.keyFeatures.join(', ')}
|
|
117
|
+
- Target audience: ${siteSummary.targetAudience}
|
|
118
|
+
|
|
119
|
+
## Tool Idea to Evaluate
|
|
120
|
+
- Keyword: ${toolIdea.keyword}
|
|
121
|
+
- Tool Name: ${toolIdea.toolName}
|
|
122
|
+
- Description: ${toolIdea.toolDescription}
|
|
123
|
+
|
|
124
|
+
## Competitive Landscape
|
|
125
|
+
${competitorInfo}
|
|
126
|
+
|
|
127
|
+
## Evaluation Criteria
|
|
128
|
+
|
|
129
|
+
Rate each criterion 1-5:
|
|
130
|
+
|
|
131
|
+
### 1. FEASIBILITY (Can this actually be built?)
|
|
132
|
+
- 1: Impossible or would require AI that doesn't exist
|
|
133
|
+
- 2: Extremely complex, would take months
|
|
134
|
+
- 3: Buildable in 1-2 weeks with moderate effort
|
|
135
|
+
- 4: Buildable in a few days
|
|
136
|
+
- 5: Trivial, could be done in hours
|
|
137
|
+
|
|
138
|
+
### 2. SPECIFICITY (Is it detailed enough to build?)
|
|
139
|
+
- 1: Completely vague, no actionable details
|
|
140
|
+
- 2: Missing critical details (what inputs? what outputs?)
|
|
141
|
+
- 3: Has basic details but needs refinement
|
|
142
|
+
- 4: Clear inputs/outputs/purpose
|
|
143
|
+
- 5: Fully specified, ready to implement
|
|
144
|
+
|
|
145
|
+
### 3. PRODUCT FIT (Does it connect to the main product?)
|
|
146
|
+
- 1: No connection whatsoever
|
|
147
|
+
- 2: Tangentially related industry
|
|
148
|
+
- 3: Same audience, different use case
|
|
149
|
+
- 4: Natural lead-in to product features
|
|
150
|
+
- 5: Direct integration possible (e.g., output feeds into product)
|
|
151
|
+
|
|
152
|
+
### 4. MARKET OPPORTUNITY (Is there demand + can we win?)
|
|
153
|
+
- 1: Saturated market with dominant free player
|
|
154
|
+
- 2: Many free alternatives exist
|
|
155
|
+
- 3: Some alternatives but room for improvement
|
|
156
|
+
- 4: Few alternatives, clear differentiation possible
|
|
157
|
+
- 5: No free alternatives, high search volume
|
|
158
|
+
|
|
159
|
+
Respond in JSON:
|
|
160
|
+
{
|
|
161
|
+
"feasibility": <1-5>,
|
|
162
|
+
"specificity": <1-5>,
|
|
163
|
+
"productFit": <1-5>,
|
|
164
|
+
"marketOpportunity": <1-5>,
|
|
165
|
+
"reasoning": "<2-3 sentences explaining scores>",
|
|
166
|
+
"blockers": ["<issues that prevent building>"],
|
|
167
|
+
"improvements": ["<suggestions to make it more specific/buildable>"]
|
|
168
|
+
}`;
|
|
169
|
+
|
|
170
|
+
const response = await openai.chat.completions.create({
|
|
171
|
+
model: 'gpt-4o-mini',
|
|
172
|
+
messages: [{ role: 'user', content: prompt }],
|
|
173
|
+
response_format: { type: 'json_object' },
|
|
174
|
+
temperature: 0.3,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const result = JSON.parse(response.choices[0].message.content || '{}');
|
|
178
|
+
|
|
179
|
+
// Calculate overall score (weighted average)
|
|
180
|
+
const overallScore =
|
|
181
|
+
result.feasibility * 0.3 +
|
|
182
|
+
result.specificity * 0.3 +
|
|
183
|
+
result.productFit * 0.2 +
|
|
184
|
+
result.marketOpportunity * 0.2;
|
|
185
|
+
|
|
186
|
+
// Determine pass/fail
|
|
187
|
+
const passes =
|
|
188
|
+
result.feasibility >= minFeasibility &&
|
|
189
|
+
result.specificity >= minSpecificity &&
|
|
190
|
+
result.productFit >= minProductFit;
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
feasibility: result.feasibility,
|
|
194
|
+
specificity: result.specificity,
|
|
195
|
+
productFit: result.productFit,
|
|
196
|
+
marketOpportunity: result.marketOpportunity,
|
|
197
|
+
overallScore: Math.round(overallScore * 10) / 10,
|
|
198
|
+
passes,
|
|
199
|
+
reasoning: result.reasoning,
|
|
200
|
+
blockers: result.blockers || [],
|
|
201
|
+
improvements: result.improvements || [],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Enhance a tool idea with specific details using AI
|
|
207
|
+
*/
|
|
208
|
+
export async function enhanceToolIdea(
|
|
209
|
+
toolIdea: {
|
|
210
|
+
keyword: string;
|
|
211
|
+
toolName: string;
|
|
212
|
+
toolDescription: string;
|
|
213
|
+
volume: number;
|
|
214
|
+
difficulty: number;
|
|
215
|
+
},
|
|
216
|
+
siteSummary: SiteSummary,
|
|
217
|
+
competitiveData: CompetitiveSearchResult | null,
|
|
218
|
+
options: LLMJudgeOptions
|
|
219
|
+
): Promise<EnhancedToolIdea | null> {
|
|
220
|
+
const openai = new OpenAI({ apiKey: options.openaiApiKey });
|
|
221
|
+
|
|
222
|
+
// First, evaluate feasibility
|
|
223
|
+
const feasibilityScore = await evaluateToolFeasibility(
|
|
224
|
+
toolIdea,
|
|
225
|
+
siteSummary,
|
|
226
|
+
competitiveData,
|
|
227
|
+
options
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// If it doesn't pass minimum thresholds, skip it
|
|
231
|
+
if (!feasibilityScore.passes) {
|
|
232
|
+
console.log(`⚠️ Skipping "${toolIdea.toolName}" - failed feasibility check: ${feasibilityScore.reasoning}`);
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Now enhance with specific details
|
|
237
|
+
const prompt = `You are specifying a free tool idea in detail. The tool must be:
|
|
238
|
+
1. SPECIFIC - exact input/output formats
|
|
239
|
+
2. BUILDABLE - realistic scope for 1-2 day build
|
|
240
|
+
3. CONNECTED - ties back to the main product
|
|
241
|
+
|
|
242
|
+
## Main Product
|
|
243
|
+
- Name: ${siteSummary.productName}
|
|
244
|
+
- Description: ${siteSummary.productDescription}
|
|
245
|
+
- Key features: ${siteSummary.keyFeatures.join(', ')}
|
|
246
|
+
|
|
247
|
+
## Tool to Specify
|
|
248
|
+
- Keyword: ${toolIdea.keyword}
|
|
249
|
+
- Current Name: ${toolIdea.toolName}
|
|
250
|
+
- Current Description: ${toolIdea.toolDescription}
|
|
251
|
+
|
|
252
|
+
## Competitive Intelligence
|
|
253
|
+
${competitiveData?.existingTools.slice(0, 3).map(t => `- ${t.name} (${t.isPaid ? 'Paid' : 'Free'})`).join('\n') || 'No competitors found'}
|
|
254
|
+
${competitiveData?.openSourceOptions.slice(0, 3).map(t => `- OSS: ${t.name} (${t.stars} stars)`).join('\n') || ''}
|
|
255
|
+
|
|
256
|
+
## Required Output (JSON)
|
|
257
|
+
{
|
|
258
|
+
"toolName": "<improved, specific name>",
|
|
259
|
+
"description": "<2-3 sentence specific description of what it does>",
|
|
260
|
+
"inputFormat": "<exact input format, e.g., 'PlantUML text syntax' or 'JSON with nested objects'>",
|
|
261
|
+
"outputFormat": "<exact output format, e.g., 'Mermaid diagram code' or 'CSV file'>",
|
|
262
|
+
"productTieIn": "<how this leads users to the main product>",
|
|
263
|
+
"ctaText": "<specific CTA, e.g., 'See your diagram animated in ${siteSummary.productName} →'>",
|
|
264
|
+
"deepLinkPattern": "<URL pattern if output can link into product, e.g., '/editor?import={output}' or null>",
|
|
265
|
+
"ourAdvantage": "<why our version is better than existing options>",
|
|
266
|
+
"suggestedLibraries": ["<npm/github libraries that could power this>"],
|
|
267
|
+
"estimatedComplexity": "<trivial|simple|moderate|complex>",
|
|
268
|
+
"canUseExistingOSS": <true if an OSS library does most of the work>,
|
|
269
|
+
"ossLibrary": "<name of OSS library to use, or null>"
|
|
270
|
+
}`;
|
|
271
|
+
|
|
272
|
+
const response = await openai.chat.completions.create({
|
|
273
|
+
model: 'gpt-4o',
|
|
274
|
+
messages: [{ role: 'user', content: prompt }],
|
|
275
|
+
response_format: { type: 'json_object' },
|
|
276
|
+
temperature: 0.5,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const enhanced = JSON.parse(response.choices[0].message.content || '{}');
|
|
280
|
+
|
|
281
|
+
// Calculate priority based on scores and metrics
|
|
282
|
+
const priority = Math.round(
|
|
283
|
+
feasibilityScore.overallScore * 20 +
|
|
284
|
+
(toolIdea.volume / 1000) * 10 +
|
|
285
|
+
((100 - toolIdea.difficulty) / 100) * 20
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
keyword: toolIdea.keyword,
|
|
290
|
+
toolName: enhanced.toolName || toolIdea.toolName,
|
|
291
|
+
description: enhanced.description || toolIdea.toolDescription,
|
|
292
|
+
inputFormat: enhanced.inputFormat || 'Unspecified',
|
|
293
|
+
outputFormat: enhanced.outputFormat || 'Unspecified',
|
|
294
|
+
productTieIn: enhanced.productTieIn || '',
|
|
295
|
+
ctaText: enhanced.ctaText || `Try ${siteSummary.productName} for more →`,
|
|
296
|
+
deepLinkPattern: enhanced.deepLinkPattern || undefined,
|
|
297
|
+
competitors: {
|
|
298
|
+
hasFreeAlternative: competitiveData?.marketInsights.hasFreeSolutions || false,
|
|
299
|
+
topCompetitor: competitiveData?.marketInsights.dominantPlayer,
|
|
300
|
+
ourAdvantage: enhanced.ourAdvantage || 'Free and integrated with our product',
|
|
301
|
+
},
|
|
302
|
+
implementationHints: {
|
|
303
|
+
suggestedLibraries: enhanced.suggestedLibraries || [],
|
|
304
|
+
estimatedComplexity: enhanced.estimatedComplexity || 'moderate',
|
|
305
|
+
canUseExistingOSS: enhanced.canUseExistingOSS || false,
|
|
306
|
+
ossLibrary: enhanced.ossLibrary || undefined,
|
|
307
|
+
},
|
|
308
|
+
feasibilityScore,
|
|
309
|
+
volume: toolIdea.volume,
|
|
310
|
+
difficulty: toolIdea.difficulty,
|
|
311
|
+
priority,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Batch evaluate and enhance tool ideas
|
|
317
|
+
*/
|
|
318
|
+
export async function evaluateAndEnhanceToolIdeas(
|
|
319
|
+
toolIdeas: Array<{
|
|
320
|
+
keyword: string;
|
|
321
|
+
toolName: string;
|
|
322
|
+
toolDescription: string;
|
|
323
|
+
volume: number;
|
|
324
|
+
difficulty: number;
|
|
325
|
+
}>,
|
|
326
|
+
siteSummary: SiteSummary,
|
|
327
|
+
getCompetitiveData: (keyword: string) => Promise<CompetitiveSearchResult | null>,
|
|
328
|
+
options: LLMJudgeOptions
|
|
329
|
+
): Promise<EnhancedToolIdea[]> {
|
|
330
|
+
const enhancedIdeas: EnhancedToolIdea[] = [];
|
|
331
|
+
|
|
332
|
+
// Process sequentially to avoid rate limits
|
|
333
|
+
for (const idea of toolIdeas) {
|
|
334
|
+
try {
|
|
335
|
+
console.log(`🔍 Evaluating "${idea.toolName}"...`);
|
|
336
|
+
|
|
337
|
+
// Get competitive data for this specific tool
|
|
338
|
+
const competitiveData = await getCompetitiveData(idea.keyword);
|
|
339
|
+
|
|
340
|
+
// Enhance the idea
|
|
341
|
+
const enhanced = await enhanceToolIdea(idea, siteSummary, competitiveData, options);
|
|
342
|
+
|
|
343
|
+
if (enhanced) {
|
|
344
|
+
enhancedIdeas.push(enhanced);
|
|
345
|
+
console.log(`✅ "${enhanced.toolName}" passed (score: ${enhanced.feasibilityScore.overallScore})`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Rate limit: wait 500ms between API calls
|
|
349
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.warn(`⚠️ Error evaluating "${idea.toolName}":`, error);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Sort by priority
|
|
356
|
+
return enhancedIdeas.sort((a, b) => b.priority - a.priority);
|
|
357
|
+
}
|