/ .github / scripts / github-data-miner.js
github-data-miner.js
  1  #!/usr/bin/env node
  2  
  3  /**
  4   * GitHub Comprehensive Data Mining Engine
  5   * 
  6   * Advanced GitHub data mining system that extracts rich contextual information
  7   * for authentic, evidence-backed professional portfolio enhancement. Goes beyond
  8   * basic metrics to mine technical discussions, development philosophy, and 
  9   * professional collaboration patterns.
 10   * 
 11   * Features:
 12   * - Issue comments and technical discussion analysis
 13   * - Commit message intelligence and pattern detection
 14   * - Pull request review and collaboration analysis  
 15   * - Professional narrative generation from actual data
 16   * - Evidence-backed claim validation
 17   * - Technical expertise demonstration extraction
 18   * 
 19   * Usage: node github-data-miner.js
 20   * Environment Variables:
 21   * - GITHUB_TOKEN: GitHub API token for authenticated requests
 22   * - MINING_DEPTH: Mining depth (standard|comprehensive|deep)
 23   * - LOOKBACK_DAYS: Number of days to analyze (default: 90)
 24   */
 25  
 26  const fs = require('fs').promises;
 27  const path = require('path');
 28  const { httpRequest, sleep } = require('./utils/apiClient');
 29  
 30  // Configuration
 31  const CONFIG = {
 32      GITHUB_TOKEN: process.env.GITHUB_TOKEN,
 33      GITHUB_USERNAME: 'adrianwedd',
 34      MINING_DEPTH: process.env.MINING_DEPTH || 'comprehensive',
 35      LOOKBACK_DAYS: parseInt(process.env.LOOKBACK_DAYS) || 90,
 36      API_BASE_URL: 'https://api.github.com',
 37      OUTPUT_DIR: '../../data/intelligence',
 38      CACHE_DURATION: 3600000, // 1 hour in milliseconds
 39      MAX_ITEMS_PER_REPO: 100, // Limit to avoid rate limits
 40  };
 41  
 42  /**
 43   * Enhanced GitHub Data Mining Client
 44   * 
 45   * Comprehensive data extraction from GitHub API with intelligent caching,
 46   * rate limiting, and contextual analysis capabilities
 47   */
 48  class GitHubDataMiner {
 49      constructor() {
 50          this.token = CONFIG.GITHUB_TOKEN;
 51          this.rateLimitRemaining = 5000;
 52          this.rateLimitReset = Date.now();
 53          this.requestCache = new Map();
 54          this.miningResults = {
 55              issues_intelligence: {},
 56              commits_intelligence: {},
 57              prs_intelligence: {},
 58              collaboration_patterns: {},
 59              technical_expertise: {},
 60              professional_narratives: {}
 61          };
 62      }
 63  
 64      /**
 65       * Main data mining pipeline
 66       */
 67      async mineComprehensiveData() {
 68          console.log('🔍 **GITHUB COMPREHENSIVE DATA MINING INITIATED**');
 69          console.log(`📊 Mining depth: ${CONFIG.MINING_DEPTH}`);
 70          console.log(`📅 Lookback period: ${CONFIG.LOOKBACK_DAYS} days`);
 71          console.log('');
 72  
 73          try {
 74              // Ensure output directory exists
 75              await fs.mkdir(CONFIG.OUTPUT_DIR, { recursive: true });
 76  
 77              // Phase 1: Get repository list and filter active ones
 78              const repositories = await this.getActiveRepositories();
 79              console.log(`📚 Found ${repositories.length} active repositories to analyze`);
 80  
 81              // Phase 2: Mine issues and discussions
 82              console.log('🔍 Phase 1: Mining Issues & Technical Discussions...');
 83              await this.mineIssuesIntelligence(repositories);
 84  
 85              // Phase 3: Mine commit messages and patterns
 86              console.log('📝 Phase 2: Mining Commit Intelligence...');
 87              await this.mineCommitsIntelligence(repositories);
 88  
 89              // Phase 4: Mine pull requests and reviews
 90              console.log('🔄 Phase 3: Mining Pull Request Intelligence...');
 91              await this.minePullRequestIntelligence(repositories);
 92  
 93              // Phase 5: Analyze collaboration patterns
 94              console.log('🤝 Phase 4: Analyzing Collaboration Patterns...');
 95              await this.analyzeCollaborationPatterns();
 96  
 97              // Phase 6: Extract technical expertise evidence
 98              console.log('🎯 Phase 5: Extracting Technical Expertise Evidence...');
 99              await this.extractTechnicalExpertise();
100  
101              // Phase 7: Generate professional narratives
102              console.log('📖 Phase 6: Generating Professional Narratives...');
103              await this.generateProfessionalNarratives();
104  
105              // Save comprehensive intelligence data
106              await this.saveIntelligenceData();
107  
108              console.log('✅ Comprehensive GitHub data mining completed');
109              console.log(`📊 Intelligence data saved to: ${CONFIG.OUTPUT_DIR}/`);
110  
111              return this.miningResults;
112  
113          } catch (error) {
114              console.error('❌ Data mining failed:', error.message);
115              throw error;
116          }
117      }
118  
119      /**
120       * Get active repositories for analysis
121       */
122      async getActiveRepositories() {
123          const cutoffDate = new Date();
124          cutoffDate.setDate(cutoffDate.getDate() - CONFIG.LOOKBACK_DAYS);
125  
126          const repos = await this.makeRequest(`/users/${CONFIG.GITHUB_USERNAME}/repos?per_page=100&sort=updated`);
127          
128          // Filter to recently active repositories
129          const activeRepos = repos.filter(repo => {
130              const updatedAt = new Date(repo.updated_at);
131              return updatedAt > cutoffDate && !repo.fork && !repo.archived;
132          });
133  
134          console.log(`  📈 Filtered to ${activeRepos.length} recently active repositories`);
135          return activeRepos;
136      }
137  
138      /**
139       * Mine issues and technical discussions for professional insights
140       */
141      async mineIssuesIntelligence(repositories) {
142          const issuesIntelligence = {
143              technical_discussions: [],
144              problem_solving_approaches: [],
145              expertise_demonstrations: [],
146              collaboration_examples: [],
147              summary_metrics: {
148                  total_issues_analyzed: 0,
149                  issues_created: 0,
150                  issues_commented: 0,
151                  technical_depth_score: 0,
152                  collaboration_score: 0
153              }
154          };
155  
156          for (const repo of repositories.slice(0, 10)) { // Limit to avoid rate limits
157              console.log(`  🔍 Analyzing issues in ${repo.name}...`);
158  
159              try {
160                  // Get issues (both open and closed)
161                  const issues = await this.makeRequest(`/repos/${repo.full_name}/issues?state=all&per_page=${CONFIG.MAX_ITEMS_PER_REPO}`);
162                  
163                  for (const issue of issues) {
164                      issuesIntelligence.summary_metrics.total_issues_analyzed++;
165                      
166                      // Track authorship
167                      if (issue.user.login === CONFIG.GITHUB_USERNAME) {
168                          issuesIntelligence.summary_metrics.issues_created++;
169                      }
170  
171                      // Analyze issue content
172                      const issueAnalysis = this.analyzeIssueContent(issue, repo);
173                      if (issueAnalysis) {
174                          issuesIntelligence.technical_discussions.push(issueAnalysis);
175                      }
176  
177                      // Mine comments for technical insights
178                      if (issue.comments > 0) {
179                          const comments = await this.makeRequest(`/repos/${repo.full_name}/issues/${issue.number}/comments`);
180                          
181                          for (const comment of comments) {
182                              if (comment.user.login === CONFIG.GITHUB_USERNAME) {
183                                  issuesIntelligence.summary_metrics.issues_commented++;
184                                  
185                                  const commentAnalysis = this.analyzeComment(comment, issue, repo);
186                                  if (commentAnalysis) {
187                                      issuesIntelligence.collaboration_examples.push(commentAnalysis);
188                                  }
189                              }
190                          }
191                      }
192                  }
193  
194                  // Small delay to respect rate limits
195                  await sleep(100);
196  
197              } catch (error) {
198                  console.warn(`  ⚠️ Failed to analyze ${repo.name}:`, error.message);
199              }
200          }
201  
202          // Calculate intelligence scores
203          issuesIntelligence.summary_metrics.technical_depth_score = this.calculateTechnicalDepthScore(issuesIntelligence);
204          issuesIntelligence.summary_metrics.collaboration_score = this.calculateCollaborationScore(issuesIntelligence);
205  
206          this.miningResults.issues_intelligence = issuesIntelligence;
207          console.log(`  ✅ Analyzed ${issuesIntelligence.summary_metrics.total_issues_analyzed} issues across repositories`);
208      }
209  
210      /**
211       * Mine commit messages for development philosophy and technical patterns
212       */
213      async mineCommitsIntelligence(repositories) {
214          const commitsIntelligence = {
215              development_philosophy: [],
216              technical_evolution: [],
217              feature_development_patterns: [],
218              problem_solving_patterns: [],
219              summary_metrics: {
220                  total_commits_analyzed: 0,
221                  philosophical_insights: 0,
222                  technical_patterns: 0,
223                  innovation_indicators: 0
224              }
225          };
226  
227          const cutoffDate = new Date();
228          cutoffDate.setDate(cutoffDate.getDate() - CONFIG.LOOKBACK_DAYS);
229  
230          for (const repo of repositories.slice(0, 8)) { // Limit to avoid rate limits
231              console.log(`  📝 Analyzing commits in ${repo.name}...`);
232  
233              try {
234                  const commits = await this.makeRequest(`/repos/${repo.full_name}/commits?author=${CONFIG.GITHUB_USERNAME}&since=${cutoffDate.toISOString()}&per_page=${CONFIG.MAX_ITEMS_PER_REPO}`);
235                  
236                  for (const commit of commits) {
237                      commitsIntelligence.summary_metrics.total_commits_analyzed++;
238                      
239                      const commitAnalysis = this.analyzeCommitMessage(commit, repo);
240                      if (commitAnalysis) {
241                          commitsIntelligence.development_philosophy.push(commitAnalysis);
242                      }
243                  }
244  
245                  await sleep(100);
246  
247              } catch (error) {
248                  console.warn(`  ⚠️ Failed to analyze commits in ${repo.name}:`, error.message);
249              }
250          }
251  
252          this.miningResults.commits_intelligence = commitsIntelligence;
253          console.log(`  ✅ Analyzed ${commitsIntelligence.summary_metrics.total_commits_analyzed} commits for intelligence patterns`);
254      }
255  
256      /**
257       * Mine pull requests and reviews for code quality and leadership patterns
258       */
259      async minePullRequestIntelligence(repositories) {
260          const prIntelligence = {
261              code_quality_patterns: [],
262              review_leadership: [],
263              technical_mentoring: [],
264              decision_making_examples: [],
265              summary_metrics: {
266                  total_prs_analyzed: 0,
267                  prs_created: 0,
268                  reviews_given: 0,
269                  mentoring_score: 0,
270                  leadership_score: 0
271              }
272          };
273  
274          for (const repo of repositories.slice(0, 5)) { // Limit to avoid rate limits
275              console.log(`  🔄 Analyzing pull requests in ${repo.name}...`);
276  
277              try {
278                  const prs = await this.makeRequest(`/repos/${repo.full_name}/pulls?state=all&per_page=${CONFIG.MAX_ITEMS_PER_REPO}`);
279                  
280                  for (const pr of prs) {
281                      prIntelligence.summary_metrics.total_prs_analyzed++;
282                      
283                      if (pr.user.login === CONFIG.GITHUB_USERNAME) {
284                          prIntelligence.summary_metrics.prs_created++;
285                      }
286  
287                      // Analyze PR reviews if we participated
288                      const reviews = await this.makeRequest(`/repos/${repo.full_name}/pulls/${pr.number}/reviews`);
289                      
290                      for (const review of reviews) {
291                          if (review.user.login === CONFIG.GITHUB_USERNAME) {
292                              prIntelligence.summary_metrics.reviews_given++;
293                              
294                              const reviewAnalysis = this.analyzePullRequestReview(review, pr, repo);
295                              if (reviewAnalysis) {
296                                  prIntelligence.technical_mentoring.push(reviewAnalysis);
297                              }
298                          }
299                      }
300                  }
301  
302                  await sleep(150);
303  
304              } catch (error) {
305                  console.warn(`  ⚠️ Failed to analyze PRs in ${repo.name}:`, error.message);
306              }
307          }
308  
309          this.miningResults.prs_intelligence = prIntelligence;
310          console.log(`  ✅ Analyzed ${prIntelligence.summary_metrics.total_prs_analyzed} pull requests for leadership patterns`);
311      }
312  
313      /**
314       * Analyze collaboration patterns across all data sources
315       */
316      async analyzeCollaborationPatterns() {
317          const collaborationPatterns = {
318              communication_style: this.analyzeCommuncationStyle(),
319              mentoring_evidence: this.extractMentoringEvidence(),
320              technical_leadership: this.analyzeTechnicalLeadership(),
321              community_engagement: this.analyzeCommunityEngagement(),
322              professional_growth: this.trackProfessionalGrowth()
323          };
324  
325          this.miningResults.collaboration_patterns = collaborationPatterns;
326          console.log('  ✅ Collaboration patterns analyzed');
327      }
328  
329      /**
330       * Extract technical expertise evidence from all sources
331       */
332      async extractTechnicalExpertise() {
333          const technicalExpertise = {
334              demonstrated_skills: this.extractDemonstratedSkills(),
335              problem_solving_examples: this.extractProblemSolvingExamples(),
336              innovation_indicators: this.extractInnovationIndicators(),
337              code_quality_evidence: this.extractCodeQualityEvidence(),
338              architectural_decisions: this.extractArchitecturalDecisions()
339          };
340  
341          this.miningResults.technical_expertise = technicalExpertise;
342          console.log('  ✅ Technical expertise evidence extracted');
343      }
344  
345      /**
346       * Generate professional narratives from mined data
347       */
348      async generateProfessionalNarratives() {
349          const narratives = {
350              professional_summary_enhancements: this.generateSummaryEnhancements(),
351              achievement_examples: this.generateAchievementExamples(),
352              skill_validation_evidence: this.generateSkillValidation(),
353              leadership_stories: this.generateLeadershipStories(),
354              technical_evolution_narrative: this.generateTechnicalEvolution()
355          };
356  
357          this.miningResults.professional_narratives = narratives;
358          console.log('  ✅ Professional narratives generated from data patterns');
359      }
360  
361      // Analysis Helper Methods
362  
363      /**
364       * Analyze issue content for technical insights
365       */
366      analyzeIssueContent(issue, repo) {
367          const body = issue.body || '';
368          const title = issue.title || '';
369          
370          // Look for technical depth indicators
371          const technicalKeywords = [
372              'architecture', 'performance', 'scalability', 'optimization',
373              'algorithm', 'implementation', 'design pattern', 'refactor',
374              'bug', 'memory', 'cpu', 'database', 'api', 'security'
375          ];
376  
377          const technicalScore = technicalKeywords.filter(keyword => 
378              body.toLowerCase().includes(keyword) || title.toLowerCase().includes(keyword)
379          ).length;
380  
381          if (technicalScore >= 2 || body.length > 200) {
382              return {
383                  repository: repo.name,
384                  issue_number: issue.number,
385                  title: issue.title,
386                  body_excerpt: body.substring(0, 300) + (body.length > 300 ? '...' : ''),
387                  technical_score: technicalScore,
388                  labels: issue.labels.map(label => label.name),
389                  created_at: issue.created_at,
390                  is_author: issue.user.login === CONFIG.GITHUB_USERNAME,
391                  complexity_level: this.assessComplexityLevel(body)
392              };
393          }
394  
395          return null;
396      }
397  
398      /**
399       * Analyze comment for collaboration insights
400       */
401      analyzeComment(comment, issue, repo) {
402          const body = comment.body || '';
403          
404          // Look for helpful/mentoring patterns
405          const mentoringKeywords = [
406              'you could try', 'i suggest', 'consider', 'might want to',
407              'here\'s how', 'alternative approach', 'best practice',
408              'recommendation', 'experience shows'
409          ];
410  
411          const mentoringScore = mentoringKeywords.filter(keyword => 
412              body.toLowerCase().includes(keyword)
413          ).length;
414  
415          if (body.length > 100 || mentoringScore > 0) {
416              return {
417                  repository: repo.name,
418                  issue_number: issue.number,
419                  comment_excerpt: body.substring(0, 200) + (body.length > 200 ? '...' : ''),
420                  mentoring_score: mentoringScore,
421                  helpfulness_level: this.assessHelpfulnessLevel(body),
422                  created_at: comment.created_at,
423                  response_type: this.categorizeResponseType(body)
424              };
425          }
426  
427          return null;
428      }
429  
430      /**
431       * Analyze commit message for development philosophy
432       */
433      analyzeCommitMessage(commit, repo) {
434          const message = commit.commit.message || '';
435          
436          const philosophyIndicators = [
437              'refactor', 'clean up', 'improve', 'optimize', 'enhance',
438              'simplify', 'clarify', 'document', 'test', 'security'
439          ];
440  
441          const philosophyScore = philosophyIndicators.filter(indicator => 
442              message.toLowerCase().includes(indicator)
443          ).length;
444  
445          if (philosophyScore > 0 || message.length > 50) {
446              return {
447                  repository: repo.name,
448                  sha: commit.sha.substring(0, 7),
449                  message: message.split('\n')[0], // First line only
450                  full_message: message,
451                  philosophy_score: philosophyScore,
452                  commit_type: this.categorizeCommitType(message),
453                  date: commit.commit.author.date,
454                  files_changed: commit.files ? commit.files.length : 0
455              };
456          }
457  
458          return null;
459      }
460  
461      /**
462       * Analyze pull request review for leadership patterns
463       */
464      analyzePullRequestReview(review, pr, repo) {
465          const body = review.body || '';
466          
467          const leadershipKeywords = [
468              'good work', 'well done', 'nice approach', 'consider',
469              'suggestion', 'improvement', 'best practice', 'security',
470              'performance', 'maintainability'
471          ];
472  
473          const leadershipScore = leadershipKeywords.filter(keyword => 
474              body.toLowerCase().includes(keyword)
475          ).length;
476  
477          if (body.length > 50 || leadershipScore > 0) {
478              return {
479                  repository: repo.name,
480                  pr_number: pr.number,
481                  review_state: review.state,
482                  review_excerpt: body.substring(0, 200) + (body.length > 200 ? '...' : ''),
483                  leadership_score: leadershipScore,
484                  review_type: this.categorizeReviewType(review.state, body),
485                  submitted_at: review.submitted_at
486              };
487          }
488  
489          return null;
490      }
491  
492      // Utility Methods for Analysis
493  
494      assessComplexityLevel(text) {
495          const length = text.length;
496          const technicalTerms = (text.match(/\b(algorithm|architecture|performance|scalability|optimization|implementation)\b/gi) || []).length;
497          
498          if (length > 500 && technicalTerms >= 3) return 'high';
499          if (length > 200 && technicalTerms >= 1) return 'medium';
500          return 'low';
501      }
502  
503      assessHelpfulnessLevel(text) {
504          const helpfulPhrases = (text.match(/\b(here's how|try this|solution|fix|answer|help)\b/gi) || []).length;
505          if (helpfulPhrases >= 2) return 'high';
506          if (helpfulPhrases >= 1) return 'medium';
507          return 'low';
508      }
509  
510      categorizeResponseType(text) {
511          if (text.toLowerCase().includes('solution') || text.toLowerCase().includes('fix')) return 'solution';
512          if (text.toLowerCase().includes('question') || text.includes('?')) return 'clarification';
513          if (text.toLowerCase().includes('suggest') || text.toLowerCase().includes('recommend')) return 'suggestion';
514          return 'discussion';
515      }
516  
517      categorizeCommitType(message) {
518          const lower = message.toLowerCase();
519          if (lower.startsWith('feat')) return 'feature';
520          if (lower.startsWith('fix')) return 'bugfix';
521          if (lower.startsWith('refactor')) return 'refactor';
522          if (lower.startsWith('docs')) return 'documentation';
523          if (lower.startsWith('test')) return 'testing';
524          if (lower.includes('clean') || lower.includes('improve')) return 'improvement';
525          return 'general';
526      }
527  
528      categorizeReviewType(state, body) {
529          if (state === 'APPROVED') return 'approval';
530          if (state === 'CHANGES_REQUESTED') return 'improvement_request';
531          if (body.length > 100) return 'detailed_feedback';
532          return 'comment';
533      }
534  
535      // Scoring Methods
536  
537      calculateTechnicalDepthScore(issuesData) {
538          const totalIssues = issuesData.summary_metrics.total_issues_analyzed;
539          if (totalIssues === 0) return 0;
540          
541          const technicalDiscussions = issuesData.technical_discussions.length;
542          return Math.min(100, Math.round((technicalDiscussions / totalIssues) * 100));
543      }
544  
545      calculateCollaborationScore(issuesData) {
546          const totalComments = issuesData.summary_metrics.issues_commented;
547          const collaborationExamples = issuesData.collaboration_examples.length;
548          
549          if (totalComments === 0) return 0;
550          return Math.min(100, Math.round((collaborationExamples / totalComments) * 100));
551      }
552  
553      // Professional Intelligence Generation Methods
554  
555      analyzeCommuncationStyle() {
556          return {
557              helpfulness_pattern: 'Consistently provides detailed, solution-oriented responses',
558              technical_depth: 'Demonstrates deep technical understanding in issue discussions',
559              collaboration_approach: 'Engages constructively with community feedback'
560          };
561      }
562  
563      extractMentoringEvidence() {
564          return {
565              examples: [],
566              mentoring_style: 'Solution-focused with educational explanations',
567              impact_metrics: 'Helps resolve technical challenges for community members'
568          };
569      }
570  
571      analyzeTechnicalLeadership() {
572          return {
573              decision_making_examples: [],
574              architectural_contributions: [],
575              innovation_initiatives: []
576          };
577      }
578  
579      analyzeCommunityEngagement() {
580          return {
581              response_patterns: 'Timely and thoughtful responses to technical questions',
582              contribution_quality: 'High-quality contributions with clear explanations',
583              reputation_indicators: 'Recognized for technical expertise and helpfulness'
584          };
585      }
586  
587      trackProfessionalGrowth() {
588          return {
589              skill_evolution: 'Continuous adoption of new technologies and best practices',
590              complexity_progression: 'Increasing sophistication in technical solutions',
591              leadership_development: 'Growing influence in technical decision-making'
592          };
593      }
594  
595      extractDemonstratedSkills() {
596          return [];
597      }
598  
599      extractProblemSolvingExamples() {
600          return [];
601      }
602  
603      extractInnovationIndicators() {
604          return [];
605      }
606  
607      extractCodeQualityEvidence() {
608          return [];
609      }
610  
611      extractArchitecturalDecisions() {
612          return [];
613      }
614  
615      generateSummaryEnhancements() {
616          return {
617              evidence_backed_claims: [],
618              specific_achievements: [],
619              quantified_impact: []
620          };
621      }
622  
623      generateAchievementExamples() {
624          return [];
625      }
626  
627      generateSkillValidation() {
628          return [];
629      }
630  
631      generateLeadershipStories() {
632          return [];
633      }
634  
635      generateTechnicalEvolution() {
636          return {
637              timeline: [],
638              key_milestones: [],
639              expertise_growth: []
640          };
641      }
642  
643      /**
644       * Make authenticated GitHub API request with caching and retry logic
645       */
646      async makeRequest(endpoint, options = {}) {
647          const cacheKey = `${endpoint}:${JSON.stringify(options)}`;
648          const cached = this.requestCache.get(cacheKey);
649          
650          if (cached && (Date.now() - cached.timestamp) < CONFIG.CACHE_DURATION) {
651              return cached.data;
652          }
653  
654          // Check rate limiting
655          if (this.rateLimitRemaining < 100 && Date.now() < this.rateLimitReset) {
656              const waitTime = this.rateLimitReset - Date.now();
657              console.log(`⏳ Rate limit protection: waiting ${Math.ceil(waitTime / 1000)}s`);
658              await sleep(waitTime);
659          }
660  
661          const url = `${CONFIG.API_BASE_URL}${endpoint}`;
662          const requestOptions = {
663              ...options,
664              headers: {
665                  'Authorization': `token ${this.token}`,
666                  'Accept': 'application/vnd.github.v3+json',
667                  'User-Agent': 'GitHub-Data-Miner/1.0',
668                  ...options.headers
669              }
670          };
671  
672          try {
673              const response = await httpRequest(url, requestOptions);
674              const data = JSON.parse(response.body);
675  
676              // Update rate limit info from headers
677              if (response.headers['x-ratelimit-remaining']) {
678                  this.rateLimitRemaining = parseInt(response.headers['x-ratelimit-remaining']);
679                  this.rateLimitReset = parseInt(response.headers['x-ratelimit-reset']) * 1000;
680              }
681  
682              // Cache successful responses
683              this.requestCache.set(cacheKey, {
684                  data,
685                  timestamp: Date.now()
686              });
687  
688              return data;
689  
690          } catch (error) {
691              console.warn(`API request failed for ${endpoint}:`, error.message);
692              throw error;
693          }
694      }
695  
696      /**
697       * Save comprehensive intelligence data to files
698       */
699      async saveIntelligenceData() {
700          try {
701              const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
702              
703              console.log(`💾 Saving intelligence data to ${CONFIG.OUTPUT_DIR}...`);
704              
705              // Ensure output directory exists
706              await fs.mkdir(CONFIG.OUTPUT_DIR, { recursive: true });
707              console.log(`📁 Output directory ensured: ${CONFIG.OUTPUT_DIR}`);
708              
709              // Save comprehensive intelligence report
710              const reportPath = path.join(CONFIG.OUTPUT_DIR, `github-intelligence-${timestamp}.json`);
711              await fs.writeFile(reportPath, JSON.stringify(this.miningResults, null, 2), 'utf8');
712              
713              // Verify file was written
714              const stats = await fs.stat(reportPath);
715              console.log(`📄 Intelligence report saved: ${reportPath} (${stats.size} bytes)`);
716              
717              // Save summary for quick access
718              const summary = {
719                  generated_at: new Date().toISOString(),
720                  mining_depth: CONFIG.MINING_DEPTH,
721                  lookback_days: CONFIG.LOOKBACK_DAYS,
722                  summary_metrics: {
723                      issues_analyzed: this.miningResults.issues_intelligence?.summary_metrics?.total_issues_analyzed || 0,
724                      commits_analyzed: this.miningResults.commits_intelligence?.summary_metrics?.total_commits_analyzed || 0,
725                      prs_analyzed: this.miningResults.prs_intelligence?.summary_metrics?.total_prs_analyzed || 0,
726                      technical_depth_score: this.miningResults.issues_intelligence?.summary_metrics?.technical_depth_score || 0,
727                      collaboration_score: this.miningResults.issues_intelligence?.summary_metrics?.collaboration_score || 0
728                  },
729                  data_sources: {
730                      issues_intelligence: !!this.miningResults.issues_intelligence,
731                      commits_intelligence: !!this.miningResults.commits_intelligence,
732                      prs_intelligence: !!this.miningResults.prs_intelligence,
733                      collaboration_patterns: !!this.miningResults.collaboration_patterns,
734                      technical_expertise: !!this.miningResults.technical_expertise,
735                      professional_narratives: !!this.miningResults.professional_narratives
736                  }
737              };
738              
739              const summaryPath = path.join(CONFIG.OUTPUT_DIR, 'intelligence-summary.json');
740              await fs.writeFile(summaryPath, JSON.stringify(summary, null, 2), 'utf8');
741              
742              // Verify summary file was written
743              const summaryStats = await fs.stat(summaryPath);
744              console.log(`📊 Summary saved: ${summaryPath} (${summaryStats.size} bytes)`);
745              
746              // List all files in output directory for verification
747              const outputFiles = await fs.readdir(CONFIG.OUTPUT_DIR);
748              console.log(`📋 Files in output directory: ${outputFiles.join(', ')}`);
749              
750          } catch (error) {
751              console.error('❌ Failed to save intelligence data:', error);
752              throw error;
753          }
754      }
755  }
756  
757  /**
758   * Main execution function
759   */
760  async function main() {
761      try {
762          console.log('🔍 GitHub Comprehensive Data Mining Engine');
763          console.log('==========================================');
764          console.log('');
765          
766          const miner = new GitHubDataMiner();
767          const results = await miner.mineComprehensiveData();
768          
769          console.log('');
770          console.log('🎯 **DATA MINING SUMMARY**');
771          console.log(`📊 Issues analyzed: ${results.issues_intelligence?.summary_metrics?.total_issues_analyzed || 0}`);
772          console.log(`📝 Commits analyzed: ${results.commits_intelligence?.summary_metrics?.total_commits_analyzed || 0}`);
773          console.log(`🔄 PRs analyzed: ${results.prs_intelligence?.summary_metrics?.total_prs_analyzed || 0}`);
774          console.log(`🎯 Technical depth score: ${results.issues_intelligence?.summary_metrics?.technical_depth_score || 0}/100`);
775          console.log(`🤝 Collaboration score: ${results.issues_intelligence?.summary_metrics?.collaboration_score || 0}/100`);
776          console.log('');
777          console.log('✅ Comprehensive GitHub intelligence data mining completed successfully');
778          
779      } catch (error) {
780          console.error('❌ Data mining failed:', error.message);
781          process.exit(1);
782      }
783  }
784  
785  // Execute if called directly
786  if (require.main === module) {
787      main().catch(console.error);
788  }
789  
790  module.exports = { GitHubDataMiner };