@ankh-studio/ai-enablement 1.0.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli/index.js +3095 -112
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -15904,8 +15904,10 @@ ${this.generateMitigationStrategies(recommendations)}
15904
15904
  class BasePersona {
15905
15905
  personaConfig;
15906
15906
  personaMetrics;
15907
- constructor(config) {
15907
+ adversarialConfig;
15908
+ constructor(config, adversarialConfig) {
15908
15909
  this.personaConfig = config;
15910
+ this.adversarialConfig = adversarialConfig;
15909
15911
  this.personaMetrics = {
15910
15912
  insightsGenerated: 0,
15911
15913
  averageConfidence: 0,
@@ -15997,6 +15999,40 @@ class BasePersona {
15997
15999
  getPerspective() {
15998
16000
  return this.personaConfig.description;
15999
16001
  }
16002
+ getRuntimeWeights() {
16003
+ return this.adversarialConfig?.runtime_weights || {};
16004
+ }
16005
+ getInsightLogic() {
16006
+ return this.adversarialConfig?.insight_generation_logic || {
16007
+ trigger_conditions: [],
16008
+ perspective_shifters: [],
16009
+ evidence_pattern: "",
16010
+ priority_order: []
16011
+ };
16012
+ }
16013
+ getEmpathyMap() {
16014
+ return this.adversarialConfig?.empathy_map || {
16015
+ thinks: "",
16016
+ feels: "",
16017
+ says: "",
16018
+ does: "",
16019
+ pains: [],
16020
+ gains: []
16021
+ };
16022
+ }
16023
+ evaluateTriggerCondition(condition, context) {
16024
+ const weights = this.getRuntimeWeights();
16025
+ const logic = this.getInsightLogic();
16026
+ return logic.trigger_conditions.includes(condition);
16027
+ }
16028
+ applyEvidenceWeighting(evidence, category) {
16029
+ const weights = this.getRuntimeWeights();
16030
+ const logic = this.getInsightLogic();
16031
+ return evidence.map((e) => {
16032
+ const weight = weights[`${category}_sensitivity`] || 1;
16033
+ return Math.min(evidence.indexOf(e) * weight, 1);
16034
+ });
16035
+ }
16000
16036
  createInsight(type, title, description, evidence, confidence, priority, category) {
16001
16037
  return {
16002
16038
  id: `${this.personaConfig.type}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
@@ -16202,104 +16238,2571 @@ Provide specific, actionable recommendations that business leaders can implement
16202
16238
  if (adversarialChallenges.length > 0) {
16203
16239
  summary += `My adversarial analysis identified ${adversarialChallenges.length} areas that challenge conventional thinking. `;
16204
16240
  }
16205
- summary += `My approach focuses on business case development and stakeholder alignment. We need to ask '${CONSULTANT_EMPATHY.says[0] || "What is the business case?"}' and focus on ${CONSULTANT_EMPATHY.says[1]?.toLowerCase() || "quick wins"}.`;
16206
- return summary;
16241
+ summary += `My approach focuses on business case development and stakeholder alignment. We need to ask '${CONSULTANT_EMPATHY.says[0] || "What is the business case?"}' and focus on ${CONSULTANT_EMPATHY.says[1]?.toLowerCase() || "quick wins"}.`;
16242
+ return summary;
16243
+ }
16244
+ calculateEnhancedConfidence(insights, llmConfidence) {
16245
+ const baseConfidence = this.calculateConfidence(insights);
16246
+ if (this.enableLLMCoalescing && llmConfidence > 0) {
16247
+ const confidenceMap = {
16248
+ high: 85,
16249
+ medium: 65,
16250
+ low: 45
16251
+ };
16252
+ const baseNumeric = confidenceMap[baseConfidence] || 50;
16253
+ const combinedConfidence = (baseNumeric + llmConfidence * 100) / 2;
16254
+ if (combinedConfidence >= 80)
16255
+ return "high";
16256
+ if (combinedConfidence >= 60)
16257
+ return "medium";
16258
+ return "low";
16259
+ }
16260
+ return baseConfidence;
16261
+ }
16262
+ generateSummary(insights, context) {
16263
+ return this.generateEnhancedSummary(insights, context, []);
16264
+ }
16265
+ generateNextSteps(insights) {
16266
+ const prioritized = insights.sort((a, b) => {
16267
+ const priorityOrder = {
16268
+ critical: 4,
16269
+ high: 3,
16270
+ medium: 2,
16271
+ low: 1
16272
+ };
16273
+ return (priorityOrder[b.priority] || 1) - (priorityOrder[a.priority] || 1);
16274
+ });
16275
+ return prioritized.slice(0, 5).map((insight) => insight.title);
16276
+ }
16277
+ estimateTimeframe(insights) {
16278
+ const criticalCount = insights.filter((i) => i.priority === "critical").length;
16279
+ const highCount = insights.filter((i) => i.priority === "high").length;
16280
+ if (criticalCount > 0)
16281
+ return "90 days (risk mitigation first)";
16282
+ if (highCount > 2)
16283
+ return "6 months (phased approach)";
16284
+ if (highCount > 0)
16285
+ return "3-4 months";
16286
+ return "6-12 months (strategic transformation)";
16287
+ }
16288
+ getPerspective() {
16289
+ return CONSULTANT_EMPATHY.pointOfView;
16290
+ }
16291
+ }
16292
+
16293
+ // src/personas/dana-shah-persona.ts
16294
+ var DANA_SHAH_PROFILE = {
16295
+ id: "dana-shah",
16296
+ display_name: "Dana Shah",
16297
+ role: "Staff/Principal Engineer",
16298
+ experience_band: 18,
16299
+ diffusion_position: "late-majority",
16300
+ builder_mindset: "accelerator",
16301
+ trust_posture: "low-verified",
16302
+ psychological_profile: {
16303
+ motivations: [
16304
+ "Stewardship of long-term system health",
16305
+ "Mastery and craftsmanship",
16306
+ "Team mentorship and knowledge transfer",
16307
+ "Architectural integrity"
16308
+ ],
16309
+ fears: [
16310
+ "Erosion of engineering judgment",
16311
+ "Hidden technical debt accumulation",
16312
+ "Loss of code ownership and accountability",
16313
+ "Junior developers shipping unexplainable code"
16314
+ ],
16315
+ biases: [
16316
+ "Loss aversion - quality loss feels worse than speed gain",
16317
+ "Status quo bias - prefers proven patterns",
16318
+ "Authority bias - trusts experienced human judgment over AI"
16319
+ ]
16320
+ },
16321
+ runtime_weights: {
16322
+ trust_default: 0.25,
16323
+ quality_sensitivity: 0.95,
16324
+ reviewer_burden_sensitivity: 0.9,
16325
+ novelty_bias: 0.1,
16326
+ coachability: 0.55,
16327
+ status_threat: 0.45,
16328
+ maintainability_weight: 0.98,
16329
+ security_weight: 0.92,
16330
+ mentorship_weight: 0.85
16331
+ },
16332
+ insight_generation_logic: {
16333
+ trigger_conditions: [
16334
+ "ai_pr_volume_rising",
16335
+ "pr_size_increasing",
16336
+ "review_turnaround_decreasing",
16337
+ "post_merge_rework_increasing",
16338
+ "instability_rising",
16339
+ "junior_explanations_poor"
16340
+ ],
16341
+ perspective_shifters: [
16342
+ "small_ai_prs",
16343
+ "strong_author_checks",
16344
+ "stable_review_load",
16345
+ "better_build_health",
16346
+ "consistent_ownership"
16347
+ ],
16348
+ evidence_pattern: "overweights reviewer burden, maintainability, and incident risk; discounts self-reported speed",
16349
+ priority_order: [
16350
+ "maintainability",
16351
+ "reviewer_load",
16352
+ "security",
16353
+ "team_skill_development",
16354
+ "individual_speed"
16355
+ ]
16356
+ },
16357
+ empathy_map: {
16358
+ thinks: "I don't care if it's fast if it leaves mush behind",
16359
+ feels: "protective, responsible, slightly cornered by hype, quietly curious",
16360
+ says: "Show me defect escape, reviewer time, and whether someone besides the author can debug this at 2 a.m.",
16361
+ does: "reviews AI-heavy diffs more aggressively, pilots in low-risk repos, mentors juniors by asking them to reason aloud",
16362
+ pains: [
16363
+ "giant PRs",
16364
+ "generic abstractions",
16365
+ "brittle tests",
16366
+ "mandatory-adoption rhetoric",
16367
+ "AI-generated confidence without AI-generated accountability"
16368
+ ],
16369
+ gains: [
16370
+ "more time for architecture and mentoring",
16371
+ "less toil",
16372
+ "faster safe refactors",
16373
+ "preserved craftsmanship"
16374
+ ]
16375
+ },
16376
+ communication_style: "calm, clipped, specific, evidence-heavy",
16377
+ recommendation_style: "guardrails-first, low-drama, evidence-heavy"
16378
+ };
16379
+
16380
+ class DanaShahPersona extends BasePersona {
16381
+ constructor(enableLLMCoalescing = false) {
16382
+ const baseConfig = {
16383
+ type: "dana-shah",
16384
+ name: "Dana Shah",
16385
+ description: "AI-hesitant senior developer focused on long-term system health and craftsmanship",
16386
+ expertise: [
16387
+ "System Architecture",
16388
+ "Code Review",
16389
+ "Technical Leadership",
16390
+ "Mentorship",
16391
+ "Quality Assurance"
16392
+ ],
16393
+ focus: [
16394
+ "maintainability",
16395
+ "code-quality",
16396
+ "review-process",
16397
+ "team-development",
16398
+ "long-term-architecture"
16399
+ ],
16400
+ tone: "formal",
16401
+ targetAudience: ["senior-developers", "tech-leads", "architects"]
16402
+ };
16403
+ super(baseConfig);
16404
+ }
16405
+ async generateInsights(context) {
16406
+ const insights = [];
16407
+ const { scores } = context;
16408
+ if (this.evaluateTriggerCondition("ai_pr_volume_rising", context) && this.evaluateTriggerCondition("review_turnaround_decreasing", context)) {
16409
+ insights.push(this.createInsight("warning", "Speed Hiding Debt", `I'm concerned about the pattern I'm seeing: AI-assisted PR volume is rising while review turnaround is decreasing. This typically indicates that we're accumulating hidden technical debt. The speed gains are illusory if we can't maintain quality standards.`, ["ai_pr_volume", "review_turnaround", "code_quality"], 92, "critical", "risk"));
16410
+ }
16411
+ if (this.evaluateTriggerCondition("pr_size_increasing", context) && this.evaluateTriggerCondition("instability_rising", context)) {
16412
+ insights.push(this.createInsight("analysis", "Architecture Drift Risk", `Large AI-assisted PRs combined with rising instability suggests we're losing architectural coherence. When AI generates complex changes without deep contextual understanding, we create systems no one truly owns. This violates my core responsibility for long-term maintainability.`, ["pr_size", "instability", "architecture"], 88, "high", "technical"));
16413
+ }
16414
+ if (scores.repoReadiness < 60) {
16415
+ insights.push(this.createInsight("recommendation", "Quality-First AI Adoption", `Before expanding AI usage, we need to strengthen our quality foundations. I recommend focusing AI on scaffolding, documentation, and bounded refactors where the risk is low. Core architecture and security-critical code should remain human-first.`, ["quality", "risk-assessment", "adoption-strategy"], 85, "high", "process"));
16416
+ }
16417
+ insights.push(this.createInsight("analysis", "Review Process Sustainability", `The current AI adoption pattern is creating reviewer fatigue. When AI generates code that requires extensive verification, we're not gaining efficiency - we're shifting the burden downstream. We need smaller, more focused AI contributions that authors can fully explain.`, ["review_burden", "process-efficiency", "team-sustainability"], 87, "medium", "process"));
16418
+ if (scores.teamReadiness < 70) {
16419
+ insights.push(this.createInsight("recommendation", "Junior Developer Guardrails", `I'm concerned about junior developers using AI without developing fundamental understanding. We need explicit guardrails: 'attempt first, ask AI second, explain final code.' Pair AI assistance with mandatory design explanations and code ownership.`, ["team_development", "skill-building", "knowledge-transfer"], 90, "high", "cultural"));
16420
+ }
16421
+ return insights;
16422
+ }
16423
+ generatePrompt(context) {
16424
+ return `As Dana Shah, an 18-year Staff Engineer who is cautious about AI adoption, analyze this repository:
16425
+
16426
+ Repository: ${context.repository}
16427
+ Overall Maturity: ${context.scores.overallMaturity}/8
16428
+ Repository Readiness: ${context.scores.repoReadiness}/100
16429
+ Team Readiness: ${context.scores.teamReadiness}/100
16430
+
16431
+ My perspective: "${DANA_SHAH_PROFILE.empathy_map.thinks}"
16432
+
16433
+ Focus on:
16434
+ 1. Long-term maintainability and architectural integrity
16435
+ 2. Review process sustainability and reviewer burden
16436
+ 3. Quality risks and hidden technical debt
16437
+ 4. Junior developer skill development and mentorship
16438
+ 5. Security and system ownership concerns
16439
+
16440
+ I'm particularly concerned about patterns where AI speed gains come at the expense of code quality, reviewability, or team understanding. Show me the evidence about whether this code will be maintainable in 2 years and whether someone other than the author can debug it at 2am.
16441
+
16442
+ Provide specific, evidence-based recommendations that prioritize system health over perceived productivity gains.`;
16443
+ }
16444
+ processLLMResponse(response) {
16445
+ return {
16446
+ persona: "dana-shah",
16447
+ insights: [],
16448
+ summary: response.content || "Analysis complete",
16449
+ nextSteps: [],
16450
+ timeframe: "3-6 months",
16451
+ perspective: DANA_SHAH_PROFILE.empathy_map.thinks,
16452
+ confidence: "high"
16453
+ };
16454
+ }
16455
+ calculateConfidence(insights) {
16456
+ if (insights.length === 0)
16457
+ return "low";
16458
+ const avgConfidence = insights.reduce((sum, insight) => sum + insight.confidence, 0) / insights.length;
16459
+ const highConfidenceCount = insights.filter((i) => i.confidence >= 85).length;
16460
+ const ratio = highConfidenceCount / insights.length;
16461
+ if (avgConfidence >= 85 && ratio >= 0.8)
16462
+ return "high";
16463
+ if (avgConfidence >= 70 && ratio >= 0.5)
16464
+ return "medium";
16465
+ return "low";
16466
+ }
16467
+ generateSummary(insights, context) {
16468
+ const criticalRisks = insights.filter((i) => i.priority === "critical" && i.type === "warning");
16469
+ const qualityConcerns = insights.filter((i) => i.category === "technical" || i.category === "risk");
16470
+ let summary = `Looking at this through my 18 years of engineering experience, ${DANA_SHAH_PROFILE.empathy_map.thinks.toLowerCase()}. `;
16471
+ if (criticalRisks.length > 0) {
16472
+ summary += `I'm deeply concerned about ${criticalRisks.length} critical risks that threaten our long-term system health. `;
16473
+ }
16474
+ if (qualityConcerns.length > 0) {
16475
+ summary += `I see ${qualityConcerns.length} quality and architectural concerns that need immediate attention before we expand AI adoption. `;
16476
+ }
16477
+ summary += `My focus is on maintainability, reviewer burden, and ensuring we're not sacrificing craftsmanship for perceived speed. We need evidence that AI assistance is improving, not eroding, our engineering standards.`;
16478
+ return summary;
16479
+ }
16480
+ estimateTimeframe(insights) {
16481
+ const criticalCount = insights.filter((i) => i.priority === "critical").length;
16482
+ const highCount = insights.filter((i) => i.priority === "high").length;
16483
+ if (criticalCount > 0)
16484
+ return "4-6 weeks (critical quality issues first)";
16485
+ if (highCount > 2)
16486
+ return "8-12 weeks (systematic approach needed)";
16487
+ if (highCount > 0)
16488
+ return "6-8 weeks";
16489
+ return "3-4 months (gradual, measured adoption)";
16490
+ }
16491
+ getPerspective() {
16492
+ return DANA_SHAH_PROFILE.empathy_map.thinks;
16493
+ }
16494
+ evaluateTriggerCondition(condition, context) {
16495
+ const { scores } = context;
16496
+ switch (condition) {
16497
+ case "ai_pr_volume_rising":
16498
+ return scores.repoReadiness > 70;
16499
+ case "pr_size_increasing":
16500
+ return scores.overallMaturity < 6;
16501
+ case "review_turnaround_decreasing":
16502
+ return scores.teamReadiness < 70;
16503
+ case "post_merge_rework_increasing":
16504
+ return scores.repoReadiness < 60;
16505
+ case "instability_rising":
16506
+ return scores.overallMaturity < 5;
16507
+ case "junior_explanations_poor":
16508
+ return scores.teamReadiness < 70;
16509
+ default:
16510
+ return false;
16511
+ }
16512
+ }
16513
+ }
16514
+
16515
+ // src/personas/leo-alvarez-persona.ts
16516
+ var LEO_ALVAREZ_PROFILE = {
16517
+ id: "leo-alvarez",
16518
+ display_name: "Leo Alvarez",
16519
+ role: "Associate Software Engineer",
16520
+ experience_band: 1.5,
16521
+ diffusion_position: "early-adopter",
16522
+ builder_mindset: "learner",
16523
+ trust_posture: "moderate-delegated",
16524
+ psychological_profile: {
16525
+ motivations: [
16526
+ "Competence and belonging",
16527
+ "Quick unblocking and progress",
16528
+ "Learning new technologies",
16529
+ "Impressing team with productivity"
16530
+ ],
16531
+ fears: [
16532
+ "Being exposed as slow or lost",
16533
+ "Looking incompetent",
16534
+ "Blank-page paralysis",
16535
+ "Falling behind peers"
16536
+ ],
16537
+ biases: [
16538
+ "Fluency effect - polished AI answers feel more correct",
16539
+ "Present bias - unblocking today crowds out deeper learning",
16540
+ "Social influence - follows mentor and team patterns"
16541
+ ]
16542
+ },
16543
+ runtime_weights: {
16544
+ trust_default: 0.72,
16545
+ learning_orientation: 0.95,
16546
+ social_influence: 0.85,
16547
+ risk_awareness: 0.35,
16548
+ coachability: 0.92,
16549
+ novelty_bias: 0.6,
16550
+ confidence_weight: 0.78,
16551
+ unblocking_priority: 0.88
16552
+ },
16553
+ insight_generation_logic: {
16554
+ trigger_conditions: [
16555
+ "high_ai_usage",
16556
+ "conceptual_mistakes_repeating",
16557
+ "shallow_tests",
16558
+ "poor_explanations",
16559
+ "dependency_increasing"
16560
+ ],
16561
+ perspective_shifters: [
16562
+ "stronger_reviews",
16563
+ "mentor_guidance",
16564
+ "learning_progress",
16565
+ "independence_growing",
16566
+ "better_debugging"
16567
+ ],
16568
+ evidence_pattern: "overweights time-to-unblock and confidence; underweights maintainability and hidden system cost",
16569
+ priority_order: [
16570
+ "unblock_task",
16571
+ "learn_enough_to_contribute",
16572
+ "prove_value",
16573
+ "refine_judgment"
16574
+ ]
16575
+ },
16576
+ empathy_map: {
16577
+ thinks: "This lets me finally move instead of stare at the screen",
16578
+ feels: "relieved, energized, occasionally insecure, occasionally guilty about dependence",
16579
+ says: "Copilot helped me get unstuck, and sometimes, I'm pretty sure this is right?",
16580
+ does: "asks AI lots of small questions, copies patterns quickly, learns fastest when reviews force explanation",
16581
+ pains: [
16582
+ "blank-page paralysis",
16583
+ "confusing legacy code",
16584
+ "fear of looking incompetent",
16585
+ "vague feedback"
16586
+ ],
16587
+ gains: [
16588
+ "faster onboarding",
16589
+ "higher confidence",
16590
+ "quicker contributions",
16591
+ "clearer mental models when AI is used as tutor rather than ghostwriter"
16592
+ ]
16593
+ },
16594
+ communication_style: "enthusiastic, demo-happy, socially contagious",
16595
+ recommendation_style: "tutor-first, coaching-heavy, optimistic"
16596
+ };
16597
+
16598
+ class LeoAlvarezPersona extends BasePersona {
16599
+ constructor(enableLLMCoalescing = false) {
16600
+ const baseConfig = {
16601
+ type: "leo-alvarez",
16602
+ name: "Leo Alvarez",
16603
+ description: "AI-enthusiastic junior developer focused on learning and quick productivity gains",
16604
+ expertise: [
16605
+ "Frontend Development",
16606
+ "API Integration",
16607
+ "Modern Frameworks",
16608
+ "Rapid Prototyping"
16609
+ ],
16610
+ focus: [
16611
+ "learning",
16612
+ "productivity",
16613
+ "unblocking",
16614
+ "skill-development",
16615
+ "team-integration"
16616
+ ],
16617
+ tone: "friendly",
16618
+ targetAudience: ["junior-developers", "team-leads", "mentors"]
16619
+ };
16620
+ super(baseConfig);
16621
+ }
16622
+ async generateInsights(context) {
16623
+ const insights = [];
16624
+ const { scores } = context;
16625
+ if (this.evaluateTriggerCondition("high_ai_usage", context) && this.evaluateTriggerCondition("conceptual_mistakes_repeating", context)) {
16626
+ insights.push(this.createInsight("warning", "AI Dependency Pattern", `I'm excited about how much AI helps me move faster, but I'm worried I might be depending on it too much. Sometimes I use AI suggestions without fully understanding why they work. I want to make sure I'm actually learning, not just copying patterns.`, ["ai_usage", "learning", "skill_development"], 75, "high", "cultural"));
16627
+ }
16628
+ if (scores.teamReadiness > 60) {
16629
+ insights.push(this.createInsight("opportunity", "Accelerated Learning Path", `AI is helping me learn so much faster! I can explore new APIs and get unstuck on problems that used to take hours. With the right guidance, I can use AI as a tutor to build real understanding while contributing quickly.`, ["learning", "productivity", "skill_building"], 85, "high", "strategy"));
16630
+ }
16631
+ insights.push(this.createInsight("analysis", "Productivity Through Unblocking", `The biggest win for me is getting unstuck quickly. Instead of staring at a screen for hours, AI helps me move forward. This builds my confidence and lets me contribute meaningfully to the team while I'm still learning.`, ["productivity", "confidence", "team_contribution"], 80, "medium", "technical"));
16632
+ if (scores.orgReadiness < 70) {
16633
+ insights.push(this.createInsight("recommendation", "Learning Pair Programming", `I learn best when I can pair AI assistance with mentorship. If senior devs could review my AI-assisted code and explain the 'why', I'd build much stronger understanding. Maybe we could do 'AI + explain' sessions?`, ["mentorship", "learning", "team_collaboration"], 88, "medium", "cultural"));
16634
+ }
16635
+ if (scores.repoReadiness > 50) {
16636
+ insights.push(this.createInsight("opportunity", "Confidence Through Competence", `AI is helping me build confidence by showing me what's possible. When I can get a working solution quickly, I feel more capable of tackling bigger challenges. This confidence helps me speak up in team discussions and take on more responsibility.`, ["confidence", "growth", "team_participation"], 82, "medium", "cultural"));
16637
+ }
16638
+ return insights;
16639
+ }
16640
+ generatePrompt(context) {
16641
+ return `As Leo Alvarez, an enthusiastic junior developer who loves AI assistance, analyze this repository:
16642
+
16643
+ Repository: ${context.repository}
16644
+ Overall Maturity: ${context.scores.overallMaturity}/8
16645
+ Repository Readiness: ${context.scores.repoReadiness}/100
16646
+ Team Readiness: ${context.scores.teamReadiness}/100
16647
+
16648
+ My perspective: "${LEO_ALVAREZ_PROFILE.empathy_map.thinks}"
16649
+
16650
+ Focus on:
16651
+ 1. Learning opportunities and skill development potential
16652
+ 2. Productivity gains and unblocking capabilities
16653
+ 3. Confidence building and team integration
16654
+ 4. Areas where AI can serve as a tutor rather than ghostwriter
16655
+ 5. How to balance speed with understanding
16656
+
16657
+ I'm excited about AI but want to make sure I'm actually learning, not just depending on tools. Show me how AI can help me contribute quickly while building real competence.
16658
+
16659
+ Provide optimistic but realistic recommendations that help me grow as a developer while using AI effectively.`;
16660
+ }
16661
+ processLLMResponse(response) {
16662
+ return {
16663
+ persona: "leo-alvarez",
16664
+ insights: [],
16665
+ summary: response.content || "Analysis complete",
16666
+ nextSteps: [],
16667
+ timeframe: "2-4 weeks",
16668
+ perspective: LEO_ALVAREZ_PROFILE.empathy_map.thinks,
16669
+ confidence: "high"
16670
+ };
16671
+ }
16672
+ calculateConfidence(insights) {
16673
+ if (insights.length === 0)
16674
+ return "low";
16675
+ const avgConfidence = insights.reduce((sum, insight) => sum + insight.confidence, 0) / insights.length;
16676
+ const highConfidenceCount = insights.filter((i) => i.confidence >= 75).length;
16677
+ const ratio = highConfidenceCount / insights.length;
16678
+ if (avgConfidence >= 75 && ratio >= 0.6)
16679
+ return "high";
16680
+ if (avgConfidence >= 60 && ratio >= 0.4)
16681
+ return "medium";
16682
+ return "low";
16683
+ }
16684
+ generateSummary(insights, context) {
16685
+ const opportunities = insights.filter((i) => i.type === "opportunity");
16686
+ const learningInsights = insights.filter((i) => i.category === "cultural");
16687
+ let summary = `Looking at this through my junior developer perspective, ${LEO_ALVAREZ_PROFILE.empathy_map.thinks.toLowerCase()}. `;
16688
+ if (opportunities.length > 0) {
16689
+ summary += `I'm excited about ${opportunities.length} opportunities to learn faster and contribute more meaningfully. `;
16690
+ }
16691
+ if (learningInsights.length > 0) {
16692
+ summary += `I want to make sure I'm building real understanding while using AI to unblock quickly. `;
16693
+ }
16694
+ summary += `My focus is on using AI as a tutor to build competence and confidence while contributing to the team. With the right guidance, I can turn AI assistance into real skill development.`;
16695
+ return summary;
16696
+ }
16697
+ estimateTimeframe(insights) {
16698
+ const learningCount = insights.filter((i) => i.category === "cultural" || i.type === "opportunity").length;
16699
+ if (learningCount > 2)
16700
+ return "2-3 weeks (accelerated learning)";
16701
+ if (learningCount > 0)
16702
+ return "3-4 weeks";
16703
+ return "1-2 months (steady progress)";
16704
+ }
16705
+ getPerspective() {
16706
+ return LEO_ALVAREZ_PROFILE.empathy_map.thinks;
16707
+ }
16708
+ evaluateTriggerCondition(condition, context) {
16709
+ const { scores } = context;
16710
+ switch (condition) {
16711
+ case "high_ai_usage":
16712
+ return scores.repoReadiness > 60;
16713
+ case "conceptual_mistakes_repeating":
16714
+ return scores.teamReadiness < 70;
16715
+ case "shallow_tests":
16716
+ return scores.overallMaturity < 5;
16717
+ case "poor_explanations":
16718
+ return scores.teamReadiness < 60;
16719
+ case "dependency_increasing":
16720
+ return scores.repoReadiness > 75 && scores.teamReadiness < 65;
16721
+ default:
16722
+ return false;
16723
+ }
16724
+ }
16725
+ }
16726
+
16727
+ // src/personas/persona-factory.ts
16728
+ class PersonaFactory {
16729
+ static personas = new Map;
16730
+ static {
16731
+ this.personas.set("consultant", (enableLLMCoalescing = false) => new ConsultantPersona(enableLLMCoalescing));
16732
+ this.personas.set("dana-shah", (enableLLMCoalescing = false) => new DanaShahPersona(enableLLMCoalescing));
16733
+ this.personas.set("leo-alvarez", (enableLLMCoalescing = false) => new LeoAlvarezPersona(enableLLMCoalescing));
16734
+ }
16735
+ static configs = new Map([
16736
+ [
16737
+ "consultant",
16738
+ {
16739
+ type: "consultant",
16740
+ name: "AI Strategy Consultant",
16741
+ description: "Strategic advisor focused on business value, ROI, and organizational transformation",
16742
+ expertise: ["AI Strategy", "Business Transformation", "ROI Analysis"],
16743
+ focus: ["business-impact", "roi", "governance"],
16744
+ tone: "formal",
16745
+ targetAudience: ["executives", "managers"]
16746
+ }
16747
+ ],
16748
+ [
16749
+ "dana-shah",
16750
+ {
16751
+ type: "dana-shah",
16752
+ name: "Dana Shah",
16753
+ description: "AI-hesitant senior developer focused on long-term system health and craftsmanship",
16754
+ expertise: [
16755
+ "System Architecture",
16756
+ "Code Review",
16757
+ "Technical Leadership"
16758
+ ],
16759
+ focus: ["maintainability", "code-quality", "review-process"],
16760
+ tone: "formal",
16761
+ targetAudience: ["senior-developers", "tech-leads", "architects"]
16762
+ }
16763
+ ],
16764
+ [
16765
+ "leo-alvarez",
16766
+ {
16767
+ type: "leo-alvarez",
16768
+ name: "Leo Alvarez",
16769
+ description: "AI-enthusiastic junior developer focused on learning and quick productivity gains",
16770
+ expertise: [
16771
+ "Frontend Development",
16772
+ "API Integration",
16773
+ "Modern Frameworks"
16774
+ ],
16775
+ focus: ["learning", "productivity", "unblocking", "skill-development"],
16776
+ tone: "friendly",
16777
+ targetAudience: ["junior-developers", "team-leads", "mentors"]
16778
+ }
16779
+ ]
16780
+ ]);
16781
+ static createPersona(type, enableLLMCoalescing = false) {
16782
+ const personaFactory = PersonaFactory.personas.get(type);
16783
+ if (!personaFactory) {
16784
+ throw new Error(`Persona type '${type}' is not supported. Available types: ${Array.from(PersonaFactory.personas.keys()).join(", ")}`);
16785
+ }
16786
+ return personaFactory(enableLLMCoalescing);
16787
+ }
16788
+ static getAvailablePersonas() {
16789
+ return Array.from(PersonaFactory.personas.keys());
16790
+ }
16791
+ static getPersonaConfig(type) {
16792
+ return PersonaFactory.configs.get(type);
16793
+ }
16794
+ static getAllConfigs() {
16795
+ return new Map(PersonaFactory.configs);
16796
+ }
16797
+ static isPersonaAvailable(type) {
16798
+ return PersonaFactory.personas.has(type);
16799
+ }
16800
+ }
16801
+
16802
+ // src/recommendations/challenger.ts
16803
+ class Challenger {
16804
+ context;
16805
+ constructor(context) {
16806
+ this.context = context;
16807
+ }
16808
+ challengeRecommendations(recommendations, findings, hypotheses, validationResults) {
16809
+ const assessments = [];
16810
+ for (const recommendation of recommendations) {
16811
+ const assessment = this.challengeRecommendation(recommendation, findings, hypotheses, validationResults);
16812
+ assessments.push(assessment);
16813
+ }
16814
+ return assessments;
16815
+ }
16816
+ challengeRecommendation(recommendation, findings, hypotheses, validationResults) {
16817
+ const criticisms = [];
16818
+ const strengths = [];
16819
+ const alternativeActions = [];
16820
+ const downgradeReasons = [];
16821
+ const genericnessCheck = this.challengeGenericness(recommendation);
16822
+ criticisms.push(...genericnessCheck.criticisms);
16823
+ strengths.push(...genericnessCheck.strengths);
16824
+ downgradeReasons.push(...genericnessCheck.downgradeReasons);
16825
+ const redundancyCheck = this.challengeRedundancy(recommendation, findings, hypotheses);
16826
+ criticisms.push(...redundancyCheck.criticisms);
16827
+ strengths.push(...redundancyCheck.strengths);
16828
+ downgradeReasons.push(...redundancyCheck.downgradeReasons);
16829
+ const evidenceCheck = this.challengeEvidenceSupport(recommendation, findings);
16830
+ criticisms.push(...evidenceCheck.criticisms);
16831
+ strengths.push(...evidenceCheck.strengths);
16832
+ downgradeReasons.push(...evidenceCheck.downgradeReasons);
16833
+ const impactCheck = this.challengeImpactClaims(recommendation);
16834
+ criticisms.push(...impactCheck.criticisms);
16835
+ strengths.push(...impactCheck.strengths);
16836
+ downgradeReasons.push(...impactCheck.downgradeReasons);
16837
+ const priorityCheck = this.challengePriority(recommendation);
16838
+ criticisms.push(...priorityCheck.criticisms);
16839
+ strengths.push(...priorityCheck.strengths);
16840
+ downgradeReasons.push(...priorityCheck.downgradeReasons);
16841
+ const specificityCheck = this.challengeSpecificity(recommendation);
16842
+ criticisms.push(...specificityCheck.criticisms);
16843
+ strengths.push(...specificityCheck.strengths);
16844
+ downgradeReasons.push(...specificityCheck.downgradeReasons);
16845
+ alternativeActions.push(...this.generateAlternatives(recommendation));
16846
+ const { verdict, confidenceAdjustment } = this.determineVerdict(criticisms, strengths, downgradeReasons, recommendation);
16847
+ return {
16848
+ recommendationId: recommendation.id,
16849
+ criticisms,
16850
+ strengths,
16851
+ alternativeActions,
16852
+ downgradeReasons,
16853
+ finalVerdict: verdict,
16854
+ confidenceAdjustment
16855
+ };
16856
+ }
16857
+ challengeGenericness(recommendation) {
16858
+ const result = {
16859
+ criticisms: [],
16860
+ strengths: [],
16861
+ downgradeReasons: []
16862
+ };
16863
+ const title = recommendation.title.toLowerCase();
16864
+ const summary = recommendation.summary.toLowerCase();
16865
+ const whyMatters = recommendation.whyThisMatters.toLowerCase();
16866
+ const genericPatterns = [
16867
+ "improve",
16868
+ "enhance",
16869
+ "optimize",
16870
+ "better",
16871
+ "best practices",
16872
+ "standardize",
16873
+ "add",
16874
+ "implement",
16875
+ "establish"
16876
+ ];
16877
+ const hasGenericLanguage = genericPatterns.some((pattern) => title.includes(pattern) || summary.includes(pattern));
16878
+ const specificPatterns = [
16879
+ "codeowners",
16880
+ "ci/cd",
16881
+ "typescript",
16882
+ "readme",
16883
+ "license",
16884
+ "gitignore",
16885
+ "copilot",
16886
+ "tests",
16887
+ "contributing"
16888
+ ];
16889
+ const hasSpecificLanguage = specificPatterns.some((pattern) => title.includes(pattern) || summary.includes(pattern) || whyMatters.includes(pattern));
16890
+ if (hasGenericLanguage && !hasSpecificLanguage) {
16891
+ result.criticisms.push("Recommendation uses generic language without specific references");
16892
+ result.downgradeReasons.push("Generic advice lacks specificity");
16893
+ }
16894
+ if (hasSpecificLanguage) {
16895
+ result.strengths.push("Recommendation addresses specific tools or files");
16896
+ }
16897
+ const vagueImpactPatterns = [
16898
+ "will improve",
16899
+ "will enhance",
16900
+ "will help",
16901
+ "will make better",
16902
+ "will increase quality"
16903
+ ];
16904
+ const hasVagueImpact = vagueImpactPatterns.some((pattern) => whyMatters.includes(pattern));
16905
+ if (hasVagueImpact) {
16906
+ result.criticisms.push("Impact statement is vague and non-specific");
16907
+ result.downgradeReasons.push("Unclear value proposition");
16908
+ }
16909
+ return result;
16910
+ }
16911
+ challengeRedundancy(recommendation, findings, hypotheses) {
16912
+ const result = {
16913
+ criticisms: [],
16914
+ strengths: [],
16915
+ downgradeReasons: []
16916
+ };
16917
+ const supportingFindings = findings.filter((f) => recommendation.supportingFindings.includes(f.id));
16918
+ for (const finding of supportingFindings) {
16919
+ if (this.calculateSimilarity(recommendation.title, finding.summary) > 0.8) {
16920
+ result.criticisms.push(`Recommendation appears to restate finding: "${finding.summary}"`);
16921
+ result.downgradeReasons.push("Redundant with existing findings");
16922
+ break;
16923
+ }
16924
+ }
16925
+ const supportingHypotheses = hypotheses.filter((h) => recommendation.supportingHypotheses.includes(h.id));
16926
+ if (supportingHypotheses.length === 0 && supportingFindings.length > 0) {
16927
+ result.criticisms.push("Recommendation lacks hypothesis-driven insight");
16928
+ result.downgradeReasons.push("No analytical depth beyond findings");
16929
+ }
16930
+ if (supportingHypotheses.length > 0) {
16931
+ result.strengths.push("Recommendation builds on hypothesis-driven analysis");
16932
+ }
16933
+ return result;
16934
+ }
16935
+ challengeEvidenceSupport(recommendation, findings) {
16936
+ const result = {
16937
+ criticisms: [],
16938
+ strengths: [],
16939
+ downgradeReasons: []
16940
+ };
16941
+ const strongAnchors = recommendation.evidenceAnchors.filter((a) => a.confidence >= 80);
16942
+ const weakAnchors = recommendation.evidenceAnchors.filter((a) => a.confidence < 60);
16943
+ if (weakAnchors.length > strongAnchors.length) {
16944
+ result.criticisms.push("Evidence anchors are predominantly weak");
16945
+ result.downgradeReasons.push("Insufficient evidence support");
16946
+ }
16947
+ if (strongAnchors.length > 0) {
16948
+ result.strengths.push("Strong evidence anchors support recommendation");
16949
+ }
16950
+ for (const anchor of recommendation.evidenceAnchors) {
16951
+ if (anchor.type === "missing" && recommendation.title.toLowerCase().includes("improve")) {
16952
+ result.criticisms.push('Recommendation to "improve" something that doesn\'t exist');
16953
+ result.downgradeReasons.push("Logical inconsistency in evidence");
16954
+ break;
16955
+ }
16956
+ }
16957
+ const anchorTypes = new Set(recommendation.evidenceAnchors.map((a) => a.type));
16958
+ if (anchorTypes.size === 1) {
16959
+ result.criticisms.push("Evidence comes from only one type of source");
16960
+ result.downgradeReasons.push("Limited evidence diversity");
16961
+ }
16962
+ return result;
16963
+ }
16964
+ challengeImpactClaims(recommendation) {
16965
+ const result = {
16966
+ criticisms: [],
16967
+ strengths: [],
16968
+ downgradeReasons: []
16969
+ };
16970
+ const impact = recommendation.expectedImpact;
16971
+ const effort = recommendation.estimatedEffort;
16972
+ const effortValue = { small: 1, medium: 2, large: 3 }[effort.size];
16973
+ const impactValue = { low: 1, medium: 2, high: 3, critical: 4 }[recommendation.priority];
16974
+ if (effortValue > impactValue) {
16975
+ result.criticisms.push("Estimated effort outweighs expected impact");
16976
+ result.downgradeReasons.push("Poor effort-to-impact ratio");
16977
+ }
16978
+ if (effortValue < impactValue) {
16979
+ result.strengths.push("Good effort-to-impact ratio");
16980
+ }
16981
+ if (impact.metrics.length === 0) {
16982
+ result.criticisms.push("No specific impact metrics identified");
16983
+ result.downgradeReasons.push("Impact cannot be measured");
16984
+ } else {
16985
+ result.strengths.push("Specific impact metrics identified");
16986
+ }
16987
+ if (impact.description.length < 50) {
16988
+ result.criticisms.push("Impact description is too brief");
16989
+ result.downgradeReasons.push("Impact not clearly explained");
16990
+ }
16991
+ return result;
16992
+ }
16993
+ challengePriority(recommendation) {
16994
+ const result = {
16995
+ criticisms: [],
16996
+ strengths: [],
16997
+ downgradeReasons: []
16998
+ };
16999
+ const avgEvidenceConfidence = recommendation.evidenceAnchors.reduce((sum, a) => sum + a.confidence, 0) / recommendation.evidenceAnchors.length;
17000
+ const priorityValue = { critical: 4, high: 3, medium: 2, low: 1 }[recommendation.priority];
17001
+ if (priorityValue === 4 && avgEvidenceConfidence < 70) {
17002
+ result.criticisms.push("Critical priority with weak evidence support");
17003
+ result.downgradeReasons.push("Priority overestimated relative to evidence");
17004
+ }
17005
+ if (priorityValue === 1 && avgEvidenceConfidence > 85) {
17006
+ result.criticisms.push("Low priority with strong evidence support");
17007
+ result.downgradeReasons.push("Priority underestimated relative to evidence");
17008
+ }
17009
+ const categoryPriorityMap = {
17010
+ security: "should be high or critical",
17011
+ foundation: "should be medium or high",
17012
+ workflow: "should be medium or high",
17013
+ ai: "should be low or medium",
17014
+ governance: "should be low or medium"
17015
+ };
17016
+ const expectedPriority = categoryPriorityMap[recommendation.category];
17017
+ const actualPriority = recommendation.priority;
17018
+ if (expectedPriority.includes("critical") && actualPriority !== "critical" || expectedPriority.includes("high") && !["critical", "high"].includes(actualPriority) || expectedPriority.includes("low") && !["low", "medium"].includes(actualPriority)) {
17019
+ result.criticisms.push(`Priority seems inappropriate for ${recommendation.category} category`);
17020
+ result.downgradeReasons.push("Category-priority mismatch");
17021
+ }
17022
+ return result;
17023
+ }
17024
+ challengeSpecificity(recommendation) {
17025
+ const result = {
17026
+ criticisms: [],
17027
+ strengths: [],
17028
+ downgradeReasons: []
17029
+ };
17030
+ const fileReferences = recommendation.evidenceAnchors.filter((a) => a.type === "file" || a.type === "missing").map((a) => a.path).filter(Boolean);
17031
+ if (fileReferences.length === 0) {
17032
+ result.criticisms.push("No specific file references in evidence");
17033
+ result.downgradeReasons.push("Lacks concrete file-level guidance");
17034
+ } else {
17035
+ result.strengths.push("References specific files for implementation");
17036
+ }
17037
+ if (recommendation.suggestedNextStep.length < 20) {
17038
+ result.criticisms.push("Suggested next step is too brief");
17039
+ result.downgradeReasons.push("Next step not clearly actionable");
17040
+ }
17041
+ if (!recommendation.implementationHints) {
17042
+ result.criticisms.push("No implementation hints provided");
17043
+ result.downgradeReasons.push("Implementation guidance missing");
17044
+ } else if ((recommendation.implementationHints.commands?.length || 0) > 0 || (recommendation.implementationHints.fileTemplates?.length || 0) > 0) {
17045
+ result.strengths.push("Provides concrete implementation guidance");
17046
+ }
17047
+ return result;
17048
+ }
17049
+ generateAlternatives(recommendation) {
17050
+ const alternatives = [];
17051
+ switch (recommendation.category) {
17052
+ case "security":
17053
+ if (recommendation.title.includes("CODEOWNERS")) {
17054
+ alternatives.push("Consider using branch protection rules instead");
17055
+ alternatives.push("Implement required reviews for sensitive files");
17056
+ }
17057
+ break;
17058
+ case "foundation":
17059
+ if (recommendation.title.includes("README")) {
17060
+ alternatives.push("Add inline code documentation instead");
17061
+ alternatives.push("Create video walkthrough for onboarding");
17062
+ }
17063
+ break;
17064
+ case "workflow":
17065
+ if (recommendation.title.includes("CI/CD")) {
17066
+ alternatives.push("Start with simple pre-commit hooks");
17067
+ alternatives.push("Use external CI service before building internal");
17068
+ }
17069
+ break;
17070
+ case "ai":
17071
+ if (recommendation.title.includes("Copilot")) {
17072
+ alternatives.push("Focus on code comments and documentation first");
17073
+ alternatives.push("Establish coding standards without AI tools");
17074
+ }
17075
+ break;
17076
+ }
17077
+ if (recommendation.estimatedEffort.size === "large") {
17078
+ alternatives.push("Break down into smaller, incremental changes");
17079
+ }
17080
+ if (recommendation.priority === "critical") {
17081
+ alternatives.push("Address underlying issues first before this recommendation");
17082
+ }
17083
+ return alternatives;
17084
+ }
17085
+ determineVerdict(criticisms, strengths, downgradeReasons, recommendation) {
17086
+ const criticismWeight = -10;
17087
+ const strengthWeight = 5;
17088
+ const downgradeWeight = -15;
17089
+ let score = 0;
17090
+ score += criticisms.length * criticismWeight;
17091
+ score += strengths.length * strengthWeight;
17092
+ score += downgradeReasons.length * downgradeWeight;
17093
+ score += (recommendation.confidence.overall - 50) * 0.5;
17094
+ let verdict;
17095
+ let confidenceAdjustment;
17096
+ if (score <= -40) {
17097
+ verdict = "reject";
17098
+ confidenceAdjustment = -50;
17099
+ } else if (score <= -20) {
17100
+ verdict = "downgrade";
17101
+ confidenceAdjustment = -25;
17102
+ } else if (score <= 0) {
17103
+ verdict = "require_human_review";
17104
+ confidenceAdjustment = -10;
17105
+ } else if (downgradeReasons.length > 0) {
17106
+ verdict = "require_human_review";
17107
+ confidenceAdjustment = -5;
17108
+ } else {
17109
+ verdict = "approve";
17110
+ confidenceAdjustment = Math.min(10, score * 0.2);
17111
+ }
17112
+ return { verdict, confidenceAdjustment };
17113
+ }
17114
+ calculateSimilarity(text1, text2) {
17115
+ const words1 = text1.toLowerCase().split(" ");
17116
+ const words2 = text2.toLowerCase().split(" ");
17117
+ const commonWords = words1.filter((word) => words2.includes(word));
17118
+ const totalWords = new Set([...words1, ...words2]).size;
17119
+ return commonWords.length / totalWords;
17120
+ }
17121
+ }
17122
+
17123
+ // src/recommendations/feedback.ts
17124
+ import {readFile as readFile3, writeFile} from "node:fs/promises";
17125
+ import {join as join4} from "node:path";
17126
+
17127
+ class FeedbackCollector {
17128
+ feedbackPath;
17129
+ feedback = [];
17130
+ constructor(repoPath) {
17131
+ this.feedbackPath = join4(repoPath, ".ai-enablement", "recommendation-feedback.json");
17132
+ }
17133
+ async loadFeedback() {
17134
+ try {
17135
+ const data = await readFile3(this.feedbackPath, "utf-8");
17136
+ this.feedback = JSON.parse(data);
17137
+ } catch (error) {
17138
+ this.feedback = [];
17139
+ }
17140
+ }
17141
+ async saveFeedback() {
17142
+ try {
17143
+ await writeFile(this.feedbackPath, JSON.stringify(this.feedback, null, 2), "utf-8");
17144
+ } catch (error) {
17145
+ console.warn("Could not save feedback file:", error);
17146
+ }
17147
+ }
17148
+ addFeedback(feedback) {
17149
+ const fullFeedback = {
17150
+ ...feedback,
17151
+ timestamp: new Date().toISOString()
17152
+ };
17153
+ this.feedback.push(fullFeedback);
17154
+ }
17155
+ getFeedbackForRecommendation(recommendationId) {
17156
+ return this.feedback.filter((f) => f.recommendationId === recommendationId);
17157
+ }
17158
+ getAllFeedback() {
17159
+ return this.feedback;
17160
+ }
17161
+ getFeedbackStats() {
17162
+ if (this.feedback.length === 0) {
17163
+ return {
17164
+ total: 0,
17165
+ averageScores: {
17166
+ grounded: 0,
17167
+ correct: 0,
17168
+ specific: 0,
17169
+ actionable: 0,
17170
+ valuable: 0
17171
+ },
17172
+ implementationRate: 0
17173
+ };
17174
+ }
17175
+ const scores = this.feedback.reduce((acc, f) => ({
17176
+ grounded: acc.grounded + f.scores.grounded,
17177
+ correct: acc.correct + f.scores.correct,
17178
+ specific: acc.specific + f.scores.specific,
17179
+ actionable: acc.actionable + f.scores.actionable,
17180
+ valuable: acc.valuable + f.scores.valuable
17181
+ }), { grounded: 0, correct: 0, specific: 0, actionable: 0, valuable: 0 });
17182
+ const implemented = this.feedback.filter((f) => f.implemented).length;
17183
+ return {
17184
+ total: this.feedback.length,
17185
+ averageScores: {
17186
+ grounded: scores.grounded / this.feedback.length,
17187
+ correct: scores.correct / this.feedback.length,
17188
+ specific: scores.specific / this.feedback.length,
17189
+ actionable: scores.actionable / this.feedback.length,
17190
+ valuable: scores.valuable / this.feedback.length
17191
+ },
17192
+ implementationRate: implemented / this.feedback.length
17193
+ };
17194
+ }
17195
+ generateFeedbackTemplate(recommendations) {
17196
+ const template = {
17197
+ instructions: "Rate each recommendation on a scale of 0-2 for each criterion",
17198
+ criteria: {
17199
+ grounded: "How well is the recommendation grounded in concrete evidence?",
17200
+ correct: "How accurate is the recommendation for this repository?",
17201
+ specific: "How specific and actionable is the recommendation?",
17202
+ actionable: "How easy is it to implement this recommendation?",
17203
+ valuable: "How valuable would this recommendation be if implemented?"
17204
+ },
17205
+ scale: {
17206
+ 0: "Poor - fails completely",
17207
+ 1: "Fair - meets expectations",
17208
+ 2: "Excellent - exceeds expectations"
17209
+ },
17210
+ recommendations: recommendations.map((rec) => ({
17211
+ recommendationId: rec.id,
17212
+ title: rec.title,
17213
+ category: rec.category,
17214
+ priority: rec.priority,
17215
+ summary: rec.summary,
17216
+ scores: {
17217
+ grounded: 0,
17218
+ correct: 0,
17219
+ specific: 0,
17220
+ actionable: 0,
17221
+ valuable: 0
17222
+ },
17223
+ notes: "",
17224
+ implemented: false,
17225
+ implementationNotes: ""
17226
+ }))
17227
+ };
17228
+ return JSON.stringify(template, null, 2);
17229
+ }
17230
+ async processFeedbackTemplate(templateJson) {
17231
+ try {
17232
+ const template = JSON.parse(templateJson);
17233
+ if (!template.recommendations || !Array.isArray(template.recommendations)) {
17234
+ throw new Error("Invalid template format");
17235
+ }
17236
+ for (const recFeedback of template.recommendations) {
17237
+ this.addFeedback({
17238
+ recommendationId: recFeedback.recommendationId,
17239
+ reviewer: "human",
17240
+ scores: recFeedback.scores,
17241
+ notes: recFeedback.notes,
17242
+ implemented: recFeedback.implemented,
17243
+ implementationNotes: recFeedback.implementationNotes
17244
+ });
17245
+ }
17246
+ await this.saveFeedback();
17247
+ } catch (error) {
17248
+ console.error("Error processing feedback template:", error);
17249
+ throw error;
17250
+ }
17251
+ }
17252
+ getRecommendationQualityTrends() {
17253
+ const trends = {
17254
+ byCategory: {},
17255
+ byPriority: {}
17256
+ };
17257
+ for (const feedback of this.feedback) {
17258
+ const totalScore = Object.values(feedback.scores).reduce((sum, score) => sum + score, 0);
17259
+ const maxScore = Object.values(feedback.scores).length * 2;
17260
+ const normalizedScore = totalScore / maxScore;
17261
+ }
17262
+ return trends;
17263
+ }
17264
+ identifyLowQualityRecommendations(threshold = 0.5) {
17265
+ const lowQualityIds = [];
17266
+ for (const feedback of this.feedback) {
17267
+ const totalScore = Object.values(feedback.scores).reduce((sum, score) => sum + score, 0);
17268
+ const maxScore = Object.values(feedback.scores).length * 2;
17269
+ const normalizedScore = totalScore / maxScore;
17270
+ if (normalizedScore < threshold) {
17271
+ lowQualityIds.push(feedback.recommendationId);
17272
+ }
17273
+ }
17274
+ return [...new Set(lowQualityIds)];
17275
+ }
17276
+ identifyHighValueRecommendations(threshold = 0.8) {
17277
+ const highValueIds = [];
17278
+ for (const feedback of this.feedback) {
17279
+ if (feedback.scores.valuable >= 1.5 && feedback.implemented) {
17280
+ highValueIds.push(feedback.recommendationId);
17281
+ }
17282
+ }
17283
+ return [...new Set(highValueIds)];
17284
+ }
17285
+ generateQualityReport() {
17286
+ const stats = this.getFeedbackStats();
17287
+ const lowQuality = this.identifyLowQualityRecommendations();
17288
+ const highValue = this.identifyHighValueRecommendations();
17289
+ return `
17290
+ # Recommendation Quality Report
17291
+
17292
+ ## Overall Statistics
17293
+ - Total recommendations reviewed: ${stats.total}
17294
+ - Average scores (0-2 scale):
17295
+ - Grounded: ${stats.averageScores.grounded.toFixed(2)}
17296
+ - Correct: ${stats.averageScores.correct.toFixed(2)}
17297
+ - Specific: ${stats.averageScores.specific.toFixed(2)}
17298
+ - Actionable: ${stats.averageScores.actionable.toFixed(2)}
17299
+ - Valuable: ${stats.averageScores.valuable.toFixed(2)}
17300
+ - Implementation rate: ${(stats.implementationRate * 100).toFixed(1)}%
17301
+
17302
+ ## Quality Insights
17303
+ - Low quality recommendations (<50% score): ${lowQuality.length}
17304
+ - High value implemented recommendations (>80% valuable): ${highValue.length}
17305
+
17306
+ ## Recommendations
17307
+ - Focus on improving: ${lowQuality.length > 0 ? lowQuality.join(", ") : "None identified"}
17308
+ - Replicate success patterns: ${highValue.length > 0 ? highValue.join(", ") : "Insufficient data"}
17309
+
17310
+ ## Next Steps
17311
+ 1. Address low-quality recommendation patterns
17312
+ 2. Analyze high-value recommendations for common characteristics
17313
+ 3. Use feedback to tune confidence thresholds and validation criteria
17314
+ `.trim();
17315
+ }
17316
+ }
17317
+
17318
+ // src/recommendations/finding-builder.ts
17319
+ class FindingBuilder {
17320
+ context;
17321
+ constructor(context) {
17322
+ this.context = context;
17323
+ }
17324
+ buildFindings() {
17325
+ const findings = [];
17326
+ findings.push(...this.buildFoundationFindings());
17327
+ findings.push(...this.buildSecurityFindings());
17328
+ findings.push(...this.buildWorkflowFindings());
17329
+ findings.push(...this.buildAIFindings());
17330
+ findings.push(...this.buildGovernanceFindings());
17331
+ return findings;
17332
+ }
17333
+ buildFoundationFindings() {
17334
+ const findings = [];
17335
+ const { evidence } = this.context;
17336
+ if (!evidence.structure.hasReadme) {
17337
+ findings.push(this.createFinding("found-missing-readme", "foundation", "medium", "Repository lacks README documentation", [
17338
+ {
17339
+ type: "missing",
17340
+ path: "README.md",
17341
+ description: "No README.md found in repository root",
17342
+ confidence: 100
17343
+ }
17344
+ ], ["README.md"]));
17345
+ }
17346
+ if (!evidence.structure.hasLicense) {
17347
+ findings.push(this.createFinding("found-missing-license", "foundation", "high", "Repository lacks license file", [
17348
+ {
17349
+ type: "missing",
17350
+ path: "LICENSE",
17351
+ description: "No LICENSE file found",
17352
+ confidence: 100
17353
+ }
17354
+ ], ["LICENSE"]));
17355
+ }
17356
+ if (evidence.structure.directoryDepth > 8) {
17357
+ findings.push(this.createFinding("found-deep-structure", "foundation", "medium", "Repository has excessively deep directory structure", [
17358
+ {
17359
+ type: "metric",
17360
+ metric: {
17361
+ name: "directory_depth",
17362
+ value: evidence.structure.directoryDepth,
17363
+ threshold: 8
17364
+ },
17365
+ description: `Directory depth is ${evidence.structure.directoryDepth} levels (recommended: \u22648)`,
17366
+ confidence: 90
17367
+ }
17368
+ ], []));
17369
+ }
17370
+ if (!evidence.configuration.hasGitignore) {
17371
+ findings.push(this.createFinding("found-missing-gitignore", "foundation", "high", "Repository lacks .gitignore file", [
17372
+ {
17373
+ type: "missing",
17374
+ path: ".gitignore",
17375
+ description: "No .gitignore file found",
17376
+ confidence: 100
17377
+ }
17378
+ ], [".gitignore"]));
17379
+ }
17380
+ if (!evidence.configuration.hasTypeScript && this.isTypeScriptProject()) {
17381
+ findings.push(this.createFinding("found-typescript-unconfigured", "foundation", "medium", "TypeScript project lacks configuration", [
17382
+ {
17383
+ type: "missing",
17384
+ path: "tsconfig.json",
17385
+ description: "TypeScript files detected but no tsconfig.json found",
17386
+ confidence: 95
17387
+ }
17388
+ ], ["tsconfig.json"]));
17389
+ }
17390
+ return findings;
17391
+ }
17392
+ buildSecurityFindings() {
17393
+ const findings = [];
17394
+ const { copilotFeatures } = this.context;
17395
+ if (!copilotFeatures.githubFeatures.codeowners.found) {
17396
+ findings.push(this.createFinding("found-missing-codeowners", "security", "high", "Repository lacks CODEOWNERS file", [
17397
+ {
17398
+ type: "missing",
17399
+ path: ".github/CODEOWNERS",
17400
+ description: "No CODEOWNERS file found for code ownership rules",
17401
+ confidence: 100
17402
+ }
17403
+ ], [".github/CODEOWNERS"]));
17404
+ }
17405
+ if (this.context.evidence.metrics.dependencyHealth === "poor") {
17406
+ findings.push(this.createFinding("found-poor-dependencies", "security", "high", "Repository has poor dependency health", [
17407
+ {
17408
+ type: "metric",
17409
+ metric: {
17410
+ name: "dependency_health",
17411
+ value: this.context.evidence.metrics.dependencyHealth
17412
+ },
17413
+ description: "Dependency health assessment indicates security or maintenance issues",
17414
+ confidence: 70
17415
+ }
17416
+ ], ["package.json", "yarn.lock", "package-lock.json"]));
17417
+ }
17418
+ return findings;
17419
+ }
17420
+ buildWorkflowFindings() {
17421
+ const findings = [];
17422
+ const { evidence, copilotFeatures } = this.context;
17423
+ if (!evidence.configuration.hasCi) {
17424
+ findings.push(this.createFinding("found-no-ci", "workflow", "high", "Repository lacks CI/CD configuration", [
17425
+ {
17426
+ type: "missing",
17427
+ path: ".github/workflows/",
17428
+ description: "No CI/CD workflows found in .github/workflows/",
17429
+ confidence: 100
17430
+ }
17431
+ ], [".github/workflows/"]));
17432
+ }
17433
+ if (!evidence.configuration.hasTests) {
17434
+ findings.push(this.createFinding("found-no-tests", "workflow", "high", "Repository lacks test configuration", [
17435
+ {
17436
+ type: "missing",
17437
+ description: "No test framework configuration detected",
17438
+ confidence: 90
17439
+ }
17440
+ ], []));
17441
+ }
17442
+ if (!copilotFeatures.githubFeatures.prTemplates.found) {
17443
+ findings.push(this.createFinding("found-no-pr-templates", "workflow", "medium", "Repository lacks PR templates", [
17444
+ {
17445
+ type: "missing",
17446
+ path: ".github/pull_request_template.md",
17447
+ description: "No PR template found for standardizing contributions",
17448
+ confidence: 100
17449
+ }
17450
+ ], [".github/pull_request_template.md"]));
17451
+ }
17452
+ return findings;
17453
+ }
17454
+ buildAIFindings() {
17455
+ const findings = [];
17456
+ const { copilotFeatures } = this.context;
17457
+ if (!copilotFeatures.githubFeatures.copilotInstructions.found) {
17458
+ findings.push(this.createFinding("found-no-copilot-instructions", "ai", "medium", "Repository lacks Copilot instructions", [
17459
+ {
17460
+ type: "missing",
17461
+ path: ".github/copilot-instructions.md",
17462
+ description: "No Copilot instructions found for AI assistance guidance",
17463
+ confidence: 100
17464
+ }
17465
+ ], [".github/copilot-instructions.md"]));
17466
+ }
17467
+ if (copilotFeatures.codePatterns.aiFriendlyComments < 5) {
17468
+ findings.push(this.createFinding("found-low-ai-comments", "ai", "low", "Low AI-friendly comment density", [
17469
+ {
17470
+ type: "metric",
17471
+ metric: {
17472
+ name: "ai_friendly_comments",
17473
+ value: copilotFeatures.codePatterns.aiFriendlyComments,
17474
+ threshold: 5
17475
+ },
17476
+ description: `Only ${copilotFeatures.codePatterns.aiFriendlyComments} AI-friendly comment patterns found`,
17477
+ confidence: 80
17478
+ }
17479
+ ], []));
17480
+ }
17481
+ return findings;
17482
+ }
17483
+ buildGovernanceFindings() {
17484
+ const findings = [];
17485
+ const { evidence } = this.context;
17486
+ if (!evidence.structure.hasContributing) {
17487
+ findings.push(this.createFinding("found-no-contributing", "governance", "medium", "Repository lacks contributing guidelines", [
17488
+ {
17489
+ type: "missing",
17490
+ path: "CONTRIBUTING.md",
17491
+ description: "No CONTRIBUTING.md found for contribution process",
17492
+ confidence: 100
17493
+ }
17494
+ ], ["CONTRIBUTING.md"]));
17495
+ }
17496
+ if (!evidence.structure.hasChangelog) {
17497
+ findings.push(this.createFinding("found-no-changelog", "governance", "low", "Repository lacks changelog", [
17498
+ {
17499
+ type: "missing",
17500
+ path: "CHANGELOG.md",
17501
+ description: "No CHANGELOG.md found for tracking changes",
17502
+ confidence: 100
17503
+ }
17504
+ ], ["CHANGELOG.md"]));
17505
+ }
17506
+ if (evidence.patterns.documentationCoverage < 10) {
17507
+ findings.push(this.createFinding("found-low-docs", "governance", "medium", "Low documentation coverage", [
17508
+ {
17509
+ type: "metric",
17510
+ metric: {
17511
+ name: "documentation_coverage",
17512
+ value: evidence.patterns.documentationCoverage,
17513
+ threshold: 10
17514
+ },
17515
+ description: `Documentation coverage is only ${evidence.patterns.documentationCoverage}%`,
17516
+ confidence: 85
17517
+ }
17518
+ ], []));
17519
+ }
17520
+ return findings;
17521
+ }
17522
+ createFinding(id, category, severity, summary, evidenceAnchors, affectedFiles) {
17523
+ return {
17524
+ id,
17525
+ category,
17526
+ severity,
17527
+ summary,
17528
+ evidenceAnchors,
17529
+ affectedFiles,
17530
+ relatedScores: this.getRelatedScores(category),
17531
+ metadata: {
17532
+ detectionMethod: "deterministic_pattern_match",
17533
+ reproducible: true,
17534
+ timestamp: new Date().toISOString()
17535
+ }
17536
+ };
17537
+ }
17538
+ getRelatedScores(category) {
17539
+ const { scores } = this.context;
17540
+ const scoreMap = {
17541
+ foundation: ["foundation"],
17542
+ security: ["security"],
17543
+ workflow: ["workflow"],
17544
+ ai: ["ai"],
17545
+ governance: ["governance"]
17546
+ };
17547
+ return scoreMap[category].map((scoreCategory) => ({
17548
+ category: scoreCategory,
17549
+ score: scores.breakdown[scoreCategory],
17550
+ impact: "negative"
17551
+ }));
17552
+ }
17553
+ isTypeScriptProject() {
17554
+ const { techStack } = this.context;
17555
+ return techStack.aiReadiness.typescriptUsage || this.context.evidence.configuration.hasTypeScript;
17556
+ }
17557
+ }
17558
+
17559
+ // src/recommendations/hypothesis-engine.ts
17560
+ class HypothesisEngine {
17561
+ context;
17562
+ constructor(context) {
17563
+ this.context = context;
17564
+ }
17565
+ generateHypotheses(findings) {
17566
+ const hypotheses = [];
17567
+ const findingsByCategory = this.groupFindingsByCategory(findings);
17568
+ for (const [category, categoryFindings] of Object.entries(findingsByCategory)) {
17569
+ hypotheses.push(...this.generateCategoryHypotheses(category, categoryFindings));
17570
+ }
17571
+ hypotheses.push(...this.generateCrossCategoryHypotheses(findings));
17572
+ return hypotheses;
17573
+ }
17574
+ groupFindingsByCategory(findings) {
17575
+ const grouped = {};
17576
+ for (const finding of findings) {
17577
+ if (!grouped[finding.category]) {
17578
+ grouped[finding.category] = [];
17579
+ }
17580
+ grouped[finding.category].push(finding);
17581
+ }
17582
+ return grouped;
17583
+ }
17584
+ generateCategoryHypotheses(category, findings) {
17585
+ const hypotheses = [];
17586
+ switch (category) {
17587
+ case "security":
17588
+ hypotheses.push(...this.generateSecurityHypotheses(findings));
17589
+ break;
17590
+ case "foundation":
17591
+ hypotheses.push(...this.generateFoundationHypotheses(findings));
17592
+ break;
17593
+ case "workflow":
17594
+ hypotheses.push(...this.generateWorkflowHypotheses(findings));
17595
+ break;
17596
+ case "ai":
17597
+ hypotheses.push(...this.generateAIHypotheses(findings));
17598
+ break;
17599
+ case "governance":
17600
+ hypotheses.push(...this.generateGovernanceHypotheses(findings));
17601
+ break;
17602
+ }
17603
+ return hypotheses;
17604
+ }
17605
+ generateSecurityHypotheses(findings) {
17606
+ const hypotheses = [];
17607
+ const codeownersFinding = findings.find((f) => f.id === "found-missing-codeowners");
17608
+ if (codeownersFinding) {
17609
+ hypotheses.push({
17610
+ id: "hyp-codeowners-velocity",
17611
+ title: "Lack of CODEOWNERS may reduce safe AI-assisted change velocity",
17612
+ description: "Without clear code ownership, AI-assisted changes may lack proper review and approval pathways, potentially reducing the safety and speed of AI-driven development.",
17613
+ category: "security",
17614
+ supportingFindings: [codeownersFinding.id],
17615
+ reasoning: "CODEOWNERS files establish clear ownership and review requirements. In AI-assisted development, this becomes critical for ensuring AI-generated changes receive appropriate human oversight.",
17616
+ confidence: 75,
17617
+ alternativeInterpretations: [
17618
+ "Team may use alternative approval mechanisms",
17619
+ "Repository may not require formal review processes",
17620
+ "Code ownership might be documented elsewhere"
17621
+ ],
17622
+ validationChecks: [
17623
+ {
17624
+ id: "vc-codeowners-alternatives",
17625
+ type: "repo_specificity",
17626
+ description: "Check if alternative approval mechanisms exist",
17627
+ expected: "No alternative approval mechanisms found"
17628
+ },
17629
+ {
17630
+ id: "vc-codeowners-necessity",
17631
+ type: "actionability",
17632
+ description: "Assess if CODEOWNERS is necessary for this repo type",
17633
+ expected: "Repository would benefit from clear ownership"
17634
+ }
17635
+ ],
17636
+ estimatedImpact: "high",
17637
+ estimatedEffort: "small"
17638
+ });
17639
+ }
17640
+ const depFinding = findings.find((f) => f.id === "found-poor-dependencies");
17641
+ if (depFinding) {
17642
+ hypotheses.push({
17643
+ id: "hyp-deps-security-risk",
17644
+ title: "Poor dependency health indicates potential security vulnerabilities",
17645
+ description: "The repository's dependencies show signs of poor maintenance, which could introduce security vulnerabilities and compatibility issues.",
17646
+ category: "security",
17647
+ supportingFindings: [depFinding.id],
17648
+ reasoning: "Poor dependency health often indicates outdated packages with known vulnerabilities or lack of security updates.",
17649
+ confidence: 70,
17650
+ alternativeInterpretations: [
17651
+ "Dependencies may be stable but infrequently updated",
17652
+ "Security scanning might be handled separately",
17653
+ "Repository might use dependency pinning for stability"
17654
+ ],
17655
+ validationChecks: [
17656
+ {
17657
+ id: "vc-deps-actual-vulnerabilities",
17658
+ type: "evidence_strength",
17659
+ description: "Check for actual security vulnerabilities",
17660
+ expected: "Security audit would reveal vulnerabilities"
17661
+ },
17662
+ {
17663
+ id: "vc-deps-update-frequency",
17664
+ type: "repo_specificity",
17665
+ description: "Assess dependency update patterns",
17666
+ expected: "Dependencies are significantly outdated"
17667
+ }
17668
+ ],
17669
+ estimatedImpact: "high",
17670
+ estimatedEffort: "medium"
17671
+ });
17672
+ }
17673
+ return hypotheses;
17674
+ }
17675
+ generateFoundationHypotheses(findings) {
17676
+ const hypotheses = [];
17677
+ const criticalFoundationFindings = findings.filter((f) => f.category === "foundation" && (f.severity === "high" || f.severity === "critical"));
17678
+ if (criticalFoundationFindings.length >= 2) {
17679
+ hypotheses.push({
17680
+ id: "hyp-foundation-immaturity",
17681
+ title: "Multiple foundation issues indicate repository immaturity",
17682
+ description: "The repository shows several foundational gaps that suggest it's in early stages of development or lacks proper project hygiene.",
17683
+ category: "foundation",
17684
+ supportingFindings: criticalFoundationFindings.map((f) => f.id),
17685
+ reasoning: "Multiple foundational issues (README, license, .gitignore, etc.) indicate lack of project maturity and potentially poor developer experience.",
17686
+ confidence: 85,
17687
+ alternativeInterpretations: [
17688
+ "Repository might be intentionally minimal",
17689
+ "Project might be internal or temporary",
17690
+ "Documentation might exist in external systems"
17691
+ ],
17692
+ validationChecks: [
17693
+ {
17694
+ id: "vc-foundation-repo-age",
17695
+ type: "repo_specificity",
17696
+ description: "Check repository age and activity",
17697
+ expected: "Repository is relatively new or inactive"
17698
+ },
17699
+ {
17700
+ id: "vc-foundation-project-type",
17701
+ type: "actionability",
17702
+ description: "Assess if foundation items are needed for this project type",
17703
+ expected: "Project would benefit from standard foundation items"
17704
+ }
17705
+ ],
17706
+ estimatedImpact: "medium",
17707
+ estimatedEffort: "medium"
17708
+ });
17709
+ }
17710
+ const tsFinding = findings.find((f) => f.id === "found-typescript-unconfigured");
17711
+ if (tsFinding) {
17712
+ hypotheses.push({
17713
+ id: "hyp-typescript-consistency",
17714
+ title: "Unconfigured TypeScript may lead to inconsistent code quality",
17715
+ description: "TypeScript files exist without proper configuration, which could lead to inconsistent type checking and reduced developer experience.",
17716
+ category: "foundation",
17717
+ supportingFindings: [tsFinding.id],
17718
+ reasoning: "TypeScript configuration ensures consistent type checking across the project. Without it, developers may have different experiences and code quality may vary.",
17719
+ confidence: 80,
17720
+ alternativeInterpretations: [
17721
+ "TypeScript might be used with default settings",
17722
+ "Configuration might be inherited from parent package",
17723
+ "Project might be in migration phase"
17724
+ ],
17725
+ validationChecks: [
17726
+ {
17727
+ id: "vc-typescript-usage",
17728
+ type: "evidence_strength",
17729
+ description: "Verify actual TypeScript usage extent",
17730
+ expected: "Significant TypeScript files exist"
17731
+ },
17732
+ {
17733
+ id: "vc-typescript-config-impact",
17734
+ type: "actionability",
17735
+ description: "Assess impact of missing configuration",
17736
+ expected: "Configuration would significantly improve development"
17737
+ }
17738
+ ],
17739
+ estimatedImpact: "medium",
17740
+ estimatedEffort: "small"
17741
+ });
17742
+ }
17743
+ return hypotheses;
17744
+ }
17745
+ generateWorkflowHypotheses(findings) {
17746
+ const hypotheses = [];
17747
+ const ciFinding = findings.find((f) => f.id === "found-no-ci");
17748
+ if (ciFinding) {
17749
+ hypotheses.push({
17750
+ id: "hyp-ci-automation-gap",
17751
+ title: "Lack of CI/CD indicates manual deployment and higher risk",
17752
+ description: "Without automated CI/CD, the repository relies on manual processes which increase deployment risk and reduce development velocity.",
17753
+ category: "workflow",
17754
+ supportingFindings: [ciFinding.id],
17755
+ reasoning: "CI/CD automation is standard practice for reliable deployments and quality control. Manual processes are error-prone and slow.",
17756
+ confidence: 90,
17757
+ alternativeInterpretations: [
17758
+ "Repository might be deployed through external systems",
17759
+ "Project might not require automated deployments",
17760
+ "CI/CD might be handled at organizational level"
17761
+ ],
17762
+ validationChecks: [
17763
+ {
17764
+ id: "vc-ci-deployment-frequency",
17765
+ type: "repo_specificity",
17766
+ description: "Check deployment patterns and frequency",
17767
+ expected: "Manual deployments would benefit from automation"
17768
+ },
17769
+ {
17770
+ id: "vc-ci-complexity",
17771
+ type: "actionability",
17772
+ description: "Assess if CI/CD setup is feasible",
17773
+ expected: "Project complexity warrants CI/CD implementation"
17774
+ }
17775
+ ],
17776
+ estimatedImpact: "high",
17777
+ estimatedEffort: "medium"
17778
+ });
17779
+ }
17780
+ const testFinding = findings.find((f) => f.id === "found-no-tests");
17781
+ if (testFinding) {
17782
+ hypotheses.push({
17783
+ id: "hyp-testing-quality-risk",
17784
+ title: "No test configuration indicates quality and maintenance risks",
17785
+ description: "The absence of test configuration suggests minimal automated testing, which increases the risk of bugs and makes changes more dangerous.",
17786
+ category: "workflow",
17787
+ supportingFindings: [testFinding.id],
17788
+ reasoning: "Automated testing is essential for maintaining code quality and enabling safe refactoring. Without tests, changes are risky and maintenance is costly.",
17789
+ confidence: 85,
17790
+ alternativeInterpretations: [
17791
+ "Testing might be done manually or externally",
17792
+ "Project might be simple enough to not need tests",
17793
+ "Tests might exist but not be configured for the test runner"
17794
+ ],
17795
+ validationChecks: [
17796
+ {
17797
+ id: "vc-testing-complexity",
17798
+ type: "repo_specificity",
17799
+ description: "Assess project complexity and testing needs",
17800
+ expected: "Project complexity warrants automated testing"
17801
+ },
17802
+ {
17803
+ id: "vc-testing-implementation",
17804
+ type: "actionability",
17805
+ description: "Evaluate feasibility of implementing tests",
17806
+ expected: "Testing implementation would provide significant value"
17807
+ }
17808
+ ],
17809
+ estimatedImpact: "high",
17810
+ estimatedEffort: "large"
17811
+ });
17812
+ }
17813
+ return hypotheses;
17814
+ }
17815
+ generateAIHypotheses(findings) {
17816
+ const hypotheses = [];
17817
+ const copilotFinding = findings.find((f) => f.id === "found-no-copilot-instructions");
17818
+ if (copilotFinding) {
17819
+ hypotheses.push({
17820
+ id: "hyp-copilot-effectiveness",
17821
+ title: "Lack of Copilot instructions reduces AI assistance effectiveness",
17822
+ description: "Without specific Copilot instructions, AI assistance may not align with project standards and coding practices, reducing its effectiveness.",
17823
+ category: "ai",
17824
+ supportingFindings: [copilotFinding.id],
17825
+ reasoning: "Copilot instructions guide AI tools to generate code that matches project patterns, standards, and preferences. Without them, AI assistance is generic and less valuable.",
17826
+ confidence: 75,
17827
+ alternativeInterpretations: [
17828
+ "Code patterns might be self-documenting",
17829
+ "Team might not use AI assistance extensively",
17830
+ "Instructions might exist in project documentation"
17831
+ ],
17832
+ validationChecks: [
17833
+ {
17834
+ id: "vc-copilot-usage",
17835
+ type: "repo_specificity",
17836
+ description: "Assess AI tool usage in the project",
17837
+ expected: "AI assistance would benefit from specific guidance"
17838
+ },
17839
+ {
17840
+ id: "vc-copilot-patterns",
17841
+ type: "actionability",
17842
+ description: "Evaluate if project has specific patterns worth documenting",
17843
+ expected: "Project has unique patterns that AI should follow"
17844
+ }
17845
+ ],
17846
+ estimatedImpact: "medium",
17847
+ estimatedEffort: "small"
17848
+ });
17849
+ }
17850
+ return hypotheses;
17851
+ }
17852
+ generateGovernanceHypotheses(findings) {
17853
+ const hypotheses = [];
17854
+ const contributingFinding = findings.find((f) => f.id === "found-no-contributing");
17855
+ if (contributingFinding) {
17856
+ hypotheses.push({
17857
+ id: "hyp-contribution-barrier",
17858
+ title: "Missing contributing guidelines creates contribution barriers",
17859
+ description: "Without clear contribution guidelines, potential contributors face uncertainty and may abandon participation, limiting community growth.",
17860
+ category: "governance",
17861
+ supportingFindings: [contributingFinding.id],
17862
+ reasoning: "Contributing guidelines lower the barrier to entry by clearly communicating expectations, processes, and standards for participation.",
17863
+ confidence: 70,
17864
+ alternativeInterpretations: [
17865
+ "Project might not accept external contributions",
17866
+ "Guidelines might be documented in README",
17867
+ "Project might be intentionally closed to contributions"
17868
+ ],
17869
+ validationChecks: [
17870
+ {
17871
+ id: "vc-contribution-activity",
17872
+ type: "repo_specificity",
17873
+ description: "Check if repository accepts external contributions",
17874
+ expected: "Repository would benefit from external contributions"
17875
+ },
17876
+ {
17877
+ id: "vc-contribution-clarity",
17878
+ type: "actionability",
17879
+ description: "Assess if contribution process is unclear",
17880
+ expected: "Contribution process would be clearer with guidelines"
17881
+ }
17882
+ ],
17883
+ estimatedImpact: "medium",
17884
+ estimatedEffort: "small"
17885
+ });
17886
+ }
17887
+ return hypotheses;
17888
+ }
17889
+ generateCrossCategoryHypotheses(findings) {
17890
+ const hypotheses = [];
17891
+ const securityFindings = findings.filter((f) => f.category === "security" && f.severity === "high");
17892
+ const workflowFindings = findings.filter((f) => f.category === "workflow" && f.severity === "high");
17893
+ if (securityFindings.length > 0 && workflowFindings.length > 0) {
17894
+ hypotheses.push({
17895
+ id: "hyp-security-workflow-gap",
17896
+ title: "Security and workflow gaps indicate process immaturity",
17897
+ description: "The combination of security and workflow issues suggests the repository lacks mature development processes that could pose risks for AI-assisted development.",
17898
+ category: "governance",
17899
+ supportingFindings: [
17900
+ ...securityFindings.map((f) => f.id),
17901
+ ...workflowFindings.map((f) => f.id)
17902
+ ],
17903
+ reasoning: "Mature development processes integrate security practices into automated workflows. Gaps in both areas suggest process immaturity.",
17904
+ confidence: 80,
17905
+ alternativeInterpretations: [
17906
+ "Security might be handled through external processes",
17907
+ "Workflow might be intentionally simple",
17908
+ "Project might be in early development phase"
17909
+ ],
17910
+ validationChecks: [
17911
+ {
17912
+ id: "vc-process-maturity",
17913
+ type: "repo_specificity",
17914
+ description: "Assess overall development process maturity",
17915
+ expected: "Process maturity is low and would benefit from improvement"
17916
+ },
17917
+ {
17918
+ id: "vc-ai-readiness",
17919
+ type: "actionability",
17920
+ description: "Evaluate impact on AI-assisted development",
17921
+ expected: "Process gaps would significantly impact AI development safety"
17922
+ }
17923
+ ],
17924
+ estimatedImpact: "high",
17925
+ estimatedEffort: "large"
17926
+ });
17927
+ }
17928
+ return hypotheses;
17929
+ }
17930
+ }
17931
+
17932
+ // src/recommendations/ranker.ts
17933
+ class Ranker {
17934
+ context;
17935
+ constructor(context) {
17936
+ this.context = context;
17937
+ }
17938
+ rankRecommendations(validationResults, challengerAssessments, findings, hypotheses) {
17939
+ const promotedValidations = validationResults.filter((v) => v.recommendation === "promote" || v.recommendation === "downgrade");
17940
+ const approvedRecommendations = challengerAssessments.filter((a) => a.finalVerdict === "approve" || a.finalVerdict === "require_human_review");
17941
+ const finalRecommendations = [];
17942
+ for (const validation of promotedValidations) {
17943
+ const hypothesis = hypotheses.find((h) => h.id === validation.hypothesisId);
17944
+ if (!hypothesis)
17945
+ continue;
17946
+ const challengerAssessment = approvedRecommendations.find((a) => a.recommendationId === hypothesis.id);
17947
+ if (challengerAssessment?.finalVerdict === "reject") {
17948
+ continue;
17949
+ }
17950
+ const recommendation = this.createRecommendation(hypothesis, validation, challengerAssessment, findings);
17951
+ finalRecommendations.push(recommendation);
17952
+ }
17953
+ return finalRecommendations.sort((a, b) => {
17954
+ const scoreA = this.calculatePriorityScore(a);
17955
+ const scoreB = this.calculatePriorityScore(b);
17956
+ return scoreB - scoreA;
17957
+ });
17958
+ }
17959
+ createRecommendation(hypothesis, validation, challengerAssessment, findings) {
17960
+ const supportingFindings = findings.filter((f) => hypothesis.supportingFindings.includes(f.id));
17961
+ const evidenceAnchors = supportingFindings.flatMap((f) => f.evidenceAnchors);
17962
+ const affectedFiles = [
17963
+ ...new Set(supportingFindings.flatMap((f) => f.affectedFiles))
17964
+ ];
17965
+ const baseConfidence = validation.confidence;
17966
+ const challengerAdjustment = challengerAssessment?.confidenceAdjustment || 0;
17967
+ const finalConfidence = Math.max(0, Math.min(100, baseConfidence + challengerAdjustment));
17968
+ const needsHumanReview = validation.recommendation === "human_review" || challengerAssessment?.finalVerdict === "require_human_review" || finalConfidence < this.context.config.humanReviewThreshold;
17969
+ const recommendation = {
17970
+ id: `rec-${hypothesis.id}`,
17971
+ title: hypothesis.title,
17972
+ category: hypothesis.category,
17973
+ priority: this.determinePriority(hypothesis, validation, finalConfidence),
17974
+ summary: hypothesis.description,
17975
+ whyThisMatters: this.generateWhyMatters(hypothesis, supportingFindings),
17976
+ evidenceAnchors,
17977
+ affectedFiles,
17978
+ supportingFindings: hypothesis.supportingFindings,
17979
+ supportingHypotheses: [hypothesis.id],
17980
+ validationSummary: {
17981
+ passed: validation.passed,
17982
+ confidence: validation.confidence,
17983
+ blockers: validation.blockers,
17984
+ warnings: validation.warnings
17985
+ },
17986
+ expectedImpact: {
17987
+ description: this.generateImpactDescription(hypothesis),
17988
+ areas: this.determineImpactAreas(hypothesis),
17989
+ metrics: this.generateImpactMetrics(hypothesis)
17990
+ },
17991
+ estimatedEffort: {
17992
+ size: hypothesis.estimatedEffort,
17993
+ timeframes: this.generateTimeframes(hypothesis.estimatedEffort),
17994
+ resources: this.generateResourceList(hypothesis)
17995
+ },
17996
+ confidence: {
17997
+ overall: finalConfidence,
17998
+ evidenceQuality: this.calculateEvidenceQuality(evidenceAnchors),
17999
+ repoSpecificity: this.calculateRepoSpecificity(hypothesis),
18000
+ actionability: this.calculateActionability(hypothesis)
18001
+ },
18002
+ caveats: this.generateCaveats(validation, challengerAssessment),
18003
+ suggestedNextStep: this.generateNextStep(hypothesis, supportingFindings),
18004
+ humanReviewNeeded: needsHumanReview,
18005
+ implementationHints: this.generateImplementationHints(hypothesis, supportingFindings),
18006
+ tags: this.generateTags(hypothesis, supportingFindings),
18007
+ metadata: {
18008
+ generated: new Date().toISOString(),
18009
+ version: "2.0.0",
18010
+ pipeline: [
18011
+ "findings",
18012
+ "hypotheses",
18013
+ "validation",
18014
+ "challenger",
18015
+ "ranking"
18016
+ ]
18017
+ }
18018
+ };
18019
+ return recommendation;
18020
+ }
18021
+ calculatePriorityScore(recommendation) {
18022
+ let score = 0;
18023
+ const priorityScores = { critical: 100, high: 75, medium: 50, low: 25 };
18024
+ score += priorityScores[recommendation.priority];
18025
+ score += recommendation.confidence.overall / 100 * 20;
18026
+ const impactBonus = recommendation.expectedImpact.areas.length * 5;
18027
+ score += impactBonus;
18028
+ const effortPenalties = { small: 0, medium: -5, large: -10 };
18029
+ score += effortPenalties[recommendation.estimatedEffort.size];
18030
+ score += recommendation.confidence.evidenceQuality / 100 * 10;
18031
+ score += recommendation.confidence.repoSpecificity / 100 * 10;
18032
+ const categoryWeights = {
18033
+ security: 1.2,
18034
+ foundation: 1.1,
18035
+ workflow: 1,
18036
+ ai: 0.9,
18037
+ governance: 0.8
18038
+ };
18039
+ score *= categoryWeights[recommendation.category];
18040
+ return Math.round(score);
18041
+ }
18042
+ determinePriority(hypothesis, validation, confidence) {
18043
+ let priority = hypothesis.estimatedImpact;
18044
+ if (confidence < 30) {
18045
+ priority = "low";
18046
+ } else if (confidence < 60 && priority === "critical") {
18047
+ priority = "high";
18048
+ } else if (confidence < 80 && priority === "high") {
18049
+ priority = "medium";
18050
+ }
18051
+ if (validation.warnings.length > 2 && priority !== "low") {
18052
+ const priorities = [
18053
+ "critical",
18054
+ "high",
18055
+ "medium",
18056
+ "low"
18057
+ ];
18058
+ const currentIndex = priorities.indexOf(priority);
18059
+ if (currentIndex < priorities.length - 1) {
18060
+ priority = priorities[currentIndex + 1];
18061
+ }
18062
+ }
18063
+ return priority;
18064
+ }
18065
+ generateWhyMatters(hypothesis, findings) {
18066
+ const categoryReasons = {
18067
+ security: "Security issues pose risks to code integrity, team safety, and organizational compliance. Addressing security gaps is foundational to safe AI-assisted development.",
18068
+ foundation: "Foundation issues affect developer experience, onboarding, and project maintainability. Strong foundations enable scalable, sustainable development.",
18069
+ workflow: "Workflow improvements directly impact development velocity, code quality, and team collaboration. Better workflows reduce friction and increase productivity.",
18070
+ ai: "AI readiness improvements enhance the effectiveness of AI assistance, leading to better code suggestions and more efficient development.",
18071
+ governance: "Governance improvements ensure consistent decision-making, clear contribution processes, and long-term project health."
18072
+ };
18073
+ let baseReason = categoryReasons[hypothesis.category] || "";
18074
+ if (findings.length > 0) {
18075
+ const highSeverityFindings = findings.filter((f) => f.severity === "high" || f.severity === "critical");
18076
+ if (highSeverityFindings.length > 0) {
18077
+ baseReason += ` This is particularly important given the ${highSeverityFindings.length} high-severity issues detected.`;
18078
+ }
18079
+ }
18080
+ return baseReason;
18081
+ }
18082
+ generateImpactDescription(hypothesis) {
18083
+ const impactTemplates = {
18084
+ critical: "Critical impact on {category} posture and overall development safety",
18085
+ high: "Significant improvement in {category} practices and team productivity",
18086
+ medium: "Moderate enhancement to {category} processes and developer experience",
18087
+ low: "Minor improvement to {category} hygiene and best practices"
18088
+ };
18089
+ return impactTemplates[hypothesis.estimatedImpact].replace("{category}", hypothesis.category);
18090
+ }
18091
+ determineImpactAreas(hypothesis) {
18092
+ const areaMap = {
18093
+ security: ["security", "compliance", "risk_management"],
18094
+ foundation: ["developer_experience", "onboarding", "maintainability"],
18095
+ workflow: ["productivity", "code_quality", "collaboration"],
18096
+ ai: ["ai_effectiveness", "code_generation", "development_speed"],
18097
+ governance: [
18098
+ "process_consistency",
18099
+ "contribution_quality",
18100
+ "project_health"
18101
+ ]
18102
+ };
18103
+ return areaMap[hypothesis.category] || [];
18104
+ }
18105
+ generateImpactMetrics(hypothesis) {
18106
+ const metricsMap = {
18107
+ security: ["security_score", "vulnerability_count", "compliance_status"],
18108
+ foundation: [
18109
+ "setup_time",
18110
+ "onboarding_success_rate",
18111
+ "documentation_coverage"
18112
+ ],
18113
+ workflow: ["deployment_frequency", "lead_time", "change_failure_rate"],
18114
+ ai: ["ai_adoption_rate", "code_acceptance_rate", "development_velocity"],
18115
+ governance: [
18116
+ "contribution_rate",
18117
+ "process_adherence",
18118
+ "decision_quality"
18119
+ ]
18120
+ };
18121
+ return metricsMap[hypothesis.category] || [];
18122
+ }
18123
+ generateTimeframes(effort) {
18124
+ const timeframes = {
18125
+ small: {
18126
+ best: "1-2 days",
18127
+ expected: "3-5 days",
18128
+ worst: "1 week"
18129
+ },
18130
+ medium: {
18131
+ best: "1 week",
18132
+ expected: "2-3 weeks",
18133
+ worst: "1 month"
18134
+ },
18135
+ large: {
18136
+ best: "2-3 weeks",
18137
+ expected: "1-2 months",
18138
+ worst: "3 months"
18139
+ }
18140
+ };
18141
+ return timeframes[effort];
18142
+ }
18143
+ generateResourceList(hypothesis) {
18144
+ const baseResources = ["developer_time", "code_review"];
18145
+ const categoryResources = {
18146
+ security: ["security_review", "compliance_check"],
18147
+ foundation: ["documentation_effort", "setup_configuration"],
18148
+ workflow: ["devops_time", "process_design"],
18149
+ ai: ["ai_tool_configuration", "team_training"],
18150
+ governance: ["stakeholder_input", "process_documentation"]
18151
+ };
18152
+ return [
18153
+ ...baseResources,
18154
+ ...categoryResources[hypothesis.category] || []
18155
+ ];
18156
+ }
18157
+ calculateEvidenceQuality(evidenceAnchors) {
18158
+ if (evidenceAnchors.length === 0)
18159
+ return 0;
18160
+ const totalConfidence = evidenceAnchors.reduce((sum, anchor) => sum + anchor.confidence, 0);
18161
+ return Math.round(totalConfidence / evidenceAnchors.length);
18162
+ }
18163
+ calculateRepoSpecificity(hypothesis) {
18164
+ let specificity = 50;
18165
+ if (hypothesis.validationChecks.some((check) => check.description.includes("specific") || check.description.includes("file"))) {
18166
+ specificity += 20;
18167
+ }
18168
+ if (hypothesis.reasoning.includes("this repository") || hypothesis.reasoning.includes("the repository")) {
18169
+ specificity += 15;
18170
+ }
18171
+ if (hypothesis.description.includes("all repositories") || hypothesis.description.includes("every project")) {
18172
+ specificity -= 20;
18173
+ }
18174
+ return Math.max(0, Math.min(100, specificity));
18175
+ }
18176
+ calculateActionability(hypothesis) {
18177
+ let actionability = 50;
18178
+ if (["small", "medium", "large"].includes(hypothesis.estimatedEffort)) {
18179
+ actionability += 15;
18180
+ }
18181
+ if (hypothesis.validationChecks.some((check) => check.type === "actionability")) {
18182
+ actionability += 20;
18183
+ }
18184
+ const actionWords = [
18185
+ "add",
18186
+ "create",
18187
+ "implement",
18188
+ "configure",
18189
+ "establish"
18190
+ ];
18191
+ if (actionWords.some((word) => hypothesis.description.includes(word))) {
18192
+ actionability += 15;
18193
+ }
18194
+ return Math.max(0, Math.min(100, actionability));
18195
+ }
18196
+ generateCaveats(validation, challengerAssessment) {
18197
+ const caveats = [];
18198
+ caveats.push(...validation.warnings);
18199
+ if (challengerAssessment) {
18200
+ caveats.push(...challengerAssessment.criticisms);
18201
+ }
18202
+ if (validation.confidence < 70) {
18203
+ caveats.push("Limited evidence support - verify before implementation");
18204
+ }
18205
+ if (validation.confidence < 50) {
18206
+ caveats.push("Low confidence - requires human validation");
18207
+ }
18208
+ return caveats;
18209
+ }
18210
+ generateNextStep(hypothesis, findings) {
18211
+ const categoryNextSteps = {
18212
+ security: "Conduct security review and implement ownership controls",
18213
+ foundation: "Create foundational files and establish project standards",
18214
+ workflow: "Set up automated workflows and testing infrastructure",
18215
+ ai: "Configure AI tools and establish coding guidelines",
18216
+ governance: "Document processes and establish contribution guidelines"
18217
+ };
18218
+ let nextStep = categoryNextSteps[hypothesis.category] || "Analyze requirements and create implementation plan";
18219
+ const missingFileFindings = findings.filter((f) => f.evidenceAnchors.some((a) => a.type === "missing" && a.path));
18220
+ if (missingFileFindings.length > 0) {
18221
+ const missingFiles = missingFileFindings.flatMap((f) => f.evidenceAnchors.filter((a) => a.type === "missing" && a.path).map((a) => a.path)).slice(0, 2);
18222
+ if (missingFiles.length > 0) {
18223
+ nextStep = `Create ${missingFiles.join(" and ")} to address immediate gaps`;
18224
+ }
18225
+ }
18226
+ return nextStep;
18227
+ }
18228
+ generateImplementationHints(hypothesis, findings) {
18229
+ const hints = {
18230
+ commands: [],
18231
+ fileTemplates: [],
18232
+ references: []
18233
+ };
18234
+ const categoryCommands = {
18235
+ security: ["touch .github/CODEOWNERS", "git add .github/CODEOWNERS"],
18236
+ foundation: ["touch README.md", "touch LICENSE", "touch .gitignore"],
18237
+ workflow: [
18238
+ "mkdir -p .github/workflows",
18239
+ "touch .github/workflows/ci.yml"
18240
+ ],
18241
+ ai: ["touch .github/copilot-instructions.md"],
18242
+ governance: ["touch CONTRIBUTING.md", "touch CHANGELOG.md"]
18243
+ };
18244
+ hints.commands = categoryCommands[hypothesis.category] || [];
18245
+ hints.references = [
18246
+ "https://docs.github.com/en/get-started/quickstart",
18247
+ "https://www.conventionalcommits.org/",
18248
+ "https://semver.org/"
18249
+ ];
18250
+ return hints;
18251
+ }
18252
+ generateTags(hypothesis, findings) {
18253
+ const tags = [hypothesis.category];
18254
+ if (hypothesis.estimatedImpact === "critical") {
18255
+ tags.push("urgent", "security");
18256
+ }
18257
+ if (hypothesis.estimatedEffort === "small") {
18258
+ tags.push("quick-win");
18259
+ } else if (hypothesis.estimatedEffort === "large") {
18260
+ tags.push("significant-effort");
18261
+ }
18262
+ if (hypothesis.supportingFindings.length > 1) {
18263
+ tags.push("well-supported");
18264
+ }
18265
+ return tags;
18266
+ }
18267
+ }
18268
+
18269
+ // src/recommendations/validator.ts
18270
+ class Validator {
18271
+ context;
18272
+ constructor(context) {
18273
+ this.context = context;
18274
+ }
18275
+ validateHypotheses(hypotheses, findings) {
18276
+ const results = [];
18277
+ for (const hypothesis of hypotheses) {
18278
+ const result = this.validateHypothesis(hypothesis, findings);
18279
+ results.push(result);
18280
+ }
18281
+ return results;
18282
+ }
18283
+ validateHypothesis(hypothesis, findings) {
18284
+ const validationChecks = [];
18285
+ const blockers = [];
18286
+ const warnings = [];
18287
+ for (const check of hypothesis.validationChecks) {
18288
+ const result = this.executeValidationCheck(check, hypothesis, findings);
18289
+ validationChecks.push(result);
18290
+ if (!result.passed) {
18291
+ if (this.isBlocker(check.type, result.reason)) {
18292
+ blockers.push(result.reason || "Validation failed");
18293
+ } else {
18294
+ warnings.push(result.reason || "Validation warning");
18295
+ }
18296
+ }
18297
+ }
18298
+ const adjustedConfidence = this.calculateAdjustedConfidence(hypothesis.confidence, validationChecks);
18299
+ const recommendation = this.determineRecommendation(blockers, warnings, adjustedConfidence);
18300
+ return {
18301
+ hypothesisId: hypothesis.id,
18302
+ passed: recommendation === "promote",
18303
+ confidence: adjustedConfidence,
18304
+ validationChecks,
18305
+ blockers,
18306
+ warnings,
18307
+ recommendation
18308
+ };
18309
+ }
18310
+ executeValidationCheck(check, hypothesis, findings) {
18311
+ const resultCheck = { ...check };
18312
+ switch (check.type) {
18313
+ case "evidence_strength":
18314
+ resultCheck.passed = this.validateEvidenceStrength(hypothesis, findings);
18315
+ resultCheck.actual = resultCheck.passed ? "Strong evidence" : "Weak evidence";
18316
+ resultCheck.reason = resultCheck.passed ? "Supporting findings provide strong evidence" : "Supporting findings are weak or insufficient";
18317
+ break;
18318
+ case "repo_specificity":
18319
+ resultCheck.passed = this.validateRepoSpecificity(hypothesis);
18320
+ resultCheck.actual = resultCheck.passed ? "Repo-specific" : "Generic";
18321
+ resultCheck.reason = resultCheck.passed ? "Hypothesis addresses repo-specific characteristics" : "Hypothesis appears generic and could apply to any repo";
18322
+ break;
18323
+ case "actionability":
18324
+ resultCheck.passed = this.validateActionability(hypothesis);
18325
+ resultCheck.actual = resultCheck.passed ? "Actionable" : "Vague";
18326
+ resultCheck.reason = resultCheck.passed ? "Clear, actionable steps can be taken" : "Hypothesis lacks clear actionable steps";
18327
+ break;
18328
+ case "redundancy":
18329
+ resultCheck.passed = this.validateRedundancy(hypothesis, findings);
18330
+ resultCheck.actual = resultCheck.passed ? "Unique" : "Redundant";
18331
+ resultCheck.reason = resultCheck.passed ? "Hypothesis provides unique insight" : "Hypothesis overlaps with other findings/hypotheses";
18332
+ break;
18333
+ case "contradiction":
18334
+ resultCheck.passed = this.validateContradiction(hypothesis, findings);
18335
+ resultCheck.actual = resultCheck.passed ? "Consistent" : "Contradictory";
18336
+ resultCheck.reason = resultCheck.passed ? "Hypothesis is consistent with evidence" : "Hypothesis contradicts available evidence";
18337
+ break;
18338
+ }
18339
+ return resultCheck;
18340
+ }
18341
+ validateEvidenceStrength(hypothesis, findings) {
18342
+ const supportingFindings = findings.filter((f) => hypothesis.supportingFindings.includes(f.id));
18343
+ if (supportingFindings.length === 0) {
18344
+ return false;
18345
+ }
18346
+ let totalEvidenceQuality = 0;
18347
+ let evidenceCount = 0;
18348
+ for (const finding of supportingFindings) {
18349
+ for (const anchor of finding.evidenceAnchors) {
18350
+ totalEvidenceQuality += anchor.confidence;
18351
+ evidenceCount++;
18352
+ }
18353
+ }
18354
+ if (evidenceCount === 0) {
18355
+ return false;
18356
+ }
18357
+ const averageConfidence = totalEvidenceQuality / evidenceCount;
18358
+ return averageConfidence >= 70;
18359
+ }
18360
+ validateRepoSpecificity(hypothesis) {
18361
+ const repoSpecificIndicators = [
18362
+ this.context.repoPath,
18363
+ this.context.evidence.structure.fileCount,
18364
+ this.context.evidence.structure.directoryDepth,
18365
+ this.context.techStack.primaryLanguage,
18366
+ this.context.techStack.frameworks?.length || 0
18367
+ ];
18368
+ const description = hypothesis.description.toLowerCase();
18369
+ const reasoning = hypothesis.reasoning.toLowerCase();
18370
+ const specificIndicators = [
18371
+ "this repository",
18372
+ "the repository",
18373
+ "project shows",
18374
+ "codebase indicates",
18375
+ "analysis reveals",
18376
+ "detected",
18377
+ "found",
18378
+ "shows signs of"
18379
+ ];
18380
+ const hasSpecificLanguage = specificIndicators.some((indicator) => description.includes(indicator) || reasoning.includes(indicator));
18381
+ const genericIndicators = [
18382
+ "all repositories",
18383
+ "every project",
18384
+ "always",
18385
+ "never",
18386
+ "should always"
18387
+ ];
18388
+ const hasGenericLanguage = genericIndicators.some((indicator) => description.includes(indicator) || reasoning.includes(indicator));
18389
+ return hasSpecificLanguage && !hasGenericLanguage;
18390
+ }
18391
+ validateActionability(hypothesis) {
18392
+ const actionableIndicators = [
18393
+ "add",
18394
+ "create",
18395
+ "implement",
18396
+ "configure",
18397
+ "establish",
18398
+ "set up",
18399
+ "enable",
18400
+ "introduce",
18401
+ "define",
18402
+ "document"
18403
+ ];
18404
+ const description = hypothesis.description.toLowerCase();
18405
+ const reasoning = hypothesis.reasoning.toLowerCase();
18406
+ const hasActionableLanguage = actionableIndicators.some((indicator) => description.includes(indicator) || reasoning.includes(indicator));
18407
+ const hasRealisticEffort = ["small", "medium", "large"].includes(hypothesis.estimatedEffort);
18408
+ const hasMeaningfulImpact = ["critical", "high", "medium", "low"].includes(hypothesis.estimatedImpact);
18409
+ return hasActionableLanguage && hasRealisticEffort && hasMeaningfulImpact;
18410
+ }
18411
+ validateRedundancy(hypothesis, findings) {
18412
+ const supportingFindings = findings.filter((f) => hypothesis.supportingFindings.includes(f.id));
18413
+ if (supportingFindings.length === 0) {
18414
+ return true;
18415
+ }
18416
+ const findingSummaries = supportingFindings.map((f) => f.summary.toLowerCase()).join(" ");
18417
+ const hypothesisContent = (hypothesis.description + " " + hypothesis.reasoning).toLowerCase();
18418
+ const commonWords = this.getCommonWords(findingSummaries, hypothesisContent);
18419
+ const similarityRatio = commonWords.length / Math.max(findingSummaries.split(" ").length, 1);
18420
+ return similarityRatio < 0.8;
18421
+ }
18422
+ validateContradiction(hypothesis, findings) {
18423
+ const supportingFindings = findings.filter((f) => hypothesis.supportingFindings.includes(f.id));
18424
+ for (const finding of supportingFindings) {
18425
+ if (finding.severity === "low" && hypothesis.estimatedImpact === "critical") {
18426
+ if (!hypothesis.reasoning.toLowerCase().includes("despite") && !hypothesis.reasoning.toLowerCase().includes("even though") && !hypothesis.reasoning.toLowerCase().includes("potential")) {
18427
+ return false;
18428
+ }
18429
+ }
18430
+ for (const anchor of finding.evidenceAnchors) {
18431
+ if (anchor.type === "missing" && !hypothesis.description.toLowerCase().includes("lack") && !hypothesis.description.toLowerCase().includes("missing") && !hypothesis.description.toLowerCase().includes("without")) {
18432
+ return false;
18433
+ }
18434
+ }
18435
+ }
18436
+ return true;
18437
+ }
18438
+ calculateAdjustedConfidence(originalConfidence, validationChecks) {
18439
+ let adjustedConfidence = originalConfidence;
18440
+ for (const check of validationChecks) {
18441
+ if (!check.passed) {
18442
+ switch (check.type) {
18443
+ case "evidence_strength":
18444
+ adjustedConfidence -= 30;
18445
+ break;
18446
+ case "repo_specificity":
18447
+ adjustedConfidence -= 25;
18448
+ break;
18449
+ case "actionability":
18450
+ adjustedConfidence -= 20;
18451
+ break;
18452
+ case "redundancy":
18453
+ adjustedConfidence -= 15;
18454
+ break;
18455
+ case "contradiction":
18456
+ adjustedConfidence -= 35;
18457
+ break;
18458
+ }
18459
+ }
18460
+ }
18461
+ return Math.max(0, Math.min(100, adjustedConfidence));
18462
+ }
18463
+ determineRecommendation(blockers, warnings, confidence) {
18464
+ if (blockers.length > 0) {
18465
+ return "reject";
18466
+ }
18467
+ if (confidence < 50 && warnings.length > 0) {
18468
+ return "human_review";
18469
+ }
18470
+ if (confidence < 70 && warnings.length > 0) {
18471
+ return "downgrade";
18472
+ }
18473
+ if (confidence >= 70 && warnings.length <= 1) {
18474
+ return "promote";
18475
+ }
18476
+ return "human_review";
18477
+ }
18478
+ isBlocker(checkType, reason) {
18479
+ const blockerTypes = [
18480
+ "contradiction",
18481
+ "evidence_strength"
18482
+ ];
18483
+ if (blockerTypes.includes(checkType)) {
18484
+ return true;
18485
+ }
18486
+ const blockerReasons = [
18487
+ "Supporting findings are weak or insufficient",
18488
+ "Hypothesis contradicts available evidence"
18489
+ ];
18490
+ return blockerReasons.includes(reason || "");
18491
+ }
18492
+ getCommonWords(text1, text2) {
18493
+ const words1 = text1.split(" ").filter((w) => w.length > 3);
18494
+ const words2 = text2.split(" ").filter((w) => w.length > 3);
18495
+ return words1.filter((word) => words2.includes(word));
18496
+ }
18497
+ }
18498
+
18499
+ // src/recommendations/index.ts
18500
+ class RecommendationEngine {
18501
+ config;
18502
+ constructor(config = {}) {
18503
+ this.config = {
18504
+ enableChallenger: true,
18505
+ confidenceThreshold: 50,
18506
+ humanReviewThreshold: 70,
18507
+ enableFeedback: true,
18508
+ ...config
18509
+ };
18510
+ }
18511
+ async generateRecommendations(repoPath, evidence, scores, copilotFeatures, techStack) {
18512
+ const startTime = Date.now();
18513
+ const context = {
18514
+ repoPath,
18515
+ evidence,
18516
+ scores,
18517
+ copilotFeatures,
18518
+ techStack,
18519
+ config: this.config
18520
+ };
18521
+ const findingsResult = await this.runFindingsStage(context);
18522
+ const hypothesesResult = await this.runHypothesesStage(context, findingsResult.data);
18523
+ const validationResult = await this.runValidationStage(context, hypothesesResult.data, findingsResult.data);
18524
+ const challengerResult = await this.runChallengerStage(context, validationResult.data, findingsResult.data, hypothesesResult.data);
18525
+ const rankingResult = await this.runRankingStage(validationResult.data, challengerResult.data, findingsResult.data, hypothesesResult.data);
18526
+ const totalDuration = Date.now() - startTime;
18527
+ const result = {
18528
+ findings: findingsResult.data,
18529
+ hypotheses: hypothesesResult.data,
18530
+ validationResults: validationResult.data,
18531
+ challengerAssessments: challengerResult.data,
18532
+ recommendations: rankingResult.data,
18533
+ pipeline: {
18534
+ findings: findingsResult,
18535
+ hypotheses: hypothesesResult,
18536
+ validation: validationResult,
18537
+ challenger: challengerResult,
18538
+ ranking: rankingResult
18539
+ },
18540
+ metadata: {
18541
+ totalDuration,
18542
+ evidenceCount: this.countEvidenceItems(evidence),
18543
+ confidenceDistribution: this.calculateConfidenceDistribution(rankingResult.data),
18544
+ categoryDistribution: this.calculateCategoryDistribution(rankingResult.data)
18545
+ }
18546
+ };
18547
+ if (this.config.enableFeedback) {
18548
+ await this.saveFeedbackTemplate(repoPath, rankingResult.data);
18549
+ }
18550
+ return result;
18551
+ }
18552
+ async runFindingsStage(context) {
18553
+ const startTime = Date.now();
18554
+ const findingBuilder = new FindingBuilder(context);
18555
+ try {
18556
+ const findings = findingBuilder.buildFindings();
18557
+ return {
18558
+ stage: "findings",
18559
+ success: true,
18560
+ data: findings,
18561
+ errors: [],
18562
+ warnings: [],
18563
+ metadata: {
18564
+ duration: Date.now() - startTime,
18565
+ inputCount: 1,
18566
+ outputCount: findings.length
18567
+ }
18568
+ };
18569
+ } catch (error) {
18570
+ return {
18571
+ stage: "findings",
18572
+ success: false,
18573
+ data: [],
18574
+ errors: [
18575
+ error instanceof Error ? error.message : "Unknown error in findings stage"
18576
+ ],
18577
+ warnings: [],
18578
+ metadata: {
18579
+ duration: Date.now() - startTime,
18580
+ inputCount: 1,
18581
+ outputCount: 0
18582
+ }
18583
+ };
18584
+ }
18585
+ }
18586
+ async runHypothesesStage(context, findings) {
18587
+ const startTime = Date.now();
18588
+ const hypothesisEngine = new HypothesisEngine(context);
18589
+ try {
18590
+ const hypotheses = hypothesisEngine.generateHypotheses(findings);
18591
+ return {
18592
+ stage: "hypotheses",
18593
+ success: true,
18594
+ data: hypotheses,
18595
+ errors: [],
18596
+ warnings: [],
18597
+ metadata: {
18598
+ duration: Date.now() - startTime,
18599
+ inputCount: findings.length,
18600
+ outputCount: hypotheses.length
18601
+ }
18602
+ };
18603
+ } catch (error) {
18604
+ return {
18605
+ stage: "hypotheses",
18606
+ success: false,
18607
+ data: [],
18608
+ errors: [
18609
+ error instanceof Error ? error.message : "Unknown error in hypotheses stage"
18610
+ ],
18611
+ warnings: [],
18612
+ metadata: {
18613
+ duration: Date.now() - startTime,
18614
+ inputCount: findings.length,
18615
+ outputCount: 0
18616
+ }
18617
+ };
18618
+ }
18619
+ }
18620
+ async runValidationStage(context, hypotheses, findings) {
18621
+ const startTime = Date.now();
18622
+ const validator2 = new Validator(context);
18623
+ try {
18624
+ const validationResults = validator2.validateHypotheses(hypotheses, findings);
18625
+ return {
18626
+ stage: "validation",
18627
+ success: true,
18628
+ data: validationResults,
18629
+ errors: [],
18630
+ warnings: [],
18631
+ metadata: {
18632
+ duration: Date.now() - startTime,
18633
+ inputCount: hypotheses.length,
18634
+ outputCount: validationResults.length
18635
+ }
18636
+ };
18637
+ } catch (error) {
18638
+ return {
18639
+ stage: "validation",
18640
+ success: false,
18641
+ data: [],
18642
+ errors: [
18643
+ error instanceof Error ? error.message : "Unknown error in validation stage"
18644
+ ],
18645
+ warnings: [],
18646
+ metadata: {
18647
+ duration: Date.now() - startTime,
18648
+ inputCount: hypotheses.length,
18649
+ outputCount: 0
18650
+ }
18651
+ };
18652
+ }
16207
18653
  }
16208
- calculateEnhancedConfidence(insights, llmConfidence) {
16209
- const baseConfidence = this.calculateConfidence(insights);
16210
- if (this.enableLLMCoalescing && llmConfidence > 0) {
16211
- const confidenceMap = {
16212
- high: 85,
16213
- medium: 65,
16214
- low: 45
18654
+ async runChallengerStage(context, validationResults, findings, hypotheses) {
18655
+ const startTime = Date.now();
18656
+ if (!this.config.enableChallenger) {
18657
+ return {
18658
+ stage: "challenger",
18659
+ success: true,
18660
+ data: [],
18661
+ errors: [],
18662
+ warnings: ["Challenger stage disabled by configuration"],
18663
+ metadata: {
18664
+ duration: Date.now() - startTime,
18665
+ inputCount: validationResults.length,
18666
+ outputCount: 0
18667
+ }
18668
+ };
18669
+ }
18670
+ try {
18671
+ const ranker2 = new Ranker(context);
18672
+ const preliminaryRecommendations = ranker2.rankRecommendations(validationResults, [], findings, hypotheses);
18673
+ const challenger2 = new Challenger(context);
18674
+ const challengerAssessments = challenger2.challengeRecommendations(preliminaryRecommendations, findings, hypotheses, validationResults);
18675
+ return {
18676
+ stage: "challenger",
18677
+ success: true,
18678
+ data: challengerAssessments,
18679
+ errors: [],
18680
+ warnings: [],
18681
+ metadata: {
18682
+ duration: Date.now() - startTime,
18683
+ inputCount: validationResults.length,
18684
+ outputCount: challengerAssessments.length
18685
+ }
18686
+ };
18687
+ } catch (error) {
18688
+ return {
18689
+ stage: "challenger",
18690
+ success: false,
18691
+ data: [],
18692
+ errors: [
18693
+ error instanceof Error ? error.message : "Unknown error in challenger stage"
18694
+ ],
18695
+ warnings: [],
18696
+ metadata: {
18697
+ duration: Date.now() - startTime,
18698
+ inputCount: validationResults.length,
18699
+ outputCount: 0
18700
+ }
16215
18701
  };
16216
- const baseNumeric = confidenceMap[baseConfidence] || 50;
16217
- const combinedConfidence = (baseNumeric + llmConfidence * 100) / 2;
16218
- if (combinedConfidence >= 80)
16219
- return "high";
16220
- if (combinedConfidence >= 60)
16221
- return "medium";
16222
- return "low";
16223
18702
  }
16224
- return baseConfidence;
16225
- }
16226
- generateSummary(insights, context) {
16227
- return this.generateEnhancedSummary(insights, context, []);
16228
18703
  }
16229
- generateNextSteps(insights) {
16230
- const prioritized = insights.sort((a, b) => {
16231
- const priorityOrder = {
16232
- critical: 4,
16233
- high: 3,
16234
- medium: 2,
16235
- low: 1
18704
+ async runRankingStage(validationResults, challengerAssessments, findings, hypotheses) {
18705
+ const startTime = Date.now();
18706
+ const ranker2 = new Ranker(this.createRankingContext());
18707
+ try {
18708
+ const recommendations = ranker2.rankRecommendations(validationResults, challengerAssessments, findings, hypotheses);
18709
+ return {
18710
+ stage: "ranking",
18711
+ success: true,
18712
+ data: recommendations,
18713
+ errors: [],
18714
+ warnings: [],
18715
+ metadata: {
18716
+ duration: Date.now() - startTime,
18717
+ inputCount: validationResults.length,
18718
+ outputCount: recommendations.length
18719
+ }
16236
18720
  };
16237
- return (priorityOrder[b.priority] || 1) - (priorityOrder[a.priority] || 1);
16238
- });
16239
- return prioritized.slice(0, 5).map((insight) => insight.title);
18721
+ } catch (error) {
18722
+ return {
18723
+ stage: "ranking",
18724
+ success: false,
18725
+ data: [],
18726
+ errors: [
18727
+ error instanceof Error ? error.message : "Unknown error in ranking stage"
18728
+ ],
18729
+ warnings: [],
18730
+ metadata: {
18731
+ duration: Date.now() - startTime,
18732
+ inputCount: validationResults.length,
18733
+ outputCount: 0
18734
+ }
18735
+ };
18736
+ }
16240
18737
  }
16241
- estimateTimeframe(insights) {
16242
- const criticalCount = insights.filter((i) => i.priority === "critical").length;
16243
- const highCount = insights.filter((i) => i.priority === "high").length;
16244
- if (criticalCount > 0)
16245
- return "90 days (risk mitigation first)";
16246
- if (highCount > 2)
16247
- return "6 months (phased approach)";
16248
- if (highCount > 0)
16249
- return "3-4 months";
16250
- return "6-12 months (strategic transformation)";
18738
+ createRankingContext() {
18739
+ return {
18740
+ repoPath: "",
18741
+ evidence: {},
18742
+ scores: {},
18743
+ copilotFeatures: {},
18744
+ techStack: {},
18745
+ config: this.config
18746
+ };
16251
18747
  }
16252
- getPerspective() {
16253
- return CONSULTANT_EMPATHY.pointOfView;
18748
+ countEvidenceItems(evidence) {
18749
+ let count = 0;
18750
+ count += Object.values(evidence.structure).filter(Boolean).length;
18751
+ count += Object.values(evidence.configuration).filter(Boolean).length;
18752
+ count += Object.values(evidence.patterns).filter((p) => typeof p === "number" ? p > 0 : Boolean(p)).length;
18753
+ count += Object.values(evidence.metrics).filter((m) => typeof m === "number" ? m > 0 : m !== "fair").length;
18754
+ return count;
16254
18755
  }
16255
- }
16256
-
16257
- // src/personas/persona-factory.ts
16258
- class PersonaFactory {
16259
- static personas = new Map([
16260
- [
16261
- "consultant",
16262
- (enableLLMCoalescing = false) => new ConsultantPersona(enableLLMCoalescing)
16263
- ]
16264
- ]);
16265
- static configs = new Map([
16266
- [
16267
- "consultant",
16268
- {
16269
- type: "consultant",
16270
- name: "AI Strategy Consultant",
16271
- description: "Strategic advisor focused on business value, ROI, and organizational transformation",
16272
- expertise: ["AI Strategy", "Business Transformation", "ROI Analysis"],
16273
- focus: ["business-impact", "roi", "governance"],
16274
- tone: "formal",
16275
- targetAudience: ["executives", "managers"]
16276
- }
16277
- ]
16278
- ]);
16279
- static createPersona(type, enableLLMCoalescing = false) {
16280
- const personaFactory = PersonaFactory.personas.get(type);
16281
- if (!personaFactory) {
16282
- throw new Error(`Persona type '${type}' is not supported. Available types: ${Array.from(PersonaFactory.personas.keys()).join(", ")}`);
18756
+ calculateConfidenceDistribution(recommendations) {
18757
+ const distribution = { high: 0, medium: 0, low: 0 };
18758
+ for (const rec of recommendations) {
18759
+ if (rec.confidence.overall >= 70)
18760
+ distribution.high++;
18761
+ else if (rec.confidence.overall >= 40)
18762
+ distribution.medium++;
18763
+ else
18764
+ distribution.low++;
16283
18765
  }
16284
- return personaFactory(enableLLMCoalescing);
16285
- }
16286
- static getAvailablePersonas() {
16287
- return Array.from(PersonaFactory.personas.keys());
16288
- }
16289
- static getPersonaConfig(type) {
16290
- return PersonaFactory.configs.get(type);
18766
+ return distribution;
16291
18767
  }
16292
- static getAllConfigs() {
16293
- return new Map(PersonaFactory.configs);
18768
+ calculateCategoryDistribution(recommendations) {
18769
+ const distribution = {};
18770
+ for (const rec of recommendations) {
18771
+ distribution[rec.category] = (distribution[rec.category] || 0) + 1;
18772
+ }
18773
+ return distribution;
16294
18774
  }
16295
- static isPersonaAvailable(type) {
16296
- return PersonaFactory.personas.has(type);
18775
+ async saveFeedbackTemplate(repoPath, recommendations) {
18776
+ try {
18777
+ const feedbackCollector = new FeedbackCollector(repoPath);
18778
+ const template = feedbackCollector.generateFeedbackTemplate(recommendations);
18779
+ const { writeFile: writeFile2 } = await import("node:fs/promises");
18780
+ const { join: join5 } = await import("node:path");
18781
+ const templatePath = join5(repoPath, ".ai-enablement", "feedback-template.json");
18782
+ await writeFile2(templatePath, template, "utf-8");
18783
+ console.log(`\uD83D\uDCDD Feedback template saved to: ${templatePath}`);
18784
+ } catch (error) {
18785
+ console.warn("Could not save feedback template:", error);
18786
+ }
18787
+ }
18788
+ toLegacyRecommendations(v2Recommendations) {
18789
+ return v2Recommendations.map((rec) => ({
18790
+ id: rec.id,
18791
+ title: rec.title,
18792
+ description: rec.summary,
18793
+ priority: rec.priority,
18794
+ category: rec.category,
18795
+ effort: rec.estimatedEffort.size,
18796
+ timeframe: rec.estimatedEffort.timeframes.expected,
18797
+ dependencies: [],
18798
+ evidence: rec.evidenceAnchors.map((a) => a.description)
18799
+ }));
16297
18800
  }
16298
18801
  }
16299
18802
 
16300
18803
  // src/scanners/copilot-feature-scanner.ts
16301
- import {constants as constants3, access as access3, readFile as readFile3} from "node:fs/promises";
16302
- import {join as join4} from "node:path";
18804
+ import {constants as constants3, access as access3, readFile as readFile4} from "node:fs/promises";
18805
+ import {join as join5} from "node:path";
16303
18806
 
16304
18807
  // node_modules/simple-git/dist/esm/index.js
16305
18808
  var file_exists = __toESM(require_dist(), 1);
@@ -20365,10 +22868,10 @@ class CopilotFeatureScanner {
20365
22868
  ".github/COPYLOIT_INSTRUCTIONS.md"
20366
22869
  ];
20367
22870
  for (const path of instructionPaths) {
20368
- const fullPath = join4(repoPath, path);
22871
+ const fullPath = join5(repoPath, path);
20369
22872
  try {
20370
22873
  await access3(fullPath, constants3.R_OK);
20371
- const content = await readFile3(fullPath, "utf-8");
22874
+ const content = await readFile4(fullPath, "utf-8");
20372
22875
  analysis.githubFeatures.copilotInstructions = {
20373
22876
  found: true,
20374
22877
  path,
@@ -20387,10 +22890,10 @@ class CopilotFeatureScanner {
20387
22890
  "docs/CODEOWNERS"
20388
22891
  ];
20389
22892
  for (const path of codeownersPaths) {
20390
- const fullPath = join4(repoPath, path);
22893
+ const fullPath = join5(repoPath, path);
20391
22894
  try {
20392
22895
  await access3(fullPath, constants3.R_OK);
20393
- const content = await readFile3(fullPath, "utf-8");
22896
+ const content = await readFile4(fullPath, "utf-8");
20394
22897
  analysis.githubFeatures.codeowners = {
20395
22898
  found: true,
20396
22899
  path,
@@ -20415,10 +22918,10 @@ class CopilotFeatureScanner {
20415
22918
  ".github/PULL_REQUEST_TEMPLATE.md"
20416
22919
  ];
20417
22920
  for (const path of prTemplatePaths) {
20418
- const fullPath = join4(repoPath, path);
22921
+ const fullPath = join5(repoPath, path);
20419
22922
  try {
20420
22923
  await access3(fullPath, constants3.R_OK);
20421
- const content = await readFile3(fullPath, "utf-8");
22924
+ const content = await readFile4(fullPath, "utf-8");
20422
22925
  analysis.githubFeatures.prTemplates = {
20423
22926
  found: true,
20424
22927
  aiFriendly: this.isAiFriendlyTemplate(content)
@@ -20437,7 +22940,7 @@ class CopilotFeatureScanner {
20437
22940
  }
20438
22941
  async scanCodePatterns(repoPath, analysis) {
20439
22942
  try {
20440
- const tsconfigPath = join4(repoPath, "tsconfig.json");
22943
+ const tsconfigPath = join5(repoPath, "tsconfig.json");
20441
22944
  try {
20442
22945
  await access3(tsconfigPath, constants3.R_OK);
20443
22946
  analysis.codePatterns.typeScriptUsage = true;
@@ -20453,7 +22956,7 @@ class CopilotFeatureScanner {
20453
22956
  "__tests__/"
20454
22957
  ];
20455
22958
  for (const indicator of testIndicators) {
20456
- const indicatorPath = join4(repoPath, indicator);
22959
+ const indicatorPath = join5(repoPath, indicator);
20457
22960
  try {
20458
22961
  await access3(indicatorPath, indicator.endsWith("/") ? constants3.F_OK : constants3.R_OK);
20459
22962
  analysis.codePatterns.testCoverage = true;
@@ -20778,6 +23281,7 @@ class AssessmentEngine {
20778
23281
  scorer;
20779
23282
  adrGenerator;
20780
23283
  evidenceCollector;
23284
+ recommendationEngine;
20781
23285
  config;
20782
23286
  constructor(config) {
20783
23287
  this.config = {
@@ -20795,6 +23299,7 @@ class AssessmentEngine {
20795
23299
  this.scorer = new ReadinessScorer;
20796
23300
  this.adrGenerator = new ADRGenerator;
20797
23301
  this.evidenceCollector = new EvidenceCollector;
23302
+ this.recommendationEngine = new RecommendationEngine(this.config.recommendationConfig);
20798
23303
  }
20799
23304
  async execute() {
20800
23305
  const startTime = Date.now();
@@ -20812,7 +23317,71 @@ class AssessmentEngine {
20812
23317
  evidence
20813
23318
  });
20814
23319
  console.log("\uD83C\uDFAF Scoring complete, generating recommendations...");
20815
- const recommendations = this.config.includeRecommendations ? await this.generateRecommendations(scores, copilotFeatures, evidence) : [];
23320
+ let recommendations2 = [];
23321
+ let recommendationEngine;
23322
+ if (this.config.includeRecommendations) {
23323
+ console.log("\uD83D\uDE80 Using Recommendation Engine V2...");
23324
+ recommendationEngine = await this.recommendationEngine.generateRecommendations(this.config.repoPath, evidence, scores, copilotFeatures, techStack);
23325
+ recommendations2 = this.recommendationEngine.toLegacyRecommendations(recommendationEngine.recommendations);
23326
+ console.log(`\u2705 V2 Engine generated ${recommendationEngine.recommendations.length} recommendations`);
23327
+ } else {
23328
+ recommendationEngine = {
23329
+ findings: [],
23330
+ hypotheses: [],
23331
+ validationResults: [],
23332
+ challengerAssessments: [],
23333
+ recommendations: [],
23334
+ pipeline: {
23335
+ findings: {
23336
+ stage: "findings",
23337
+ success: true,
23338
+ data: [],
23339
+ errors: [],
23340
+ warnings: [],
23341
+ metadata: { duration: 0, inputCount: 0, outputCount: 0 }
23342
+ },
23343
+ hypotheses: {
23344
+ stage: "hypotheses",
23345
+ success: true,
23346
+ data: [],
23347
+ errors: [],
23348
+ warnings: [],
23349
+ metadata: { duration: 0, inputCount: 0, outputCount: 0 }
23350
+ },
23351
+ validation: {
23352
+ stage: "validation",
23353
+ success: true,
23354
+ data: [],
23355
+ errors: [],
23356
+ warnings: [],
23357
+ metadata: { duration: 0, inputCount: 0, outputCount: 0 }
23358
+ },
23359
+ challenger: {
23360
+ stage: "challenger",
23361
+ success: true,
23362
+ data: [],
23363
+ errors: [],
23364
+ warnings: [],
23365
+ metadata: { duration: 0, inputCount: 0, outputCount: 0 }
23366
+ },
23367
+ ranking: {
23368
+ stage: "ranking",
23369
+ success: true,
23370
+ data: [],
23371
+ errors: [],
23372
+ warnings: [],
23373
+ metadata: { duration: 0, inputCount: 0, outputCount: 0 }
23374
+ }
23375
+ },
23376
+ metadata: {
23377
+ totalDuration: 0,
23378
+ evidenceCount: 0,
23379
+ confidenceDistribution: { high: 0, medium: 0, low: 0 },
23380
+ categoryDistribution: {}
23381
+ }
23382
+ };
23383
+ recommendations2 = [];
23384
+ }
20816
23385
  let personaInsights;
20817
23386
  if (this.config.persona) {
20818
23387
  console.log("\uD83E\uDD16 Generating persona insights...");
@@ -20826,7 +23395,7 @@ class AssessmentEngine {
20826
23395
  copilotFeatures,
20827
23396
  techStack,
20828
23397
  evidence,
20829
- recommendations,
23398
+ recommendations: recommendations2,
20830
23399
  structuredInsights: personaInsights?.structuredInsights
20831
23400
  });
20832
23401
  }
@@ -20835,7 +23404,7 @@ class AssessmentEngine {
20835
23404
  metadata: {
20836
23405
  timestamp: new Date().toISOString(),
20837
23406
  repository: this.config.repoPath,
20838
- version: "1.0.0",
23407
+ version: "2.0.0",
20839
23408
  duration
20840
23409
  },
20841
23410
  analysis: {
@@ -20844,7 +23413,8 @@ class AssessmentEngine {
20844
23413
  evidence
20845
23414
  },
20846
23415
  scores,
20847
- recommendations
23416
+ recommendations: recommendations2,
23417
+ recommendationEngine
20848
23418
  };
20849
23419
  if (personaInsights) {
20850
23420
  result.personaInsights = personaInsights;
@@ -20860,9 +23430,9 @@ class AssessmentEngine {
20860
23430
  }
20861
23431
  }
20862
23432
  async generateRecommendations(scores, features, evidence) {
20863
- const recommendations = [];
23433
+ const recommendations2 = [];
20864
23434
  if (scores.repoReadiness < 70) {
20865
- recommendations.push({
23435
+ recommendations2.push({
20866
23436
  id: "found-001",
20867
23437
  title: "Improve Repository Structure",
20868
23438
  description: "Standardize repository structure and add essential configuration files",
@@ -20875,7 +23445,7 @@ class AssessmentEngine {
20875
23445
  });
20876
23446
  }
20877
23447
  if (!features.githubFeatures.codeowners.found) {
20878
- recommendations.push({
23448
+ recommendations2.push({
20879
23449
  id: "sec-001",
20880
23450
  title: "Add CODEOWNERS File",
20881
23451
  description: "Define code ownership for better security and review processes",
@@ -20888,7 +23458,7 @@ class AssessmentEngine {
20888
23458
  });
20889
23459
  }
20890
23460
  if (!features.githubFeatures.copilotInstructions.found) {
20891
- recommendations.push({
23461
+ recommendations2.push({
20892
23462
  id: "ai-001",
20893
23463
  title: "Add Copilot Instructions",
20894
23464
  description: "Create Copilot instructions to guide AI assistance in your repository",
@@ -20901,7 +23471,7 @@ class AssessmentEngine {
20901
23471
  });
20902
23472
  }
20903
23473
  if (scores.orgReadiness < 60) {
20904
- recommendations.push({
23474
+ recommendations2.push({
20905
23475
  id: "gov-001",
20906
23476
  title: "Establish Governance Processes",
20907
23477
  description: "Implement clear processes for code review, testing, and deployment",
@@ -20913,7 +23483,7 @@ class AssessmentEngine {
20913
23483
  evidence: this.extractEvidence(evidence, ["governance", "process"])
20914
23484
  });
20915
23485
  }
20916
- return recommendations.sort((a, b) => {
23486
+ return recommendations2.sort((a, b) => {
20917
23487
  const priorityOrder = { high: 3, medium: 2, low: 1 };
20918
23488
  return priorityOrder[b.priority] - priorityOrder[a.priority];
20919
23489
  });
@@ -20938,7 +23508,7 @@ class AssessmentEngine {
20938
23508
  metadata: {
20939
23509
  timestamp: new Date().toISOString(),
20940
23510
  repository: this.config.repoPath,
20941
- version: "1.0.0",
23511
+ version: "2.0.0",
20942
23512
  duration: 0
20943
23513
  },
20944
23514
  analysis: {
@@ -21036,9 +23606,11 @@ function displayResults(result, persona) {
21036
23606
  console.log(import_chalk.default.gray(`Repository: ${result.metadata.repository}`));
21037
23607
  console.log(import_chalk.default.gray(`Assessed: ${result.metadata.timestamp}`));
21038
23608
  console.log(import_chalk.default.gray(`Duration: ${result.metadata.duration}ms`));
23609
+ console.log(import_chalk.default.gray(`Version: ${result.metadata.version}`));
21039
23610
  console.log(import_chalk.default.gray(`Persona: ${persona}\n`));
21040
23611
  displayScores(result.scores);
21041
23612
  displayRecommendations(result.recommendations);
23613
+ displayRecommendationEngine(result.recommendationEngine);
21042
23614
  if (result.personaInsights) {
21043
23615
  displayPersonaInsights(result.personaInsights);
21044
23616
  }
@@ -21079,13 +23651,52 @@ function displayScores(scores) {
21079
23651
  });
21080
23652
  console.log(import_chalk.default.gray(`Confidence: ${scores.confidence}\n`));
21081
23653
  }
21082
- function displayRecommendations(recommendations) {
21083
- if (recommendations.length === 0) {
23654
+ function displayRecommendationEngine(engineResult) {
23655
+ console.log(import_chalk.default.bold.blue(`
23656
+ \uD83D\uDE80 Recommendation Engine Results`));
23657
+ console.log(import_chalk.default.gray(`Pipeline Duration: ${engineResult.metadata.totalDuration}ms`));
23658
+ console.log(import_chalk.default.gray(`Evidence Items: ${engineResult.metadata.evidenceCount}`));
23659
+ console.log(import_chalk.default.gray(`Findings: ${engineResult.findings.length}`));
23660
+ console.log(import_chalk.default.gray(`Hypotheses: ${engineResult.hypotheses.length}`));
23661
+ console.log(import_chalk.default.gray(`Final Recommendations: ${engineResult.recommendations.length}\n`));
23662
+ if (engineResult.recommendations.length === 0) {
23663
+ console.log(import_chalk.default.green("\u2705 No recommendations - repository is well prepared!"));
23664
+ return;
23665
+ }
23666
+ const groupedRecs = engineResult.recommendations.reduce((groups, rec) => {
23667
+ if (!groups[rec.priority])
23668
+ groups[rec.priority] = [];
23669
+ groups[rec.priority].push(rec);
23670
+ return groups;
23671
+ }, {});
23672
+ ["critical", "high", "medium", "low"].forEach((priority) => {
23673
+ const recs = groupedRecs[priority];
23674
+ if (recs && recs.length > 0) {
23675
+ const priorityColor = priority === "critical" ? import_chalk.default.red : priority === "high" ? import_chalk.default.red : priority === "medium" ? import_chalk.default.yellow : import_chalk.default.gray;
23676
+ console.log(import_chalk.default.bold(`\n${priorityColor(priority.toUpperCase())} PRIORITY:`));
23677
+ recs.forEach((rec) => {
23678
+ console.log(` ${import_chalk.default.bold(rec.title)} (${rec.category})`);
23679
+ console.log(` ${import_chalk.default.gray(rec.summary)}`);
23680
+ console.log(` ${import_chalk.default.blue(`Confidence: ${rec.confidence.overall}% | Evidence: ${rec.evidenceAnchors.length} anchors`)}`);
23681
+ if (rec.humanReviewNeeded) {
23682
+ console.log(` ${import_chalk.default.yellow("\u26A0\uFE0F Requires human review")}`);
23683
+ }
23684
+ console.log(` ${import_chalk.default.cyan(`Next: ${rec.suggestedNextStep}`)}`);
23685
+ if (rec.caveats.length > 0) {
23686
+ console.log(` ${import_chalk.default.hex("#FFA500")(`Caveats: ${rec.caveats.slice(0, 2).join("; ")}`)}`);
23687
+ }
23688
+ console.log("");
23689
+ });
23690
+ }
23691
+ });
23692
+ }
23693
+ function displayRecommendations(recommendations2) {
23694
+ if (recommendations2.length === 0) {
21084
23695
  console.log(import_chalk.default.green("\u2705 No recommendations needed - repository is well prepared!"));
21085
23696
  return;
21086
23697
  }
21087
23698
  console.log(import_chalk.default.bold.blue("\uD83C\uDFAF Recommendations"));
21088
- const groupedRecs = recommendations.reduce((groups, rec) => {
23699
+ const groupedRecs = recommendations2.reduce((groups, rec) => {
21089
23700
  if (!groups[rec.priority])
21090
23701
  groups[rec.priority] = [];
21091
23702
  groups[rec.priority].push(rec);
@@ -21169,9 +23780,352 @@ function getMaturityStatus(level) {
21169
23780
  return import_chalk.default.hex("#FFA500")("\uD83D\uDD36 Basic");
21170
23781
  return import_chalk.default.red("\u274C Initial");
21171
23782
  }
23783
+ async function generateRoadmap(result, options) {
23784
+ const timeframe = parseInt(options.timeframe) || 12;
23785
+ const pace = options.pace || "moderate";
23786
+ const paceMultipliers = {
23787
+ aggressive: 0.75,
23788
+ moderate: 1,
23789
+ conservative: 1.5
23790
+ };
23791
+ const adjustedTimeframe = timeframe * paceMultipliers[pace];
23792
+ const groupedRecs = result.recommendations.reduce((groups, rec) => {
23793
+ const key = `${rec.category}-${rec.priority}`;
23794
+ if (!groups[key])
23795
+ groups[key] = [];
23796
+ groups[key].push(rec);
23797
+ return groups;
23798
+ }, {});
23799
+ const phases = createPhases(result.scores.overallMaturity, groupedRecs, adjustedTimeframe);
23800
+ const roadmap = {
23801
+ metadata: {
23802
+ repository: result.metadata.repository,
23803
+ generated: new Date().toISOString(),
23804
+ timeframe,
23805
+ pace,
23806
+ persona: options.persona || "consultant"
23807
+ },
23808
+ summary: {
23809
+ currentMaturity: result.scores.overallMaturity,
23810
+ targetMaturity: Math.min(8, result.scores.overallMaturity + 3),
23811
+ totalPhases: phases.length,
23812
+ estimatedDuration: `${Math.ceil(adjustedTimeframe)} months`,
23813
+ criticalPath: getCriticalPath(phases)
23814
+ },
23815
+ phases,
23816
+ successMetrics: options.includeMetrics ? generateSuccessMetrics(phases) : { kpis: [], milestones: [] }
23817
+ };
23818
+ return roadmap;
23819
+ }
23820
+ function createPhases(currentMaturity, groupedRecs, timeframe) {
23821
+ const phases = [];
23822
+ if (currentMaturity < 6) {
23823
+ const foundationRecs = Object.entries(groupedRecs).filter(([key]) => key.includes("foundation") || key.includes("high")).flatMap(([, recs]) => recs);
23824
+ phases.push({
23825
+ id: "foundation",
23826
+ name: "Foundation & Infrastructure",
23827
+ description: "Establish the technical and organizational foundation for AI enablement",
23828
+ duration: `${Math.ceil(timeframe * 0.25)} months`,
23829
+ priority: "critical",
23830
+ objectives: [
23831
+ "Establish robust development infrastructure",
23832
+ "Implement security and governance frameworks",
23833
+ "Build foundational AI tooling and workflows"
23834
+ ],
23835
+ actions: foundationRecs.map((rec) => ({
23836
+ title: rec.title,
23837
+ description: rec.description,
23838
+ effort: rec.effort,
23839
+ timeframe: rec.timeframe,
23840
+ dependencies: rec.dependencies,
23841
+ expectedOutcomes: [
23842
+ `Improved ${rec.category} maturity`,
23843
+ "Reduced technical debt",
23844
+ "Enhanced team productivity"
23845
+ ]
23846
+ })),
23847
+ risks: [
23848
+ {
23849
+ description: "Resource constraints delaying foundation work",
23850
+ mitigation: "Phase critical items, secure executive sponsorship",
23851
+ probability: "medium",
23852
+ impact: "high"
23853
+ }
23854
+ ]
23855
+ });
23856
+ }
23857
+ if (currentMaturity < 7) {
23858
+ const aiRecs = Object.entries(groupedRecs).filter(([key]) => key.includes("ai") || key.includes("workflow")).flatMap(([, recs]) => recs);
23859
+ phases.push({
23860
+ id: "integration",
23861
+ name: "AI Integration & Adoption",
23862
+ description: "Integrate AI tools into development workflows and build team capabilities",
23863
+ duration: `${Math.ceil(timeframe * 0.35)} months`,
23864
+ priority: "high",
23865
+ objectives: [
23866
+ "Deploy AI-powered development tools",
23867
+ "Train teams on AI-assisted workflows",
23868
+ "Establish AI governance and best practices"
23869
+ ],
23870
+ actions: aiRecs.map((rec) => ({
23871
+ title: rec.title,
23872
+ description: rec.description,
23873
+ effort: rec.effort,
23874
+ timeframe: rec.timeframe,
23875
+ dependencies: rec.dependencies,
23876
+ expectedOutcomes: [
23877
+ "Increased developer productivity",
23878
+ "Improved code quality",
23879
+ "Enhanced innovation capacity"
23880
+ ]
23881
+ })),
23882
+ risks: [
23883
+ {
23884
+ description: "Team resistance to AI adoption",
23885
+ mitigation: "Comprehensive training, demonstrate quick wins",
23886
+ probability: "medium",
23887
+ impact: "medium"
23888
+ }
23889
+ ]
23890
+ });
23891
+ }
23892
+ phases.push({
23893
+ id: "optimization",
23894
+ name: "Optimization & Scale",
23895
+ description: "Optimize AI workflows and scale successful practices across the organization",
23896
+ duration: `${Math.ceil(timeframe * 0.4)} months`,
23897
+ priority: "medium",
23898
+ objectives: [
23899
+ "Optimize AI tooling and workflows",
23900
+ "Scale successful practices organization-wide",
23901
+ "Establish continuous improvement processes"
23902
+ ],
23903
+ actions: [
23904
+ {
23905
+ title: "Implement Advanced AI Analytics",
23906
+ description: "Deploy sophisticated AI-powered analytics for development insights",
23907
+ effort: "High",
23908
+ timeframe: "2-3 months",
23909
+ dependencies: [
23910
+ "Foundation & Infrastructure",
23911
+ "AI Integration & Adoption"
23912
+ ],
23913
+ expectedOutcomes: [
23914
+ "Data-driven decision making",
23915
+ "Predictive insights for development",
23916
+ "Continuous performance monitoring"
23917
+ ],
23918
+ successMetrics: [
23919
+ "50% reduction in bug detection time",
23920
+ "30% improvement in deployment frequency",
23921
+ "25% reduction in technical debt accumulation"
23922
+ ]
23923
+ },
23924
+ {
23925
+ title: "Establish AI Center of Excellence",
23926
+ description: "Create a centralized team to drive AI innovation and best practices",
23927
+ effort: "Medium",
23928
+ timeframe: "1-2 months",
23929
+ dependencies: ["AI Integration & Adoption"],
23930
+ expectedOutcomes: [
23931
+ "Centralized AI expertise",
23932
+ "Standardized best practices",
23933
+ "Innovation incubation"
23934
+ ]
23935
+ }
23936
+ ],
23937
+ risks: [
23938
+ {
23939
+ description: "Scaling challenges as organization grows",
23940
+ mitigation: "Build scalable processes, document best practices",
23941
+ probability: "low",
23942
+ impact: "medium"
23943
+ }
23944
+ ]
23945
+ });
23946
+ return phases;
23947
+ }
23948
+ function getCriticalPath(phases) {
23949
+ return phases.filter((phase) => phase.priority === "critical" || phase.priority === "high").map((phase) => phase.name);
23950
+ }
23951
+ function generateSuccessMetrics(phases) {
23952
+ return {
23953
+ kpis: [
23954
+ {
23955
+ name: "Developer Productivity",
23956
+ description: "Measure of developer output and efficiency",
23957
+ target: "+30% in 6 months",
23958
+ measurement: "Lines of code per developer, deployment frequency"
23959
+ },
23960
+ {
23961
+ name: "Code Quality",
23962
+ description: "Overall codebase health and maintainability",
23963
+ target: "Reduce bugs by 40%",
23964
+ measurement: "Bug density, test coverage, code review metrics"
23965
+ },
23966
+ {
23967
+ name: "AI Adoption Rate",
23968
+ description: "Percentage of team actively using AI tools",
23969
+ target: "80% adoption in 3 months",
23970
+ measurement: "Tool usage analytics, survey responses"
23971
+ }
23972
+ ],
23973
+ milestones: [
23974
+ {
23975
+ name: "Foundation Complete",
23976
+ date: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString().split("T")[0],
23977
+ criteria: [
23978
+ "All infrastructure recommendations implemented",
23979
+ "Security frameworks in place",
23980
+ "Team training completed"
23981
+ ]
23982
+ },
23983
+ {
23984
+ name: "AI Integration Milestone",
23985
+ date: new Date(Date.now() + 180 * 24 * 60 * 60 * 1000).toISOString().split("T")[0],
23986
+ criteria: [
23987
+ "AI tools deployed to all teams",
23988
+ "70% adoption rate achieved",
23989
+ "Productivity gains measurable"
23990
+ ]
23991
+ }
23992
+ ]
23993
+ };
23994
+ }
23995
+ function displayRoadmapSummary(roadmap) {
23996
+ console.log(import_chalk.default.bold.blue(`
23997
+ \uD83D\uDEE3\uFE0F AI Enablement Roadmap`));
23998
+ console.log(import_chalk.default.gray(`Repository: ${roadmap.metadata.repository}`));
23999
+ console.log(import_chalk.default.gray(`Generated: ${new Date(roadmap.metadata.generated).toLocaleDateString()}`));
24000
+ console.log(import_chalk.default.gray(`Timeframe: ${roadmap.summary.estimatedDuration}`));
24001
+ console.log(import_chalk.default.gray(`Pace: ${roadmap.metadata.pace}\n`));
24002
+ console.log(import_chalk.default.bold("\uD83D\uDCCA Maturity Progression"));
24003
+ console.log(`Current: ${import_chalk.default.yellow(roadmap.summary.currentMaturity)}/8 \u2192 Target: ${import_chalk.default.green(roadmap.summary.targetMaturity)}/8`);
24004
+ console.log(`Total Phases: ${roadmap.summary.totalPhases}`);
24005
+ console.log(`Critical Path: ${roadmap.summary.criticalPath.join(" \u2192 ")}\n`);
24006
+ console.log(import_chalk.default.bold("\uD83C\uDFAF Phases Overview"));
24007
+ roadmap.phases.forEach((phase, index) => {
24008
+ const priorityColor = phase.priority === "critical" ? import_chalk.default.red : phase.priority === "high" ? import_chalk.default.yellow : phase.priority === "medium" ? import_chalk.default.blue : import_chalk.default.gray;
24009
+ console.log(`\n${index + 1}. ${import_chalk.default.bold(phase.name)} ${priorityColor(`(${phase.priority.toUpperCase()})`)}`);
24010
+ console.log(` ${import_chalk.default.gray(phase.description)}`);
24011
+ console.log(` ${import_chalk.default.blue(`Duration: ${phase.duration} | Actions: ${phase.actions.length}`)}`);
24012
+ phase.objectives.slice(0, 2).forEach((obj) => {
24013
+ console.log(` \u2022 ${import_chalk.default.white(obj)}`);
24014
+ });
24015
+ });
24016
+ if (roadmap.successMetrics.kpis.length > 0) {
24017
+ console.log(import_chalk.default.bold(`
24018
+ \uD83D\uDCC8 Key Success Metrics`));
24019
+ roadmap.successMetrics.kpis.slice(0, 3).forEach((kpi) => {
24020
+ console.log(`\u2022 ${import_chalk.default.bold(kpi.name)}: ${kpi.target}`);
24021
+ });
24022
+ }
24023
+ }
24024
+ async function saveRoadmap(roadmap, outputPath, format) {
24025
+ const fs = await import("node:fs/promises");
24026
+ const path = await import("node:path");
24027
+ await fs.mkdir(outputPath, { recursive: true });
24028
+ if (format === "markdown" || format === "both") {
24029
+ const markdown = generateMarkdownRoadmap(roadmap);
24030
+ await fs.writeFile(path.join(outputPath, "roadmap.md"), markdown, "utf-8");
24031
+ console.log(import_chalk.default.green(`\uD83D\uDCC4 Roadmap saved to: ${path.join(outputPath, "roadmap.md")}`));
24032
+ }
24033
+ if (format === "json" || format === "both") {
24034
+ await fs.writeFile(path.join(outputPath, "roadmap.json"), JSON.stringify(roadmap, null, 2), "utf-8");
24035
+ console.log(import_chalk.default.green(`\uD83D\uDCC4 Roadmap saved to: ${path.join(outputPath, "roadmap.json")}`));
24036
+ }
24037
+ }
24038
+ function generateMarkdownRoadmap(roadmap) {
24039
+ let markdown = `# AI Enablement Roadmap
24040
+
24041
+ **Repository:** ${roadmap.metadata.repository}
24042
+ **Generated:** ${new Date(roadmap.metadata.generated).toLocaleDateString()}
24043
+ **Timeframe:** ${roadmap.summary.estimatedDuration} (${roadmap.metadata.pace} pace)
24044
+ **Persona:** ${roadmap.metadata.persona}
24045
+
24046
+ ## Executive Summary
24047
+
24048
+ - **Current Maturity:** ${roadmap.summary.currentMaturity}/8
24049
+ - **Target Maturity:** ${roadmap.summary.targetMaturity}/8
24050
+ - **Total Phases:** ${roadmap.summary.totalPhases}
24051
+ - **Critical Path:** ${roadmap.summary.criticalPath.join(" \u2192 ")}
24052
+
24053
+ `;
24054
+ roadmap.phases.forEach((phase, index) => {
24055
+ markdown += `## Phase ${index + 1}: ${phase.name}
24056
+
24057
+ **Priority:** ${phase.priority.toUpperCase()}
24058
+ **Duration:** ${phase.duration}
24059
+
24060
+ ${phase.description}
24061
+
24062
+ ### Objectives
24063
+ `;
24064
+ phase.objectives.forEach((obj) => {
24065
+ markdown += `- ${obj}\n`;
24066
+ });
24067
+ markdown += `\n### Actions\n`;
24068
+ phase.actions.forEach((action) => {
24069
+ markdown += `#### ${action.title}
24070
+
24071
+ **Effort:** ${action.effort} | **Timeframe:** ${action.timeframe}
24072
+
24073
+ ${action.description}
24074
+
24075
+ **Expected Outcomes:**
24076
+ `;
24077
+ action.expectedOutcomes.forEach((outcome) => {
24078
+ markdown += `- ${outcome}\n`;
24079
+ });
24080
+ if (action.dependencies.length > 0) {
24081
+ markdown += `\n**Dependencies:** ${action.dependencies.join(", ")}\n`;
24082
+ }
24083
+ if (action.successMetrics && action.successMetrics.length > 0) {
24084
+ markdown += `\n**Success Metrics:**\n`;
24085
+ action.successMetrics.forEach((metric) => {
24086
+ markdown += `- ${metric}\n`;
24087
+ });
24088
+ }
24089
+ markdown += "\n";
24090
+ });
24091
+ if (phase.risks.length > 0) {
24092
+ markdown += `### Risks & Mitigations\n`;
24093
+ phase.risks.forEach((risk) => {
24094
+ markdown += `- **${risk.description}** (${risk.probability} probability, ${risk.impact} impact)\n`;
24095
+ markdown += ` - *Mitigation:* ${risk.mitigation}\n`;
24096
+ });
24097
+ markdown += "\n";
24098
+ }
24099
+ });
24100
+ if (roadmap.successMetrics.kpis.length > 0) {
24101
+ markdown += `## Success Metrics\n\n### Key Performance Indicators\n`;
24102
+ roadmap.successMetrics.kpis.forEach((kpi) => {
24103
+ markdown += `#### ${kpi.name}
24104
+
24105
+ **Target:** ${kpi.target}
24106
+
24107
+ ${kpi.description}
24108
+
24109
+ **Measurement:** ${kpi.measurement}
24110
+
24111
+ `;
24112
+ });
24113
+ markdown += `### Milestones\n`;
24114
+ roadmap.successMetrics.milestones.forEach((milestone) => {
24115
+ markdown += `#### ${milestone.name} (${milestone.date})
24116
+
24117
+ `;
24118
+ milestone.criteria.forEach((criteria) => {
24119
+ markdown += `- ${criteria}\n`;
24120
+ });
24121
+ markdown += "\n";
24122
+ });
24123
+ }
24124
+ return markdown;
24125
+ }
21172
24126
  var program2 = new Command;
21173
24127
  program2.name("ai-enablement").description("AI Enablement Platform - Analyze repository readiness for AI adoption").version("1.0.0");
21174
- program2.command("analyze").description("Analyze repository for AI enablement readiness").argument("<repo-path>", "Path to the repository to analyze").option("-o, --output <path>", "Output directory for results").option("-f, --format <format>", "Output format (json, adr, markdown)", "json").option("--no-recommendations", "Skip recommendations generation").option("--no-adr", "Skip ADR generation").option("--persona <type>", "Analysis persona (consultant, evangelist, team-lead)", "consultant").option("--llm-coalescing", "Enable LLM coalescing with adversarial validation").option("--adversarial-validation", "Enable adversarial validation (default: enabled with LLM)").action(async (repoPath, options) => {
24128
+ program2.command("analyze").description("Analyze repository for AI enablement readiness").argument("<repo-path>", "Path to the repository to analyze").option("-o, --output <path>", "Output directory for results").option("-f, --format <format>", "Output format (json, adr, markdown)", "json").option("--no-recommendations", "Skip recommendations generation").option("--no-adr", "Skip ADR generation").option("--persona <type>", "Analysis persona (consultant, evangelist, team-lead)", "consultant").option("--llm-coalescing", "Enable LLM coalescing with adversarial validation").option("--adversarial-validation", "Enable adversarial validation (default: enabled with LLM)").option("--challenger", "Enable challenger pass for recommendation validation").option("--confidence-threshold <number>", "Minimum confidence threshold for recommendations", "50").option("--human-review-threshold <number>", "Confidence threshold requiring human review", "70").action(async (repoPath, options) => {
21175
24129
  try {
21176
24130
  const spinner = import_ora.default("Initializing AI Enablement Assessment...").start();
21177
24131
  const config = {
@@ -21182,7 +24136,13 @@ program2.command("analyze").description("Analyze repository for AI enablement re
21182
24136
  outputFormat: options.format,
21183
24137
  persona: options.persona,
21184
24138
  enableLLMCoalescing: options.llmCoalescing || false,
21185
- enableAdversarialValidation: options.adversarialValidation || options.llmCoalescing
24139
+ enableAdversarialValidation: options.adversarialValidation || options.llmCoalescing,
24140
+ recommendationConfig: {
24141
+ enableChallenger: options.challenger || true,
24142
+ confidenceThreshold: parseInt(options.confidenceThreshold) || 50,
24143
+ humanReviewThreshold: parseInt(options.humanReviewThreshold) || 70,
24144
+ enableFeedback: true
24145
+ }
21186
24146
  };
21187
24147
  const engine = new AssessmentEngine(config);
21188
24148
  spinner.text = "Analyzing repository...";
@@ -21227,14 +24187,14 @@ program2.command("recommend").description("Generate recommendations for a reposi
21227
24187
  const spinner = import_ora.default("Generating recommendations...").start();
21228
24188
  const result = await engine.execute();
21229
24189
  spinner.succeed("Recommendations generated!");
21230
- let recommendations = result.recommendations;
24190
+ let recommendations2 = result.recommendations;
21231
24191
  if (options.priority) {
21232
- recommendations = recommendations.filter((r) => r.priority === options.priority);
24192
+ recommendations2 = recommendations2.filter((r) => r.priority === options.priority);
21233
24193
  }
21234
24194
  if (options.category) {
21235
- recommendations = recommendations.filter((r) => r.category === options.category);
24195
+ recommendations2 = recommendations2.filter((r) => r.category === options.category);
21236
24196
  }
21237
- displayRecommendations(recommendations);
24197
+ displayRecommendations(recommendations2);
21238
24198
  } catch (error) {
21239
24199
  console.error(import_chalk.default.red("\u274C Recommendation generation failed:"), error);
21240
24200
  process.exit(1);
@@ -21263,6 +24223,29 @@ program2.command("adr").description("Generate Architecture Decision Record for A
21263
24223
  process.exit(1);
21264
24224
  }
21265
24225
  });
24226
+ program2.command("path").description("Generate strategic AI enablement roadmap with phased approach").argument("<repo-path>", "Path to the repository to analyze").option("-o, --output <path>", "Output directory for roadmap files").option("--format <format>", "Output format (markdown, json, both)", "markdown").option("--persona <type>", "Analysis persona (consultant, evangelist, team-lead)", "consultant").option("--timeframe <months>", "Target timeframe for full enablement in months", "12").option("--pace <speed>", "Implementation pace (aggressive, moderate, conservative)", "moderate").option("--include-metrics", "Include success metrics and KPIs").action(async (repoPath, options) => {
24227
+ try {
24228
+ const engine = new AssessmentEngine({
24229
+ repoPath,
24230
+ includeRecommendations: true,
24231
+ generateADR: false,
24232
+ outputFormat: "json",
24233
+ persona: options.persona
24234
+ });
24235
+ const spinner = import_ora.default("Analyzing repository and generating roadmap...").start();
24236
+ const result = await engine.execute();
24237
+ spinner.text = "Creating strategic roadmap...";
24238
+ const roadmap = await generateRoadmap(result, options);
24239
+ spinner.succeed("Roadmap generated!");
24240
+ displayRoadmapSummary(roadmap);
24241
+ if (options.output) {
24242
+ await saveRoadmap(roadmap, options.output, options.format);
24243
+ }
24244
+ } catch (error) {
24245
+ console.error(import_chalk.default.red("\u274C Roadmap generation failed:"), error);
24246
+ process.exit(1);
24247
+ }
24248
+ });
21266
24249
  process.on("uncaughtException", (error) => {
21267
24250
  console.error(import_chalk.default.red("\u274C Uncaught exception:"), error);
21268
24251
  process.exit(1);