legends810/testingnew
0
1import ignore from 'ignore';2import type { ProviderInfo } from '~/types/model';3import type { Template } from '~/types/template';4import { STARTER_TEMPLATES } from './constants';5import Cookies from 'js-cookie';6 7const starterTemplateSelectionPrompt = (templates: Template[]) => `8You are an experienced developer who helps people choose the best starter template for their projects.9 10Available templates:11<template>12 <name>blank</name>13 <description>Empty starter for simple scripts and trivial tasks that don't require a full template setup</description>14 <tags>basic, script</tags>15</template>16${templates17 .map(18 (template) => `19<template>20 <name>${template.name}</name>21 <description>${template.description}</description>22 ${template.tags ? `<tags>${template.tags.join(', ')}</tags>` : ''}23</template>24`,25 )26 .join('\n')}27 28Response Format:29<selection>30 <templateName>{selected template name}</templateName>31 <title>{a proper title for the project}</title>32</selection>33 34Examples:35 36<example>37User: I need to build a todo app38Response:39<selection>40 <templateName>react-basic-starter</templateName>41 <title>Simple React todo application</title>42</selection>43</example>44 45<example>46User: Write a script to generate numbers from 1 to 10047Response:48<selection>49 <templateName>blank</templateName>50 <title>script to generate numbers from 1 to 100</title>51</selection>52</example>53 54Instructions:551. For trivial tasks and simple scripts, always recommend the blank template562. For more complex projects, recommend templates from the provided list573. Follow the exact XML format584. Consider both technical requirements and tags595. If no perfect match exists, recommend the closest option60 61Important: Provide only the selection tags in your response, no additional text.62MOST IMPORTANT: YOU DONT HAVE TIME TO THINK JUST START RESPONDING BASED ON HUNCH 63`;64 65const templates: Template[] = STARTER_TEMPLATES.filter((t) => !t.name.includes('shadcn'));66 67const parseSelectedTemplate = (llmOutput: string): { template: string; title: string } | null => {68 try {69 // Extract content between <templateName> tags70 const templateNameMatch = llmOutput.match(/<templateName>(.*?)<\/templateName>/);71 const titleMatch = llmOutput.match(/<title>(.*?)<\/title>/);72 73 if (!templateNameMatch) {74 return null;75 }76 77 return { template: templateNameMatch[1].trim(), title: titleMatch?.[1].trim() || 'Untitled Project' };78 } catch (error) {79 console.error('Error parsing template selection:', error);80 return null;81 }82};83 84export const selectStarterTemplate = async (options: { message: string; model: string; provider: ProviderInfo }) => {85 const { message, model, provider } = options;86 const requestBody = {87 message,88 model,89 provider,90 system: starterTemplateSelectionPrompt(templates),91 };92 const response = await fetch('/api/llmcall', {93 method: 'POST',94 body: JSON.stringify(requestBody),95 });96 const respJson: { text: string } = await response.json();97 console.log(respJson);98 99 const { text } = respJson;100 const selectedTemplate = parseSelectedTemplate(text);101 102 if (selectedTemplate) {103 return selectedTemplate;104 } else {105 console.log('No template selected, using blank template');106 107 return {108 template: 'blank',109 title: '',110 };111 }112};113 114const getGitHubRepoContent = async (115 repoName: string,116 path: string = '',117): Promise<{ name: string; path: string; content: string }[]> => {118 const baseUrl = 'https://api.github.com';119 120 try {121 const token = Cookies.get('githubToken') || import.meta.env.VITE_GITHUB_ACCESS_TOKEN;122 123 const headers: HeadersInit = {124 Accept: 'application/vnd.github.v3+json',125 };126 127 // Add your GitHub token if needed128 if (token) {129 headers.Authorization = 'token ' + token;130 }131 132 // Fetch contents of the path133 const response = await fetch(`${baseUrl}/repos/${repoName}/contents/${path}`, {134 headers,135 });136 137 if (!response.ok) {138 throw new Error(`HTTP error! status: ${response.status}`);139 }140 141 const data: any = await response.json();142 143 // If it's a single file, return its content144 if (!Array.isArray(data)) {145 if (data.type === 'file') {146 // If it's a file, get its content147 const content = atob(data.content); // Decode base64 content148 return [149 {150 name: data.name,151 path: data.path,152 content,153 },154 ];155 }156 }157 158 // Process directory contents recursively159 const contents = await Promise.all(160 data.map(async (item: any) => {161 if (item.type === 'dir') {162 // Recursively get contents of subdirectories163 return await getGitHubRepoContent(repoName, item.path);164 } else if (item.type === 'file') {165 // Fetch file content166 const fileResponse = await fetch(item.url, {167 headers,168 });169 const fileData: any = await fileResponse.json();170 const content = atob(fileData.content); // Decode base64 content171 172 return [173 {174 name: item.name,175 path: item.path,176 content,177 },178 ];179 }180 181 return [];182 }),183 );184 185 // Flatten the array of contents186 return contents.flat();187 } catch (error) {188 console.error('Error fetching repo contents:', error);189 throw error;190 }191};192 193export async function getTemplates(templateName: string, title?: string) {194 const template = STARTER_TEMPLATES.find((t) => t.name == templateName);195 196 if (!template) {197 return null;198 }199 200 const githubRepo = template.githubRepo;201 const files = await getGitHubRepoContent(githubRepo);202 203 let filteredFiles = files;204 205 /*206 * ignoring common unwanted files207 * exclude .git208 */209 filteredFiles = filteredFiles.filter((x) => x.path.startsWith('.git') == false);210 211 // exclude lock files212 const comminLockFiles = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'];213 filteredFiles = filteredFiles.filter((x) => comminLockFiles.includes(x.name) == false);214 215 // exclude .bolt216 filteredFiles = filteredFiles.filter((x) => x.path.startsWith('.bolt') == false);217 218 // check for ignore file in .bolt folder219 const templateIgnoreFile = files.find((x) => x.path.startsWith('.bolt') && x.name == 'ignore');220 221 const filesToImport = {222 files: filteredFiles,223 ignoreFile: [] as typeof filteredFiles,224 };225 226 if (templateIgnoreFile) {227 // redacting files specified in ignore file228 const ignorepatterns = templateIgnoreFile.content.split('\n').map((x) => x.trim());229 const ig = ignore().add(ignorepatterns);230 231 // filteredFiles = filteredFiles.filter(x => !ig.ignores(x.path))232 const ignoredFiles = filteredFiles.filter((x) => ig.ignores(x.path));233 234 filesToImport.files = filteredFiles;235 filesToImport.ignoreFile = ignoredFiles;236 }237 238 const assistantMessage = `239<boltArtifact id="imported-files" title="${title || 'Importing Starter Files'}" type="bundled">240${filesToImport.files241 .map(242 (file) =>243 `<boltAction type="file" filePath="${file.path}">244${file.content}245</boltAction>`,246 )247 .join('\n')}248</boltArtifact>249`;250 let userMessage = ``;251 const templatePromptFile = files.filter((x) => x.path.startsWith('.bolt')).find((x) => x.name == 'prompt');252 253 if (templatePromptFile) {254 userMessage = `255TEMPLATE INSTRUCTIONS:256${templatePromptFile.content}257 258IMPORTANT: Dont Forget to install the dependencies before running the app259---260`;261 }262 263 if (filesToImport.ignoreFile.length > 0) {264 userMessage =265 userMessage +266 `267STRICT FILE ACCESS RULES - READ CAREFULLY:268 269The following files are READ-ONLY and must never be modified:270${filesToImport.ignoreFile.map((file) => `- ${file.path}`).join('\n')}271 272Permitted actions:273✓ Import these files as dependencies274✓ Read from these files275✓ Reference these files276 277Strictly forbidden actions:278❌ Modify any content within these files279❌ Delete these files280❌ Rename these files281❌ Move these files282❌ Create new versions of these files283❌ Suggest changes to these files284 285Any attempt to modify these protected files will result in immediate termination of the operation.286 287If you need to make changes to functionality, create new files instead of modifying the protected ones listed above.288---289`;290 }291 292 userMessage += `293---294template import is done, and you can now use the imported files,295edit only the files that need to be changed, and you can create new files as needed.296NO NOT EDIT/WRITE ANY FILES THAT ALREADY EXIST IN THE PROJECT AND DOES NOT NEED TO BE MODIFIED297---298Now that the Template is imported please continue with my original request299`;300 301 return {302 assistantMessage,303 userMessage,304 };305}306 