| | 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)); |
| | } |
| |
|