// mcp/generator/enrichComponents.ts
import { ComponentInfo } from '../crawler/components';
import { mcpAgentConfig } from '../config/mcp-ai.config';
export interface AIProp {
name: string;
description: string;
defaultValue?: string;
}
export interface AIComponentResponse {
props: AIProp[];
usage: string;
}
// --------------------
// Helper: build prompt
// --------------------
function buildComponentPrompt(comp: ComponentInfo) {
return `
You are a senior React/TypeScript expert.
Analyze this component and enrich its props with:
1. Short description (what it represents)
2. Realistic default values
3. Usage snippet
Component:
- Name: ${comp.name}
- File: ${comp.filePath}
- Props:
${comp.props.map((p) => ` - ${p.name}: ${p.type}${p.optional ? ' (optional)' : ''}`).join('\n')}
Reply in JSON ONLY with:
{
"props": [
{ "name": "...", "description": "...", "defaultValue": "..." }
],
"usage": "tsx code example"
}
`;
}
// --------------------
// Helper: retry wrapper
// --------------------
async function tryWithRetries<T>(fn: () => Promise<T>, maxRetries = 2): Promise<T | null> {
let lastError: any;
for (let i = 0; i <= maxRetries; i++) {
try {
return await fn();
} catch (err) {
lastError = err;
console.warn(`⚠️ AI call failed, retry ${i + 1}/${maxRetries}...`);
}
}
console.error('❌ All AI retries failed:', lastError);
return null;
}
// --------------------
// Mock AI backends
// --------------------
async function callOpenAI(comp: ComponentInfo, prompt: string): Promise<AIComponentResponse> {
// Placeholder for actual OpenAI API call
return {
props: comp.props.map((p) => ({
name: p.name,
description: `OpenAI description for ${p.name}`,
defaultValue: p.defaultValue,
})),
usage: `OpenAI usage snippet for ${comp.name}`,
};
}
async function callCopilot(comp: ComponentInfo, prompt: string): Promise<AIComponentResponse> {
return {
props: comp.props.map((p) => ({
name: p.name,
description: `Copilot description for ${p.name}`,
defaultValue: p.defaultValue,
})),
usage: `Copilot usage snippet for ${comp.name}`,
};
}
async function callCustomAI(comp: ComponentInfo, prompt: string): Promise<AIComponentResponse> {
return {
props: comp.props.map((p) => ({
name: p.name,
description: `Custom AI description for ${p.name}`,
defaultValue: p.defaultValue,
})),
usage: `Custom AI usage snippet for ${comp.name}`,
};
}
// --------------------
// Main function
// --------------------
export async function enrichComponents(
components: ComponentInfo[],
aiAgent?: 'none' | 'copilot' | 'openai' | 'custom',
): Promise<ComponentInfo[]> {
const enriched: ComponentInfo[] = [];
aiAgent = aiAgent || mcpAgentConfig.defaultAgent;
for (const comp of components) {
const prompt = buildComponentPrompt(comp);
// Skip AI enrichment
if (aiAgent === 'none' || !mcpAgentConfig.enableAI) {
enriched.push(comp);
continue;
}
// Dynamic AI selection
let aiResponse: AIComponentResponse | null = null;
switch (aiAgent) {
case 'openai':
aiResponse = await tryWithRetries(
() => callOpenAI(comp, prompt),
mcpAgentConfig.maxRetries,
);
break;
case 'copilot':
aiResponse = await tryWithRetries(
() => callCopilot(comp, prompt),
mcpAgentConfig.maxRetries,
);
break;
case 'custom':
aiResponse = await tryWithRetries(
() => callCustomAI(comp, prompt),
mcpAgentConfig.maxRetries,
);
break;
}
// Merge AI data into ComponentInfo
if (aiResponse) {
comp.props = comp.props.map((p) => {
const found = aiResponse!.props.find((ai) => ai.name === p.name);
return {
...p,
description: found?.description ?? '',
defaultValue: found?.defaultValue ?? '',
};
});
(comp as any).usage = aiResponse.usage ?? '';
}
enriched.push(comp);
}
return enriched;
}