Reuse automatic setup commands for git import

This commit is contained in:
eduardruzga
2024-12-08 12:16:09 +02:00
parent 6e61a4fb95
commit 823f536a47
6 changed files with 171 additions and 63 deletions

View File

@@ -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: '',
};
}

View File

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

View 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(),
};
}