Spaces:
Sleeping
Sleeping
| import ignore from 'ignore'; | |
| import type { ProviderInfo } from '~/types/model'; | |
| import type { Template } from '~/types/template'; | |
| import { STARTER_TEMPLATES } from './constants'; | |
| const starterTemplateSelectionPrompt = (templates: Template[]) => ` | |
| You are an experienced developer who helps people choose the best starter template for their projects, Vite is preferred. | |
| Available templates: | |
| <template> | |
| <name>blank</name> | |
| <description>Empty starter for simple scripts and trivial tasks that don't require a full template setup</description> | |
| <tags>basic, script</tags> | |
| </template> | |
| ${templates | |
| .map( | |
| (template) => ` | |
| <template> | |
| <name>${template.name}</name> | |
| <description>${template.description}</description> | |
| ${template.tags ? `<tags>${template.tags.join(', ')}</tags>` : ''} | |
| </template> | |
| `, | |
| ) | |
| .join('\n')} | |
| Response Format: | |
| <selection> | |
| <templateName>{selected template name}</templateName> | |
| <title>{a proper title for the project}</title> | |
| </selection> | |
| Examples: | |
| <example> | |
| User: I need to build a todo app | |
| Response: | |
| <selection> | |
| <templateName>react-basic-starter</templateName> | |
| <title>Simple React todo application</title> | |
| </selection> | |
| </example> | |
| <example> | |
| User: Write a script to generate numbers from 1 to 100 | |
| Response: | |
| <selection> | |
| <templateName>blank</templateName> | |
| <title>script to generate numbers from 1 to 100</title> | |
| </selection> | |
| </example> | |
| Instructions: | |
| 1. For trivial tasks and simple scripts, always recommend the blank template | |
| 2. For more complex projects, recommend templates from the provided list | |
| 3. Follow the exact XML format | |
| 4. Consider both technical requirements and tags | |
| 5. If no perfect match exists, recommend the closest option | |
| Important: Provide only the selection tags in your response, no additional text. | |
| MOST IMPORTANT: YOU DONT HAVE TIME TO THINK JUST START RESPONDING BASED ON HUNCH | |
| `; | |
| const templates: Template[] = STARTER_TEMPLATES.filter((t) => !t.name.includes('shadcn')); | |
| const parseSelectedTemplate = (llmOutput: string): { template: string; title: string } | null => { | |
| try { | |
| // Extract content between <templateName> tags | |
| const templateNameMatch = llmOutput.match(/<templateName>(.*?)<\/templateName>/); | |
| const titleMatch = llmOutput.match(/<title>(.*?)<\/title>/); | |
| if (!templateNameMatch) { | |
| return null; | |
| } | |
| return { template: templateNameMatch[1].trim(), title: titleMatch?.[1].trim() || 'Untitled Project' }; | |
| } catch (error) { | |
| console.error('Error parsing template selection:', error); | |
| return null; | |
| } | |
| }; | |
| export const selectStarterTemplate = async (options: { message: string; model: string; provider: ProviderInfo }) => { | |
| const { message, model, provider } = options; | |
| const requestBody = { | |
| message, | |
| model, | |
| provider, | |
| system: starterTemplateSelectionPrompt(templates), | |
| }; | |
| const response = await fetch('/api/llmcall', { | |
| method: 'POST', | |
| body: JSON.stringify(requestBody), | |
| }); | |
| const respJson: { text: string } = await response.json(); | |
| console.log(respJson); | |
| const { text } = respJson; | |
| const selectedTemplate = parseSelectedTemplate(text); | |
| if (selectedTemplate) { | |
| return selectedTemplate; | |
| } else { | |
| console.log('No template selected, using blank template'); | |
| return { | |
| template: 'blank', | |
| title: '', | |
| }; | |
| } | |
| }; | |
| const getGitHubRepoContent = async (repoName: string): Promise<{ name: string; path: string; content: string }[]> => { | |
| try { | |
| // Instead of directly fetching from GitHub, use our own API endpoint as a proxy | |
| const response = await fetch(`/api/github-template?repo=${encodeURIComponent(repoName)}`); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| // Our API will return the files in the format we need | |
| const files = (await response.json()) as any; | |
| return files; | |
| } catch (error) { | |
| console.error('Error fetching release contents:', error); | |
| throw error; | |
| } | |
| }; | |
| export async function getTemplates(templateName: string, title?: string) { | |
| const template = STARTER_TEMPLATES.find((t) => t.name == templateName); | |
| if (!template) { | |
| return null; | |
| } | |
| const githubRepo = template.githubRepo; | |
| const files = await getGitHubRepoContent(githubRepo); | |
| let filteredFiles = files; | |
| /* | |
| * ignoring common unwanted files | |
| * exclude .git | |
| */ | |
| filteredFiles = filteredFiles.filter((x) => x.path.startsWith('.git') == false); | |
| /* | |
| * exclude lock files | |
| * WE NOW INCLUDE LOCK FILES FOR IMPROVED INSTALL TIMES | |
| */ | |
| { | |
| /* | |
| *const comminLockFiles = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml']; | |
| *filteredFiles = filteredFiles.filter((x) => comminLockFiles.includes(x.name) == false); | |
| */ | |
| } | |
| // exclude .bolt | |
| filteredFiles = filteredFiles.filter((x) => x.path.startsWith('.bolt') == false); | |
| // check for ignore file in .bolt folder | |
| const templateIgnoreFile = files.find((x) => x.path.startsWith('.bolt') && x.name == 'ignore'); | |
| const filesToImport = { | |
| files: filteredFiles, | |
| ignoreFile: [] as typeof filteredFiles, | |
| }; | |
| if (templateIgnoreFile) { | |
| // redacting files specified in ignore file | |
| const ignorepatterns = templateIgnoreFile.content.split('\n').map((x) => x.trim()); | |
| const ig = ignore().add(ignorepatterns); | |
| // filteredFiles = filteredFiles.filter(x => !ig.ignores(x.path)) | |
| const ignoredFiles = filteredFiles.filter((x) => ig.ignores(x.path)); | |
| filesToImport.files = filteredFiles; | |
| filesToImport.ignoreFile = ignoredFiles; | |
| } | |
| const assistantMessage = ` | |
| Bolt is initializing your project with the required files using the ${template.name} template. | |
| <boltArtifact id="imported-files" title="${title || 'Create initial files'}" type="bundled"> | |
| ${filesToImport.files | |
| .map( | |
| (file) => | |
| `<boltAction type="file" filePath="${file.path}"> | |
| ${file.content} | |
| </boltAction>`, | |
| ) | |
| .join('\n')} | |
| </boltArtifact> | |
| `; | |
| let userMessage = ``; | |
| const templatePromptFile = files.filter((x) => x.path.startsWith('.bolt')).find((x) => x.name == 'prompt'); | |
| if (templatePromptFile) { | |
| userMessage = ` | |
| TEMPLATE INSTRUCTIONS: | |
| ${templatePromptFile.content} | |
| --- | |
| `; | |
| } | |
| if (filesToImport.ignoreFile.length > 0) { | |
| userMessage = | |
| userMessage + | |
| ` | |
| STRICT FILE ACCESS RULES - READ CAREFULLY: | |
| The following files are READ-ONLY and must never be modified: | |
| ${filesToImport.ignoreFile.map((file) => `- ${file.path}`).join('\n')} | |
| Permitted actions: | |
| β Import these files as dependencies | |
| β Read from these files | |
| β Reference these files | |
| Strictly forbidden actions: | |
| β Modify any content within these files | |
| β Delete these files | |
| β Rename these files | |
| β Move these files | |
| β Create new versions of these files | |
| β Suggest changes to these files | |
| Any attempt to modify these protected files will result in immediate termination of the operation. | |
| If you need to make changes to functionality, create new files instead of modifying the protected ones listed above. | |
| --- | |
| `; | |
| } | |
| userMessage += ` | |
| --- | |
| template import is done, and you can now use the imported files, | |
| edit only the files that need to be changed, and you can create new files as needed. | |
| NO NOT EDIT/WRITE ANY FILES THAT ALREADY EXIST IN THE PROJECT AND DOES NOT NEED TO BE MODIFIED | |
| --- | |
| Now that the Template is imported please continue with my original request | |
| IMPORTANT: Dont Forget to install the dependencies before running the app by using \`npm install && npm run dev\` | |
| `; | |
| return { | |
| assistantMessage, | |
| userMessage, | |
| }; | |
| } | |