agp-cli
Version:
Agentic Programming Project CLI - Standardized knowledge layer for AI-assisted development
455 lines (433 loc) • 15 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyzeProject = analyzeProject;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
async function analyzeProject(projectPath, projectInfo) {
const agpPath = path.join(projectPath, '.agp');
// Find all source files
const sourceFiles = await findSourceFiles(projectPath);
// Generate architecture documentation
await generateArchitectureFiles(agpPath, projectInfo, sourceFiles);
// Generate pattern documentation
await generatePatternFiles(agpPath, projectInfo, sourceFiles);
// Generate initial knowledge files for existing source files
for (const file of sourceFiles.slice(0, 10)) {
// Limit to first 10 files for demo
await generateSourceFileKnowledge(agpPath, file);
}
// Additional files can be processed as needed
}
async function findSourceFiles(projectPath) {
const sourceFiles = [];
const extensions = [
// JavaScript/TypeScript
'.ts',
'.tsx',
'.js',
'.jsx',
'.mjs',
'.cjs',
// Web
'.vue',
'.svelte',
'.html',
'.css',
'.scss',
'.sass',
'.less',
// Backend languages
'.py',
'.go',
'.rs',
'.java',
'.kt',
'.scala',
'.rb',
'.php',
'.cs',
'.fs',
'.vb',
// Systems programming
'.c',
'.cpp',
'.cc',
'.cxx',
'.h',
'.hpp',
'.hxx',
// Functional
'.hs',
'.elm',
'.clj',
'.cljs',
'.ml',
'.mli',
// Data/Config
'.sql',
'.json',
'.yaml',
'.yml',
'.toml',
'.xml',
// Mobile
'.swift',
'.dart',
'.m',
'.mm',
// Shell/Scripts
'.sh',
'.bash',
'.zsh',
'.ps1',
'.py',
'.pl',
// Other
'.r',
'.jl',
'.nim',
'.zig',
'.odin',
];
const excludeDirs = [
'node_modules',
'dist',
'build',
'.git',
'.agp',
'coverage',
'target',
'bin',
'obj',
'__pycache__',
'.venv',
'venv',
'vendor',
'.idea',
'.vscode',
'tmp',
'temp',
];
async function scanDirectory(dirPath) {
try {
const items = await fs.readdir(dirPath);
for (const item of items) {
const fullPath = path.join(dirPath, item);
const stat = await fs.stat(fullPath);
if (stat.isDirectory() && !excludeDirs.includes(item)) {
await scanDirectory(fullPath);
}
else if (stat.isFile()) {
const ext = path.extname(item);
if (extensions.includes(ext)) {
const relativePath = path.relative(projectPath, fullPath);
sourceFiles.push({
path: fullPath,
relativePath,
type: determineFileType(relativePath),
language: determineLanguage(ext),
});
}
}
}
}
catch (error) {
// Skip directories we can't read
}
}
await scanDirectory(projectPath);
return sourceFiles;
}
function determineFileType(filePath) {
const segments = filePath.toLowerCase().split('/');
const fileName = path.basename(filePath, path.extname(filePath)).toLowerCase();
const ext = path.extname(filePath).toLowerCase();
// Test files
if (fileName.includes('test') ||
fileName.includes('spec') ||
segments.includes('__tests__') ||
segments.includes('tests') ||
segments.includes('test') ||
segments.includes('spec'))
return 'test';
// Config files
if (fileName.includes('config') ||
fileName === 'index' ||
['.json', '.yaml', '.yml', '.toml', '.xml'].includes(ext) ||
segments.includes('config') ||
segments.includes('configuration'))
return 'config';
// Documentation
if (['.md', '.rst', '.txt'].includes(ext) ||
segments.includes('docs') ||
segments.includes('doc') ||
segments.includes('documentation'))
return 'docs';
// Build/deployment related
if (segments.includes('build') ||
segments.includes('scripts') ||
segments.includes('deploy') ||
segments.includes('ci') ||
fileName.includes('docker') ||
fileName.includes('make'))
return 'build';
// Everything else is source code
return 'source';
}
function determineLanguage(extension) {
const langMap = {
// JavaScript/TypeScript
'.ts': 'typescript',
'.tsx': 'typescript',
'.js': 'javascript',
'.jsx': 'javascript',
'.mjs': 'javascript',
'.cjs': 'javascript',
// Web
'.vue': 'vue',
'.svelte': 'svelte',
'.html': 'html',
'.css': 'css',
'.scss': 'scss',
'.sass': 'sass',
'.less': 'less',
// Backend
'.py': 'python',
'.go': 'go',
'.rs': 'rust',
'.java': 'java',
'.kt': 'kotlin',
'.scala': 'scala',
'.rb': 'ruby',
'.php': 'php',
'.cs': 'csharp',
'.fs': 'fsharp',
'.vb': 'vbnet',
// Systems
'.c': 'c',
'.cpp': 'cpp',
'.cc': 'cpp',
'.cxx': 'cpp',
'.h': 'c',
'.hpp': 'cpp',
'.hxx': 'cpp',
// Functional
'.hs': 'haskell',
'.elm': 'elm',
'.clj': 'clojure',
'.cljs': 'clojurescript',
'.ml': 'ocaml',
'.mli': 'ocaml',
// Data/Config
'.json': 'json',
'.yaml': 'yaml',
'.yml': 'yaml',
'.toml': 'toml',
'.xml': 'xml',
'.sql': 'sql',
// Mobile
'.swift': 'swift',
'.dart': 'dart',
'.m': 'objectivec',
'.mm': 'objectivec',
// Shell/Scripts
'.sh': 'shell',
'.bash': 'bash',
'.zsh': 'zsh',
'.ps1': 'powershell',
'.pl': 'perl',
// Other
'.r': 'r',
'.jl': 'julia',
'.nim': 'nim',
'.zig': 'zig',
'.odin': 'odin',
};
return langMap[extension.toLowerCase()] || 'other';
}
async function generateArchitectureFiles(agpPath, projectInfo, sourceFiles) {
const architecturePath = path.join(agpPath, 'architecture');
// Generate feature domains based on directory structure
const domains = extractFeatureDomains(sourceFiles);
const featureDomainsContent = generateFeatureDomainsContent(domains, projectInfo);
await fs.writeFile(path.join(architecturePath, 'feature-domains.md'), featureDomainsContent);
// Generate project overview
const projectOverviewContent = generateProjectOverviewContent(projectInfo, sourceFiles);
await fs.writeFile(path.join(architecturePath, 'project-overview.md'), projectOverviewContent);
// Architecture documentation completed
}
async function generatePatternFiles(agpPath, projectInfo, sourceFiles) {
const patternsPath = path.join(agpPath, 'patterns');
// Generate code organization patterns based on detected directory structure
const sourceCodeFiles = sourceFiles.filter((f) => f.type === 'source');
if (sourceCodeFiles.length > 0) {
const codePatternContent = generateCodeOrganizationPatternContent(sourceCodeFiles, projectInfo);
await fs.writeFile(path.join(patternsPath, 'code-organization-patterns.md'), codePatternContent);
}
// Generate testing patterns if test files are found
const testFiles = sourceFiles.filter((f) => f.type === 'test');
if (testFiles.length > 0) {
const testPatternContent = generateTestPatternContent(testFiles);
await fs.writeFile(path.join(patternsPath, 'testing-patterns.md'), testPatternContent);
}
// Pattern documentation completed
}
function extractFeatureDomains(sourceFiles) {
const domains = {};
sourceFiles.forEach((file) => {
const segments = file.relativePath.split('/');
if (segments.length > 2) {
const domain = segments[1]; // e.g., src/auth/... -> auth
if (domain) {
if (!domains[domain])
domains[domain] = [];
domains[domain].push(file.relativePath);
}
}
});
return domains;
}
function generateFeatureDomainsContent(domains, projectInfo) {
return `# Feature Domains
This document maps the feature domains in this ${projectInfo.type} project.
${Object.keys(domains)
.map((domain) => {
const domainFiles = domains[domain] || [];
const firstFile = domainFiles[0];
const knowledgePath = firstFile ? firstFile.split('/').slice(0, -1).join('/') : domain;
return `
## ${domain.charAt(0).toUpperCase() + domain.slice(1)}
- **Files**: ${domainFiles.slice(0, 3).join(', ')}${domainFiles.length > 3 ? ` and ${domainFiles.length - 3} more` : ''}
- **Knowledge**: .agp/project/${knowledgePath}/
`;
})
.join('')}
## Adding New Features
When adding new features, follow the existing domain structure and create corresponding knowledge files in .agp/project/.
`;
}
function generateProjectOverviewContent(projectInfo, sourceFiles) {
const fileTypes = sourceFiles.reduce((acc, file) => {
acc[file.type] = (acc[file.type] || 0) + 1;
return acc;
}, {});
return `# Project Overview
## Project Type
- **Framework**: ${projectInfo.framework || projectInfo.type}
- **Build Tool**: ${projectInfo.buildTool || 'Not detected'}
- **Package Manager**: ${projectInfo.hasPackageJson ? 'Detected' : 'None'}
## File Structure
${Object.entries(fileTypes)
.map(([type, count]) => `- **${type}**: ${count} files`)
.join('\n')}
## Architecture Notes
This is a ${projectInfo.type} project with ${sourceFiles.length} source files organized into a standard directory structure.
## Key Directories
${Array.from(new Set(sourceFiles.map((f) => f.relativePath.split('/')[1]).filter(Boolean)))
.map((dir) => `- \`${dir}/\` - Contains ${sourceFiles.filter((f) => f.relativePath.includes(dir || '')).length} files`)
.join('\n')}
`;
}
function generateCodeOrganizationPatternContent(sourceFiles, projectInfo) {
const languages = [...new Set(sourceFiles.map((f) => f.language))];
const directories = [...new Set(sourceFiles.map((f) => f.relativePath.split('/')[1]).filter(Boolean))];
return `# Code Organization Patterns
This document describes the code organization patterns used in this ${projectInfo.type} project.
## Languages Used
${languages.map((lang) => `- **${lang}**: ${sourceFiles.filter((f) => f.language === lang).length} files`).join('\n')}
## Directory Structure
${directories.map((dir) => `- **${dir}/**: ${sourceFiles.filter((f) => f.relativePath.includes(dir || '')).length} files`).join('\n')}
## File Naming Patterns
Common file naming patterns found:
${sourceFiles
.slice(0, 10)
.map((f) => `- \`${path.basename(f.relativePath)}\` (${f.language})`)
.join('\n')}
## Best Practices
- Follow the established directory structure
- Use consistent naming conventions across similar files
- Keep related functionality grouped in the same directories
- Document module purposes and dependencies in AGP knowledge files
`;
}
function generateTestPatternContent(testFiles) {
const testDirs = [...new Set(testFiles.map((f) => f.relativePath.split('/').slice(0, -1).join('/')))];
const testLanguages = [...new Set(testFiles.map((f) => f.language))];
return `# Testing Patterns
This document describes the testing patterns used in this project.
## Test Structure
- **Total Test Files**: ${testFiles.length}
- **Languages**: ${testLanguages.join(', ')}
## Test Locations
${testDirs.map((dir) => `- \`${dir}/\``).join('\n')}
## Test Files
${testFiles
.slice(0, 10)
.map((f) => `- \`${f.relativePath}\` (${f.language})`)
.join('\n')}
## Best Practices
- Keep tests close to the code they test
- Use consistent naming conventions for test files
- Follow the established test directory structure
- Document test scenarios and coverage expectations
`;
}
async function generateSourceFileKnowledge(agpPath, file) {
const knowledgePath = path.join(agpPath, 'project', `${file.relativePath}.md`);
const knowledgeDir = path.dirname(knowledgePath);
await fs.ensureDir(knowledgeDir);
const content = `# ${path.basename(file.relativePath)}
## Purpose
- ${file.type.charAt(0).toUpperCase() + file.type.slice(1)} file in ${file.language}
- Auto-generated knowledge file - update with specific implementation details
## Dependencies
- **Input**: (Files this depends on)
- **Output**: (Files that depend on this)
- **External**: (Third-party libraries)
## Context
- Located in \`${path.dirname(file.relativePath)}\`
- Type: ${file.type}
- Language: ${file.language}
## Gotchas
- (Add specific gotchas after implementation)
## Related
- **Patterns**: .agp/patterns/${file.type}-patterns.md
- **Architecture**: .agp/architecture/feature-domains.md
- **Similar**: (Add links to similar files)
`;
await fs.writeFile(knowledgePath, content);
}
//# sourceMappingURL=project-analyzer.js.map