diff --git a/backend/src/build-system/__tests__/fullstack-gen.spec.ts b/backend/src/build-system/__tests__/fullstack-gen.spec.ts index a7d31bea..d62758af 100644 --- a/backend/src/build-system/__tests__/fullstack-gen.spec.ts +++ b/backend/src/build-system/__tests__/fullstack-gen.spec.ts @@ -5,16 +5,11 @@ import { PRDHandler } from '../handlers/product-manager/product-requirements-doc import { UXSMDHandler } from '../handlers/ux/sitemap-document'; import { UXSMSHandler } from '../handlers/ux/sitemap-structure'; import { DBRequirementHandler } from '../handlers/database/requirements-document'; -import { FileStructureHandler } from '../handlers/file-manager/file-structure'; -import { UXSMSPageByPageHandler } from '../handlers/ux/sitemap-structure/sms-page'; -import { DBSchemaHandler } from '../handlers/database/schemas/schemas'; -import { FileFAHandler } from '../handlers/file-manager/file-arch'; -import { BackendRequirementHandler } from '../handlers/backend/requirements-document'; -import { BackendCodeHandler } from '../handlers/backend/code-generate'; -import { BackendFileReviewHandler } from '../handlers/backend/file-review/file-review'; import { UXDMDHandler } from '../handlers/ux/datamap'; import { BuilderContext } from '../context'; import { FrontendCodeHandler } from '../handlers/frontend-code-generate'; +import { FileStructureAndArchitectureHandler } from '../handlers/file-manager/file-struct'; +import { BackendRequirementHandler } from '../handlers/backend/requirements-document'; (isIntegrationTest ? describe : describe.skip)('Build Sequence Test', () => { it('should execute build sequence successfully', async () => { @@ -23,8 +18,10 @@ import { FrontendCodeHandler } from '../handlers/frontend-code-generate'; version: '1.0.0', name: 'Wrtie a Cool personal website', description: - 'A personal blog website. I am a cybersecurity engineer so i want it to show i am a really cool hacker', + 'A personal blog website. I am a cybersecurity engineer so i want it to show i am a really cool hacker, with cool terminal functionality', databaseType: 'SQLite', + model: 'gpt-4o-mini', + projectSize: 'medium', // limit for fun nodes: [ { handler: ProjectInitHandler, @@ -47,47 +44,20 @@ import { FrontendCodeHandler } from '../handlers/frontend-code-generate'; handler: UXDMDHandler, name: 'UX DataMap Document Node', }, + { + handler: FileStructureAndArchitectureHandler, + name: 'File Structure and Architecture', + }, { handler: DBRequirementHandler, name: 'Database Requirements Node', // requires: ['op:UX:DATAMAP:DOC'], }, - { - handler: FileStructureHandler, - name: 'File Structure Generation', - // requires: ['op:UX:SMD', 'op:UX:DATAMAP:DOC'], - options: { - projectPart: 'frontend', - }, - }, - { - handler: UXSMSPageByPageHandler, - name: 'Level 2 UX Sitemap Structure Node details', - // requires: ['op:UX:SMS'], - }, - { - handler: DBSchemaHandler, - name: 'Database Schemas Node', - // requires: ['op:DATABASE_REQ'], - }, - { - handler: FileFAHandler, - name: 'File Arch', - // requires: ['op:FILE:STRUCT', 'op:UX:DATAMAP:DOC'], - }, { handler: BackendRequirementHandler, name: 'Backend Requirements Node', // requires: ['op:DATABASE_REQ', 'op:UX:DATAMAP:DOC', 'op:UX:SMD'], }, - { - handler: BackendCodeHandler, - name: 'Backend Code Generator Node', - }, - { - handler: BackendFileReviewHandler, - name: 'Backend File Review Node', - }, { handler: FrontendCodeHandler, name: 'Frontend Code Generator Node', @@ -97,5 +67,5 @@ import { FrontendCodeHandler } from '../handlers/frontend-code-generate'; }; const context = new BuilderContext(sequence, 'fullstack-code-gen'); await context.execute(); - }, 300000); + }, 2000000); }); diff --git a/backend/src/build-system/__tests__/mock/MockBuilderContext.ts b/backend/src/build-system/__tests__/mock/MockBuilderContext.ts index 3cebd04c..123f15c4 100644 --- a/backend/src/build-system/__tests__/mock/MockBuilderContext.ts +++ b/backend/src/build-system/__tests__/mock/MockBuilderContext.ts @@ -5,7 +5,6 @@ import * as path from 'path'; import { UXSMSHandler } from 'src/build-system/handlers/ux/sitemap-structure'; import { UXDMDHandler } from 'src/build-system/handlers/ux/datamap'; import { BackendRequirementHandler } from 'src/build-system/handlers/backend/requirements-document'; -import { FileFAHandler } from 'src/build-system/handlers/file-manager/file-arch'; import { BuilderContext, GlobalDataKeys } from 'src/build-system/context'; import { v4 as uuidv4 } from 'uuid'; // UUID generator for unique identifiers import { @@ -37,18 +36,20 @@ export class MockBuilderContext extends BuilderContext { const backendRequirements = this.readMockFile( path.join(__dirname, 'test_files', 'Backend_Requirements_Node.md'), ); - const fileStructure = this.readMockFile( - path.join(__dirname, 'test_files', 'File_Structure_Generation.md'), - ); - const fileArchitecture = this.readMockFile( - path.join(__dirname, 'test_files', 'File_Arch.md'), - ); + //To do integrate FileStructureAndArchitectureHandler + const fileStructureAndArchitectureHandler = this.readMockFile( + path.join(__dirname, 'test_files', 'File_Structure_Architecture_Node.md'), + ); this.mockNodeData.set(UXSMSHandler, uxSitemapStructure); this.mockNodeData.set(UXDMDHandler, uxDataMapDocument); this.mockNodeData.set(BackendRequirementHandler, backendRequirements); - this.mockNodeData.set(FileFAHandler, fileArchitecture); - this.buildVirtualDirectory(fileStructure); + // this.mockNodeData.set( + // fileStructureAndArchitectureHandler, + // FileStructureAndArchitectureHandler, + // ); + + // this.buildVirtualDirectory(fileStructure); copyProjectTemplate( path.join(__dirname, '..', '..', '..', '..', 'template', 'react-ts'), diff --git a/backend/src/build-system/__tests__/test.frontend-code-generate.spec.ts b/backend/src/build-system/__tests__/test.frontend-code-generate.spec.ts index c7b68576..58ab32e2 100644 --- a/backend/src/build-system/__tests__/test.frontend-code-generate.spec.ts +++ b/backend/src/build-system/__tests__/test.frontend-code-generate.spec.ts @@ -12,6 +12,7 @@ describe('FrontendCodeHandler', () => { name: 'Spotify-like Music Web', description: 'Users can play music', databaseType: 'SQLite', + model: 'o3-mini-high', nodes: [ { handler: FrontendCodeHandler, @@ -19,11 +20,12 @@ describe('FrontendCodeHandler', () => { // requires: ['op:FILE:STRUCT', 'op:UX:DATAMAP:DOC'], }, ], + packages: [], }; beforeEach(() => { handler = new FrontendCodeHandler(); - context = new MockBuilderContext(sequence, 'test'); + context = new MockBuilderContext(sequence, 'frontend-only'); }); //rember to comment requirement in FrontendCodeHandler @@ -32,5 +34,5 @@ describe('FrontendCodeHandler', () => { const result = await handler.run(context); expect(result.success).toBe(true); - }); + }, 6000000); }); diff --git a/backend/src/build-system/context.ts b/backend/src/build-system/context.ts index 6541d5cf..27630e25 100644 --- a/backend/src/build-system/context.ts +++ b/backend/src/build-system/context.ts @@ -81,6 +81,7 @@ export class BuilderContext { private logFolder: string | null = null; + public defaultModel: string; /** * Constructor to initialize the BuilderContext. * Sets up the handler manager, retry handler, model provider, logger, and virtual directory. @@ -99,21 +100,41 @@ export class BuilderContext { this.monitor = BuildMonitor.getInstance(); this.logger = new Logger(`builder-context-${id ?? sequence.id}`); this.virtualDirectory = new VirtualDirectory(); + this.defaultModel = this.sequence.model; - // Initialize global context with default project values this.globalContext.set('projectName', sequence.name); this.globalContext.set('description', sequence.description || ''); this.globalContext.set('platform', 'web'); // Default platform is 'web' this.globalContext.set('databaseType', sequence.databaseType || 'SQLite'); + if (sequence.projectSize) { + this.globalContext.set('projectSize', sequence.projectSize); + } else { + switch (sequence.model) { + case 'gpt-4o-mini': + this.globalContext.set('projectSize', 'small'); + break; + case 'gpt-4o': + case 'o3-mini-high': + this.globalContext.set('projectSize', 'medium'); + break; + default: + this.globalContext.set('projectSize', 'small'); + break; + } + } + const now = new Date(); const projectUUIDPath = - new Date().toISOString().slice(0, 18).replaceAll(/:/g, '-') + + `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}` + + `-${String(now.getHours()).padStart(2, '0')}-${String(now.getMinutes()).padStart(2, '0')}-${String(now.getSeconds()).padStart(2, '0')}-${String(now.getMilliseconds()).padStart(3, '0')}` + '-' + uuidv4(); this.globalContext.set('projectUUID', projectUUIDPath); if (process.env.DEBUG) { - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const timestamp = + `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}` + + `-${String(now.getHours()).padStart(2, '0')}-${String(now.getMinutes()).padStart(2, '0')}-${String(now.getSeconds()).padStart(2, '0')}-${String(now.getMilliseconds()).padStart(3, '0')}`; this.logFolder = path.join( process.cwd(), 'logs', diff --git a/backend/src/build-system/handlers/file-manager/file-arch/index.ts b/backend/src/build-system/handlers/file-manager/file-arch/index.ts deleted file mode 100644 index f5dcd93d..00000000 --- a/backend/src/build-system/handlers/file-manager/file-arch/index.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { BuildHandler, BuildResult } from 'src/build-system/types'; -import { BuilderContext } from 'src/build-system/context'; -import { generateFileArchPrompt } from './prompt'; -import { Logger } from '@nestjs/common'; -import { - extractJsonFromText, - formatResponse, - parseGenerateTag, -} from 'src/build-system/utils/strings'; -import { chatSyncWithClocker } from 'src/build-system/utils/handler-helper'; -import { - ResponseParsingError, - InvalidParameterError, - ModelUnavailableError, -} from 'src/build-system/errors'; -import { VirtualDirectory } from 'src/build-system/virtual-dir'; - -import { FileStructureHandler } from '../file-structure'; -import { UXDMDHandler } from '../../ux/datamap'; -import { BuildNode, BuildNodeRequire } from 'src/build-system/hanlder-manager'; -import { - buildDependencyGraph, - validateAgainstVirtualDirectory, -} from 'src/build-system/utils/file_generator_util'; - -@BuildNode() -@BuildNodeRequire([FileStructureHandler, UXDMDHandler]) -export class FileFAHandler implements BuildHandler { - private readonly logger: Logger = new Logger('FileArchGenerateHandler'); - private virtualDir: VirtualDirectory; - - async run(context: BuilderContext): Promise> { - this.logger.log('Generating File Architecture Document...'); - - this.virtualDir = context.virtualDirectory; - - const fileStructure = context.getNodeData(FileStructureHandler); - const datamapDoc = context.getNodeData(UXDMDHandler); - - this.logger.log('fileStructure:', fileStructure); - if (!fileStructure || !datamapDoc) { - throw new InvalidParameterError( - `Missing required parameters: fileStructure or datamapDoc, current fileStructure: ${!!fileStructure}, datamapDoc: ${!!datamapDoc}`, - ); - } - - const prompt = generateFileArchPrompt(); - - const messages = [ - { - role: 'system' as const, - content: prompt, - }, - { - role: 'user' as const, - content: ` - **Page-by-Page Analysis** - The following is a detailed analysis of each page. Use this information to understand specific roles, interactions, and dependencies. - - ${datamapDoc} - - Next, I will provide the **Directory Structure** to help you understand the full project architecture.`, - }, - { - role: 'user' as const, - content: ` - **Directory Structure**: - The following is the project's directory structure. Use this to identify files and folders. - - ${fileStructure} - - Based on this structure and the analysis provided earlier, please generate the File Architecture JSON object. Ensure the output adheres to all rules and guidelines specified in the system prompt.`, - }, - { - role: 'user' as const, - content: `**Final Check** - Before returning the output, ensure the following: - - The JSON structure is correctly formatted and wrapped in tags. - - File extensions and paths match those in the Directory Structure. - - All files and dependencies are included.`, - }, - ]; - - let fileArchContent: string; - try { - fileArchContent = await chatSyncWithClocker( - context, - { - model: 'gpt-4o-mini', - messages, - }, - 'generateFileArch', - FileFAHandler.name, - ); - } catch (error) { - this.logger.error('Model is unavailable:' + error); - throw new ModelUnavailableError('Model is unavailable:' + error); - } - - // Validate the generated file architecture document - try { - const tagContent = parseGenerateTag(fileArchContent); - const jsonData = extractJsonFromText(tagContent); - - if (!jsonData) { - this.logger.error('Failed to extract JSON from text'); - throw new ResponseParsingError('Failed to extract JSON from text.'); - } - - if (!this.validateJsonData(jsonData)) { - this.logger.error('File architecture JSON validation failed.'); - throw new ResponseParsingError( - 'File architecture JSON validation failed.', - ); - } - - // validate with virutual dir - const { graph, nodes, fileInfos } = buildDependencyGraph(jsonData); - - const invalidFiles = validateAgainstVirtualDirectory( - nodes, - this.virtualDir, - ); - - if (invalidFiles) { - this.logger.error('Validate Against Virtual Directory Fail !!!'); - this.logger.error(`Invalid files detected:\n${invalidFiles}`); - this.logger.error(`${fileArchContent}`); - this.logger.error(`${fileStructure}`); - throw new ResponseParsingError( - 'Failed to validate against virtualDirectory.', - ); - } - } catch (error) { - this.logger.error('File architecture validation failed.'); - throw new ResponseParsingError( - `File architecture JSON validation failed. ${error.message}`, - ); - } - - this.logger.log('File architecture document generated successfully.'); - return { - success: true, - data: formatResponse(fileArchContent), - }; - } - - /** - * Validates the structure and content of the JSON data. - * @param jsonData The JSON data to validate. - * @returns A boolean indicating whether the JSON data is valid. - */ - private validateJsonData(jsonData: { - files: Record; - }): boolean { - const validPathRegex = /^[a-zA-Z0-9_\-/.]+$/; - - for (const [file, details] of Object.entries(jsonData.files)) { - if (!validPathRegex.test(file)) { - this.logger.error(`Invalid file path: ${file}`); - return false; - } - - for (const dependency of details.dependsOn) { - if (!validPathRegex.test(dependency)) { - this.logger.error( - `Invalid dependency path "${dependency}" in file "${file}".`, - ); - return false; - } - - if (dependency.includes('//') || dependency.endsWith('/')) { - this.logger.error( - `Malformed dependency path "${dependency}" in file "${file}".`, - ); - return false; - } - } - } - return true; - } -} diff --git a/backend/src/build-system/handlers/file-manager/file-arch/prompt.ts b/backend/src/build-system/handlers/file-manager/file-arch/prompt.ts deleted file mode 100644 index c19f0c02..00000000 --- a/backend/src/build-system/handlers/file-manager/file-arch/prompt.ts +++ /dev/null @@ -1,55 +0,0 @@ -export const generateFileArchPrompt = (): string => { - return `Your task is to analyze the given project directory structure and create a detailed JSON object mapping file dependencies. The output JSON must be precisely formatted and wrapped in tags. - -### Instructions -1. **Analyze the Inputs**: - - Use the directory structure to identify all files and folders. - - Do not assume any additional files or paths. The structure must be based exclusively on the given list. - - Leverage the page-by-page analysis to understand the roles and interactions of different components and pages. - - Determine the role of each file based on its path and the provided analysis (e.g., page, component, context, hook, styles). - - Identify direct dependencies for each file by considering typical imports based on roles, naming conventions, and the provided analysis. - -2. **Generate File Dependency JSON**: - - Each file must be represented using its full path starting from src/. - - Ensure dependencies are strictly limited to files in the "Paths" array. - - Use absolute file paths from "Paths" for all "dependsOn" values. - Do not use relative paths (./, ../). - Every dependency must match exactly one of the files in "Paths". - - Any file without dependencies should have "dependsOn": []. - - For each file, list its direct dependencies as an array of relative paths in the \`dependsOn\` field. - - Organize the output in a \`files\` object where keys are file paths, and values are their dependency objects. - - For the router, remember to include all the page components as dependencies, as the router imports them to define the application routes. - - For the src/index.tsx, remember to include router.ts. - -3. **Output Requirements**: - - The JSON object must strictly follow this structure: - \`\`\`json - - { - "files": { - "src/path/to/file1": { - "dependsOn": ["path/to/dependency1", "path/to/dependency2"] - }, - "src/path/to/file2": { - "dependsOn": [] - } - } - } - - \`\`\` - - Keys: Every file must be represented with its full path, starting from src/. - - Dependency Rules: - All dependencies must exist in the "Paths" array. - No inferred or assumed files should be added. - - Wrap the JSON output with \`\` tags. - -### Notes -- The \`dependsOn\` field should reflect logical dependencies inferred from both the directory structure and the page-by-page analysis. -- Use common project patterns to deduce dependencies (e.g., pages depend on components, contexts, hooks, and styles). -- Include all files in the output, even if they have no dependencies. - -### Output -Return only the JSON object wrapped in \`\` tags. -Do not forget tags. -`; -}; diff --git a/backend/src/build-system/handlers/file-manager/file-struct/index.ts b/backend/src/build-system/handlers/file-manager/file-struct/index.ts new file mode 100644 index 00000000..7e8345ec --- /dev/null +++ b/backend/src/build-system/handlers/file-manager/file-struct/index.ts @@ -0,0 +1,630 @@ +import { BuildHandler, BuildOpts, BuildResult } from 'src/build-system/types'; +import { BuilderContext } from 'src/build-system/context'; +import { Logger } from '@nestjs/common'; +import { + parseGenerateTag, + removeCodeBlockFences, + extractJsonFromText, + formatResponse, +} from 'src/build-system/utils/strings'; +import { chatSyncWithClocker } from 'src/build-system/utils/handler-helper'; +import { + ResponseParsingError, + MissingConfigurationError, + InvalidParameterError, + ModelUnavailableError, +} from 'src/build-system/errors'; +import { UXSMDHandler } from '../../ux/sitemap-document'; +import { UXDMDHandler } from '../../ux/datamap'; +import { BuildNode, BuildNodeRequire } from 'src/build-system/hanlder-manager'; +import { VirtualDirectory } from 'src/build-system/virtual-dir'; +import { + buildDependencyGraph, + validateAgainstVirtualDirectory, +} from 'src/build-system/utils/file_generator_util'; + +export const prompts = { + convertTreeToJsonPrompt: (): string => { + return `You are a highly skilled developer. Your task is to convert the previous file and folder structure, currently represented in an ASCII tree format, into a JSON structure. The JSON structure must: + + - Represent all file paths in a flat list under the "Paths" array. + - Each file path must be a relative path that begins exactly with "src/" (do not include any leading "/" or absolute paths). + - Directories should not be included—only file paths. + + Output Format: + Return a JSON object in the following format: + Surround the JSON object with tags. + + + { + "Paths": [ + "src/full/path/to/file1.ext", + "src/full/path/to/file2.ext", + "src/another/path/to/file3.ext" + ] + } + + + Additional Rules: + + - Maintain the original directory structure but only return files in the JSON output. + - Keep file names and paths exactly as they appear in the ASCII tree. + - **Important**: Ensure that all file paths are relative and begin exactly with "src/". Do not output any paths that start with a leading "/". + - Do not include comments or extra fields besides "Paths". + - Return only the JSON structure (no explanations, no additional comments). This JSON will be used directly in the application. + `; + }, + + generateCommonFileStructurePrompt: ( + projectName: string, + sitemapDoc: string, + dataAnalysisDoc: string, + framework: string, + projectPart: string, + projectSize: string, + ): string => { + let roleDescription = ''; + let includeSections = ''; + let excludeSections = ''; + let fileNamingGuidelines = ''; + let projectSizeNote = ''; + let spaDetectionRules = ''; + + switch (projectSize.toLowerCase()) { + case 'small': + projectSizeNote = `* Note: For a small project (1-3 unique UI pages), generate a minimal file structure that only includes the essential files and folders.`; + break; + case 'medium': + projectSizeNote = `* Note: For a medium project (1-6 unique UI pages), generate a file structure that covers all necessary pages and components with moderate detail.`; + break; + case 'large': + projectSizeNote = `* Note: For a large project (more than 6 unique UI pages), generate a comprehensive file structure including all pages, components, contexts, and utilities.`; + break; + default: + projectSizeNote = `* Note: The project size is unspecified. Please use a balanced approach.`; + } + + const pageViewCount = (sitemapDoc.match(/page_view_/g) || []).length; + const isSPAFlag = pageViewCount === 1; + + if (isSPAFlag) { + spaDetectionRules = ` + SPA Detected: Exactly one "page_view_" entry found in the Sitemap Document. + Enforce the SPA folder structure: + src/ + index.tsx // Main entry point + pages/ + / + index.tsx // Contains all application logic + + For SPAs, do NOT create any additional files or folders. + `; + } else { + spaDetectionRules = ` + Multi-Page Application Detected: ${pageViewCount} "page_view_" entries found in the Sitemap Document. + Generate a comprehensive file structure that includes multiple pages, components, contexts, and any other necessary directories. + `; + } + + switch (projectPart.toLowerCase()) { + case 'frontend': + roleDescription = 'an expert frontend developer'; + includeSections = ` + Non-SPA Folder Structure example: + src/ + contexts/ - Global state management + pages/ - Route-specific views (e.g., Home, Search, etc.) + index.tsx - Application entry point with routing configuration IMPORTANT!: it's mandatory to have this file + + ${ + isSPAFlag + ? `SPA Folder Structure (MANDATORY for all SPAs): + src/ + index.tsx - Main entry point that imports Home page + pages/ + Home/ + index.tsx - Contains ALL component code and application logic + + For SPAs, you MUST use exactly this structure - no variations allowed.` + : '' + } + `; + excludeSections = ` + Do Not Include: + - Asset folders (e.g., images, icons, fonts) + - Test folders or files + - Service folders unrelated to API logic + - .css files + ${isSPAFlag ? '- For SPAs: DO NOT include components/, contexts/, or any other folders beside pages/Home/\n' : ''} + `; + fileNamingGuidelines = ` + File and Folder Naming Guidelines: + - Must use .tsx extension for all files + - Use meaningful and descriptive file names + - Do NOT use page_view_* and global_view_* prefixes for folder or file names + + ${ + isSPAFlag + ? `For SPAs: + - Only create the exact files specified: src/index.tsx and src/pages/Home/index.tsx + - Do not create any additional files or folders` + : '' + } + `; + break; + + case 'backend': + roleDescription = 'an expert backend developer'; + includeSections = ` + Folder Structure: + controllers/ - Handle incoming requests and return responses + models/ - Define data schemas and interact with the database + routes/ - Define API endpoints and route requests to controllers + services/ - Business logic and interaction with external services + middleware/ - Custom middleware for request processing + utils/ - Utility functions and helpers + config/ - Configuration files + app.js/server.js - Application entry point + `; + excludeSections = ` + Do Not Include: + Frontend-specific folders (e.g., components, contexts) + Asset folders (e.g., images, icons, fonts) + `; + fileNamingGuidelines = ` + File Naming Guidelines: + Use meaningful and descriptive file names + Controllers should be named after their resource (e.g., userController.js) + Models should represent data entities (e.g., User.js) + Routes should be grouped by resource (e.g., userRoutes.js) + Use consistent naming conventions (e.g., camelCase or snake_case) throughout the project. + `; + break; + + default: + throw new Error( + 'Invalid project part specified. Must be "frontend" or "backend".', + ); + } + + return `You are ${roleDescription}. Your task is to generate a complete folder and file structure for the ${projectPart} of a project named "${projectName}". Include all necessary files and folders to cover the essential aspects while ensuring scalability and maintainability. + +Based on the following input: + + - Project name: ${projectName} + - Sitemap Documentation (provided below) + - Data Analysis Documentation (provided below) + - isSPA: ${isSPAFlag ? 'Yes' : 'No'} + +${projectSizeNote} + +${spaDetectionRules} + +### Instructions and Rules: + +Include: +${includeSections} + +${fileNamingGuidelines} + +${excludeSections} + +File Comments: + Include comments describing the purpose of each file or folder to improve readability. + +Ask yourself: + 1. Have I properly analyzed the Sitemap Document to determine if this is an SPA? + 2. For non-SPAs: Are you considering all the pages based on the sitemap doc? If not, add new folder or file. + +### Sitemap Document Analysis +First, carefully read through and analyze the Sitemap Document below: + +This final result must be 100% complete and ready for direct use in production. + +Output Format: + + Start with: "\`\`\`FolderStructure" + Tree format: + Include folder names with placeholder files inside. + Add comments to describe the purpose of each file/folder. + End with: "\`\`\`" +`; + }, + + generateFileArchPrompt: (sitemapDoc: string): string => { + const isSPAFlag = (sitemapDoc.match(/page_view_/g) || []).length === 1; + + return `Your task is to analyze the given project directory structure and create a detailed JSON object mapping file dependencies. The output JSON must be precisely formatted and wrapped in tags. + +### Instructions + + ${ + isSPAFlag + ? `**SPA Special Case**: + - If the structure only contains src/index.tsx and src/pages/Home/index.tsx, this is a Single Page Application (SPA) with the mandatory minimal structure. + - For SPAs with this exact structure, the JSON must look like this: + \`\`\`json + + { + "files": { + "src/index.tsx": { + "dependsOn": ["src/pages/Home/index.tsx"] + }, + "src/pages/Home/index.tsx": { + "dependsOn": [] + } + } + } + + \`\`\` + - This is MANDATORY: for SPAs, create exactly these two files with exactly these dependencies - no more, no less. + +**For non-SPA projects**: ` + : 'For projects' + } + - Analyze the directory structure to identify all files and folders. + - Do not assume any additional files or paths. The structure must be based exclusively on the given list. + - Leverage the page-by-page analysis to understand the roles and interactions of different components and pages. + - Determine the role of each file based on its path and the provided analysis (e.g., page, component, context, hook, styles). + - Identify direct dependencies for each file by considering typical imports based on roles, naming conventions, and the provided analysis. + - For context files, ensure they are properly referenced in index.tsx or router.tsx, as contexts typically need to be provided at a high level in the application. + +3. **Generate File Dependency JSON**: + - Each file must be represented using its full path starting from src/. + - Ensure dependencies are strictly limited to files in the "Paths" array. + - Use absolute file paths from "Paths" for all "dependsOn" values. + Do not use relative paths (./, ../). + Every dependency must match exactly one of the files in "Paths". + - Any file without dependencies should have "dependsOn": []. + - For each file, list its direct dependencies as an array of relative paths in the \`dependsOn\` field. + - Organize the output in a \`files\` object where keys are file paths, and values are their dependency objects. + - For the router, remember to include all the page components as dependencies, as the router imports them to define the application routes. + +4. **Output Requirements**: + - The JSON object must strictly follow this structure: + \`\`\`json + + { + "files": { + "src/path/to/file1": { + "dependsOn": ["src/path/to/dependency1", "src/path/to/dependency2"] + }, + "src/path/to/file2": { + "dependsOn": [] + } + } + } + + \`\`\` + - Keys: Every file must be represented with its full path, starting from src/. + - Dependency Rules: + All dependencies must exist in the "Paths" array. + No inferred or assumed files should be added. + - Wrap the JSON output with \`\` tags. +### Notes +- The \`dependsOn\` field should reflect logical dependencies inferred from both the directory structure and the page-by-page analysis. +- Use common project patterns to deduce dependencies (e.g., pages depend on components, contexts, hooks, and styles). +- Include all files in the output, even if they have no dependencies. +- For context providers, ensure they are included as dependencies in either index.tsx or router.tsx to maintain proper context hierarchy in the React application. +- Include all files in the output, even if they have no dependencies. + +### Output +Return only the JSON object wrapped in \`\` tags. +Do not forget tags. +`; + }, +}; + +@BuildNode() +@BuildNodeRequire([UXSMDHandler, UXDMDHandler]) +export class FileStructureAndArchitectureHandler + implements BuildHandler +{ + readonly id = 'op:FILE:STRUCT_AND_ARCH'; + private readonly logger: Logger = new Logger( + 'FileStructureAndArchitectureHandler', + ); + private virtualDir: VirtualDirectory; + + async run( + context: BuilderContext, + opts?: BuildOpts, + ): Promise> { + this.logger.log('Generating File Structure Document...'); + + const projectName = + context.getGlobalContext('projectName') || 'Default Project Name'; + const sitemapDoc = context.getNodeData(UXSMDHandler); + const datamapDoc = context.getNodeData(UXDMDHandler); + const projectPart = opts?.projectPart ?? 'frontend'; + const framework = context.getGlobalContext('framework') ?? 'react'; + + const projectSize = context.getGlobalContext('projectSize') || 'small'; + try { + this.validateInputs(sitemapDoc, datamapDoc, framework, projectPart); + } catch (error) { + return { + success: false, + error, + }; + } + + const fileStructPrompt = prompts.generateCommonFileStructurePrompt( + projectName, + sitemapDoc, + datamapDoc, + framework, + projectPart, + projectSize, + ); + const convertToJsonPrompt = prompts.convertTreeToJsonPrompt(); + + const fileStructMessages = [ + { + role: 'system' as const, + content: fileStructPrompt, + }, + { + role: 'user' as const, + content: ` + **Sitemap Documentation** + ${sitemapDoc} + `, + }, + { + role: 'user' as const, + content: ` + **Data map Analysis Documentation:** + ${datamapDoc} + + Now please generate tree folder structure. + `, + }, + { + role: 'system' as const, + content: convertToJsonPrompt, + }, + { + role: 'user' as const, + content: `**Final Check:** + Before returning the output, ensure the following: + - The JSON structure is correctly formatted and wrapped in tags. + - File extensions and paths match those in the Directory Structure. + - All files and dependencies are included, with relative paths used wherever possible.`, + }, + ]; + + let fileStructureContent: string; + try { + fileStructureContent = await chatSyncWithClocker( + context, + { + model: context.defaultModel || 'gpt-4o-mini', + messages: fileStructMessages, + }, + 'generateCommonFileStructure', + this.id, + ); + this.logger.debug('fileStructureContent', fileStructureContent); + + if (!fileStructureContent || fileStructureContent.trim() === '') { + throw new ResponseParsingError( + `Generated content is empty during op:FILE:STRUCT_AND_ARCH.`, + ); + } + } catch (error) { + this.logger.error( + `Failed to generate file structure: ${error.message}`, + error.stack, + ); + return { + success: false, + error: new ResponseParsingError( + `File structure generation failed. ${error.message}`, + ), + }; + } + + let fileStructureJsonContent = ''; + try { + fileStructureJsonContent = parseGenerateTag(fileStructureContent); + } catch (error) { + return { + success: false, + error: new ResponseParsingError( + `Failed to parse file Structure Json Content. ${error.message}`, + ), + }; + } + + this.logger.log('Building virtual directory from file structure...'); + try { + const successBuild = context.buildVirtualDirectory( + fileStructureJsonContent, + ); + if (!successBuild) { + this.logger.error( + 'Failed to build virtual directory.' + fileStructureJsonContent, + ); + throw new ResponseParsingError('Failed to build virtual directory.'); + } + } catch (error) { + return { + success: false, + error: new ResponseParsingError( + `Failed to build virtual directory. ${error.message}`, + ), + }; + } + + context.virtualDirectory.getAllFiles().forEach((file) => { + this.logger.log(file); + }); + + this.logger.log('File Structure Document generated successfully.'); + + this.logger.log('Generating File Architecture Document...'); + + this.virtualDir = context.virtualDirectory; + const fileStructure = removeCodeBlockFences(fileStructureContent); + if (!fileStructure || !datamapDoc) { + return { + success: false, + error: new InvalidParameterError( + `Missing required parameters: fileStructure or datamapDoc, current fileStructure: ${!!fileStructure}, datamapDoc: ${!!datamapDoc}`, + ), + }; + } + + const fileArchPrompt = prompts.generateFileArchPrompt(sitemapDoc); + + let invalidFiles = 'none'; + let fileArchContent: string; + + while (invalidFiles) { + const fileArchMessages = [ + { + role: 'system' as const, + content: fileArchPrompt, + }, + { + role: 'user' as const, + content: ` + **Page-by-Page Analysis** + The following is a detailed analysis of each page. Use this information to understand specific roles, interactions, and dependencies. + + ${datamapDoc} + + Next, I will provide the **Directory Structure** to help you understand the full project architecture.`, + }, + { + role: 'user' as const, + content: ` + **Directory Structure**: + The following is the project's directory structure. Use this to identify files and folders. + + ${fileStructure} + + Based on this structure and the analysis provided earlier, please generate the File Architecture JSON object. Ensure the output adheres to all rules and guidelines specified in the system prompt. + `, + }, + { + role: 'user' as const, + content: `**Final Check** + Before returning the output, ensure the following: + - The JSON structure is correctly formatted and wrapped in tags. + - File extensions and paths match those in the Directory Structure. + - All files and dependencies are included. + `, + }, + { + role: 'user' as const, + content: + "here is the invalid file, trying to fix it, if it's none, then you can ignore it: " + + invalidFiles, + }, + ]; + + try { + fileArchContent = await chatSyncWithClocker( + context, + { + model: context.defaultModel || 'gpt-4o-mini', + messages: fileArchMessages, + }, + 'generateFileArch', + this.id, + ); + } catch (error) { + this.logger.error('Model is unavailable:' + error); + return { + success: false, + error: new ModelUnavailableError('Model is unavailable:' + error), + }; + } + + const tagContent = parseGenerateTag(fileArchContent); + const jsonData = extractJsonFromText(tagContent); + + if (!jsonData) { + this.logger.error('Failed to extract JSON from text'); + throw new ResponseParsingError('Failed to extract JSON from text.'); + } + + if (!this.validateJsonData(jsonData)) { + this.logger.error('File architecture JSON validation failed.'); + throw new ResponseParsingError( + 'File architecture JSON validation failed.', + ); + } + + const { nodes } = buildDependencyGraph(jsonData); + invalidFiles = validateAgainstVirtualDirectory(nodes, this.virtualDir); + if (invalidFiles) { + this.logger.warn('arch json content', fileArchContent); + this.logger.warn( + 'Validation against virtual directory failed. here is the invalid file, trying to fix it', + invalidFiles, + ); + } + } + + this.logger.log('File architecture document generated successfully.'); + return { + success: true, + data: formatResponse(fileArchContent), + }; + } + + private validateInputs( + sitemapDoc: any, + datamapDoc: any, + framework: string, + projectPart: string, + ): void { + if (!sitemapDoc || typeof sitemapDoc !== 'string') { + throw new MissingConfigurationError('Missing or invalid sitemapDoc.'); + } + if (!datamapDoc || typeof datamapDoc !== 'string') { + throw new MissingConfigurationError('Missing or invalid datamapDoc.'); + } + if (!framework || typeof framework !== 'string') { + throw new MissingConfigurationError('Missing or invalid framework.'); + } + if (!['frontend', 'backend'].includes(projectPart)) { + throw new MissingConfigurationError( + 'Invalid projectPart. Must be either "frontend" or "backend".', + ); + } + } + + private validateJsonData(jsonData: { + files: Record; + }): boolean { + const validPathRegex = /^[a-zA-Z0-9_\-/.]+$/; + + for (const [file, details] of Object.entries(jsonData.files)) { + if (!validPathRegex.test(file)) { + this.logger.error(`Invalid file path: ${file}`); + return false; + } + + for (const dependency of details.dependsOn) { + if (!validPathRegex.test(dependency)) { + this.logger.error( + `Invalid dependency path "${dependency}" in file "${file}".`, + ); + return false; + } + + if (dependency.includes('//') || dependency.endsWith('/')) { + this.logger.error( + `Malformed dependency path "${dependency}" in file "${file}".`, + ); + return false; + } + } + } + return true; + } +} diff --git a/backend/src/build-system/handlers/file-manager/file-structure/index.ts b/backend/src/build-system/handlers/file-manager/file-structure/index.ts deleted file mode 100644 index 18721fe3..00000000 --- a/backend/src/build-system/handlers/file-manager/file-structure/index.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { BuildHandler, BuildOpts, BuildResult } from 'src/build-system/types'; -import { BuilderContext } from 'src/build-system/context'; -import { prompts } from './prompt'; -import { Logger } from '@nestjs/common'; -import { - parseGenerateTag, - removeCodeBlockFences, -} from 'src/build-system/utils/strings'; -import { chatSyncWithClocker } from 'src/build-system/utils/handler-helper'; -import { - ResponseParsingError, - MissingConfigurationError, -} from 'src/build-system/errors'; -import { UXSMDHandler } from '../../ux/sitemap-document'; -import { UXDMDHandler } from '../../ux/datamap'; -import { BuildNode, BuildNodeRequire } from 'src/build-system/hanlder-manager'; - -/** - * FileStructureHandler is responsible for generating the project's file and folder structure - * based on the provided documentation. - */ -@BuildNode() -@BuildNodeRequire([UXSMDHandler, UXDMDHandler]) -export class FileStructureHandler implements BuildHandler { - readonly id = 'op:FILE:STRUCT'; - private readonly logger: Logger = new Logger('FileStructureHandler'); - - async run( - context: BuilderContext, - opts?: BuildOpts, - ): Promise> { - this.logger.log('Generating File Structure Document...'); - - // Retrieve projectName from context - const projectName = - context.getGlobalContext('projectName') || 'Default Project Name'; - const sitemapDoc = context.getNodeData(UXSMDHandler); - const datamapDoc = context.getNodeData(UXDMDHandler); - // const projectPart = opts?.projectPart ?? 'frontend'; - const projectPart = opts?.projectPart ?? 'frontend'; - const framework = context.getGlobalContext('framework') ?? 'react'; - - // Validate required arguments - this.validateInputs(sitemapDoc, datamapDoc, framework, projectPart); - - // Generate the common file structure prompt - const prompt = prompts.generateCommonFileStructurePrompt( - projectName, - sitemapDoc, - datamapDoc, - framework, - projectPart, - ); - - const convertToJsonPrompt = prompts.convertTreeToJsonPrompt(); - - const messages = [ - { - role: 'system' as const, - content: prompt, - }, - { - role: 'user' as const, - content: ` - **Sitemap Documentation** - ${sitemapDoc} - `, - }, - { - role: 'user' as const, - content: ` - **Data map Analysis Documentation:**: - ${datamapDoc} - - Now please generate tree folder structure. - `, - }, - { - role: 'system' as const, - content: convertToJsonPrompt, - }, - { - role: 'user' as const, - content: `**Final Check:** - **Final Check** - Before returning the output, ensure the following: - - The JSON structure is correctly formatted and wrapped in tags. - - File extensions and paths match those in the Directory Structure. - - All files and dependencies are included, with relative paths used wherever possible.`, - }, - ]; - - // Get the generated file structure content - let fileStructureContent: string; - try { - fileStructureContent = await chatSyncWithClocker( - context, - { - model: 'gpt-4o-mini', - messages, - }, - 'generateCommonFileStructure', - this.id, - ); - - if (!fileStructureContent || fileStructureContent.trim() === '') { - throw new ResponseParsingError( - `Generated content is empty during op:FILE:STRUCT.`, - ); - } - } catch (error) { - this.logger.error( - `Failed to generate file structure: ${error.message}`, - error.stack, - ); - return { - success: false, - error: new ResponseParsingError( - `File structure generation failed. ${error.message}`, - ), - }; - } - - // Parse the file structure content - let fileStructureJsonContent = ''; - try { - fileStructureJsonContent = parseGenerateTag(fileStructureContent); - } catch (error) { - return { - success: false, - error: new ResponseParsingError( - `Failed to parse file Structure Json Content. ${error.message}`, - ), - }; - } - - // Build the virtual directory - this.logger.log('start building'); - try { - const successBuild = context.buildVirtualDirectory( - fileStructureJsonContent, - ); - if (!successBuild) { - this.logger.error( - 'Failed to build virtual directory.' + fileStructureJsonContent, - ); - throw new ResponseParsingError('Failed to build virtual directory.'); - } - } catch (error) { - return { - success: false, - error: new ResponseParsingError( - `Failed to build virtual directory. ${error.message}`, - ), - }; - } - - //debug script print all files - context.virtualDirectory.getAllFiles().forEach((file) => { - this.logger.log(file); - }); - - return { - success: true, - data: removeCodeBlockFences(fileStructureContent), - }; - } - - private validateInputs( - sitemapDoc: any, - datamapDoc: any, - framework: string, - projectPart: string, - ): void { - if (!sitemapDoc || typeof sitemapDoc !== 'string') { - throw new MissingConfigurationError('Missing or invalid sitemapDoc.'); - } - if (!datamapDoc || typeof datamapDoc !== 'string') { - throw new MissingConfigurationError('Missing or invalid datamapDoc.'); - } - if (!framework || typeof framework !== 'string') { - throw new MissingConfigurationError('Missing or invalid framework.'); - } - if (!['frontend', 'backend'].includes(projectPart)) { - throw new MissingConfigurationError( - 'Invalid projectPart. Must be either "frontend" or "backend".', - ); - } - } -} diff --git a/backend/src/build-system/handlers/file-manager/file-structure/prompt.ts b/backend/src/build-system/handlers/file-manager/file-structure/prompt.ts deleted file mode 100644 index 10c75286..00000000 --- a/backend/src/build-system/handlers/file-manager/file-structure/prompt.ts +++ /dev/null @@ -1,159 +0,0 @@ -// src/build-system/prompts.ts - -export const prompts = { - // 已被通用的 generateCommonFileStructurePrompt 取代 - /* - generateFileStructurePrompt: ( - projectName: string, - sitemapDoc: string, - DataAnalysisDoc: string, - framework: string, - ): string => { - // 原有的 generateFileStructurePrompt 内容 - }, - */ - - convertTreeToJsonPrompt: (): string => { - return `You are a highly skilled developer. Your task is to convert the previous file and folder structure, currently represented in an ASCII tree format, into a JSON structure. The JSON structure must: - - Represent all file paths in a flat list under the "Paths" array. - Maintain the full paths for each file exactly as they appear in the ASCII tree. - Directories should not be included—only file paths. - -Output Format: -Return a JSON object in the following format: -Surround the JSON object with tags. - - -{ - "Paths": [ - "/full/path/to/file1.ext", - "/full/path/to/file2.ext", - "/another/path/to/file3.ext" - ] -} - - -Additional Rules: - - Maintain the original directory structure but only return files in the JSON output. - Keep file names and paths exactly as they appear in the ASCII tree. - Remeber to start with src/ as the root directory (src/...). - The root node should correspond to the top-level directory in the tree. - Do not include comments or extra fields besides "Paths". - Return only the JSON structure (no explanations, no additional comments). This JSON will be used directly in the application. - `; - }, - - generateCommonFileStructurePrompt: ( - projectName: string, - sitemapDoc: string, - dataAnalysisDoc: string, - framework: string, - projectPart: string, - ): string => { - let roleDescription = ''; - let includeSections = ''; - let excludeSections = ''; - let fileNamingGuidelines = ''; - - switch (projectPart.toLowerCase()) { - case 'frontend': - roleDescription = 'an expert frontend developer'; - includeSections = ` - Folder Structure example: - src: Main source code folder. - contexts: Global state management. - pages: Route-specific views. For Example: Home, Search, Playlist. - router.tsx: Central routing configuration. - index.tsx: Application entry point. - `; - excludeSections = ` - Do Not Include: - Asset folders (e.g., images, icons, fonts). - Test folders or files. - Service folders unrelated to API logic. - .css files. - `; - fileNamingGuidelines = ` - File and Folder Naming Guidelines: - Must use .tsx extension for all files. - Use meaningful and descriptive file names. - Do NOT use page_view_* and global_view_* prefixes for folder or file names. - For components, include an index.tsx file in each folder to simplify imports. - Each component should have its own folder named after the component (e.g., Button/). - Use index.tsx as the main file inside the component folder. - `; - break; - - case 'backend': - roleDescription = 'an expert backend developer'; - includeSections = ` - Folder Structure: - controllers: Handle incoming requests and return responses. - models: Define data schemas and interact with the database. - routes: Define API endpoints and route requests to controllers. - services: Business logic and interaction with external services. - middleware: Custom middleware for request processing (e.g., authentication, logging). - utils: Utility functions and helpers. - config: Configuration files (e.g., database connection, environment variables). - tests: Unit and integration tests. - app.js/server.js: Application entry point. - `; - excludeSections = ` - Do Not Include: - Frontend-specific folders (e.g., components, contexts). - Asset folders (e.g., images, icons, fonts). - `; - fileNamingGuidelines = ` - File Naming Guidelines: - Use meaningful and descriptive file names. - Controllers should be named after their resource (e.g., userController.js). - Models should represent data entities (e.g., User.js). - Routes should be grouped by resource (e.g., userRoutes.js). - Use consistent naming conventions (e.g., camelCase or snake_case) throughout the project. - `; - break; - - default: - throw new Error( - 'Invalid project part specified. Must be "frontend" or "backend".', - ); - } - - return `You are ${roleDescription}. Your task is to generate a complete folder and file structure for the ${projectPart} of a project named "${projectName}". Include all necessary files and folders to cover the essential aspects while ensuring scalability and maintainability. - - Based on the following input: - - - Project name: ${projectName} - - Sitemap Documentation (provide by user) - - Data Analysis Doc: (provide by user) - - ### Instructions and Rules: - - Include: - ${includeSections} - - ${fileNamingGuidelines} - - ${excludeSections} - - File Comments: - Include comments describing the purpose of each file or folder to improve readability. - - Ask yourself: - 1. Are you considering all the cases based on the sitemap doc? If not, add new folder or file. - 2. Are you considering all the components/hooks/services/APIs/routes based on the sitemap doc? If not, add new folder or file. - - This final result must be 100% complete and ready for direct use in production. - - Output Format: - - Start with: "\`\`\`FolderStructure" - Tree format: - Include folder names with placeholder files inside. - Add comments to describe the purpose of each file/folder. - End with: "\`\`\`" - `; - }, -}; diff --git a/backend/src/build-system/handlers/frontend-code-generate/CodeReview.ts b/backend/src/build-system/handlers/frontend-code-generate/CodeReview.ts index d283be00..3b5035ec 100644 --- a/backend/src/build-system/handlers/frontend-code-generate/CodeReview.ts +++ b/backend/src/build-system/handlers/frontend-code-generate/CodeReview.ts @@ -109,7 +109,7 @@ export class FrontendQueueProcessor { ); // 1. Write the file to disk - createFileWithRetries(currentFullFilePath, task.fileContents); + await createFileWithRetries(currentFullFilePath, task.fileContents); const maxFixAttempts = 2; @@ -122,12 +122,6 @@ export class FrontendQueueProcessor { ); return; // done, move on } - - // Build failed. We'll feed the entire `validationResult.error` back to GPT - // this.logger.warn( - // `Build failed on attempt #${attempt} for file ${task.filePath}. Error:\n${validationResult.error}`, - // ); - this.logger.warn( `Build failed on attempt #${attempt} for file ${task.filePath}.`, ); @@ -141,16 +135,10 @@ export class FrontendQueueProcessor { ); if (newFilePath !== null) { - this.logger.log( - `File was renamed: ${task.filePath} → ${newFilePath}`, - ); task.filePath = newFilePath; currentFullFilePath = normalizePath( path.resolve(this.frontendPath, newFilePath), ); - this.logger.log( - `Updated currentFullFilePath: ${currentFullFilePath}`, - ); } } catch (error) { this.logger.error( @@ -207,7 +195,7 @@ export class FrontendQueueProcessor { let fixResponse = await chatSyncWithClocker( this.context, { - model: 'gpt-4o', + model: 'o3-mini-high', messages: [ { role: 'system', content: fixPrompt }, { @@ -282,7 +270,7 @@ export class FrontendQueueProcessor { fixResponse = await chatSyncWithClocker( this.context, { - model: 'gpt-4o', + model: 'o3-mini-high', messages: [ { role: 'system', content: fixPrompt }, { @@ -342,7 +330,6 @@ export class FrontendQueueProcessor { this.logger.log(`Generic fix applied to file: ${task.filePath}`); if (newFilePath) { - this.logger.log(`File was renamed: ${task.filePath} → ${newFilePath}`); return newFilePath; } diff --git a/backend/src/build-system/handlers/frontend-code-generate/CodeValidator.ts b/backend/src/build-system/handlers/frontend-code-generate/CodeValidator.ts index bdea580b..a15f7e6f 100644 --- a/backend/src/build-system/handlers/frontend-code-generate/CodeValidator.ts +++ b/backend/src/build-system/handlers/frontend-code-generate/CodeValidator.ts @@ -27,6 +27,7 @@ export class FrontendCodeValidator { * @returns A promise that resolves with the ValidationResult. */ public async validate(): Promise { + await this.installDependencies(); return new Promise((resolve, reject) => { this.logger.log('Starting frontend code validation...'); // Spawn the npm build process in the provided frontend project path. @@ -83,6 +84,7 @@ export class FrontendCodeValidator { const npmInstall = spawn('npm', ['install'], { cwd: this.frontendPath, + shell: true, }); let stdoutBuffer = ''; diff --git a/backend/src/build-system/handlers/frontend-code-generate/index.ts b/backend/src/build-system/handlers/frontend-code-generate/index.ts index 3a6d46a3..8a13f3ee 100644 --- a/backend/src/build-system/handlers/frontend-code-generate/index.ts +++ b/backend/src/build-system/handlers/frontend-code-generate/index.ts @@ -9,7 +9,6 @@ import { VirtualDirectory } from '../../virtual-dir'; import { UXSMSHandler } from '../ux/sitemap-structure'; import { UXDMDHandler } from '../ux/datamap'; import { BackendRequirementHandler } from '../backend/requirements-document'; -import { FileFAHandler } from '../file-manager/file-arch'; import { BuildNode, BuildNodeRequire } from 'src/build-system/hanlder-manager'; import normalizePath from 'normalize-path'; import path from 'path'; @@ -20,6 +19,8 @@ import { MessageInterface } from 'src/common/model-provider/types'; import { FrontendCodeValidator } from './CodeValidator'; import { FrontendQueueProcessor, CodeTaskQueue } from './CodeReview'; +// import { FileFAHandler } from '../file-manager/file-arch'; +import { FileStructureAndArchitectureHandler } from '../file-manager/file-struct'; interface FileInfos { [fileName: string]: { @@ -36,8 +37,9 @@ interface FileInfos { UXSMSHandler, UXDMDHandler, BackendRequirementHandler, - FileFAHandler, + FileStructureAndArchitectureHandler, ]) +// FileFAHandler, export class FrontendCodeHandler implements BuildHandler { readonly logger: Logger = new Logger('FrontendCodeHandler'); private virtualDir: VirtualDirectory; @@ -57,7 +59,9 @@ export class FrontendCodeHandler implements BuildHandler { const backendRequirementDoc = context.getNodeData( BackendRequirementHandler, ); - const fileArchDoc = context.getNodeData(FileFAHandler); + const fileArchDoc = context.getNodeData( + FileStructureAndArchitectureHandler, + ); // 2. Grab any globally stored context as needed this.virtualDir = context.virtualDirectory; @@ -147,29 +151,32 @@ export class FrontendCodeHandler implements BuildHandler { this.logger.debug('dependency: ' + directDepsPathString); // generate code - const generatedCode = await this.generateFileCode( - context, - file, - dependenciesText, - directDepsPathString, - sitemapStruct, - uxDataMapDoc, - failedFiles, - ); + let generatedCode = ''; + // Adding into retry part. + while (generatedCode === '') { + this.logger.log(`Attempt to generate code for file: ${file}`); + generatedCode = await this.generateFileCode( + context, + file, + dependenciesText, + directDepsPathString, + sitemapStruct, + uxDataMapDoc, + failedFiles, + ); + } // 7. Add the file to the queue for writing + // Ensure the file path is relative by removing any leading slash + this.logger.log('filepath: ' + file); + const relativePath = file.startsWith('/') + ? file.substring(1) + : file; queue.enqueue({ - filePath: file, // relative path + filePath: relativePath, fileContents: generatedCode, dependenciesPath: directDepsPathString, }); - - // await createFileWithRetries( - // currentFullFilePath, - // generatedCode, - // maxRetries, - // delayMs, - // ); }), ); @@ -189,7 +196,6 @@ export class FrontendCodeHandler implements BuildHandler { remainingFiles = []; // All files in this layer succeeded } } - // Now process the entire queue for this layer: // This writes each file, runs build, fixes if needed, etc. const queueProcessor = new FrontendQueueProcessor( @@ -200,7 +206,6 @@ export class FrontendCodeHandler implements BuildHandler { renameMap, ); await queueProcessor.processAllTasks(); - this.logger.log( `\n==== Finished concurrency layer #${layerIndex + 1} ====\n`, ); @@ -258,10 +263,12 @@ export class FrontendCodeHandler implements BuildHandler { if (fileExtension === '.css') { frontendCodePrompt = generateCSSPrompt(file, directDepsPathString); } else { + const theme = context.getGlobalContext('theme'); // default: treat as e.g. .ts, .js, .vue, .jsx, etc. frontendCodePrompt = generateFrontEndCodePrompt( file, directDepsPathString, + theme, ); } // this.logger.log( @@ -312,7 +319,7 @@ export class FrontendCodeHandler implements BuildHandler { }, { role: 'user', - content: `Now you can provide the code, don't forget the tags.`, + content: `Now you can provide the code, don't forget the tags. Do not be lazy.`, }, // { // role: 'assistant', @@ -324,13 +331,15 @@ export class FrontendCodeHandler implements BuildHandler { modelResponse = await chatSyncWithClocker( context, { - model: 'gpt-4o', + // model: context.defaultModel || 'gpt-4o', + model: 'o3-mini-high', messages, }, 'generate frontend code', FrontendCodeHandler.name, ); + this.logger.debug('generated code: ', modelResponse); generatedCode = formatResponse(modelResponse); return generatedCode; diff --git a/backend/src/build-system/handlers/frontend-code-generate/prompt.ts b/backend/src/build-system/handlers/frontend-code-generate/prompt.ts index 5508ac20..f023a926 100644 --- a/backend/src/build-system/handlers/frontend-code-generate/prompt.ts +++ b/backend/src/build-system/handlers/frontend-code-generate/prompt.ts @@ -1,40 +1,45 @@ -export const generateFrontEndCodePrompt = ( +export function generateFrontEndCodePrompt( currentFile: string, dependencyFilePath: string, -): string => { + theme: string, +): string { return `Role: You are an expert frontend developer specializing in building scalable, maintainable, and production-ready React applications using TypeScript. - Task: Generate complete, type-safe, and maintainable React code. - Current File: ${currentFile}. +Task: Generate complete, type-safe, and maintainable React code. +Current File: ${currentFile}. + +## Theme Information: +${theme} + +# Instructions and Rules: + 1. Implement Only One file: Implement only the given file. + 2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. + 3. Type Safe: Follow TypeScript standards. DO NOT create custom type definitions for React, React.FC, or any other React-related types - these are already provided by @types/react. + 4. Follow design: DON'T CHANGE ANY DESIGN in the Document. + 5. Import Types: Always import React types from the 'react' package, never create your own React type definitions. + 6. CAREFULLY CHECK: + - Before importing a file, verify its existence. + - Ensure that you haven't missed any internal dependencies import. + - If missing, suggest an alternative or define mock data. + 7. Before using an external variable/module, make sure you import it first. + 8. Error Handling: Implement proper error handling in API calls and interactions with external modules. + 9. Code Standards: Adhere to styling guidelines (e.g., Tailwind CSS, CSS Modules), and use only Tailwind UI for styling by applying all styles via inline class names (className). + 10. Mock the response if the API returns an empty or undefined value, and you don't need to explicitly show that it is mock data. + 11. Write EVERY CODE DETAIL, DON'T LEAVE TODO. + 12. Image Assets: If your implementation requires any images except some button logo, you can use placeholder image URLs from https://picsum.photos//. Note that the width and height values (e.g., 500/300) are adjustable as needed. + +## Library: + "react-router": "^6", + "react": "^18", + "@tailwindcss/vite": "^4.0.0" + +## Output Format: + Output your final code wrapped in tags ONLY, like: - # Instructions and Rules: - 1. Implement Only One file: Implement only the given file. - 2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. - 3. Type Safe: Follow typscript standard. - 4. Follow design: DONT CHANGE ANY DESIGN IN Document. - 5. CAREFULLY CHECK: - Before importing a file, verify its existence. - THAT YOU DONT MISSED ANY Internal Dependencies import. - If missing, suggest an alternative or define mock data. - 6. Before using a external variable/module, make sure you import it first. - 7. Error Handling: Implement proper error handling in API calls and interactions with external modules. - 8. Code Standards: Adhere to styling guidelines (e.g., Tailwind CSS, CSS Modules), and Use only Tailwind UI for styling, applying all styles via inline class names (className). - 9. Mock the response: if the API returns an empty or undefined value. - 10. Write EVERY CODE DETAIL, DON'T LEAVE TODO. - - ## Library: - "react-router": "^6", - "react": "^18", - "@tailwindcss/vite": "^4.0.0" - - - ## Output Format: - Output your final code wrapped in tags ONLY, like: - - - ...full code... - - `; -}; + + ...full code... + +`; +} export function generateCSSPrompt( fileName: string, @@ -91,6 +96,8 @@ export function generateFixPrompt( **Expected Output:** - Provide the **fixed code** without changing the structure unnecessarily. - Ensure the code is TypeScript-safe and follows React best practices. +- DO NOT create custom React type definitions - use types from '@types/react'. +- Always import React types from 'react' package, never create your own. - Provide a **brief explanation** of the fixes and improvements. The file Name: @@ -144,6 +151,9 @@ Available operations: - Keep existing code style/conventions - Add type guards where necessary - Prefer generics over 'any' + - DO NOT create custom React type definitions + - Always import React types from '@types/react' + - Never create your own React-related types 7. File Operations: - Use RENAME only for extension issues (e.g., .ts → .tsx) diff --git a/backend/src/build-system/handlers/product-manager/product-requirements-document/prd.ts b/backend/src/build-system/handlers/product-manager/product-requirements-document/prd.ts index 3cf7888c..3d79aea1 100644 --- a/backend/src/build-system/handlers/product-manager/product-requirements-document/prd.ts +++ b/backend/src/build-system/handlers/product-manager/product-requirements-document/prd.ts @@ -24,7 +24,7 @@ export class PRDHandler implements BuildHandler { context.getGlobalContext('projectName') || 'Default Project Name'; const description = context.getGlobalContext('description') || 'Default Description'; - const platform = context.getGlobalContext('platform') || 'Default Platform'; + const platform = context.getGlobalContext('platform') || 'web'; // Validate extracted data if (!projectName || typeof projectName !== 'string') { @@ -71,7 +71,7 @@ export class PRDHandler implements BuildHandler { ]; const prdContent = await chatSyncWithClocker( context, - { messages, model: 'gpt-4o-mini' }, + { messages, model: context.defaultModel || 'gpt-4o-mini' }, 'generatePRDFromLLM', PRDHandler.name, ); diff --git a/backend/src/build-system/handlers/product-manager/product-requirements-document/prompt.ts b/backend/src/build-system/handlers/product-manager/product-requirements-document/prompt.ts index 848a8125..0cfef346 100644 --- a/backend/src/build-system/handlers/product-manager/product-requirements-document/prompt.ts +++ b/backend/src/build-system/handlers/product-manager/product-requirements-document/prompt.ts @@ -1,5 +1,3 @@ -// Define and export the system prompts object - export const prompts = { generatePRDPrompt: ( projectName: string, @@ -11,61 +9,56 @@ export const prompts = { - Description: ${description} - Platform: ${platform} - Follow these guidelines to ensure clarity, thoroughness, and usability in the PRD, which will be directly utilized in development. - ### Instructions and Rules: +Follow these guidelines to ensure clarity, thoroughness, and usability in the PRD, which will be directly utilized in development. +### Instructions and Rules: - 1, Your need to analysis the requirement for the project and covered all the things you can think about. - 2, You should focus on core features for the project - 3, ask yourself: - - what are all expect features require for a {project name} and {Description}? - - what are the user stories for the feature? - - what are the details description for those features? - - Am I cover all expect features ? if not then add new feature. - - Are those covered all user expected features ? if not then add new feature. - - what are the target audience for the project? Is all the features meet their expectation? if not then add new feature. - - Ask your self what are the function requirement and none functional requirement for those features? - - Are all features could be agree by others in the team? including product manager, developer.... - - ### PRD Structure: - - Start with the following structure. Do not include sections for Milestones, Deliverables, or Technical Requirements. - - --- - +1. Analyze the project requirements in detail, focusing on what matters most to users. +2. Analyze the scope and scale requirements based on the project description - determine appropriate size constraints. +3. Focus on core features for the project, prioritizing essential functionality over nice-to-haves. +4. Ask yourself: + - What are the must-have features required for ${projectName} considering the project's scope? + - What are the critical user stories for each core feature? + - What specific UI dimensions and responsive design considerations are needed? + - What are the minimum viable game mechanics needed for a satisfying user experience? + - Who is the target audience for the project? What features would they prioritize? + - What are the essential functional and non-functional requirements? + - What visual elements and UI components are appropriate for this project? + - What performance considerations are important for this type of project? + +### PRD Structure: +Start with the following structure. Be concise and focused on implementation-ready details. +--- ### Product Requirement Document - #### 1. Project Overview - **Project Name**: - - **Description**: Provide a brief overview of the project’s objective and purpose. + - **Description**: Provide a brief overview of the project's objective and purpose. - **Platform**: Indicate the platform(s) (e.g., Web, Mobile). - + - **Size and Scope**: Analyze and specify appropriate dimensions, layout constraints, and responsive design considerations based on project requirements. #### 2. Goals and Objectives - Define the primary goals and purpose of the project. - Describe any key performance indicators or success criteria. - #### 3. Target Audience - Identify the intended users or customer segments. - Describe user needs, pain points, and how the product will address them. - #### 4. User Stories - - List user stories that illustrate interactions for each main feature. + - List essential user stories that illustrate interactions for each main feature. - Make each story actionable, focusing on user goals. - #### 5. Features - - Outline each feature required for the project. - - Write the requirement for the feature - + - Outline each core feature required for the project. + - Prioritize features based on user needs and project scope. + - Write the requirement for the feature. #### 6. Functional Requirements - Specify the functional requirements for each feature and sub-feature. + - Include appropriate size and performance considerations for each requirement. - Use clear, concise statements outlining required functionality. - #### 7. Non-Functional Requirements - - Describe performance, security, usability, and other quality-related requirements. - -#### 8. Additional Analysis - - Provide any further insights or considerations to ensure a holistic view of the project requirements. + - Describe performance, responsiveness, usability, and other quality-related requirements. + - Include appropriate constraints and optimization requirements. +#### 8. UI/UX Specifications + - Define the appropriate layout dimensions and structure. + - Specify component sizes, spacing, and visual hierarchy. + - Outline responsive design breakpoints and adaptations if applicable. --- - Your reply must start with : "\`\`\`ProductDoc" and end with "\`\`\`". Be thorough, and make sure each section is fully developed and ready for implementation. `; }, diff --git a/backend/src/build-system/handlers/ux/datamap/index.ts b/backend/src/build-system/handlers/ux/datamap/index.ts index 5fefb75f..6d04192c 100644 --- a/backend/src/build-system/handlers/ux/datamap/index.ts +++ b/backend/src/build-system/handlers/ux/datamap/index.ts @@ -48,7 +48,7 @@ export class UXDMDHandler implements BuildHandler { const uxDatamapContent = await chatSyncWithClocker( context, { - model: 'gpt-4o-mini', + model: context.defaultModel || 'gpt-4o-mini', messages: [{ content: prompt, role: 'system' }], }, 'generateUXDataMap', diff --git a/backend/src/build-system/handlers/ux/sitemap-document/index.ts b/backend/src/build-system/handlers/ux/sitemap-document/index.ts index 95fb5eb4..de1d9a0e 100644 --- a/backend/src/build-system/handlers/ux/sitemap-document/index.ts +++ b/backend/src/build-system/handlers/ux/sitemap-document/index.ts @@ -15,17 +15,22 @@ export class UXSMDHandler implements BuildHandler { async run(context: BuilderContext): Promise> { this.logger.log('Generating UXSMD...'); - // Extract project data from the context const projectName = context.getGlobalContext('projectName') || 'Default Project Name'; const platform = context.getGlobalContext('platform') || 'Default Platform'; + const projectSize = context.getGlobalContext('projectSize') || 'medium'; + const description = + context.getGlobalContext('description') || 'Default Description'; const prdContent = context.getNodeData(PRDHandler); this.logger.debug('prd in uxsmd', prdContent); - // Generate the prompt dynamically - const prompt = prompts.generateUxsmdPrompt(projectName, platform); + const prompt = prompts.generateUxsmdPrompt( + projectName, + platform, + projectSize, + description, + ); - // Send the prompt to the LLM server and process the response const uxsmdContent = await this.generateUXSMDFromLLM( context, prompt, @@ -39,10 +44,8 @@ export class UXSMDHandler implements BuildHandler { }; } - // Store the generated document in the context context.setGlobalContext('uxsmdDocument', uxsmdContent); - // Return the generated document return { success: true, data: removeCodeBlockFences(uxsmdContent), @@ -91,7 +94,7 @@ export class UXSMDHandler implements BuildHandler { const uxsmdContent = await chatSyncWithClocker( context, { - model: 'gpt-4o-mini', + model: context.defaultModel || 'gpt-4o-mini', messages: messages, }, 'generateUXSMDFromLLM', diff --git a/backend/src/build-system/handlers/ux/sitemap-document/prompt.ts b/backend/src/build-system/handlers/ux/sitemap-document/prompt.ts index 8ca4cb2b..8d6e6477 100644 --- a/backend/src/build-system/handlers/ux/sitemap-document/prompt.ts +++ b/backend/src/build-system/handlers/ux/sitemap-document/prompt.ts @@ -1,82 +1,120 @@ -// Define and export the system prompts object export const prompts = { - generateUxsmdPrompt: (projectName: string, platform: string): string => { - return `You are an expert frontend develper and ux designer. Your job is to analyze and expand upon the details provided, generating a Full UX Sitemap Document based on the following inputs: - - Project name: ${projectName} - - Platform: ${platform} - - product requirements document - - Your primary goal is to create a fully comprehensive, development-ready UX Sitemap Document that will be directly transferred to the development team for implementation. - This document will be used for an application expected to serve thousands of users, and it must cover all use cases, ensuring a complete and detailed roadmap of all UI components and navigation. - - Formatting & Output Guidelines: - 1, Use Markdown for structuring the document. - 2, Your reply should start with : "\`\`\`UXSitemap" and end with "\`\`\`", Use proper markdown syntax for headings, subheadings, and hierarchical lists. - 3, Ensure proper markdown syntax for headings, subheadings, and hierarchical lists. - 4, Strict Naming Conventions: - Global Shared UI Views → Prefix with global_view_* . - Unique UI Pages → Prefix with page_view_* . - No "Container" Views → Do not create abstract container views . - global_view_* and page_view_* must be independent → page_view_* does not embed global_view_*, but they share screen space. - - UX Sitemap Requirements: - Your UX Sitemap Document should include detailed breakdowns of: - - 1, Global Shared UI Views (global_view_*) - Definition: Shared UI components (e.g., navigation, footers, side menus) used across multiple pages. - Structure: - Each must have a unique ID (global_view_*). - Clearly describe authentication conditions: - Logged-in users (full access, personalized elements). - Logged-out users (restricted access, call-to-actions). - Document all shared elements and their variations: - Example: global_view_top_nav (changes based on authentication state). - - 2, Unique UI Pages (page_view_*) - Definition: Individual, standalone pages (e.g., page_view_home, page_view_settings). - Structure: - Path (URL Route): Clearly define the URL structure of the page. - Example: /home - Covers all unique screens (e.g., page_view_home, page_view_onboarding, page_view_settings). - Describe authentication conditions, permissions, and state dependencies: - What features are available to guest vs. logged-in users? - Are any actions restricted based on user type (e.g., admin, regular user)? - Ensure no duplicate inclusion of global_view_* views (they only share screen space). - Components: - List all UI components that appear on this page. - Describe their functionality and interaction behavior. - Provide detailed descriptions of features, interactions, and user flows. - - 3, Functional & Feature Analysis - Break down each UI page before detailing its components. - Clearly map features to UI views. - Ensure that: - Every feature described in the PRD has a corresponding page (page_view_*). - Features are not missing any expected UI elements. - - 4, Navigation & User Journey Mapping - Map each user journey from the PRD into a step-by-step navigation path. - Example format: - - 1. page_view_home → page_view_explore → page_view_product_details → .... - - Cover both static and dynamic navigation flows. - - Final Instructions: - Self Check Before finalizing, ask yourself: - Did I cover all global shared UI views (global_view_*) separately? - Did I assign unique and expressive IDs (global_view_* for shared views, page_view_* for unique pages)? - Did I avoid embedding global_view_* inside page_view_*? - Did I ensure authentication-based conditions (logged-in vs. guest)? - Did I extensively describe every UI element, interaction, and user flow? - Did I include URL paths for all pages? - Did I include 100% of views required by all features? - Did I avoid unnecessary secondary/tertiary features? - Did I describe inter-app linking and navigation comprehensively? - Strictly follow the naming and formatting conventions. - No extra comments or surrounding text—your reply will be directly used as the final UX Sitemap Analysis Document. - Your response should only contain the markdown-formatted UX Sitemap Document. - Your final document must be exhaustive and 100% complete for development use. - `; + generateUxsmdPrompt: ( + projectName: string, + platform: string, + projectSize: string, + projectDescription: string, + ): string => { + return `You are an expert frontend developer and UX designer. Your job is to analyze and expand upon the details provided, generating a Full UX Sitemap Document based on the following inputs: + - Project name: ${projectName} + - Description: ${projectDescription} + - Platform: ${platform} + - Project size: ${projectSize} + * Definitions: + - Small: 1-3 unique UI pages. + - Medium: 2-5 unique UI pages. + - Large: 3-7 unique UI pages. + - Product requirements document + + Your primary goal is to create a comprehensive, development-ready UX Sitemap Document that focuses on the most critical UI components, navigation flows, and core functionalities required for the application. Analyze the user requirements carefully and ensure every essential feature is covered. + + Note: Depending on the project size (${projectSize}), adjust the level of detail appropriately. For smaller projects like a one-page game, ensure minimal yet complete coverage with clear specifications for UI elements and their dimensions. + + Global Component Decision: + Before starting, determine whether global components are needed: + - If the application is a Single Page Application (SPA) with only one main view/page, DO NOT create global_view_* components + - If the application has multiple pages but minimal component reuse, DO NOT create global_view_* components + - If the application has multiple pages with significant component reuse (e.g., headers, navigation bars, footers appearing on multiple pages), DO create global_view_* components + - Look for explicit indicators in the project description or requirements: + * Terms like "single page app", "SPA", "one-page application" suggest global components are NOT needed + * Terms like "multi-page", "navigation between pages", "common header/footer" suggest global components ARE needed + - When in doubt for small projects (1-3 pages), prefer NOT using global components and include all UI elements directly within page descriptions + + Single Page Application (SPA) Treatment: + For Single Page Applications: + - DO NOT create separate page_view_* entries for different "sections" or "views" within the SPA + - Instead, treat different sections as components within the single page_view_* + - For example, if an SPA has a "home section", "about section", and "contact section": + * Create ONE page_view_* (e.g., page_view_main) + * List each section as a component within that page (e.g., C1.1. HomeSection, C1.2. AboutSection, C1.3. ContactSection) + - Document how users navigate between sections (e.g., scrolling, tab switching, dynamic content replacement) + - Remember: In SPAs, different "views" are UI states of the same page, not separate pages + + Formatting & Output Guidelines: + 1. Use Markdown for structuring the document. + 2. Your reply should start with "\`\`\`UXSitemap" and end with "\`\`\`", using proper markdown syntax for headings, subheadings, and hierarchical lists. + 3. Ensure proper markdown syntax for headings, subheadings, and hierarchical lists. + 4. Naming Conventions: + For projects with multiple pages or significant component reuse: + Global Shared UI Views → Prefix with global_view_*. + Unique UI Pages → Prefix with page_view_*. + global_view_* and page_view_* must be independent → page_view_* does not embed global_view_* but they share screen space. + For single-page projects or projects with minimal reuse: + You may include all UI components directly within the page_view_* description without creating separate global_view_* elements. + No "Container" Views → Do not create abstract container views. + + UX Sitemap Requirements: + Your UX Sitemap Document should include detailed breakdowns of: + + 1. UI Pages and Components + For multi-page applications with reusable components: + a. Global Shared UI Views (global_view_*) + Definition: Shared UI components (e.g., navigation, game controls, score displays) used across multiple sections. + Structure: + - Each must have a unique ID (global_view_*). + - Document all shared elements with exact dimensions and layout specifications. + - For games or interactive applications, clearly specify control mechanisms and interactive elements. + Example: global_view_game_controls (dimensions: WxH, positioning: absolute/relative). + + b. Unique UI Pages (page_view_*) + Definition: Individual, standalone pages or screens. + Structure: + - Path (URL Route): Clearly define the URL structure of the page. + - Specify exact dimensions and responsive breakpoints. + - Components: + - List UI components that are unique to this page. + - Reference which global_view_* components appear on this page. + + For single-page applications or projects with minimal component reuse: + a. Unique UI Pages (page_view_*) + Definition: The main page or screen of the application. + Structure: + - Path (URL Route): Clearly define the URL structure of the page. + - Specify exact dimensions and responsive breakpoints. + - Components: + - List ALL UI components directly with their precise sizes and positions. + - For game boards or interactive elements, specify grid sizes, tile dimensions, and spacing. + - Provide detailed descriptions of animations, transitions, and visual feedback. + + 2. Size and Layout Specifications + - Provide exact dimensions for all UI elements (in pixels or relative units). + - Specify responsive behavior and breakpoints. + - For games, detail the game board dimensions, tile sizes, and spacing. + - Describe how the UI adapts to different screen sizes. + + 3. Interaction & Animation Specifications + - Detail all interaction patterns (clicks, swipes, drags). + - Specify animation timings, easing functions, and visual effects. + - For games, document score animations, tile movements, and feedback effects. + + 4. Theme Analysis + - Analyze any design theme indicated in the product requirements document. + - Include details on color palette, typography, and visual style. + - Specify exact color codes and font sizes. + - you must explain why choices were made base on user's project name and description. + - please provide a brief explanation of the theme and how it aligns with the project's goals. + + Final Instructions: + Self Check Before finalizing, ask yourself: + - Is my approach appropriate for the project size? (For single-page applications, have I included all components directly within the page description?) + - Have I provided exact dimensions for all UI elements? + - Have I specified all interactive components with their size and behavior? + - For games, have I detailed the game board layout and dimensions? + - Have I documented all animations and transitions? + - Have I specified responsive breakpoints and adaptations? + - Have I included all essential user interface elements required for the core functionality? + Focus on implementation-ready details that developers can directly use. + Your final document must be precise, comprehensive, and ready for immediate development use. + `; }, }; diff --git a/backend/src/build-system/handlers/ux/sitemap-structure/index.ts b/backend/src/build-system/handlers/ux/sitemap-structure/index.ts index f156aa7e..561a6ea9 100644 --- a/backend/src/build-system/handlers/ux/sitemap-structure/index.ts +++ b/backend/src/build-system/handlers/ux/sitemap-structure/index.ts @@ -94,13 +94,16 @@ export class UXSMSHandler implements BuildHandler { const uxStructureContent = await chatSyncWithClocker( context, { - model: 'gpt-4o-mini', + model: context.defaultModel || 'gpt-4o-mini', messages, }, 'generateUXSiteMapStructre', UXSMSHandler.name, ); + const themeString = extractThemeString(uxStructureContent); + context.setGlobalContext('theme', themeString); + if (!uxStructureContent || uxStructureContent.trim() === '') { this.logger.error('Generated UX Sitemap Structure content is empty.'); throw new ResponseParsingError( @@ -118,3 +121,10 @@ export class UXSMSHandler implements BuildHandler { } } } +function extractThemeString(content: string): string { + const match = content.match(/([\s\S]*?)<\/theme_view>/); + if (match && match[1]) { + return match[1].trim(); + } + return 'none theme using regular theme'; +} diff --git a/backend/src/build-system/handlers/ux/sitemap-structure/prompt.ts b/backend/src/build-system/handlers/ux/sitemap-structure/prompt.ts index 5e8d3d91..1306d9e9 100644 --- a/backend/src/build-system/handlers/ux/sitemap-structure/prompt.ts +++ b/backend/src/build-system/handlers/ux/sitemap-structure/prompt.ts @@ -3,138 +3,168 @@ export const prompts = { projectName: string, platform: string, ): string => { - return `You are an expert frontend develper and ux designer. Your job is to analyze and expand upon the details provided, generating a Full UX Sitemap Structure Document based on the following inputs: - - Project name: ${projectName} - - Platform: ${platform} - - UX Sitemap Documentation: (Provided by the user next) - - Formatting & Output Guidelines: - Output Requirements: - Use plain text (no Markdown). - Begin with and end with . - Within , start with , and generate multiple blocks, one for each page. - - UX Sitemap Structure: - The sitemap should be structured as follows: - - - - - G#. [Component Name] - Authentication Conditions: [Rules for visibility based on user authentication] - Elements: - - [List all UI elements in this component] - 1. **Type** (Layout, Interactive, Display, Input, etc.) - 2. **Purpose** (What does it do for the user or the interface?) - 3. **States** (Possible UI states: Default, Hover, Clicked, Expanded, Loading, etc.) - 4. **Interactions** (User actions or system responses: clicking, hovering, dragging, scrolling, etc.) - - - - - - P#. [Page Name] - URL Path: /[path] - Parent Page: [Parent page if nested, or "None" if top-level] - Description: [Brief description of page purpose] - Authentication Conditions: [Public/Private/Login Required] - #### Core Components: - - C#.1. [Component Name] - - Definition: Core Components are **distinct UI elements** or **functional blocks** on the page that have a clear, identifiable role. Each component must include: - 1. **Type** (Layout, Interactive, Display, Input, etc.) - 2. **Purpose** (What does it do for the user or the interface?) - 3. **States** (Possible UI states: Default, Hover, Clicked, Expanded, Loading, etc.) - 4. **Interactions** (User actions or system responses: clicking, hovering, dragging, scrolling, etc.) - #### Features & Functionality: - - Focus on how these features tie back to user stories, and which **Core Components** are used to achieve them - - F#.1. [Feature Name] - - Description: [Functionality Overview] - - User Stories: [Relevant user stories from PRD] - - Components Used: [Which UI elements power this feature?] - #### Page-Specific User Flows: - Step-by-step sequences describing user interactions and system responses - Flow #. [Flow Name] - [Step 1] - [Step 2] - - - + return `You are an expert frontend developer and UX designer. Your job is to analyze and expand upon the details provided, generating a Full UX Sitemap Structure Document based on the following inputs: + - Project name: ${projectName} + - Platform: ${platform} + - UX Sitemap Documentation: (Provided by the user next) + + Formatting & Output Guidelines: + Output Requirements: + Use plain text (no Markdown). + Begin with and end with . + + IMPORTANT: you should typically NOT create sections unless explicitly required in the UX Sitemap Document. Instead, include all UI components directly within the block. + + UX Sitemap Structure: + The sitemap should be structured as follows: + + + + Theme Name: [Theme name] + Description: [Brief description of the overall visual theme] + Design Style Note: If the sitemap documentation or user's description indicates a specific design style (e.g., minimalist, modern, etc.), explicitly include that style here. + Color Palette: + - Primary: [Primary color with hex code] + - Secondary: [Secondary color with hex code] + - Accent: [Accent color with hex code] + - Background: [Background color with hex code] + - Text: [Text color with hex code] + Typography: + - Headings: [Font family for headings] + - Body: [Font family for body text] + - Size Scale: [Font size scale for different text elements] + Component Styling: + - Button Style: [Default button styling] + - Input Style: [Default input field styling] + - Card Style: [Default card/container styling] + - Shadow Style: [Default shadow effects] + Spacing: + - Layout Grid: [Grid system specifications] + - Margins: [Default margin sizes] + - Padding: [Default padding sizes] + Animations: + - Transitions: [Default transition effects] + - Hover Effects: [Default hover animations] + + + + + G#. [Component Name] + Authentication Conditions: [Rules for visibility based on user authentication] + Elements: + - [List all UI elements in this component] + 1. **Type** (Layout, Interactive, Display, Input, etc.) + 2. **Purpose** (What does it do for the user or the interface?) + 3. **States** (Possible UI states: Default, Hover, Clicked, Expanded, Loading, etc.) + 4. **Interactions** (User actions or system responses: clicking, hovering, dragging, scrolling, etc.) + 5. **Theme Application**: [How this component uses the defined theme] + + + + + + P#. [Page Name] + URL Path: /[path] + Parent Page: [Parent page if nested, or "None" if top-level] + Description: [Brief description of page purpose] + Authentication Conditions: [Public/Private/Login Required] + #### Core Components: + - C#.1. [Component Name] + - Definition: Core Components are **distinct UI elements** or **functional blocks** on the page that have a clear, identifiable role. Each component must include: + 1. **Type** (Layout, Interactive, Display, Input, etc.) + 2. **Purpose** (What does it do for the user or the interface?) + 3. **States** (Possible UI states: Default, Hover, Clicked, Expanded, Loading, etc.) + 4. **Interactions** (User actions or system responses: clicking, hovering, dragging, scrolling, etc.) + #### Features & Functionality: + - Focus on how these features tie back to user stories, and which **Core Components** are used to achieve them + - F#.1. [Feature Name] + - Description: [Functionality Overview] + - User Stories: [Relevant user stories from PRD] + - Components Used: [Which UI elements power this feature?] + #### Page-Specific User Flows: + Step-by-step sequences describing user interactions and system responses + Flow #. [Flow Name] + [Step 1] + [Step 2] + + + 1. **Goal**: Produce a complete UX Structure Map describing how each page/screen is laid out, including which global components are reused across pages. - + 2. **Global Components**: - Mark all reusable or site-wide elements with \`\` tags and end tag . - - Provide a short but clear definition for each global component (e.g., Navigation Bar, Footer, etc.). - - Explain how/why these components appear on multiple pages (if applicable). - -3. **Page Definitions**: - - Use \`\` tags to define each individual page or screen from the Sitemap Documentation. - - For each \`\`, provide: - - **Page id** a unique page id. - - **Page name** (P#). - - **URL Path**: The route or path used to access this page. + - Provide a short but clear definition for each global component (e.g., Navigation Bar, Footer, etc.). + - Explain how/why these components appear on multiple pages (if applicable). + + 3. **Page Definitions**: + - Use \`\` tags to define each individual page or screen from the Sitemap Documentation. + - For each \`\`, provide: + - **Page id** a unique page id. + - **Page name** (P#). + - **URL Path**: The route or path used to access this page. - **Description**: Explain the purpose of the page, the users goal, and how it supports the user journey. - **Core Elements**: List and describe the UI elements on this page (headers, buttons, sidebars, etc.). - - Explain their states and interactions. - - Reference **global components** (if used) plus any page-specific components. - - **Content Display**: What information is shown, and why is it essential? - - **Navigation and Routes**: Which routes lead here? Which links or buttons lead out? - - **Restrictions**: Note if login is required, or if only certain user roles can access. - -4. **Focus on Detail**: + - Explain their states and interactions. + - Reference **global components** (if used) plus any page-specific components. + - **Content Display**: What information is shown, and why is it essential? + - **Navigation and Routes**: Which routes lead here? Which links or buttons lead out? + - **Restrictions**: Note if login is required, or if only certain user roles can access. + + 4. **Focus on Detail**: - Provide a component-level breakdown for each pages layout and user interactions. - - Address all features from the Sitemap Documentation; confirm no item is missed. + - Address all features from the Sitemap Documentation; confirm no item is missed. - Make sure each pages structure is thorough enough for front-end implementation. - -5. **Consider**: - - User goals on each page. - - The user journey and how each page supports it. - - The purpose of each element (why it exists, how it helps the user). - - The presence of dynamic or personalized content. - - 6. **Output Format**: + + 5. **Consider**: + - User goals on each page. + - The user journey and how each page supports it. + - The purpose of each element (why it exists, how it helps the user). + - The presence of dynamic or personalized content. + + 6. **Output Format**: - Your reply must begin with: and end with (plain text, no Markdown headings). - - Inside, you must include: + - Inside, you must include: 1. One or more \`\` blocks (if relevant). 2. Multiple \`\` blocks (one per page). - Each \`\` or \`\` should include all relevant fields as stated above. - **Number** Goal Component in tag sequentially (G1., G2., etc.). - **Number** pages sequentially (P1., P2., etc.). - **Number** each component and feature sequentially within that page (C1.1, C1.2, F1.1, F1.2, etc.). - Thoroughly parse the PRD to include: - - **All** pages. - - **All** features, functionalities, user stories, and flows. - - **All** major/minor navigation and user journeys. - -Sitemap Coverage - - Comprehensive Analysis: - Capture all features, functionalities, and user stories. - Represent all primary and secondary user flows. - - Page & Navigation Structure: - Identify all main and nested pages. - Ensure clear parent-child relationships. - - Detailed User Journeys: - Provide step-by-step navigational flows unique to each page. - - Thorough Coverage: + **Number** Goal Component in tag sequentially (G1., G2., etc.). + **Number** pages sequentially (P1., P2., etc.). + **Number** each component and feature sequentially within that page (C1.1, C1.2, F1.1, F1.2, etc.). + Thoroughly parse the PRD to include: + - **All** pages. + - **All** features, functionalities, user stories, and flows. + - **All** major/minor navigation and user journeys. + + Sitemap Coverage + + Comprehensive Analysis: + Capture all features, functionalities, and user stories. + Represent all primary and secondary user flows. + + Page & Navigation Structure: + Identify all main and nested pages. + Ensure clear parent-child relationships. + + Detailed User Journeys: + Provide step-by-step navigational flows unique to each page. + + Thorough Coverage: Verify every requirement, global UI element, unique UI, and user expectation from the PRD is addressed. - No user flow or screen should be missing. - -Self-Check Before Submitting - - Have you accounted for all sitemap details? + No user flow or screen should be missing. + + Self-Check Before Submitting + + Have you accounted for all sitemap details? Are all global UI elements correctly categorized? - Have you included all relevant user flows and navigation structures? - Have you detailed each page's components, features, and flows completely? - Is every page from the Sitemap Documentation represented in a \`\` block? + Have you included all relevant user flows and navigation structures? + Have you detailed each page's components, features, and flows completely? + Is every page from the Sitemap Documentation represented in a \`\` block? Are all global components defined in \`\` blocks? - Are user flows, interactions, and relevant content needs included? - -Deliver a single XML-like document that strictly follows the structure above. Start with and close with , containing one block per page. - - `; + Are user flows, interactions, and relevant content needs included? + + Deliver a single XML-like document that strictly follows the structure above. Start with and close with , containing one block per page. + + `; }, generatePagebyPageSiteMapStructrePrompt: (): string => { const guidelines = prompts.HTML_Guidelines_Page_view_Prompt(); diff --git a/backend/src/build-system/handlers/ux/sitemap-structure/sms-page.ts b/backend/src/build-system/handlers/ux/sitemap-structure/sms-page.ts index f14b48f9..2eb450c2 100644 --- a/backend/src/build-system/handlers/ux/sitemap-structure/sms-page.ts +++ b/backend/src/build-system/handlers/ux/sitemap-structure/sms-page.ts @@ -57,7 +57,7 @@ export class UXSMSPageByPageHandler implements BuildHandler { this.logger.log('Processing each Global Component...'); const requests = globalSections.map((globalSection) => ({ - model: 'gpt-4o', + model: context.defaultModel || 'gpt-4o', messages: [ { role: 'system' as const, @@ -95,7 +95,7 @@ export class UXSMSPageByPageHandler implements BuildHandler { // Page View const page_view_requests = sections.map((section) => ({ - model: 'gpt-4o', + model: context.defaultModel || 'gpt-4o', messages: [ { role: 'system' as const, @@ -119,14 +119,6 @@ export class UXSMSPageByPageHandler implements BuildHandler { Please generate the Full UX Sitemap Structre for this section now. Provide the information exclusively within tags.`, }, - // { - // role: 'user' as const, - // content: ` - // Next you need to generating a Draft HTML Layout for each . - // Your output must emphasize component placement, layout context, and styling directions to ensure developers can implement a responsive and accessible UI effectively. - // ${prompts.HTML_Guidelines_Page_view_Prompt} - // `, - // }, { role: 'user' as const, content: `Please enrich the details of Core Components in each block. diff --git a/backend/src/build-system/types.ts b/backend/src/build-system/types.ts index 69abc990..9056c388 100644 --- a/backend/src/build-system/types.ts +++ b/backend/src/build-system/types.ts @@ -11,6 +11,7 @@ export interface BuildBase { name?: string; description?: string; options?: BuildOpts; + model?: string; } /** @@ -31,6 +32,8 @@ export interface BuildSequence { databaseType?: string; nodes: BuildNode[]; packages: BuildProjectPackage[]; + model?: string; + projectSize?: 'small' | 'medium' | 'large'; } export interface BuildProjectPackage { name: string; diff --git a/backend/src/build-system/utils/file_generator_util.ts b/backend/src/build-system/utils/file_generator_util.ts index 0d40d157..f97bff05 100644 --- a/backend/src/build-system/utils/file_generator_util.ts +++ b/backend/src/build-system/utils/file_generator_util.ts @@ -213,7 +213,7 @@ export function validateAgainstVirtualDirectory( ); return invalidFiles.join('\n'); } - return null; + return ''; } /** diff --git a/backend/src/build-system/utils/files.ts b/backend/src/build-system/utils/files.ts index f6b6bbd3..129179d3 100644 --- a/backend/src/build-system/utils/files.ts +++ b/backend/src/build-system/utils/files.ts @@ -2,6 +2,7 @@ import { Logger } from '@nestjs/common'; import * as fs from 'fs-extra'; import * as path from 'path'; import { getProjectsDir, getProjectPath } from 'codefox-common'; +import { FileWriteError } from '../errors'; const logger = new Logger('file-utils'); /** * Saves the given content to the specified file path using fs-extra. @@ -146,7 +147,9 @@ export async function createFileWithRetries( attempt++; // Optionally log a warning - logger.warn(`Failed to write file: ${filePath}, attempt #${attempt}`); + logger.warn( + `Failed to write file: ${filePath}, attempt #${attempt}, content: ${content}`, + ); // Wait before retrying if (attempt < maxRetries) { @@ -156,5 +159,5 @@ export async function createFileWithRetries( } // If all retries fail, rethrow the last error - throw lastError; + throw new FileWriteError(lastError); } diff --git a/backend/src/common/model-provider/openai-model-provider.ts b/backend/src/common/model-provider/openai-model-provider.ts index abcb0707..6b2945b1 100644 --- a/backend/src/common/model-provider/openai-model-provider.ts +++ b/backend/src/common/model-provider/openai-model-provider.ts @@ -46,7 +46,7 @@ export class OpenAIModelProvider implements IModelProvider { const queue = new PQueue({ concurrency, - timeout: 120000, // 120 second timeout + timeout: 300000, // 300 second timeout }); // Log queue events for monitoring diff --git a/backend/src/project/build-system-utils.ts b/backend/src/project/build-system-utils.ts index 6ff1bf57..92667007 100644 --- a/backend/src/project/build-system-utils.ts +++ b/backend/src/project/build-system-utils.ts @@ -3,8 +3,6 @@ import { BackendFileReviewHandler } from 'src/build-system/handlers/backend/file import { BackendRequirementHandler } from 'src/build-system/handlers/backend/requirements-document'; import { DBRequirementHandler } from 'src/build-system/handlers/database/requirements-document'; import { DBSchemaHandler } from 'src/build-system/handlers/database/schemas/schemas'; -import { FileFAHandler } from 'src/build-system/handlers/file-manager/file-arch'; -import { FileStructureHandler } from 'src/build-system/handlers/file-manager/file-structure'; import { FrontendCodeHandler } from 'src/build-system/handlers/frontend-code-generate'; import { PRDHandler } from 'src/build-system/handlers/product-manager/product-requirements-document/prd'; import { ProjectInitHandler } from 'src/build-system/handlers/project-init'; @@ -15,6 +13,7 @@ import { UXSMSPageByPageHandler } from 'src/build-system/handlers/ux/sitemap-str import { BuildSequence } from 'src/build-system/types'; import { v4 as uuidv4 } from 'uuid'; import { CreateProjectInput } from './dto/project.input'; +import { FileStructureAndArchitectureHandler } from 'src/build-system/handlers/file-manager/file-struct'; export function buildProjectSequenceByProject( input: CreateProjectInput, @@ -52,11 +51,8 @@ export function buildProjectSequenceByProject( name: 'Database Requirements Node', }, { - handler: FileStructureHandler, - name: 'File Structure Generation', - options: { - projectPart: 'frontend', - }, + handler: FileStructureAndArchitectureHandler, + name: 'File Structure and Architecture', }, { handler: UXSMSPageByPageHandler, @@ -66,10 +62,6 @@ export function buildProjectSequenceByProject( handler: DBSchemaHandler, name: 'Database Schemas Node', }, - { - handler: FileFAHandler, - name: 'File Arch', - }, { handler: BackendRequirementHandler, name: 'Backend Requirements Node', diff --git a/backend/template/react-ts/index.html b/backend/template/react-ts/index.html index 6b16f407..2548720f 100644 --- a/backend/template/react-ts/index.html +++ b/backend/template/react-ts/index.html @@ -3,6 +3,7 @@ + Codefox generated project diff --git a/backend/template/react-ts/vite.config.ts b/backend/template/react-ts/vite.config.ts index 97ca8705..d44a0663 100644 --- a/backend/template/react-ts/vite.config.ts +++ b/backend/template/react-ts/vite.config.ts @@ -8,6 +8,7 @@ export default defineConfig({ resolve: { alias: { '@': path.resolve(__dirname, './src'), + src: path.resolve(__dirname, './src'), }, }, esbuild: { diff --git a/frontend/src/app/api/runProject/route.ts b/frontend/src/app/api/runProject/route.ts index e2d62a76..9ec93ec9 100644 --- a/frontend/src/app/api/runProject/route.ts +++ b/frontend/src/app/api/runProject/route.ts @@ -97,7 +97,9 @@ async function buildAndRunDocker( const runCommand = `docker run -d --name ${containerName} -l "traefik.enable=true" \ -l "traefik.http.routers.${subdomain}.rule=Host(\\"${domain}\\")" \ -l "traefik.http.services.${subdomain}.loadbalancer.server.port=5173" \ - --network=traefik_network -p ${exposedPort}:5173 ${imageName}`; + --network=traefik_network -p ${exposedPort}:5173 \ + -v "${directory}:/app" \ + ${imageName}`; console.log(runCommand); exec(runCommand, (runErr, runStdout, runStderr) => { diff --git a/llm-server/src/model/remote-model-instance.ts b/llm-server/src/model/remote-model-instance.ts index 57248874..f75876a6 100644 --- a/llm-server/src/model/remote-model-instance.ts +++ b/llm-server/src/model/remote-model-instance.ts @@ -46,7 +46,7 @@ export class RemoteOpenAIModelEngine implements ModelInstance { } private createModelError(error: unknown): ModelError { - const modelError = new Error('Model error occurred') as ModelError; + const modelError = new Error('Model error occurred:' + error) as ModelError; if (error instanceof OpenAI.APIError) { modelError.message = error.message;