| 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<ProjectCommands> { |
| 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 || {}; |
|
|
| |
| 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 += ` |
| <boltAction type="shell">${commands.setupCommand}</boltAction>`; |
| } |
|
|
| if (commands.startCommand) { |
| commandString += ` |
| <boltAction type="start">${commands.startCommand}</boltAction> |
| `; |
| } |
|
|
| return { |
| role: 'assistant', |
| content: ` |
| <boltArtifact id="project-setup" title="Project Setup"> |
| ${commandString} |
| </boltArtifact>${commands.followupMessage ? `\n\n${commands.followupMessage}` : ''}`, |
| id: generateId(), |
| createdAt: new Date(), |
| }; |
| } |
|
|
| export function escapeBoltArtifactTags(input: string) { |
| |
| const regex = /(<boltArtifact[^>]*>)([\s\S]*?)(<\/boltArtifact>)/g; |
|
|
| return input.replace(regex, (match, openTag, content, closeTag) => { |
| |
| const escapedOpenTag = openTag.replace(/</g, '<').replace(/>/g, '>'); |
|
|
| |
| const escapedCloseTag = closeTag.replace(/</g, '<').replace(/>/g, '>'); |
|
|
| |
| return `${escapedOpenTag}${content}${escapedCloseTag}`; |
| }); |
| } |
|
|
| export function escapeBoltAActionTags(input: string) { |
| |
| const regex = /(<boltAction[^>]*>)([\s\S]*?)(<\/boltAction>)/g; |
|
|
| return input.replace(regex, (match, openTag, content, closeTag) => { |
| |
| const escapedOpenTag = openTag.replace(/</g, '<').replace(/>/g, '>'); |
|
|
| |
| const escapedCloseTag = closeTag.replace(/</g, '<').replace(/>/g, '>'); |
|
|
| |
| return `${escapedOpenTag}${content}${escapedCloseTag}`; |
| }); |
| } |
|
|
| export function escapeBoltTags(input: string) { |
| return escapeBoltArtifactTags(escapeBoltAActionTags(input)); |
| } |
|
|