Reuse automatic setup commands for git import
This commit is contained in:
@@ -29,10 +29,12 @@ export const isBinaryFile = async (file: File): Promise<boolean> => {
|
||||
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
const byte = buffer[i];
|
||||
|
||||
if (byte === 0 || (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -41,8 +43,11 @@ export const shouldIncludeFile = (path: string): boolean => {
|
||||
};
|
||||
|
||||
const readPackageJson = async (files: File[]): Promise<{ scripts?: Record<string, string> } | null> => {
|
||||
const packageJsonFile = files.find(f => f.webkitRelativePath.endsWith('package.json'));
|
||||
if (!packageJsonFile) return null;
|
||||
const packageJsonFile = files.find((f) => f.webkitRelativePath.endsWith('package.json'));
|
||||
|
||||
if (!packageJsonFile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await new Promise<string>((resolve, reject) => {
|
||||
@@ -59,29 +64,32 @@ const readPackageJson = async (files: File[]): Promise<{ scripts?: Record<string
|
||||
}
|
||||
};
|
||||
|
||||
export const detectProjectType = async (files: File[]): Promise<{ type: string; setupCommand: string; followupMessage: string }> => {
|
||||
const hasFile = (name: string) => files.some(f => f.webkitRelativePath.endsWith(name));
|
||||
export const detectProjectType = async (
|
||||
files: File[],
|
||||
): Promise<{ type: string; setupCommand: string; followupMessage: string }> => {
|
||||
const hasFile = (name: string) => files.some((f) => f.webkitRelativePath.endsWith(name));
|
||||
|
||||
if (hasFile('package.json')) {
|
||||
const packageJson = await readPackageJson(files);
|
||||
const scripts = packageJson?.scripts || {};
|
||||
|
||||
|
||||
// Check for preferred commands in priority order
|
||||
const preferredCommands = ['dev', 'start', 'preview'];
|
||||
const availableCommand = preferredCommands.find(cmd => scripts[cmd]);
|
||||
|
||||
const availableCommand = preferredCommands.find((cmd) => scripts[cmd]);
|
||||
|
||||
if (availableCommand) {
|
||||
return {
|
||||
type: 'Node.js',
|
||||
setupCommand: `npm install && npm run ${availableCommand}`,
|
||||
followupMessage: `Found "${availableCommand}" script in package.json. Running "npm run ${availableCommand}" after installation.`
|
||||
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?'
|
||||
followupMessage:
|
||||
'Would you like me to inspect package.json to determine the available scripts for running this project?',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,7 +97,7 @@ export const detectProjectType = async (files: File[]): Promise<{ type: string;
|
||||
return {
|
||||
type: 'Static',
|
||||
setupCommand: 'npx --yes serve',
|
||||
followupMessage: ''
|
||||
followupMessage: '',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import type { Message } from 'ai';
|
||||
import { generateId, detectProjectType } from './fileUtils';
|
||||
import { generateId } from './fileUtils';
|
||||
import { detectProjectCommands, createCommandsMessage } from './projectCommands';
|
||||
|
||||
export const createChatFromFolder = async (
|
||||
files: File[],
|
||||
binaryFiles: string[],
|
||||
folderName: string
|
||||
folderName: string,
|
||||
): Promise<Message[]> => {
|
||||
const fileArtifacts = await Promise.all(
|
||||
files.map(async (file) => {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
return new Promise<{ content: string; path: string }>((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = () => {
|
||||
const content = reader.result as string;
|
||||
const relativePath = file.webkitRelativePath.split('/').slice(1).join('/');
|
||||
resolve(
|
||||
`<boltAction type="file" filePath="${relativePath}">
|
||||
${content}
|
||||
</boltAction>`,
|
||||
);
|
||||
resolve({
|
||||
content,
|
||||
path: relativePath,
|
||||
});
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.readAsText(file);
|
||||
@@ -25,32 +26,30 @@ ${content}
|
||||
}),
|
||||
);
|
||||
|
||||
const project = await detectProjectType(files);
|
||||
const setupCommand = project.setupCommand ? `\n\n<boltAction type="shell">\n${project.setupCommand}\n</boltAction>` : '';
|
||||
const followupMessage = project.followupMessage ? `\n\n${project.followupMessage}` : '';
|
||||
const commands = await detectProjectCommands(fileArtifacts);
|
||||
const commandsMessage = createCommandsMessage(commands);
|
||||
|
||||
const binaryFilesMessage = binaryFiles.length > 0
|
||||
? `\n\nSkipped ${binaryFiles.length} binary files:\n${binaryFiles.map((f) => `- ${f}`).join('\n')}`
|
||||
: '';
|
||||
const binaryFilesMessage =
|
||||
binaryFiles.length > 0
|
||||
? `\n\nSkipped ${binaryFiles.length} binary files:\n${binaryFiles.map((f) => `- ${f}`).join('\n')}`
|
||||
: '';
|
||||
|
||||
const assistantMessages: Message[] = [{
|
||||
const filesMessage: Message = {
|
||||
role: 'assistant',
|
||||
content: `I've imported the contents of the "${folderName}" folder.${binaryFilesMessage}
|
||||
|
||||
<boltArtifact id="imported-files" title="Imported Files">
|
||||
${fileArtifacts.join('\n\n')}
|
||||
${fileArtifacts
|
||||
.map(
|
||||
(file) => `<boltAction type="file" filePath="${file.path}">
|
||||
${file.content}
|
||||
</boltAction>`,
|
||||
)
|
||||
.join('\n\n')}
|
||||
</boltArtifact>`,
|
||||
id: generateId(),
|
||||
createdAt: new Date(),
|
||||
},{
|
||||
role: 'assistant',
|
||||
content: `
|
||||
<boltArtifact id="imported-files" title="Imported Files">
|
||||
${setupCommand}
|
||||
</boltArtifact>${followupMessage}`,
|
||||
id: generateId(),
|
||||
createdAt: new Date(),
|
||||
}];
|
||||
};
|
||||
|
||||
const userMessage: Message = {
|
||||
role: 'user',
|
||||
@@ -59,5 +58,11 @@ ${setupCommand}
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
return [ userMessage, ...assistantMessages ];
|
||||
const messages = [userMessage, filesMessage];
|
||||
|
||||
if (commandsMessage) {
|
||||
messages.push(commandsMessage);
|
||||
}
|
||||
|
||||
return messages;
|
||||
};
|
||||
|
||||
80
app/utils/projectCommands.ts
Normal file
80
app/utils/projectCommands.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { Message } from 'ai';
|
||||
import { generateId } from './fileUtils';
|
||||
|
||||
export interface ProjectCommands {
|
||||
type: string;
|
||||
setupCommand: 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 || {};
|
||||
|
||||
// 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 && 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',
|
||||
setupCommand: 'npx --yes serve',
|
||||
followupMessage: '',
|
||||
};
|
||||
}
|
||||
|
||||
return { type: '', setupCommand: '', followupMessage: '' };
|
||||
}
|
||||
|
||||
export function createCommandsMessage(commands: ProjectCommands): Message | null {
|
||||
if (!commands.setupCommand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
role: 'assistant',
|
||||
content: `
|
||||
<boltArtifact id="project-setup" title="Project Setup">
|
||||
<boltAction type="shell">
|
||||
${commands.setupCommand}
|
||||
</boltAction>
|
||||
</boltArtifact>${commands.followupMessage ? `\n\n${commands.followupMessage}` : ''}`,
|
||||
id: generateId(),
|
||||
createdAt: new Date(),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user