UNPKG

together-code

Version:

AI-powered coding assistant that plans, then builds

133 lines (110 loc) 3.62 kB
import fs from 'fs-extra'; import path from 'path'; export interface FileToWrite { path: string; content: string; } export class FileWriter { private basePath: string; constructor(basePath: string = process.cwd()) { this.basePath = basePath; } async writeFiles(files: FileToWrite[]): Promise<void> { for (const file of files) { await this.writeFile(file.path, file.content); } } async writeFile(filePath: string, content: string): Promise<void> { const fullPath = path.resolve(this.basePath, filePath); const dir = path.dirname(fullPath); // Ensure directory exists await fs.ensureDir(dir); // Write file await fs.writeFile(fullPath, content, 'utf8'); } parseCodeBlocks(aiResponse: string): FileToWrite[] { const files: FileToWrite[] = []; // Match code blocks with filename: prefix const codeBlockRegex = /```(?:filename:([^\n]+)\n)?([\s\S]*?)```/g; let match; while ((match = codeBlockRegex.exec(aiResponse)) !== null) { const [, filename, content] = match; if (filename && content) { files.push({ path: filename.trim(), content: content.trim() }); } else if (content && files.length === 0) { // If no filename specified and it's the first code block, try to infer from content const inferredName = this.inferFilename(content); if (inferredName) { files.push({ path: inferredName, content: content.trim() }); } } } return files; } private inferFilename(content: string): string | null { // Simple inference based on content if (content.includes('import React') || content.includes('export default')) { return 'component.tsx'; } if (content.includes('function ') || content.includes('const ') || content.includes('export ')) { return 'index.js'; } if (content.includes('<!DOCTYPE html') || content.includes('<html')) { return 'index.html'; } if (content.includes('body {') || content.includes('.class')) { return 'style.css'; } if (content.includes('"name":') && content.includes('"version":')) { return 'package.json'; } return null; } async fileExists(filePath: string): Promise<boolean> { const fullPath = path.resolve(this.basePath, filePath); return fs.pathExists(fullPath); } async createBackup(filePath: string): Promise<void> { const fullPath = path.resolve(this.basePath, filePath); const backupPath = `${fullPath}.backup.${Date.now()}`; if (await this.fileExists(filePath)) { await fs.copy(fullPath, backupPath); } } async checkExistingFiles(files: FileToWrite[]): Promise<string[]> { const existingFiles: string[] = []; for (const file of files) { if (await this.fileExists(file.path)) { existingFiles.push(file.path); } } return existingFiles; } async getFileContent(filePath: string): Promise<string | null> { try { const fullPath = path.resolve(this.basePath, filePath); if (await this.fileExists(filePath)) { return await fs.readFile(fullPath, 'utf8'); } return null; } catch (error) { return null; } } async writeFilesWithBackup(files: FileToWrite[]): Promise<void> { // Create backups for existing files for (const file of files) { if (await this.fileExists(file.path)) { await this.createBackup(file.path); } } // Write the files await this.writeFiles(files); } }