Skip to main content
Glama
index.tsβ€’10.2 kB
import { GitHubConfig, ToolResponse, GitHubIssue } from '../../shared/types.js'; import { GitHubUtils } from '../../shared/utils.js'; export async function analyzeTaskComplexity(config: GitHubConfig, args: any): Promise<ToolResponse> { try { GitHubUtils.validateRepoConfig(config); const response = await config.octokit.rest.issues.get({ owner: config.owner, repo: config.repo, issue_number: args.issue_number }); const issue = response.data as GitHubIssue; const complexity = GitHubUtils.analyzeIssueComplexity(issue); const priority = GitHubUtils.calculateIssuePriority(issue); let result = `🧠 **AI-Powered Task Complexity Analysis**\n\n`; result += `**Issue:** #${issue.number} - ${issue.title}\n\n`; result += `πŸ“Š **Complexity Score:** ${complexity}/8 story points\n`; result += `🎯 **Priority Level:** ${priority}/5\n\n`; // Complexity breakdown result += `**Complexity Analysis:**\n`; if (issue.title.split(' ').length > 10) { result += `β€’ πŸ“ Complex title (${issue.title.split(' ').length} words)\n`; } if (issue.body) { if (issue.body.length > 1000) { result += `β€’ πŸ“„ Extensive description (${issue.body.length} characters)\n`; } const technicalKeywords = ['API', 'database', 'migration', 'refactor', 'architecture', 'integration', 'security']; const foundKeywords = technicalKeywords.filter(keyword => issue.body!.toLowerCase().includes(keyword.toLowerCase()) ); if (foundKeywords.length > 0) { result += `β€’ πŸ”§ Technical keywords found: ${foundKeywords.join(', ')}\n`; } if (issue.body.includes('#')) { result += `β€’ πŸ”— Contains issue references (potential dependencies)\n`; } } const complexityLabels = issue.labels.filter(label => ['epic', 'large', 'complex', 'research', 'spike'].some(keyword => label.name.toLowerCase().includes(keyword) ) ); if (complexityLabels.length > 0) { result += `β€’ 🏷️ Complexity labels: ${complexityLabels.map(l => l.name).join(', ')}\n`; } result += `\n**Priority Factors:**\n`; const priorityLabels = issue.labels.filter(label => ['critical', 'high', 'medium', 'low'].some(keyword => label.name.toLowerCase().includes(keyword) ) ); if (priorityLabels.length > 0) { result += `β€’ 🚨 Priority labels: ${priorityLabels.map(l => l.name).join(', ')}\n`; } const isBug = issue.labels.some(label => label.name.toLowerCase().includes('bug') ); if (isBug) { result += `β€’ πŸ› Bug fix (priority boost applied)\n`; } const daysSinceUpdate = Math.floor( (Date.now() - new Date(issue.updated_at).getTime()) / (1000 * 60 * 60 * 24) ); if (daysSinceUpdate < 7) { result += `β€’ ⚑ Recently updated (${daysSinceUpdate} days ago)\n`; } result += `\n**Recommendations:**\n`; if (complexity >= 6) { result += `β€’ ⚠️ High complexity - consider breaking into smaller tasks\n`; } if (priority >= 4) { result += `β€’ πŸ”₯ High priority - should be addressed soon\n`; } if (!issue.assignees || issue.assignees.length === 0) { result += `β€’ πŸ‘€ No assignee - should be assigned to a team member\n`; } if (complexity >= 4 && (!issue.assignees || issue.assignees.length === 0)) { result += `β€’ 🎯 Complex unassigned issue - needs experienced developer\n`; } return GitHubUtils.createSuccessResponse(result); } catch (error) { return GitHubUtils.createErrorResponse(new Error(`Failed to analyze task complexity: ${(error as Error).message}`)); } } export async function getRepositorySummary(config: GitHubConfig, args: any): Promise<ToolResponse> { try { GitHubUtils.validateRepoConfig(config); // Get repository info const repoResponse = await config.octokit.rest.repos.get({ owner: config.owner, repo: config.repo }); // Get issues const issuesResponse = await config.octokit.rest.issues.listForRepo({ owner: config.owner, repo: config.repo, state: 'all', per_page: 100 }); // Get milestones const milestonesResponse = await config.octokit.rest.issues.listMilestones({ owner: config.owner, repo: config.repo, state: 'all', per_page: 100 }); // Get labels const labelsResponse = await config.octokit.rest.issues.listLabelsForRepo({ owner: config.owner, repo: config.repo, per_page: 100 }); const repo = repoResponse.data; const allIssues = issuesResponse.data.filter(issue => !issue.pull_request); const milestones = milestonesResponse.data; const labels = labelsResponse.data; // Calculate metrics const openIssues = allIssues.filter(issue => issue.state === 'open'); const closedIssues = allIssues.filter(issue => issue.state === 'closed'); const bugIssues = allIssues.filter(issue => issue.labels.some((label: any) => label.name.toLowerCase().includes('bug')) ); const featureIssues = allIssues.filter(issue => issue.labels.some((label: any) => label.name.toLowerCase().includes('feature') || label.name.toLowerCase().includes('enhancement') ) ); // Recent activity (last 7 days) const weekAgo = new Date(); weekAgo.setDate(weekAgo.getDate() - 7); const recentIssues = allIssues.filter(issue => new Date(issue.updated_at) >= weekAgo ); // Overdue milestones const today = new Date(); const overdueMilestones = milestones.filter(milestone => milestone.state === 'open' && milestone.due_on && new Date(milestone.due_on) < today ); // Calculate health score let healthScore = 100; // Penalty for high ratio of open issues const openRatio = allIssues.length > 0 ? (openIssues.length / allIssues.length) : 0; if (openRatio > 0.8) healthScore -= 30; else if (openRatio > 0.6) healthScore -= 20; else if (openRatio > 0.4) healthScore -= 10; // Penalty for overdue milestones if (overdueMilestones.length > 0) healthScore -= (overdueMilestones.length * 15); // Penalty for old open issues (older than 30 days) const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); const oldIssues = openIssues.filter(issue => new Date(issue.created_at) < thirtyDaysAgo ); if (oldIssues.length > 5) healthScore -= 20; // Bonus for recent activity if (recentIssues.length > 5) healthScore += 10; healthScore = Math.max(0, Math.min(100, healthScore)); let result = `πŸ“ˆ **Repository Summary: ${repo.full_name}**\n\n`; result += `**Repository Overview:**\n`; result += `β€’ πŸ“… Created: ${new Date(repo.created_at).toLocaleDateString()}\n`; result += `β€’ πŸ’» Language: ${repo.language || 'Not specified'}\n`; result += `β€’ ⭐ Stars: ${repo.stargazers_count}\n`; result += `β€’ 🍴 Forks: ${repo.forks_count}\n`; result += `β€’ πŸ‘€ Watchers: ${repo.watchers_count}\n\n`; result += `**Issues Analysis:**\n`; result += `β€’ πŸ“Š Total Issues: ${allIssues.length}\n`; result += `β€’ 🟒 Open: ${openIssues.length} (${Math.round((openIssues.length / allIssues.length) * 100) || 0}%)\n`; result += `β€’ βœ… Closed: ${closedIssues.length} (${Math.round((closedIssues.length / allIssues.length) * 100) || 0}%)\n`; result += `β€’ πŸ› Bugs: ${bugIssues.length}\n`; result += `β€’ ✨ Features: ${featureIssues.length}\n\n`; result += `**Milestones:**\n`; result += `β€’ 🎯 Total: ${milestones.length}\n`; result += `β€’ 🟒 Open: ${milestones.filter(m => m.state === 'open').length}\n`; result += `β€’ βœ… Completed: ${milestones.filter(m => m.state === 'closed').length}\n`; if (overdueMilestones.length > 0) { result += `β€’ ⚠️ Overdue: ${overdueMilestones.length}\n`; } result += `\n`; result += `**Labels & Organization:**\n`; result += `β€’ 🏷️ Total Labels: ${labels.length}\n\n`; result += `**Activity Trends:**\n`; result += `β€’ ⚑ Active Issues (7 days): ${recentIssues.length}\n`; const mostUsedLabels = labels .map(label => ({ name: label.name, count: allIssues.filter(issue => issue.labels.some((l: any) => l.name === label.name) ).length })) .filter(label => label.count > 0) .sort((a, b) => b.count - a.count) .slice(0, 3); if (mostUsedLabels.length > 0) { result += `β€’ πŸ† Most Used Labels: ${mostUsedLabels.map(l => `${l.name} (${l.count})`).join(', ')}\n`; } result += `\n`; // Health score let healthEmoji = 'πŸ’š'; let healthStatus = 'Excellent'; if (healthScore < 70) { healthEmoji = '🟑'; healthStatus = 'Good'; } if (healthScore < 50) { healthEmoji = '🟠'; healthStatus = 'Needs Attention'; } if (healthScore < 30) { healthEmoji = 'πŸ”΄'; healthStatus = 'Critical'; } result += `**Repository Health:** ${healthEmoji} ${healthScore}/100 (${healthStatus})\n\n`; result += `**Recommendations:**\n`; if (overdueMilestones.length > 0) { result += `β€’ ⚠️ Address ${overdueMilestones.length} overdue milestone(s)\n`; } if (openRatio > 0.6) { result += `β€’ πŸ“ High number of open issues - consider triage and prioritization\n`; } if (oldIssues.length > 5) { result += `β€’ πŸ•°οΈ ${oldIssues.length} issues are older than 30 days - review and update\n`; } if (recentIssues.length === 0) { result += `β€’ πŸ“ˆ No recent activity - consider project status review\n`; } if (milestones.length === 0) { result += `β€’ 🎯 No milestones defined - consider adding project milestones\n`; } return GitHubUtils.createSuccessResponse(result); } catch (error) { return GitHubUtils.createErrorResponse(new Error(`Failed to get repository summary: ${(error as Error).message}`)); } }

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/Faresabdelghany/github-project-manager-mcp'

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