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