UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

343 lines 15 kB
import { THRESHOLD_LARGE_CODEBASE_FILES } from '../constants.js'; import { ExistingRulesExtractor, } from '../init/existing-rules-extractor.js'; export class AgentsTemplateGenerator { /** * Generate AGENTS.md content based on project analysis */ static generateAgentsMd(analysis, existingRules) { const sections = []; // Header sections.push('# AGENTS.md'); sections.push(''); sections.push(`AI coding agent instructions for **${analysis.projectName}**`); sections.push(''); // Project Overview sections.push('## Project Overview'); sections.push(''); if (analysis.description) { sections.push(analysis.description); sections.push(''); } sections.push(`**Project Type:** ${analysis.projectType}`); if (analysis.languages.primary) { sections.push(`**Primary Language:** ${analysis.languages.primary.name} (${analysis.languages.primary.percentage}% of codebase)`); } if (analysis.languages.secondary.length > 0) { const secondaryLangs = analysis.languages.secondary .map(lang => `${lang.name} (${lang.percentage}%)`) .join(', '); sections.push(`**Secondary Languages:** ${secondaryLangs}`); } sections.push(''); // Architecture Summary if (analysis.dependencies.frameworks.length > 0 || analysis.structure.importantDirectories.length > 0) { sections.push('## Architecture'); sections.push(''); if (analysis.dependencies.frameworks.length > 0) { sections.push('**Key Frameworks & Libraries:**'); for (const framework of analysis.dependencies.frameworks) { const version = framework.version ? ` (${framework.version})` : ''; sections.push(`- ${framework.name}${version} - ${framework.category}`); } sections.push(''); } if (analysis.structure.importantDirectories.length > 0) { sections.push('**Project Structure:**'); for (const dir of analysis.structure.importantDirectories.slice(0, 20)) { sections.push(`- \`${dir}/\` - ${this.getDirectoryDescription(dir)}`); } sections.push(''); } } // Key Files if (Object.values(analysis.keyFiles).some(files => files.length > 0)) { sections.push('## Key Files'); sections.push(''); if (analysis.keyFiles.config.length > 0) { sections.push('**Configuration:**'); analysis.keyFiles.config.slice(0, 5).forEach(file => { sections.push(`- \`${file}\` - ${this.getFileDescription(file)}`); }); sections.push(''); } if (analysis.keyFiles.documentation.length > 0) { sections.push('**Documentation:**'); analysis.keyFiles.documentation.slice(0, 3).forEach(file => { sections.push(`- \`${file}\``); }); sections.push(''); } } // Build and Test Commands if (Object.keys(analysis.buildCommands).length > 0) { sections.push('## Development Commands'); sections.push(''); for (const [action, command] of Object.entries(analysis.buildCommands)) { sections.push(`**${action}:**`); sections.push('```bash'); sections.push(command); sections.push('```'); sections.push(''); } } // Code Style Guidelines const conventions = this.getCodingConventions(analysis); if (conventions.length > 0) { sections.push('## Code Style Guidelines'); sections.push(''); for (const convention of conventions) { sections.push(`- ${convention}`); } sections.push(''); } // Testing Instructions if (analysis.dependencies.testingFrameworks.length > 0 || analysis.keyFiles.test.length > 0) { sections.push('## Testing'); sections.push(''); if (analysis.dependencies.testingFrameworks.length > 0) { sections.push(`**Testing Frameworks:** ${analysis.dependencies.testingFrameworks.join(', ')}`); sections.push(''); } if (analysis.buildCommands.Test) { sections.push('**Run Tests:**'); sections.push('```bash'); sections.push(analysis.buildCommands.Test); sections.push('```'); sections.push(''); } if (analysis.keyFiles.test.length > 0) { sections.push('**Test Files:**'); analysis.keyFiles.test.slice(0, 5).forEach(file => { sections.push(`- \`${file}\``); }); sections.push(''); } } // Existing AI rules and guidelines if (existingRules && existingRules.length > 0) { const mergedRules = ExistingRulesExtractor.mergeExistingRules(existingRules); if (mergedRules.trim()) { sections.push(mergedRules); sections.push(''); } } // Special Considerations for AI sections.push('## AI Coding Assistance Notes'); sections.push(''); sections.push('**Important Considerations:**'); // Language-specific notes if (analysis.languages.primary) { const langNotes = this.getLanguageSpecificNotes(analysis.languages.primary.name); langNotes.forEach(note => sections.push(`- ${note}`)); } // Framework-specific notes for (const framework of analysis.dependencies.frameworks.slice(0, 3)) { const frameworkNotes = this.getFrameworkSpecificNotes(framework.name); frameworkNotes.forEach(note => sections.push(`- ${note}`)); } // General notes sections.push(`- Project has ${analysis.structure.scannedFiles} files across ${analysis.structure.directories.length} directories`); if (analysis.structure.scannedFiles >= THRESHOLD_LARGE_CODEBASE_FILES) { sections.push('- Large codebase: Focus on specific areas when making changes'); } if (analysis.keyFiles.build.length > 0) { sections.push('- Check build configuration files before making structural changes'); } sections.push(''); // Repository info if (analysis.repository) { sections.push('## Repository'); sections.push(''); sections.push(`**Source:** ${analysis.repository}`); sections.push(''); } // Footer sections.push('---'); sections.push(''); sections.push('*This AGENTS.md file was generated by Nanocoder. Update it as your project evolves.*'); return sections.join('\n'); } /** * Get description for a directory based on its name */ static getDirectoryDescription(dir) { const dirName = dir.split('/').pop()?.toLowerCase() || ''; const descriptions = { src: 'Source code', source: 'Source code', app: 'Application code', lib: 'Library code', libs: 'Libraries', components: 'React/UI components', pages: 'Page components', views: 'View components', routes: 'Routing logic', api: 'API endpoints', server: 'Server code', backend: 'Backend code', frontend: 'Frontend code', models: 'Data models', controllers: 'Controllers', services: 'Service layer', utils: 'Utility functions', config: 'Configuration files', configs: 'Configuration files', settings: 'Settings', assets: 'Static assets', static: 'Static files', public: 'Public assets', docs: 'Documentation', documentation: 'Documentation', test: 'Test files', tests: 'Test files', __tests__: 'Test files', spec: 'Test specifications', }; return descriptions[dirName] || 'Project files'; } /** * Get description for a file based on its name */ static getFileDescription(file) { const fileName = file.toLowerCase(); if (fileName.includes('package.json')) return 'Node.js dependencies and scripts'; if (fileName.includes('cargo.toml')) return 'Rust package configuration'; if (fileName.includes('go.mod')) return 'Go module dependencies'; if (fileName.includes('requirements.txt')) return 'Python dependencies'; if (fileName.includes('tsconfig.json')) return 'TypeScript configuration'; if (fileName.includes('webpack')) return 'Webpack build configuration'; if (fileName.includes('vite')) return 'Vite build configuration'; if (fileName.includes('rollup')) return 'Rollup build configuration'; if (fileName.includes('docker')) return 'Docker configuration'; if (fileName.includes('makefile')) return 'Build automation'; return 'Configuration file'; } /** * Get coding conventions based on project analysis */ static getCodingConventions(analysis) { const conventions = []; if (analysis.languages.primary) { const lang = analysis.languages.primary.name; switch (lang) { case 'JavaScript': case 'TypeScript': conventions.push('Use camelCase for variables and functions'); conventions.push('Use PascalCase for classes and components'); conventions.push('Prefer const/let over var'); conventions.push('Use async/await over callbacks when possible'); if (analysis.dependencies.frameworks.some((f) => f.name.includes('React'))) { conventions.push('Use functional components with hooks'); conventions.push('Follow React naming conventions for components'); } break; case 'Python': conventions.push('Follow PEP 8 style guide'); conventions.push('Use snake_case for variables and functions'); conventions.push('Use PascalCase for classes'); conventions.push('Include docstrings for functions and classes'); conventions.push('Use type hints where appropriate'); break; case 'Rust': conventions.push('Follow Rust naming conventions (snake_case)'); conventions.push('Use cargo fmt for code formatting'); conventions.push('Handle errors explicitly with Result<T, E>'); conventions.push('Prefer owned types over references when possible'); break; case 'Go': conventions.push('Follow Go naming conventions'); conventions.push('Use gofmt for formatting'); conventions.push('Handle errors explicitly'); conventions.push('Use interfaces for abstraction'); conventions.push('Keep functions and methods concise'); break; case 'Java': conventions.push('Follow Java naming conventions'); conventions.push('Use camelCase for methods and variables'); conventions.push('Use PascalCase for classes'); conventions.push('Include JavaDoc for public methods'); break; } } return conventions; } /** * Get language-specific notes for AI assistance */ static getLanguageSpecificNotes(language) { switch (language) { case 'JavaScript': case 'TypeScript': return [ 'Check package.json for available scripts before running commands', 'Be aware of Node.js version requirements', 'Consider impact on bundle size when adding dependencies', ]; case 'Python': return [ 'Check virtual environment setup before running commands', 'Be mindful of Python version compatibility', 'Follow import organization (stdlib, third-party, local)', ]; case 'Rust': return [ 'Run cargo check before cargo build for faster feedback', 'Consider memory safety and ownership when suggesting changes', 'Use cargo clippy for additional linting', ]; case 'Go': return [ 'Run go mod tidy after adding dependencies', 'Consider goroutine usage for concurrent operations', 'Follow Go idioms for error handling', ]; default: return [ 'Check project documentation for specific conventions', 'Test changes thoroughly before committing', ]; } } /** * Get framework-specific notes for AI assistance */ static getFrameworkSpecificNotes(framework) { switch (framework) { case 'React': return [ 'Follow React hooks best practices', 'Consider component reusability when creating new components', ]; case 'Next.js': return [ 'Be aware of SSR/SSG implications when making changes', 'Check routing structure before adding new pages', ]; case 'Django': return [ 'Run migrations after model changes', 'Follow Django project structure conventions', ]; case 'Express.js': return [ 'Consider middleware order when adding new routes', 'Use proper error handling middleware', ]; default: return []; } } } //# sourceMappingURL=agents-template-generator.js.map