UNPKG

aios-core

Version:

Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework

313 lines (268 loc) 8.07 kB
/** * Gitignore Generator Module * * Generates .gitignore files based on detected tech stack. * Supports brownfield merge mode to preserve existing ignores. * * @module documentation-integrity/gitignore-generator * @version 1.0.0 * @story 6.9 */ const fs = require('fs'); const path = require('path'); // Template directory const TEMPLATES_DIR = path.join(__dirname, '..', '..', 'templates', 'gitignore'); /** * Template file names * @enum {string} */ const GitignoreTemplates = { AIOS_BASE: 'gitignore-aios-base.tmpl', NODE: 'gitignore-node.tmpl', PYTHON: 'gitignore-python.tmpl', BROWNFIELD_MERGE: 'gitignore-brownfield-merge.tmpl', }; /** * Tech stack identifiers * @enum {string} */ const TechStack = { NODE: 'node', PYTHON: 'python', GO: 'go', RUST: 'rust', }; /** * Loads a gitignore template * * @param {string} templateName - Template file name * @returns {string} Template content * @throws {Error} If template not found */ function loadGitignoreTemplate(templateName) { const templatePath = path.join(TEMPLATES_DIR, templateName); if (!fs.existsSync(templatePath)) { throw new Error(`Gitignore template not found: ${templatePath}`); } return fs.readFileSync(templatePath, 'utf8'); } /** * Detects tech stack from project markers * * @param {Object} markers - Detected project markers * @returns {string[]} List of detected tech stacks */ function detectTechStacks(markers) { const stacks = []; if (markers.hasPackageJson) stacks.push(TechStack.NODE); if (markers.hasPythonProject) stacks.push(TechStack.PYTHON); if (markers.hasGoMod) stacks.push(TechStack.GO); if (markers.hasCargoToml) stacks.push(TechStack.RUST); return stacks; } /** * Gets gitignore templates for detected tech stacks * * @param {string[]} techStacks - Detected tech stacks * @returns {string[]} List of template names to use */ function getTemplatesForStacks(techStacks) { const templates = [GitignoreTemplates.AIOS_BASE]; for (const stack of techStacks) { switch (stack) { case TechStack.NODE: templates.push(GitignoreTemplates.NODE); break; case TechStack.PYTHON: templates.push(GitignoreTemplates.PYTHON); break; // Go and Rust would have their own templates // Add when implemented } } return templates; } /** * Generates a complete .gitignore file * * @param {Object} markers - Detected project markers * @param {Object} [options] - Generation options * @param {string} [options.projectName] - Project name for header * @returns {string} Generated gitignore content */ function generateGitignore(markers, options = {}) { const techStacks = detectTechStacks(markers); const templates = getTemplatesForStacks(techStacks); const sections = []; // Add project header const projectName = options.projectName || 'Project'; sections.push(`# ${projectName} .gitignore`); sections.push('# Generated by AIOS Documentation Integrity System'); sections.push(`# Date: ${new Date().toISOString().split('T')[0]}`); sections.push(`# Tech Stack: ${techStacks.join(', ') || 'Generic'}`); sections.push(''); // Load and append each template for (const templateName of templates) { try { const content = loadGitignoreTemplate(templateName); sections.push(content); sections.push(''); // Add blank line between sections } catch (error) { console.warn(`Warning: Could not load template ${templateName}: ${error.message}`); } } return sections.join('\n').trim() + '\n'; } /** * Merges AIOS ignores with existing .gitignore * * @param {string} existingContent - Existing .gitignore content * @param {Object} [options] - Merge options * @returns {string} Merged gitignore content */ function mergeGitignore(existingContent, _options = {}) { // Check if AIOS section already exists if (existingContent.includes('AIOS Integration Section')) { console.log('AIOS section already exists in .gitignore, skipping merge'); return existingContent; } // Load merge template let mergeSection; try { mergeSection = loadGitignoreTemplate(GitignoreTemplates.BROWNFIELD_MERGE); } catch { // Fallback to minimal section mergeSection = ` # ======================================== # AIOS Integration Section # ======================================== # AIOS Local Configuration .aios-core/local/ .aios-core/*.local.yaml .aios-core/logs/ .aios-core/cache/ # ======================================== # End of AIOS Integration Section # ======================================== `; } // Replace date placeholder mergeSection = mergeSection.replace( '{{GENERATED_DATE}}', new Date().toISOString().split('T')[0], ); // Append to existing content const merged = existingContent.trimEnd() + '\n\n' + mergeSection.trim() + '\n'; return merged; } /** * Generates or merges .gitignore for a project * * @param {string} targetDir - Target directory * @param {Object} markers - Detected project markers * @param {Object} [options] - Generation options * @param {boolean} [options.dryRun] - Don't write file, just return content * @param {boolean} [options.merge] - Merge with existing instead of replace * @param {string} [options.projectName] - Project name for header * @returns {Object} Generation result */ function generateGitignoreFile(targetDir, markers, options = {}) { const gitignorePath = path.join(targetDir, '.gitignore'); const existingPath = fs.existsSync(gitignorePath); let content; let mode; if (existingPath && options.merge !== false) { // Brownfield mode - merge with existing const existing = fs.readFileSync(gitignorePath, 'utf8'); content = mergeGitignore(existing, options); mode = 'merged'; } else { // Greenfield mode - generate new content = generateGitignore(markers, options); mode = existingPath ? 'replaced' : 'created'; } const result = { success: true, path: gitignorePath, content, mode, techStacks: detectTechStacks(markers), }; if (!options.dryRun) { try { fs.writeFileSync(gitignorePath, content, 'utf8'); } catch (error) { result.success = false; result.error = error.message; } } return result; } /** * Checks if a .gitignore file has AIOS integration * * @param {string} targetDir - Target directory * @returns {boolean} True if AIOS section exists */ function hasAiosIntegration(targetDir) { const gitignorePath = path.join(targetDir, '.gitignore'); if (!fs.existsSync(gitignorePath)) { return false; } const content = fs.readFileSync(gitignorePath, 'utf8'); return content.includes('AIOS Integration Section') || content.includes('.aios-core/'); } /** * Parses an existing .gitignore into sections * * @param {string} content - .gitignore content * @returns {Object} Parsed sections */ function parseGitignore(content) { const lines = content.split('\n'); const sections = { header: [], patterns: [], aiosSection: null, comments: [], }; let aiosStart = -1; let aiosEnd = -1; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.includes('AIOS Integration Section')) { if (aiosStart === -1) { aiosStart = i; } } if (line.includes('End of AIOS Integration Section')) { aiosEnd = i; } if (line.startsWith('#') && !line.includes('AIOS')) { sections.comments.push({ line: i, content: line }); } if (!line.startsWith('#') && line.trim()) { sections.patterns.push({ line: i, pattern: line.trim() }); } } if (aiosStart !== -1) { sections.aiosSection = { start: aiosStart, end: aiosEnd !== -1 ? aiosEnd : lines.length - 1, }; } return sections; } module.exports = { loadGitignoreTemplate, detectTechStacks, getTemplatesForStacks, generateGitignore, mergeGitignore, generateGitignoreFile, hasAiosIntegration, parseGitignore, GitignoreTemplates, TechStack, TEMPLATES_DIR, };