@ankh-studio/ai-enablement 1.0.4 → 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 +2511 -30
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -16799,9 +16799,2010 @@ class PersonaFactory {
16799
16799
  }
16800
16800
  }
16801
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
+ }
18653
+ }
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
+ }
18701
+ };
18702
+ }
18703
+ }
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
+ }
18720
+ };
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
+ }
18737
+ }
18738
+ createRankingContext() {
18739
+ return {
18740
+ repoPath: "",
18741
+ evidence: {},
18742
+ scores: {},
18743
+ copilotFeatures: {},
18744
+ techStack: {},
18745
+ config: this.config
18746
+ };
18747
+ }
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;
18755
+ }
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++;
18765
+ }
18766
+ return distribution;
18767
+ }
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;
18774
+ }
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
+ }));
18800
+ }
18801
+ }
18802
+
16802
18803
  // src/scanners/copilot-feature-scanner.ts
16803
- import {constants as constants3, access as access3, readFile as readFile3} from "node:fs/promises";
16804
- 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";
16805
18806
 
16806
18807
  // node_modules/simple-git/dist/esm/index.js
16807
18808
  var file_exists = __toESM(require_dist(), 1);
@@ -20867,10 +22868,10 @@ class CopilotFeatureScanner {
20867
22868
  ".github/COPYLOIT_INSTRUCTIONS.md"
20868
22869
  ];
20869
22870
  for (const path of instructionPaths) {
20870
- const fullPath = join4(repoPath, path);
22871
+ const fullPath = join5(repoPath, path);
20871
22872
  try {
20872
22873
  await access3(fullPath, constants3.R_OK);
20873
- const content = await readFile3(fullPath, "utf-8");
22874
+ const content = await readFile4(fullPath, "utf-8");
20874
22875
  analysis.githubFeatures.copilotInstructions = {
20875
22876
  found: true,
20876
22877
  path,
@@ -20889,10 +22890,10 @@ class CopilotFeatureScanner {
20889
22890
  "docs/CODEOWNERS"
20890
22891
  ];
20891
22892
  for (const path of codeownersPaths) {
20892
- const fullPath = join4(repoPath, path);
22893
+ const fullPath = join5(repoPath, path);
20893
22894
  try {
20894
22895
  await access3(fullPath, constants3.R_OK);
20895
- const content = await readFile3(fullPath, "utf-8");
22896
+ const content = await readFile4(fullPath, "utf-8");
20896
22897
  analysis.githubFeatures.codeowners = {
20897
22898
  found: true,
20898
22899
  path,
@@ -20917,10 +22918,10 @@ class CopilotFeatureScanner {
20917
22918
  ".github/PULL_REQUEST_TEMPLATE.md"
20918
22919
  ];
20919
22920
  for (const path of prTemplatePaths) {
20920
- const fullPath = join4(repoPath, path);
22921
+ const fullPath = join5(repoPath, path);
20921
22922
  try {
20922
22923
  await access3(fullPath, constants3.R_OK);
20923
- const content = await readFile3(fullPath, "utf-8");
22924
+ const content = await readFile4(fullPath, "utf-8");
20924
22925
  analysis.githubFeatures.prTemplates = {
20925
22926
  found: true,
20926
22927
  aiFriendly: this.isAiFriendlyTemplate(content)
@@ -20939,7 +22940,7 @@ class CopilotFeatureScanner {
20939
22940
  }
20940
22941
  async scanCodePatterns(repoPath, analysis) {
20941
22942
  try {
20942
- const tsconfigPath = join4(repoPath, "tsconfig.json");
22943
+ const tsconfigPath = join5(repoPath, "tsconfig.json");
20943
22944
  try {
20944
22945
  await access3(tsconfigPath, constants3.R_OK);
20945
22946
  analysis.codePatterns.typeScriptUsage = true;
@@ -20955,7 +22956,7 @@ class CopilotFeatureScanner {
20955
22956
  "__tests__/"
20956
22957
  ];
20957
22958
  for (const indicator of testIndicators) {
20958
- const indicatorPath = join4(repoPath, indicator);
22959
+ const indicatorPath = join5(repoPath, indicator);
20959
22960
  try {
20960
22961
  await access3(indicatorPath, indicator.endsWith("/") ? constants3.F_OK : constants3.R_OK);
20961
22962
  analysis.codePatterns.testCoverage = true;
@@ -21280,6 +23281,7 @@ class AssessmentEngine {
21280
23281
  scorer;
21281
23282
  adrGenerator;
21282
23283
  evidenceCollector;
23284
+ recommendationEngine;
21283
23285
  config;
21284
23286
  constructor(config) {
21285
23287
  this.config = {
@@ -21297,6 +23299,7 @@ class AssessmentEngine {
21297
23299
  this.scorer = new ReadinessScorer;
21298
23300
  this.adrGenerator = new ADRGenerator;
21299
23301
  this.evidenceCollector = new EvidenceCollector;
23302
+ this.recommendationEngine = new RecommendationEngine(this.config.recommendationConfig);
21300
23303
  }
21301
23304
  async execute() {
21302
23305
  const startTime = Date.now();
@@ -21314,7 +23317,71 @@ class AssessmentEngine {
21314
23317
  evidence
21315
23318
  });
21316
23319
  console.log("\uD83C\uDFAF Scoring complete, generating recommendations...");
21317
- 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
+ }
21318
23385
  let personaInsights;
21319
23386
  if (this.config.persona) {
21320
23387
  console.log("\uD83E\uDD16 Generating persona insights...");
@@ -21328,7 +23395,7 @@ class AssessmentEngine {
21328
23395
  copilotFeatures,
21329
23396
  techStack,
21330
23397
  evidence,
21331
- recommendations,
23398
+ recommendations: recommendations2,
21332
23399
  structuredInsights: personaInsights?.structuredInsights
21333
23400
  });
21334
23401
  }
@@ -21337,7 +23404,7 @@ class AssessmentEngine {
21337
23404
  metadata: {
21338
23405
  timestamp: new Date().toISOString(),
21339
23406
  repository: this.config.repoPath,
21340
- version: "1.0.0",
23407
+ version: "2.0.0",
21341
23408
  duration
21342
23409
  },
21343
23410
  analysis: {
@@ -21346,7 +23413,8 @@ class AssessmentEngine {
21346
23413
  evidence
21347
23414
  },
21348
23415
  scores,
21349
- recommendations
23416
+ recommendations: recommendations2,
23417
+ recommendationEngine
21350
23418
  };
21351
23419
  if (personaInsights) {
21352
23420
  result.personaInsights = personaInsights;
@@ -21362,9 +23430,9 @@ class AssessmentEngine {
21362
23430
  }
21363
23431
  }
21364
23432
  async generateRecommendations(scores, features, evidence) {
21365
- const recommendations = [];
23433
+ const recommendations2 = [];
21366
23434
  if (scores.repoReadiness < 70) {
21367
- recommendations.push({
23435
+ recommendations2.push({
21368
23436
  id: "found-001",
21369
23437
  title: "Improve Repository Structure",
21370
23438
  description: "Standardize repository structure and add essential configuration files",
@@ -21377,7 +23445,7 @@ class AssessmentEngine {
21377
23445
  });
21378
23446
  }
21379
23447
  if (!features.githubFeatures.codeowners.found) {
21380
- recommendations.push({
23448
+ recommendations2.push({
21381
23449
  id: "sec-001",
21382
23450
  title: "Add CODEOWNERS File",
21383
23451
  description: "Define code ownership for better security and review processes",
@@ -21390,7 +23458,7 @@ class AssessmentEngine {
21390
23458
  });
21391
23459
  }
21392
23460
  if (!features.githubFeatures.copilotInstructions.found) {
21393
- recommendations.push({
23461
+ recommendations2.push({
21394
23462
  id: "ai-001",
21395
23463
  title: "Add Copilot Instructions",
21396
23464
  description: "Create Copilot instructions to guide AI assistance in your repository",
@@ -21403,7 +23471,7 @@ class AssessmentEngine {
21403
23471
  });
21404
23472
  }
21405
23473
  if (scores.orgReadiness < 60) {
21406
- recommendations.push({
23474
+ recommendations2.push({
21407
23475
  id: "gov-001",
21408
23476
  title: "Establish Governance Processes",
21409
23477
  description: "Implement clear processes for code review, testing, and deployment",
@@ -21415,7 +23483,7 @@ class AssessmentEngine {
21415
23483
  evidence: this.extractEvidence(evidence, ["governance", "process"])
21416
23484
  });
21417
23485
  }
21418
- return recommendations.sort((a, b) => {
23486
+ return recommendations2.sort((a, b) => {
21419
23487
  const priorityOrder = { high: 3, medium: 2, low: 1 };
21420
23488
  return priorityOrder[b.priority] - priorityOrder[a.priority];
21421
23489
  });
@@ -21440,7 +23508,7 @@ class AssessmentEngine {
21440
23508
  metadata: {
21441
23509
  timestamp: new Date().toISOString(),
21442
23510
  repository: this.config.repoPath,
21443
- version: "1.0.0",
23511
+ version: "2.0.0",
21444
23512
  duration: 0
21445
23513
  },
21446
23514
  analysis: {
@@ -21538,9 +23606,11 @@ function displayResults(result, persona) {
21538
23606
  console.log(import_chalk.default.gray(`Repository: ${result.metadata.repository}`));
21539
23607
  console.log(import_chalk.default.gray(`Assessed: ${result.metadata.timestamp}`));
21540
23608
  console.log(import_chalk.default.gray(`Duration: ${result.metadata.duration}ms`));
23609
+ console.log(import_chalk.default.gray(`Version: ${result.metadata.version}`));
21541
23610
  console.log(import_chalk.default.gray(`Persona: ${persona}\n`));
21542
23611
  displayScores(result.scores);
21543
23612
  displayRecommendations(result.recommendations);
23613
+ displayRecommendationEngine(result.recommendationEngine);
21544
23614
  if (result.personaInsights) {
21545
23615
  displayPersonaInsights(result.personaInsights);
21546
23616
  }
@@ -21581,13 +23651,52 @@ function displayScores(scores) {
21581
23651
  });
21582
23652
  console.log(import_chalk.default.gray(`Confidence: ${scores.confidence}\n`));
21583
23653
  }
21584
- function displayRecommendations(recommendations) {
21585
- 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) {
21586
23695
  console.log(import_chalk.default.green("\u2705 No recommendations needed - repository is well prepared!"));
21587
23696
  return;
21588
23697
  }
21589
23698
  console.log(import_chalk.default.bold.blue("\uD83C\uDFAF Recommendations"));
21590
- const groupedRecs = recommendations.reduce((groups, rec) => {
23699
+ const groupedRecs = recommendations2.reduce((groups, rec) => {
21591
23700
  if (!groups[rec.priority])
21592
23701
  groups[rec.priority] = [];
21593
23702
  groups[rec.priority].push(rec);
@@ -21671,9 +23780,352 @@ function getMaturityStatus(level) {
21671
23780
  return import_chalk.default.hex("#FFA500")("\uD83D\uDD36 Basic");
21672
23781
  return import_chalk.default.red("\u274C Initial");
21673
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
+ }
21674
24126
  var program2 = new Command;
21675
24127
  program2.name("ai-enablement").description("AI Enablement Platform - Analyze repository readiness for AI adoption").version("1.0.0");
21676
- 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) => {
21677
24129
  try {
21678
24130
  const spinner = import_ora.default("Initializing AI Enablement Assessment...").start();
21679
24131
  const config = {
@@ -21684,7 +24136,13 @@ program2.command("analyze").description("Analyze repository for AI enablement re
21684
24136
  outputFormat: options.format,
21685
24137
  persona: options.persona,
21686
24138
  enableLLMCoalescing: options.llmCoalescing || false,
21687
- 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
+ }
21688
24146
  };
21689
24147
  const engine = new AssessmentEngine(config);
21690
24148
  spinner.text = "Analyzing repository...";
@@ -21729,14 +24187,14 @@ program2.command("recommend").description("Generate recommendations for a reposi
21729
24187
  const spinner = import_ora.default("Generating recommendations...").start();
21730
24188
  const result = await engine.execute();
21731
24189
  spinner.succeed("Recommendations generated!");
21732
- let recommendations = result.recommendations;
24190
+ let recommendations2 = result.recommendations;
21733
24191
  if (options.priority) {
21734
- recommendations = recommendations.filter((r) => r.priority === options.priority);
24192
+ recommendations2 = recommendations2.filter((r) => r.priority === options.priority);
21735
24193
  }
21736
24194
  if (options.category) {
21737
- recommendations = recommendations.filter((r) => r.category === options.category);
24195
+ recommendations2 = recommendations2.filter((r) => r.category === options.category);
21738
24196
  }
21739
- displayRecommendations(recommendations);
24197
+ displayRecommendations(recommendations2);
21740
24198
  } catch (error) {
21741
24199
  console.error(import_chalk.default.red("\u274C Recommendation generation failed:"), error);
21742
24200
  process.exit(1);
@@ -21765,6 +24223,29 @@ program2.command("adr").description("Generate Architecture Decision Record for A
21765
24223
  process.exit(1);
21766
24224
  }
21767
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
+ });
21768
24249
  process.on("uncaughtException", (error) => {
21769
24250
  console.error(import_chalk.default.red("\u274C Uncaught exception:"), error);
21770
24251
  process.exit(1);