Skip to main content
Glama
analyze-repository.tsβ€’9.65 kB
import { Octokit } from "@octokit/rest"; interface RepositoryStructureArgs { owner: string; repo: string; path?: string; branch?: string; max_depth?: number; } interface FileTreeItem { name: string; path: string; type: 'file' | 'dir'; size?: number; download_url?: string; } export const analyzeRepositoryStructure = { name: "analyze_repository_structure", description: "Analyze the structure and architecture of a GitHub repository. This tool provides a comprehensive overview of the repository's file and folder organization, helping to understand the project layout and architecture.", parameters: { type: "object", properties: { owner: { type: "string", description: "The GitHub username or organization name that owns the repository" }, repo: { type: "string", description: "The name of the GitHub repository" }, path: { type: "string", description: "Optional: Specific path within the repository to analyze (default: root directory)", default: "" }, branch: { type: "string", description: "Optional: Branch name to analyze (default: main/master branch)", default: "main" }, max_depth: { type: "number", description: "Optional: Maximum depth to traverse the directory structure (default: 3, max: 5)", default: 3, minimum: 1, maximum: 5 } }, required: ["owner", "repo"] }, async run(args: RepositoryStructureArgs) { try { // Parameter validation if (!args.owner || !args.repo) { throw new Error("Both 'owner' and 'repo' parameters are required"); } const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }); const maxDepth = Math.min(args.max_depth || 3, 5); const branch = args.branch || "main"; const basePath = args.path || ""; // Get repository information const repoInfo = await octokit.rest.repos.get({ owner: args.owner, repo: args.repo }); // Get repository contents recursively const structure = await getRepositoryStructure( octokit, args.owner, args.repo, basePath, branch, maxDepth ); // Generate analysis report const analysis = generateStructureAnalysis(structure, repoInfo.data); return { content: [{ type: "text", text: analysis }], isError: false }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; return { content: [{ type: "text", text: `❌ **Error analyzing repository structure**\n\n${errorMessage}\n\n**Common issues:**\n- Repository not found or private\n- Invalid branch name\n- Rate limit exceeded (consider adding GITHUB_TOKEN)\n- Network connectivity issues` }], isError: true }; } } }; async function getRepositoryStructure( octokit: Octokit, owner: string, repo: string, path: string, branch: string, maxDepth: number, currentDepth: number = 0 ): Promise<FileTreeItem[]> { if (currentDepth >= maxDepth) { return []; } try { const response = await octokit.rest.repos.getContent({ owner, repo, path, ref: branch }); const contents = Array.isArray(response.data) ? response.data : [response.data]; const structure: FileTreeItem[] = []; for (const item of contents) { if ('type' in item) { const fileItem: FileTreeItem = { name: item.name, path: item.path, type: item.type === 'dir' ? 'dir' : 'file', size: item.size, download_url: item.download_url || undefined }; structure.push(fileItem); // Recursively get subdirectory contents if (item.type === 'dir' && currentDepth < maxDepth - 1) { const subStructure = await getRepositoryStructure( octokit, owner, repo, item.path, branch, maxDepth, currentDepth + 1 ); structure.push(...subStructure); } } } return structure; } catch (error) { console.error(`Error fetching contents for path: ${path}`, error); return []; } } function generateStructureAnalysis(structure: FileTreeItem[], repoData: any): string { const files = structure.filter(item => item.type === 'file'); const directories = structure.filter(item => item.type === 'dir'); // Analyze file types const fileExtensions = new Map<string, number>(); const importantFiles: string[] = []; files.forEach(file => { const ext = file.name.split('.').pop()?.toLowerCase() || 'no-extension'; fileExtensions.set(ext, (fileExtensions.get(ext) || 0) + 1); // Identify important files const importantPatterns = [ 'readme', 'license', 'package.json', 'requirements.txt', 'dockerfile', 'makefile', 'cmake', 'cargo.toml', 'go.mod', 'pom.xml', 'build.gradle', '.gitignore', '.env', 'config', 'settings' ]; if (importantPatterns.some(pattern => file.name.toLowerCase().includes(pattern.toLowerCase()) )) { importantFiles.push(file.name); } }); // Generate tree structure const treeStructure = generateTreeView(structure); // Calculate total size const totalSize = files.reduce((sum, file) => sum + (file.size || 0), 0); const formattedSize = formatBytes(totalSize); return `# πŸ“Š Repository Structure Analysis ## πŸ“‹ Repository Information - **Repository:** ${repoData.full_name} - **Description:** ${repoData.description || 'No description available'} - **Language:** ${repoData.language || 'Not specified'} - **Stars:** ${repoData.stargazers_count} ⭐ - **Forks:** ${repoData.forks_count} 🍴 - **Last Updated:** ${new Date(repoData.updated_at).toLocaleDateString()} ## πŸ“ Directory Structure \`\`\` ${treeStructure} \`\`\` ## πŸ“ˆ Statistics - **Total Files:** ${files.length} - **Total Directories:** ${directories.length} - **Total Size:** ${formattedSize} ## πŸ“„ File Type Distribution ${Array.from(fileExtensions.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([ext, count]) => `- **${ext}:** ${count} files`) .join('\n')} ## πŸ” Important Files Detected ${importantFiles.length > 0 ? importantFiles.map(file => `- \`${file}\``).join('\n') : 'No important configuration files detected in the analyzed depth.'} ## πŸ—οΈ Project Architecture Insights ${generateArchitectureInsights(structure, fileExtensions)} --- *Analysis completed at ${new Date().toLocaleString()}*`; } function generateTreeView(structure: FileTreeItem[]): string { const pathMap = new Map<string, FileTreeItem>(); structure.forEach(item => pathMap.set(item.path, item)); const tree: string[] = []; const processed = new Set<string>(); // Sort by path depth and name const sortedItems = structure.sort((a, b) => { const depthA = a.path.split('/').length; const depthB = b.path.split('/').length; if (depthA !== depthB) return depthA - depthB; return a.path.localeCompare(b.path); }); sortedItems.forEach(item => { if (processed.has(item.path)) return; const depth = item.path.split('/').length - 1; const indent = ' '.repeat(depth); const icon = item.type === 'dir' ? 'πŸ“' : 'πŸ“„'; const sizeInfo = item.type === 'file' && item.size ? ` (${formatBytes(item.size)})` : ''; tree.push(`${indent}${icon} ${item.name}${sizeInfo}`); processed.add(item.path); }); return tree.slice(0, 50).join('\n') + (tree.length > 50 ? '\n... (truncated)' : ''); } function generateArchitectureInsights(structure: FileTreeItem[], fileExtensions: Map<string, number>): string { const insights: string[] = []; // Detect project type if (fileExtensions.has('js') || fileExtensions.has('ts') || fileExtensions.has('json')) { insights.push('🟨 **JavaScript/TypeScript Project** - Modern web development stack detected'); } if (fileExtensions.has('py')) { insights.push('🐍 **Python Project** - Python-based application or library'); } if (fileExtensions.has('java')) { insights.push('β˜• **Java Project** - Enterprise or Android development'); } if (fileExtensions.has('go')) { insights.push('🐹 **Go Project** - High-performance backend service'); } if (fileExtensions.has('rs')) { insights.push('πŸ¦€ **Rust Project** - Systems programming with memory safety'); } // Detect frameworks and tools const hasPackageJson = structure.some(item => item.name === 'package.json'); const hasDockerfile = structure.some(item => item.name.toLowerCase().includes('dockerfile')); const hasReadme = structure.some(item => item.name.toLowerCase().includes('readme')); if (hasPackageJson) insights.push('πŸ“¦ **Node.js Ecosystem** - Uses npm/yarn package management'); if (hasDockerfile) insights.push('🐳 **Containerized** - Docker deployment ready'); if (hasReadme) insights.push('πŸ“š **Well Documented** - README file present'); return insights.length > 0 ? insights.join('\n') : 'No specific architecture patterns detected in the analyzed structure.'; } function formatBytes(bytes: number): string { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/guangxiangdebizi/github-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server