together-code
Version:
AI-powered coding assistant that plans, then builds
108 lines (107 loc) • 3.68 kB
JavaScript
import fs from 'fs-extra';
import path from 'path';
export class FileWriter {
constructor(basePath = process.cwd()) {
this.basePath = basePath;
}
async writeFiles(files) {
for (const file of files) {
await this.writeFile(file.path, file.content);
}
}
async writeFile(filePath, content) {
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) {
const files = [];
// 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;
}
inferFilename(content) {
// 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) {
const fullPath = path.resolve(this.basePath, filePath);
return fs.pathExists(fullPath);
}
async createBackup(filePath) {
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) {
const existingFiles = [];
for (const file of files) {
if (await this.fileExists(file.path)) {
existingFiles.push(file.path);
}
}
return existingFiles;
}
async getFileContent(filePath) {
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) {
// 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);
}
}