import type { Message } from 'ai'; import { generateId } from './fileUtils'; export interface ProjectCommands { type: string; setupCommand?: string; startCommand?: string; followupMessage: string; } interface FileContent { content: string; path: string; } export async function detectProjectCommands(files: FileContent[]): Promise { const hasFile = (name: string) => files.some((f) => f.path.endsWith(name)); if (hasFile('package.json')) { const packageJsonFile = files.find((f) => f.path.endsWith('package.json')); if (!packageJsonFile) { return { type: '', setupCommand: '', followupMessage: '' }; } try { const packageJson = JSON.parse(packageJsonFile.content); const scripts = packageJson?.scripts || {}; // Check for preferred commands in priority order const preferredCommands = ['dev', 'start', 'preview']; const availableCommand = preferredCommands.find((cmd) => scripts[cmd]); if (availableCommand) { return { type: 'Node.js', setupCommand: `npm install`, startCommand: `npm run ${availableCommand}`, followupMessage: `Found "${availableCommand}" script in package.json. Running "npm run ${availableCommand}" after installation.`, }; } return { type: 'Node.js', setupCommand: 'npm install', followupMessage: 'Would you like me to inspect package.json to determine the available scripts for running this project?', }; } catch (error) { console.error('Error parsing package.json:', error); return { type: '', setupCommand: '', followupMessage: '' }; } } if (hasFile('index.html')) { return { type: 'Static', startCommand: 'npx --yes serve', followupMessage: '', }; } return { type: '', setupCommand: '', followupMessage: '' }; } export function createCommandsMessage(commands: ProjectCommands): Message | null { if (!commands.setupCommand && !commands.startCommand) { return null; } let commandString = ''; if (commands.setupCommand) { commandString += ` ${commands.setupCommand}`; } if (commands.startCommand) { commandString += ` ${commands.startCommand} `; } return { role: 'assistant', content: ` ${commandString} ${commands.followupMessage ? `\n\n${commands.followupMessage}` : ''}`, id: generateId(), createdAt: new Date(), }; } export function escapeBoltArtifactTags(input: string) { // Regular expression to match boltArtifact tags and their content const regex = /(]*>)([\s\S]*?)(<\/boltArtifact>)/g; return input.replace(regex, (match, openTag, content, closeTag) => { // Escape the opening tag const escapedOpenTag = openTag.replace(//g, '>'); // Escape the closing tag const escapedCloseTag = closeTag.replace(//g, '>'); // Return the escaped version return `${escapedOpenTag}${content}${escapedCloseTag}`; }); } export function escapeBoltAActionTags(input: string) { // Regular expression to match boltArtifact tags and their content const regex = /(]*>)([\s\S]*?)(<\/boltAction>)/g; return input.replace(regex, (match, openTag, content, closeTag) => { // Escape the opening tag const escapedOpenTag = openTag.replace(//g, '>'); // Escape the closing tag const escapedCloseTag = closeTag.replace(//g, '>'); // Return the escaped version return `${escapedOpenTag}${content}${escapedCloseTag}`; }); } export function escapeBoltTags(input: string) { return escapeBoltArtifactTags(escapeBoltAActionTags(input)); }