All files / core/ai-providers shared.ts

100% Statements 21/21
87.5% Branches 7/8
100% Functions 4/4
100% Lines 20/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67        1x   1x 5x 12x     5x                                   1x   14x 14x 14x 2x       14x 14x   4x         10x 10x 10x     30x 10x 3x           7x              
// Copyright 2026 ForgeKit Contributors
// SPDX-License-Identifier: Apache-2.0
// https://github.com/SubhanshuMG/ForgeKit
import { ProjectSpec, Template } from '../../types';
import { sanitizeProjectName } from '../security';
 
export function buildSystemPrompt(templates: Template[]): string {
  const templateList = templates
    .map(t => `- ${t.id}: ${t.name} (${t.stack.join(', ')}) — ${t.description}`)
    .join('\n');
 
  return `You are ForgeKit's AI assistant. Given a project description, select the best matching template and suggest a project name.
 
Available templates:
${templateList}
 
Respond with ONLY a valid JSON object (no markdown, no explanation outside the JSON):
{
  "templateId": "the-template-id",
  "projectName": "suggested-kebab-case-name",
  "explanation": "Brief explanation of why this template fits the request"
}
 
Rules:
- templateId MUST be one of the available template IDs listed above
- projectName should be lowercase kebab-case, descriptive, and under 50 characters
- If the description doesn't clearly match any template, pick the closest one and explain why`;
}
 
export function parseAIResponse(content: string, templates: Template[]): ProjectSpec {
  // Extract JSON from response (handle markdown code blocks)
  let jsonStr = content.trim();
  const jsonMatch = jsonStr.match(/```(?:json)?\s*([\s\S]*?)```/);
  if (jsonMatch) {
    jsonStr = jsonMatch[1].trim();
  }
 
  let parsed: Record<string, unknown>;
  try {
    parsed = JSON.parse(jsonStr);
  } catch {
    throw new Error(
      'AI returned an invalid response. Please try rephrasing your project description.'
    );
  }
 
  const templateId = String(parsed.templateId || '');
  const projectName = sanitizeProjectName(String(parsed.projectName || 'my-project'));
  const explanation = String(parsed.explanation || '');
 
  // Validate templateId exists
  const validIds = templates.map(t => t.id);
  if (!validIds.includes(templateId)) {
    throw new Error(
      `AI suggested template "${templateId}" which doesn't exist. ` +
      `Available: ${validIds.join(', ')}`
    );
  }
 
  return {
    templateId,
    projectName,
    variables: { name: projectName },
    explanation,
  };
}