codereview-aia 0.1.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 (153) hide show
  1. package/.cr-aia.yml +23 -0
  2. package/.crignore +0 -0
  3. package/dist/index.js +27 -0
  4. package/package.json +85 -0
  5. package/src/analysis/FindingsExtractor.ts +431 -0
  6. package/src/analysis/ai-detection/analyzers/BaseAnalyzer.ts +267 -0
  7. package/src/analysis/ai-detection/analyzers/DocumentationAnalyzer.ts +622 -0
  8. package/src/analysis/ai-detection/analyzers/GitHistoryAnalyzer.ts +430 -0
  9. package/src/analysis/ai-detection/core/AIDetectionEngine.ts +467 -0
  10. package/src/analysis/ai-detection/types/DetectionTypes.ts +406 -0
  11. package/src/analysis/ai-detection/utils/SubmissionConverter.ts +390 -0
  12. package/src/analysis/context/ReviewContext.ts +378 -0
  13. package/src/analysis/context/index.ts +7 -0
  14. package/src/analysis/index.ts +8 -0
  15. package/src/analysis/tokens/TokenAnalysisFormatter.ts +154 -0
  16. package/src/analysis/tokens/TokenAnalyzer.ts +747 -0
  17. package/src/analysis/tokens/index.ts +8 -0
  18. package/src/clients/base/abstractClient.ts +190 -0
  19. package/src/clients/base/httpClient.ts +160 -0
  20. package/src/clients/base/index.ts +12 -0
  21. package/src/clients/base/modelDetection.ts +107 -0
  22. package/src/clients/base/responseProcessor.ts +586 -0
  23. package/src/clients/factory/clientFactory.ts +55 -0
  24. package/src/clients/factory/index.ts +8 -0
  25. package/src/clients/implementations/index.ts +8 -0
  26. package/src/clients/implementations/openRouterClient.ts +411 -0
  27. package/src/clients/openRouterClient.ts +863 -0
  28. package/src/clients/openRouterClientWrapper.ts +44 -0
  29. package/src/clients/utils/directoryStructure.ts +52 -0
  30. package/src/clients/utils/index.ts +11 -0
  31. package/src/clients/utils/languageDetection.ts +44 -0
  32. package/src/clients/utils/promptFormatter.ts +105 -0
  33. package/src/clients/utils/promptLoader.ts +53 -0
  34. package/src/clients/utils/tokenCounter.ts +297 -0
  35. package/src/core/ApiClientSelector.ts +37 -0
  36. package/src/core/ConfigurationService.ts +591 -0
  37. package/src/core/ConsolidationService.ts +423 -0
  38. package/src/core/InteractiveDisplayManager.ts +81 -0
  39. package/src/core/OutputManager.ts +275 -0
  40. package/src/core/ReviewGenerator.ts +140 -0
  41. package/src/core/fileDiscovery.ts +237 -0
  42. package/src/core/handlers/EstimationHandler.ts +104 -0
  43. package/src/core/handlers/FileProcessingHandler.ts +204 -0
  44. package/src/core/handlers/OutputHandler.ts +125 -0
  45. package/src/core/handlers/ReviewExecutor.ts +104 -0
  46. package/src/core/reviewOrchestrator.ts +333 -0
  47. package/src/core/utils/ModelInfoUtils.ts +56 -0
  48. package/src/formatters/outputFormatter.ts +62 -0
  49. package/src/formatters/utils/IssueFormatters.ts +83 -0
  50. package/src/formatters/utils/JsonFormatter.ts +77 -0
  51. package/src/formatters/utils/MarkdownFormatters.ts +609 -0
  52. package/src/formatters/utils/MetadataFormatter.ts +269 -0
  53. package/src/formatters/utils/ModelInfoExtractor.ts +115 -0
  54. package/src/index.ts +27 -0
  55. package/src/plugins/PluginInterface.ts +50 -0
  56. package/src/plugins/PluginManager.ts +126 -0
  57. package/src/prompts/PromptManager.ts +69 -0
  58. package/src/prompts/cache/PromptCache.ts +50 -0
  59. package/src/prompts/promptText/common/variables/css-frameworks.json +33 -0
  60. package/src/prompts/promptText/common/variables/framework-versions.json +45 -0
  61. package/src/prompts/promptText/frameworks/react/comprehensive.hbs +19 -0
  62. package/src/prompts/promptText/languages/css/comprehensive.hbs +18 -0
  63. package/src/prompts/promptText/languages/generic/comprehensive.hbs +20 -0
  64. package/src/prompts/promptText/languages/html/comprehensive.hbs +18 -0
  65. package/src/prompts/promptText/languages/javascript/comprehensive.hbs +18 -0
  66. package/src/prompts/promptText/languages/python/comprehensive.hbs +18 -0
  67. package/src/prompts/promptText/languages/typescript/comprehensive.hbs +18 -0
  68. package/src/runtime/auth/service.ts +58 -0
  69. package/src/runtime/auth/session.ts +103 -0
  70. package/src/runtime/auth/types.ts +11 -0
  71. package/src/runtime/cliEntry.ts +196 -0
  72. package/src/runtime/errors.ts +13 -0
  73. package/src/runtime/fileCollector.ts +188 -0
  74. package/src/runtime/manifest.ts +64 -0
  75. package/src/runtime/openrouterProxy.ts +45 -0
  76. package/src/runtime/proxyConfig.ts +94 -0
  77. package/src/runtime/proxyEnvironment.ts +71 -0
  78. package/src/runtime/reportMerge.ts +102 -0
  79. package/src/runtime/reporting/markdownReportBuilder.ts +138 -0
  80. package/src/runtime/reporting/reportDataCollector.ts +234 -0
  81. package/src/runtime/reporting/summaryGenerator.ts +86 -0
  82. package/src/runtime/reviewPipeline.ts +155 -0
  83. package/src/runtime/runAiCodeReview.ts +153 -0
  84. package/src/runtime/runtimeConfig.ts +5 -0
  85. package/src/runtime/ui/Layout.tsx +57 -0
  86. package/src/runtime/ui/RuntimeApp.tsx +150 -0
  87. package/src/runtime/ui/inkModules.ts +73 -0
  88. package/src/runtime/ui/screens/AuthScreen.tsx +128 -0
  89. package/src/runtime/ui/screens/ModeSelection.tsx +52 -0
  90. package/src/runtime/ui/screens/ProgressScreen.tsx +55 -0
  91. package/src/runtime/ui/screens/ResultsScreen.tsx +76 -0
  92. package/src/strategies/ArchitecturalReviewStrategy.ts +54 -0
  93. package/src/strategies/CodingTestReviewStrategy.ts +920 -0
  94. package/src/strategies/ConsolidatedReviewStrategy.ts +59 -0
  95. package/src/strategies/ExtractPatternsReviewStrategy.ts +64 -0
  96. package/src/strategies/MultiPassReviewStrategy.ts +785 -0
  97. package/src/strategies/ReviewStrategy.ts +64 -0
  98. package/src/strategies/StrategyFactory.ts +79 -0
  99. package/src/strategies/index.ts +14 -0
  100. package/src/tokenizers/baseTokenizer.ts +61 -0
  101. package/src/tokenizers/gptTokenizer.ts +27 -0
  102. package/src/tokenizers/index.ts +8 -0
  103. package/src/types/apiResponses.ts +40 -0
  104. package/src/types/cli.ts +24 -0
  105. package/src/types/common.ts +39 -0
  106. package/src/types/configuration.ts +201 -0
  107. package/src/types/handlebars.d.ts +5 -0
  108. package/src/types/patch.d.ts +25 -0
  109. package/src/types/review.ts +294 -0
  110. package/src/types/reviewContext.d.ts +65 -0
  111. package/src/types/reviewSchema.ts +181 -0
  112. package/src/types/structuredReview.ts +167 -0
  113. package/src/types/tokenAnalysis.ts +56 -0
  114. package/src/utils/FileReader.ts +93 -0
  115. package/src/utils/FileWriter.ts +76 -0
  116. package/src/utils/PathGenerator.ts +97 -0
  117. package/src/utils/api/apiUtils.ts +14 -0
  118. package/src/utils/api/index.ts +1 -0
  119. package/src/utils/apiErrorHandler.ts +287 -0
  120. package/src/utils/ciDataCollector.ts +252 -0
  121. package/src/utils/codingTestConfigLoader.ts +466 -0
  122. package/src/utils/dependencies/aiDependencyAnalyzer.ts +454 -0
  123. package/src/utils/detection/frameworkDetector.ts +879 -0
  124. package/src/utils/detection/index.ts +10 -0
  125. package/src/utils/detection/projectTypeDetector.ts +518 -0
  126. package/src/utils/diagramGenerator.ts +206 -0
  127. package/src/utils/errorLogger.ts +60 -0
  128. package/src/utils/estimationUtils.ts +407 -0
  129. package/src/utils/fileFilters.ts +373 -0
  130. package/src/utils/fileSystem.ts +57 -0
  131. package/src/utils/index.ts +36 -0
  132. package/src/utils/logger.ts +240 -0
  133. package/src/utils/pathValidator.ts +98 -0
  134. package/src/utils/priorityFilter.ts +59 -0
  135. package/src/utils/projectDocs.ts +189 -0
  136. package/src/utils/promptPaths.ts +29 -0
  137. package/src/utils/promptTemplateManager.ts +157 -0
  138. package/src/utils/review/consolidateReview.ts +553 -0
  139. package/src/utils/review/fixDisplay.ts +100 -0
  140. package/src/utils/review/fixImplementation.ts +61 -0
  141. package/src/utils/review/index.ts +36 -0
  142. package/src/utils/review/interactiveProcessing.ts +294 -0
  143. package/src/utils/review/progressTracker.ts +296 -0
  144. package/src/utils/review/reviewExtraction.ts +382 -0
  145. package/src/utils/review/types.ts +46 -0
  146. package/src/utils/reviewActionHandler.ts +18 -0
  147. package/src/utils/reviewParser.ts +253 -0
  148. package/src/utils/sanitizer.ts +238 -0
  149. package/src/utils/smartFileSelector.ts +255 -0
  150. package/src/utils/templateLoader.ts +514 -0
  151. package/src/utils/treeGenerator.ts +153 -0
  152. package/tsconfig.build.json +14 -0
  153. package/tsconfig.json +59 -0
@@ -0,0 +1,269 @@
1
+ /**
2
+ * @fileoverview Utilities for formatting metadata sections in review outputs.
3
+ *
4
+ * This module provides functions to format metadata sections for review outputs,
5
+ * including cost information, model details, and other metadata.
6
+ */
7
+
8
+ import type { PassCost, ReviewType } from '../../types/review';
9
+ import { extractModelInfoFromString } from './ModelInfoExtractor';
10
+
11
+ /**
12
+ * Format a metadata section for a review
13
+ * @param reviewType Type of review
14
+ * @param timestamp Timestamp of the review
15
+ * @param modelInfo Model information string
16
+ * @param cost Cost information object
17
+ * @param toolVersion Tool version
18
+ * @param commandOptions Command options used
19
+ * @param detectedLanguage Detected language
20
+ * @param detectedFramework Detected framework
21
+ * @param frameworkVersion Framework version
22
+ * @param cssFrameworks CSS frameworks detected
23
+ * @returns Formatted metadata section as markdown
24
+ */
25
+ export function formatMetadataSection(
26
+ reviewType: string,
27
+ timestamp: string,
28
+ modelInfo: string,
29
+ cost?: any,
30
+ toolVersion?: string,
31
+ commandOptions?: string,
32
+ detectedLanguage?: string,
33
+ detectedFramework?: string,
34
+ frameworkVersion?: string,
35
+ cssFrameworks?: Array<{ name: string; version?: string }>,
36
+ ): string {
37
+ // Extract model vendor and name from modelInfo
38
+ const { modelVendor, modelName } = extractModelInfoFromString(modelInfo);
39
+
40
+ // Format the date
41
+ const formattedDate = new Date(timestamp).toLocaleString(undefined, {
42
+ year: 'numeric',
43
+ month: 'long',
44
+ day: 'numeric',
45
+ hour: '2-digit',
46
+ minute: '2-digit',
47
+ second: '2-digit',
48
+ timeZoneName: 'short',
49
+ });
50
+
51
+ // Create metadata section
52
+ let metadataSection = `## Metadata\n| Property | Value |\n|----------|-------|\n| Review Type | ${reviewType} |\n| Generated At | ${formattedDate} |\n| Model Provider | ${modelVendor} |\n| Model Name | ${modelName} |`;
53
+
54
+ // Add framework detection information if available
55
+ if (detectedLanguage) {
56
+ metadataSection += `\n| Detected Language | ${detectedLanguage} |`;
57
+
58
+ if (detectedFramework && detectedFramework !== 'none') {
59
+ metadataSection += `\n| Detected Framework | ${detectedFramework}${frameworkVersion ? ` v${frameworkVersion}` : ''} |`;
60
+ }
61
+
62
+ if (cssFrameworks && cssFrameworks.length > 0) {
63
+ const cssFrameworksStr = cssFrameworks
64
+ .map((cf) => (cf.version ? `${cf.name} v${cf.version.replace(/[^\d.]/g, '')}` : cf.name))
65
+ .join(', ');
66
+
67
+ metadataSection += `\n| CSS Frameworks | ${cssFrameworksStr} |`;
68
+ }
69
+ }
70
+
71
+ // Add cost information if available
72
+ if (cost) {
73
+ metadataSection += `\n| Input Tokens | ${cost.inputTokens.toLocaleString()} |\n| Output Tokens | ${cost.outputTokens.toLocaleString()} |\n| Total Tokens | ${cost.totalTokens.toLocaleString()} |\n| Estimated Cost | ${cost.formattedCost} |`;
74
+
75
+ // Add multi-pass information if available
76
+ if (cost.passCount && cost.passCount > 1) {
77
+ metadataSection += `\n| Multi-pass Review | ${cost.passCount} passes |`;
78
+ }
79
+ }
80
+
81
+ // Add tool version if available
82
+ if (toolVersion) {
83
+ metadataSection += `\n| Tool Version | ${toolVersion} |`;
84
+ }
85
+
86
+ // Add command options if available
87
+ if (commandOptions) {
88
+ metadataSection += `\n| Command Options | \`${commandOptions}\` |`;
89
+ }
90
+
91
+ // Close the metadata table
92
+ metadataSection += `\n`;
93
+
94
+ return metadataSection;
95
+ }
96
+
97
+ /**
98
+ * Parse metadata from a review object
99
+ * @param metadata Metadata object or string
100
+ * @returns Parsed metadata object
101
+ */
102
+ export function parseMetadata(metadata: any): any {
103
+ if (!metadata) {
104
+ return {};
105
+ }
106
+
107
+ try {
108
+ return typeof metadata === 'string' ? JSON.parse(metadata) : metadata;
109
+ } catch (_error) {
110
+ // Silently continue if metadata parsing fails
111
+ return {};
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Create enhanced metadata object with detection info
117
+ * @param modelVendor Model vendor
118
+ * @param modelName Model name
119
+ * @param modelInfo Full model info string
120
+ * @param reviewType Review type
121
+ * @param displayPath Display path
122
+ * @param timestamp Timestamp
123
+ * @param cost Cost information
124
+ * @param toolVersion Tool version
125
+ * @param commandOptions Command options
126
+ * @param additionalMetadata Additional metadata
127
+ * @param detectedLanguage Detected language
128
+ * @param detectedFramework Detected framework
129
+ * @param frameworkVersion Framework version
130
+ * @param cssFrameworks CSS frameworks
131
+ * @returns Enhanced metadata object
132
+ */
133
+ export function createEnhancedMetadata(
134
+ modelVendor: string,
135
+ modelName: string,
136
+ modelInfo: string,
137
+ reviewType: ReviewType,
138
+ displayPath: string,
139
+ timestamp: string,
140
+ cost: any,
141
+ toolVersion?: string,
142
+ commandOptions?: string,
143
+ additionalMetadata: any = {},
144
+ detectedLanguage?: string,
145
+ detectedFramework?: string,
146
+ frameworkVersion?: string,
147
+ cssFrameworks?: Array<{ name: string; version?: string }>,
148
+ ): any {
149
+ // Format the date
150
+ const formattedDate = new Date(timestamp).toLocaleString(undefined, {
151
+ year: 'numeric',
152
+ month: 'long',
153
+ day: 'numeric',
154
+ hour: '2-digit',
155
+ minute: '2-digit',
156
+ second: '2-digit',
157
+ timeZoneName: 'short',
158
+ });
159
+
160
+ // Create enhanced metadata object
161
+ const enhancedMetadata: any = {
162
+ model: {
163
+ provider: modelVendor,
164
+ name: modelName,
165
+ fullName: modelInfo,
166
+ },
167
+ review: {
168
+ type: reviewType,
169
+ path: displayPath,
170
+ generatedAt: new Date(timestamp).toISOString(),
171
+ formattedDate: formattedDate,
172
+ multiPass:
173
+ cost?.passCount && cost.passCount > 1
174
+ ? {
175
+ enabled: true,
176
+ passCount: cost.passCount || 1,
177
+ perPassCosts: cost.perPassCosts || null,
178
+ }
179
+ : null,
180
+ },
181
+ cost: cost || null,
182
+ tool: {
183
+ version: toolVersion || process.env.npm_package_version || '2.1.1',
184
+ commandOptions: commandOptions || null,
185
+ ...additionalMetadata,
186
+ },
187
+ };
188
+
189
+ // Add framework detection information if available
190
+ if (detectedLanguage) {
191
+ enhancedMetadata.detection = {
192
+ language: detectedLanguage,
193
+ };
194
+
195
+ if (detectedFramework && detectedFramework !== 'none') {
196
+ enhancedMetadata.detection.framework = detectedFramework;
197
+ if (frameworkVersion) {
198
+ enhancedMetadata.detection.frameworkVersion = frameworkVersion;
199
+ }
200
+ }
201
+
202
+ if (cssFrameworks && cssFrameworks.length > 0) {
203
+ enhancedMetadata.detection.cssFrameworks = cssFrameworks;
204
+ }
205
+ }
206
+
207
+ return enhancedMetadata;
208
+ }
209
+
210
+ /**
211
+ * Parse cost information from a cost info string
212
+ * @param costInfo Cost information string
213
+ * @returns Parsed cost object or null
214
+ */
215
+ export function parseCostInfo(costInfo: string): any {
216
+ if (!costInfo) {
217
+ return null;
218
+ }
219
+
220
+ // Try to extract cost information from the costInfo string
221
+ const inputTokensMatch = costInfo.match(/Input tokens: ([\d,]+)/);
222
+ const outputTokensMatch = costInfo.match(/Output tokens: ([\d,]+)/);
223
+ const totalTokensMatch = costInfo.match(/Total tokens: ([\d,]+)/);
224
+ const estimatedCostMatch = costInfo.match(/Estimated cost: (.*?)$/m);
225
+ const passCountMatch = costInfo.match(/Multi-pass review: (\d+) passes/);
226
+
227
+ if (inputTokensMatch || outputTokensMatch || totalTokensMatch || estimatedCostMatch) {
228
+ return {
229
+ inputTokens: inputTokensMatch ? parseInt(inputTokensMatch[1].replace(/,/g, '')) : 0,
230
+ outputTokens: outputTokensMatch ? parseInt(outputTokensMatch[1].replace(/,/g, '')) : 0,
231
+ totalTokens: totalTokensMatch ? parseInt(totalTokensMatch[1].replace(/,/g, '')) : 0,
232
+ estimatedCost: estimatedCostMatch
233
+ ? parseFloat(estimatedCostMatch[1].replace('$', '').replace(' USD', ''))
234
+ : 0,
235
+ formattedCost: estimatedCostMatch ? estimatedCostMatch[1] : '$0.00 USD',
236
+ passCount: passCountMatch ? parseInt(passCountMatch[1]) : 1,
237
+ };
238
+ }
239
+
240
+ return null;
241
+ }
242
+
243
+ /**
244
+ * Format cost information as a markdown string
245
+ * @param cost Cost information object
246
+ * @returns Formatted cost information string
247
+ */
248
+ export function formatCostInfo(cost: any): string {
249
+ if (!cost) {
250
+ return '';
251
+ }
252
+
253
+ let costInfo = `\n\n## Token Usage and Cost\n- Input tokens: ${cost.inputTokens.toLocaleString()}\n- Output tokens: ${cost.outputTokens.toLocaleString()}\n- Total tokens: ${cost.totalTokens.toLocaleString()}\n- Estimated cost: ${cost.formattedCost}`;
254
+
255
+ // Add multi-pass information if available
256
+ if (cost.passCount && cost.passCount > 1) {
257
+ costInfo += `\n- Multi-pass review: ${cost.passCount} passes`;
258
+
259
+ // Add per-pass breakdown if available
260
+ if (cost.perPassCosts && Array.isArray(cost.perPassCosts)) {
261
+ costInfo += `\n\n### Pass Breakdown`;
262
+ cost.perPassCosts.forEach((passCost: PassCost) => {
263
+ costInfo += `\nPass ${passCost.passNumber}:\n- Input tokens: ${passCost.inputTokens.toLocaleString()}\n- Output tokens: ${passCost.outputTokens.toLocaleString()}\n- Total tokens: ${passCost.totalTokens.toLocaleString()}\n- Cost: ${typeof passCost.estimatedCost === 'number' ? `$${passCost.estimatedCost.toFixed(4)} USD` : 'N/A'}`;
264
+ });
265
+ }
266
+ }
267
+
268
+ return costInfo;
269
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @fileoverview Utility for extracting model information from model strings.
3
+ *
4
+ * This module provides functions to extract and parse model information from
5
+ * model strings in various formats. It handles different provider prefixes
6
+ * and standardizes the output format.
7
+ */
8
+
9
+ import logger from '../../utils/logger';
10
+
11
+ /**
12
+ * Model information extracted from a model string
13
+ */
14
+ export interface ModelInfo {
15
+ /** The provider of the model (e.g., 'Google', 'Anthropic', 'OpenAI') */
16
+ modelVendor: string;
17
+ /** The name of the model without the provider prefix */
18
+ modelName: string;
19
+ /** A formatted string combining the provider and model name */
20
+ modelInfo: string;
21
+ }
22
+
23
+ /**
24
+ * Extract model information from a model string
25
+ * @param modelString The model string to extract information from (e.g., 'gemini:gemini-1.5-pro')
26
+ * @returns Object containing modelVendor, modelName, and modelInfo
27
+ */
28
+ export function extractModelInfo(modelString?: string): ModelInfo {
29
+ // Default values
30
+ let modelVendor = 'Unknown';
31
+ let modelName = 'AI';
32
+ let modelInfo = 'AI';
33
+
34
+ if (!modelString) {
35
+ logger.warn('No model string provided. Using default values.');
36
+ return { modelVendor, modelName, modelInfo };
37
+ }
38
+
39
+ // Extract model information based on provider prefix
40
+ if (modelString.startsWith('openrouter:')) {
41
+ modelVendor = 'OpenRouter';
42
+ modelName = modelString.substring('openrouter:'.length);
43
+ modelInfo = `OpenRouter (${modelName})`;
44
+ } else if (modelString.startsWith('anthropic:')) {
45
+ modelVendor = 'Anthropic';
46
+ modelName = modelString.substring('anthropic:'.length);
47
+ modelInfo = `Anthropic (${modelName})`;
48
+ } else if (modelString.startsWith('openai:')) {
49
+ modelVendor = 'OpenAI';
50
+ modelName = modelString.substring('openai:'.length);
51
+ modelInfo = `OpenAI (${modelName})`;
52
+ } else if (modelString.startsWith('gemini:')) {
53
+ modelVendor = 'Google';
54
+ modelName = modelString.substring('gemini:'.length);
55
+ modelInfo = `Google Gemini AI (${modelName})`;
56
+ } else if (modelString.startsWith('Google:')) {
57
+ // Handle miscapitalized provider names
58
+ modelVendor = 'Google';
59
+ modelName = modelString.substring('Google:'.length);
60
+ modelInfo = `Google Gemini AI (${modelName})`;
61
+ } else if (modelString.startsWith('Anthropic:')) {
62
+ modelVendor = 'Anthropic';
63
+ modelName = modelString.substring('Anthropic:'.length);
64
+ modelInfo = `Anthropic (${modelName})`;
65
+ } else if (modelString.startsWith('OpenAI:')) {
66
+ modelVendor = 'OpenAI';
67
+ modelName = modelString.substring('OpenAI:'.length);
68
+ modelInfo = `OpenAI (${modelName})`;
69
+ } else if (modelString.startsWith('OpenRouter:')) {
70
+ modelVendor = 'OpenRouter';
71
+ modelName = modelString.substring('OpenRouter:'.length);
72
+ modelInfo = `OpenRouter (${modelName})`;
73
+ } else {
74
+ modelVendor = 'Unknown';
75
+ modelName = modelString;
76
+ modelInfo = `AI (${modelName})`;
77
+ }
78
+
79
+ return { modelVendor, modelName, modelInfo };
80
+ }
81
+
82
+ /**
83
+ * Extract model information from a formatted model info string
84
+ * @param modelInfo Formatted model info string (e.g., "Google Gemini AI (gemini-1.5-pro)")
85
+ * @returns Object containing modelVendor and modelName
86
+ */
87
+ export function extractModelInfoFromString(modelInfo: string): {
88
+ modelVendor: string;
89
+ modelName: string;
90
+ } {
91
+ let modelVendor = 'Unknown';
92
+ let modelName = 'AI';
93
+
94
+ if (modelInfo) {
95
+ if (modelInfo.includes('Google Gemini AI')) {
96
+ modelVendor = 'Google';
97
+ const match = modelInfo.match(/\((.*?)\)/);
98
+ modelName = match ? match[1] : 'Gemini';
99
+ } else if (modelInfo.includes('Anthropic')) {
100
+ modelVendor = 'Anthropic';
101
+ const match = modelInfo.match(/\((.*?)\)/);
102
+ modelName = match ? match[1] : 'Claude';
103
+ } else if (modelInfo.includes('OpenAI')) {
104
+ modelVendor = 'OpenAI';
105
+ const match = modelInfo.match(/\((.*?)\)/);
106
+ modelName = match ? match[1] : 'GPT';
107
+ } else if (modelInfo.includes('OpenRouter')) {
108
+ modelVendor = 'OpenRouter';
109
+ const match = modelInfo.match(/\((.*?)\)/);
110
+ modelName = match ? match[1] : 'AI';
111
+ }
112
+ }
113
+
114
+ return { modelVendor, modelName };
115
+ }
package/src/index.ts ADDED
@@ -0,0 +1,27 @@
1
+ import logger from './utils/logger';
2
+ import { ensureProxyEnvironmentInitialized } from './runtime/proxyEnvironment';
3
+ import { handleRuntimeCliEntry } from './runtime/cliEntry';
4
+
5
+ // Minimal bootstrap that wires the runtime CLI into the proxy-enabled environment.
6
+
7
+ async function bootstrap(): Promise<void> {
8
+ try {
9
+ ensureProxyEnvironmentInitialized(process.cwd());
10
+ } catch (error) {
11
+ logger.warn(
12
+ `Failed to load proxy configuration: ${error instanceof Error ? error.message : String(error)}`,
13
+ );
14
+ }
15
+
16
+ const handled = await handleRuntimeCliEntry();
17
+
18
+ if (!handled) {
19
+ logger.error('Unknown command. Run `cr-aia --help` for usage.');
20
+ process.exit(1);
21
+ }
22
+ }
23
+
24
+ bootstrap().catch((error) => {
25
+ logger.error(`Fatal runtime error: ${error instanceof Error ? error.message : String(error)}`);
26
+ process.exit(1);
27
+ });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @fileoverview Plugin interface for custom review strategies.
3
+ *
4
+ * This module defines the interface for plugins that can be registered with the
5
+ * plugin manager to provide custom review strategies.
6
+ */
7
+
8
+ import type { IReviewStrategy } from '../strategies/ReviewStrategy';
9
+
10
+ /**
11
+ * Interface for plugin registration
12
+ */
13
+ export interface PluginRegistration {
14
+ /**
15
+ * Name of the plugin
16
+ */
17
+ name: string;
18
+
19
+ /**
20
+ * Description of the plugin
21
+ */
22
+ description: string;
23
+
24
+ /**
25
+ * Strategy implementation
26
+ */
27
+ strategy: IReviewStrategy;
28
+ }
29
+
30
+ /**
31
+ * Interface for plugin modules
32
+ */
33
+ export interface Plugin {
34
+ /**
35
+ * Register the plugin with the plugin manager
36
+ * @param pluginManager The plugin manager instance
37
+ */
38
+ register: (pluginManager: any) => void;
39
+
40
+ /**
41
+ * Get information about the plugin
42
+ * @returns Plugin information
43
+ */
44
+ getInfo: () => {
45
+ name: string;
46
+ description: string;
47
+ version: string;
48
+ author: string;
49
+ };
50
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @fileoverview Plugin manager for custom review strategies.
3
+ *
4
+ * This module provides a singleton manager for loading and registering plugins
5
+ * that provide custom review strategies.
6
+ */
7
+
8
+ import fs from 'node:fs/promises';
9
+ import path from 'node:path';
10
+ import type { IReviewStrategy } from '../strategies/ReviewStrategy';
11
+ import logger from '../utils/logger';
12
+ import type { PluginRegistration } from './PluginInterface';
13
+
14
+ /**
15
+ * Singleton manager for plugins
16
+ */
17
+ export class PluginManager {
18
+ private static instance: PluginManager;
19
+ private plugins: Map<string, PluginRegistration> = new Map();
20
+
21
+ /**
22
+ * Private constructor to enforce singleton pattern
23
+ */
24
+ private constructor() {}
25
+
26
+ /**
27
+ * Get the singleton instance
28
+ * @returns The plugin manager instance
29
+ */
30
+ static getInstance(): PluginManager {
31
+ if (!PluginManager.instance) {
32
+ PluginManager.instance = new PluginManager();
33
+ }
34
+ return PluginManager.instance;
35
+ }
36
+
37
+ /**
38
+ * Register a plugin strategy
39
+ * @param registration Plugin registration information
40
+ */
41
+ registerPlugin(registration: PluginRegistration): void {
42
+ if (this.plugins.has(registration.name)) {
43
+ logger.warn(`Plugin with name "${registration.name}" is already registered. Overwriting...`);
44
+ }
45
+
46
+ this.plugins.set(registration.name, registration);
47
+ logger.info(`Registered plugin strategy: ${registration.name}`);
48
+ }
49
+
50
+ /**
51
+ * Get a plugin strategy by name
52
+ * @param name Plugin name
53
+ * @returns The strategy or undefined if not found
54
+ */
55
+ getPlugin(name: string): IReviewStrategy | undefined {
56
+ const registration = this.plugins.get(name);
57
+ return registration?.strategy;
58
+ }
59
+
60
+ /**
61
+ * Get plugin information by name
62
+ * @param name Plugin name
63
+ * @returns Plugin information or undefined if not found
64
+ */
65
+ getPluginInfo(name: string): PluginRegistration | undefined {
66
+ return this.plugins.get(name);
67
+ }
68
+
69
+ /**
70
+ * List all registered plugins
71
+ * @returns Array of plugin registrations
72
+ */
73
+ listPlugins(): PluginRegistration[] {
74
+ return Array.from(this.plugins.values());
75
+ }
76
+
77
+ /**
78
+ * Load plugins from a directory
79
+ * @param pluginsDir Directory containing plugins
80
+ */
81
+ async loadPlugins(pluginsDir: string): Promise<void> {
82
+ try {
83
+ // Check if the directory exists
84
+ try {
85
+ await fs.access(pluginsDir);
86
+ } catch (_error) {
87
+ // Silently ignore missing plugins directory - this is expected in most cases
88
+ logger.debug(`Plugins directory not found: ${pluginsDir}`);
89
+ return;
90
+ }
91
+
92
+ // Read the directory
93
+ const files = await fs.readdir(pluginsDir);
94
+
95
+ // Load each plugin
96
+ for (const file of files) {
97
+ if (file.endsWith('.js')) {
98
+ try {
99
+ const pluginPath = path.join(pluginsDir, file);
100
+ // Dynamic import to load the plugin
101
+ const plugin = await import(pluginPath);
102
+
103
+ // Check if the plugin has a register function
104
+ if (plugin.default && typeof plugin.default.register === 'function') {
105
+ // Register the plugin
106
+ plugin.default.register(this);
107
+ logger.info(`Loaded plugin from ${file}`);
108
+ } else {
109
+ logger.warn(`File ${file} is not a valid plugin (missing register function)`);
110
+ }
111
+ } catch (error) {
112
+ logger.error(
113
+ `Error loading plugin ${file}: ${error instanceof Error ? error.message : String(error)}`,
114
+ );
115
+ }
116
+ }
117
+ }
118
+
119
+ logger.info(`Loaded ${this.plugins.size} plugins from ${pluginsDir}`);
120
+ } catch (error) {
121
+ logger.error(
122
+ `Error loading plugins: ${error instanceof Error ? error.message : String(error)}`,
123
+ );
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @fileoverview Lightweight prompt manager.
3
+ *
4
+ * The upstream project exposes a fairly sophisticated prompt management system.
5
+ * For this fork we keep the API surface that strategies rely on while providing
6
+ * a much smaller implementation that pulls templates from the bundled prompt
7
+ * utilities when available and falls back to opinionated defaults otherwise.
8
+ */
9
+
10
+ import type { ReviewOptions, ReviewType } from '../types/review';
11
+ import logger from '../utils/logger';
12
+ import { getPromptTemplate as fetchTemplate } from '../utils/promptTemplateManager';
13
+ import PromptCache from './cache/PromptCache';
14
+
15
+ /**
16
+ * Singleton prompt manager responsible for serving prompt templates.
17
+ */
18
+ export class PromptManager {
19
+ private static instance: PromptManager | null = null;
20
+ private cache = PromptCache.getInstance();
21
+
22
+ private constructor() {}
23
+
24
+ /**
25
+ * Get the shared prompt manager instance.
26
+ */
27
+ public static getInstance(): PromptManager {
28
+ if (!PromptManager.instance) {
29
+ PromptManager.instance = new PromptManager();
30
+ }
31
+ return PromptManager.instance;
32
+ }
33
+
34
+ /**
35
+ * Retrieve a prompt template for a given review type and options.
36
+ */
37
+ public getPromptTemplate(reviewType: ReviewType, options?: ReviewOptions): string {
38
+ const keyParts = [reviewType, options?.language ?? 'default', options?.framework ?? 'any'];
39
+ const cacheKey = keyParts.join('::');
40
+
41
+ const cached = this.cache.get(cacheKey);
42
+ if (cached) {
43
+ return cached;
44
+ }
45
+
46
+ const template =
47
+ fetchTemplate(reviewType, options?.language, options?.framework) ??
48
+ this.buildFallbackTemplate(reviewType);
49
+
50
+ this.cache.set(cacheKey, template);
51
+ return template;
52
+ }
53
+
54
+ /**
55
+ * Build a minimal fallback template when no custom template exists.
56
+ */
57
+ private buildFallbackTemplate(reviewType: ReviewType): string {
58
+ logger.warn(
59
+ `Prompt template for review type "${reviewType}" not found. Using fallback instructions.`,
60
+ );
61
+
62
+ return [
63
+ `You are an experienced reviewer performing a ${reviewType} assessment.`,
64
+ 'Focus on the provided files, identify actionable issues, and respond with concise findings.',
65
+ ].join('\n\n');
66
+ }
67
+ }
68
+
69
+ export default PromptManager;