Spaces:
Build error
Build error
File size: 11,784 Bytes
d83e271 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
import { FileManifest, EditIntent, EditType } from '@/types/file-manifest';
import { analyzeEditIntent } from '@/lib/edit-intent-analyzer';
import { getEditExamplesPrompt, getComponentPatternPrompt } from '@/lib/edit-examples';
export interface FileContext {
primaryFiles: string[]; // Files to edit
contextFiles: string[]; // Files to include for reference
systemPrompt: string; // Enhanced prompt with file info
editIntent: EditIntent;
}
/**
* Select files and build context based on user prompt
*/
export function selectFilesForEdit(
userPrompt: string,
manifest: FileManifest
): FileContext {
// Analyze the edit intent
const editIntent = analyzeEditIntent(userPrompt, manifest);
// Get the files based on intent - only edit target files, but provide all others as context
const primaryFiles = editIntent.targetFiles;
const allFiles = Object.keys(manifest.files);
let contextFiles = allFiles.filter(file => !primaryFiles.includes(file));
// ALWAYS include key files in context if they exist and aren't already primary files
const keyFiles: string[] = [];
// App.jsx is most important - shows component structure
const appFile = allFiles.find(f => f.endsWith('App.jsx') || f.endsWith('App.tsx'));
if (appFile && !primaryFiles.includes(appFile)) {
keyFiles.push(appFile);
}
// Include design system files for style context
const tailwindConfig = allFiles.find(f => f.endsWith('tailwind.config.js') || f.endsWith('tailwind.config.ts'));
if (tailwindConfig && !primaryFiles.includes(tailwindConfig)) {
keyFiles.push(tailwindConfig);
}
const indexCss = allFiles.find(f => f.endsWith('index.css') || f.endsWith('globals.css'));
if (indexCss && !primaryFiles.includes(indexCss)) {
keyFiles.push(indexCss);
}
// Include package.json to understand dependencies
const packageJson = allFiles.find(f => f.endsWith('package.json'));
if (packageJson && !primaryFiles.includes(packageJson)) {
keyFiles.push(packageJson);
}
// Put key files at the beginning of context for visibility
contextFiles = [...keyFiles, ...contextFiles.filter(f => !keyFiles.includes(f))];
// Build enhanced system prompt
const systemPrompt = buildSystemPrompt(
userPrompt,
editIntent,
primaryFiles,
contextFiles,
manifest
);
return {
primaryFiles,
contextFiles,
systemPrompt,
editIntent,
};
}
/**
* Build an enhanced system prompt with file structure context
*/
function buildSystemPrompt(
userPrompt: string,
editIntent: EditIntent,
primaryFiles: string[],
contextFiles: string[],
manifest: FileManifest
): string {
const sections: string[] = [];
// Add edit examples first for better understanding
if (editIntent.type !== EditType.FULL_REBUILD) {
sections.push(getEditExamplesPrompt());
}
// Add edit intent section
sections.push(`## Edit Intent
Type: ${editIntent.type}
Description: ${editIntent.description}
Confidence: ${(editIntent.confidence * 100).toFixed(0)}%
User Request: "${userPrompt}"`);
// Add file structure overview
sections.push(buildFileStructureSection(manifest));
// Add component patterns
const fileList = Object.keys(manifest.files).map(f => f.replace('/home/user/app/', '')).join('\n');
sections.push(getComponentPatternPrompt(fileList));
// Add primary files section
if (primaryFiles.length > 0) {
sections.push(`## Files to Edit
${primaryFiles.map(f => {
const fileInfo = manifest.files[f];
return `- ${f}${fileInfo?.componentInfo ? ` (${fileInfo.componentInfo.name} component)` : ''}`;
}).join('\n')}`);
}
// Add context files section
if (contextFiles.length > 0) {
sections.push(`## Context Files (for reference only)
${contextFiles.map(f => {
const fileInfo = manifest.files[f];
return `- ${f}${fileInfo?.componentInfo ? ` (${fileInfo.componentInfo.name} component)` : ''}`;
}).join('\n')}`);
}
// Add specific instructions based on edit type
sections.push(buildEditInstructions(editIntent.type));
// Add component relationships if relevant
if (editIntent.type === EditType.UPDATE_COMPONENT ||
editIntent.type === EditType.ADD_FEATURE) {
sections.push(buildComponentRelationships(primaryFiles, manifest));
}
return sections.join('\n\n');
}
/**
* Build file structure overview section
*/
function buildFileStructureSection(manifest: FileManifest): string {
const allFiles = Object.entries(manifest.files)
.map(([path]) => path.replace('/home/user/app/', ''))
.filter(path => !path.includes('node_modules'))
.sort();
const componentFiles = Object.entries(manifest.files)
.filter(([, info]) => info.type === 'component' || info.type === 'page')
.map(([path, info]) => ({
path: path.replace('/home/user/app/', ''),
name: info.componentInfo?.name || path.split('/').pop(),
type: info.type,
}));
return `## π¨ EXISTING PROJECT FILES - DO NOT CREATE NEW FILES WITH SIMILAR NAMES π¨
### ALL PROJECT FILES (${allFiles.length} files)
\`\`\`
${allFiles.join('\n')}
\`\`\`
### Component Files (USE THESE EXACT NAMES)
${componentFiles.map(f =>
`- ${f.name} β ${f.path} (${f.type})`
).join('\n')}
### CRITICAL: Component Relationships
**ALWAYS CHECK App.jsx FIRST** to understand what components exist and how they're imported!
Common component overlaps to watch for:
- "nav" or "navigation" β Often INSIDE Header.jsx, not a separate file
- "menu" β Usually part of Header/Nav, not separate
- "logo" β Typically in Header, not standalone
When user says "nav" or "navigation":
1. First check if Header.jsx exists
2. Look inside Header.jsx for navigation elements
3. Only create Nav.jsx if navigation doesn't exist anywhere
Entry Point: ${manifest.entryPoint}
### Routes
${manifest.routes.map(r =>
`- ${r.path} β ${r.component.split('/').pop()}`
).join('\n') || 'No routes detected'}`;
}
/**
* Build edit-type specific instructions
*/
function buildEditInstructions(editType: EditType): string {
const instructions: Record<EditType, string> = {
[EditType.UPDATE_COMPONENT]: `## SURGICAL EDIT INSTRUCTIONS
- You MUST preserve 99% of the original code
- ONLY edit the specific component(s) mentioned
- Make ONLY the minimal change requested
- DO NOT rewrite or refactor unless explicitly asked
- DO NOT remove any existing code unless explicitly asked
- DO NOT change formatting or structure
- Preserve all imports and exports
- Maintain the existing code style
- Return the COMPLETE file with the surgical change applied
- Think of yourself as a surgeon making a precise incision, not an artist repainting`,
[EditType.ADD_FEATURE]: `## Instructions
- Create new components in appropriate directories
- IMPORTANT: Update parent components to import and use the new component
- Update routing if adding new pages
- Follow existing patterns and conventions
- Add necessary styles to match existing design
- Example workflow:
1. Create NewComponent.jsx
2. Import it in the parent: import NewComponent from './NewComponent'
3. Use it in the parent's render: <NewComponent />`,
[EditType.FIX_ISSUE]: `## Instructions
- Identify and fix the specific issue
- Test the fix doesn't break other functionality
- Preserve existing behavior except for the bug
- Add error handling if needed`,
[EditType.UPDATE_STYLE]: `## SURGICAL STYLE EDIT INSTRUCTIONS
- Change ONLY the specific style/class mentioned
- If user says "change background to blue", change ONLY the background class
- DO NOT touch any other styles, classes, or attributes
- DO NOT refactor or "improve" the styling
- DO NOT change the component structure
- Preserve ALL other classes and styles exactly as they are
- Return the COMPLETE file with only the specific style change`,
[EditType.REFACTOR]: `## Instructions
- Improve code quality without changing functionality
- Follow project conventions
- Maintain all existing features
- Improve readability and maintainability`,
[EditType.FULL_REBUILD]: `## Instructions
- You may rebuild the entire application
- Keep the same core functionality
- Improve upon the existing design
- Use modern best practices`,
[EditType.ADD_DEPENDENCY]: `## Instructions
- Update package.json with new dependency
- Add necessary import statements
- Configure the dependency if needed
- Update any build configuration`,
};
return instructions[editType] || instructions[EditType.UPDATE_COMPONENT];
}
/**
* Build component relationship information
*/
function buildComponentRelationships(
files: string[],
manifest: FileManifest
): string {
const relationships: string[] = ['## Component Relationships'];
for (const file of files) {
const fileInfo = manifest.files[file];
if (!fileInfo?.componentInfo) continue;
const componentName = fileInfo.componentInfo.name;
const treeNode = manifest.componentTree[componentName];
if (treeNode) {
relationships.push(`\n### ${componentName}`);
if (treeNode.imports.length > 0) {
relationships.push(`Imports: ${treeNode.imports.join(', ')}`);
}
if (treeNode.importedBy.length > 0) {
relationships.push(`Used by: ${treeNode.importedBy.join(', ')}`);
}
if (fileInfo.componentInfo.childComponents?.length) {
relationships.push(`Renders: ${fileInfo.componentInfo.childComponents.join(', ')}`);
}
}
}
return relationships.join('\n');
}
/**
* Get file content for selected files
*/
export async function getFileContents(
files: string[],
manifest: FileManifest
): Promise<Record<string, string>> {
const contents: Record<string, string> = {};
for (const file of files) {
const fileInfo = manifest.files[file];
if (fileInfo) {
contents[file] = fileInfo.content;
}
}
return contents;
}
/**
* Format files for AI context
*/
export function formatFilesForAI(
primaryFiles: Record<string, string>,
contextFiles: Record<string, string>
): string {
const sections: string[] = [];
// Add primary files
sections.push('## Files to Edit (ONLY OUTPUT THESE FILES)\n');
sections.push('π¨ You MUST ONLY generate the files listed below. Do NOT generate any other files! π¨\n');
sections.push('β οΈ CRITICAL: Return the COMPLETE file - NEVER truncate with "..." or skip any lines! β οΈ\n');
sections.push('The file MUST include ALL imports, ALL functions, ALL JSX, and ALL closing tags.\n\n');
for (const [path, content] of Object.entries(primaryFiles)) {
sections.push(`### ${path}
**IMPORTANT: This is the COMPLETE file. Your output must include EVERY line shown below, modified only where necessary.**
\`\`\`${getFileExtension(path)}
${content}
\`\`\`
`);
}
// Add context files if any - but truncate large files
if (Object.keys(contextFiles).length > 0) {
sections.push('\n## Context Files (Reference Only - Do Not Edit)\n');
for (const [path, content] of Object.entries(contextFiles)) {
// Truncate very large context files to save tokens
let truncatedContent = content;
if (content.length > 2000) {
truncatedContent = content.substring(0, 2000) + '\n// ... [truncated for context length]';
}
sections.push(`### ${path}
\`\`\`${getFileExtension(path)}
${truncatedContent}
\`\`\`
`);
}
}
return sections.join('\n');
}
/**
* Get file extension for syntax highlighting
*/
function getFileExtension(path: string): string {
const ext = path.split('.').pop() || '';
const mapping: Record<string, string> = {
'js': 'javascript',
'jsx': 'javascript',
'ts': 'typescript',
'tsx': 'typescript',
'css': 'css',
'json': 'json',
};
return mapping[ext] || ext;
} |