codereview-aia 0.1.2 → 0.1.4
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.
- package/dist/analysis/static/wpPhpcsRunner.d.ts +11 -0
- package/dist/analysis/static/wpPhpcsRunner.js +219 -0
- package/dist/analysis/static/wpPhpcsRunner.js.map +1 -0
- package/dist/clients/implementations/openRouterClient.js +2 -2
- package/dist/clients/implementations/openRouterClient.js.map +1 -1
- package/dist/clients/openRouterClient.js +2 -2
- package/dist/clients/openRouterClient.js.map +1 -1
- package/dist/clients/utils/promptFormatter.d.ts +3 -2
- package/dist/clients/utils/promptFormatter.js +82 -24
- package/dist/clients/utils/promptFormatter.js.map +1 -1
- package/dist/core/ConfigurationService.d.ts +21 -0
- package/dist/core/ConfigurationService.js +39 -0
- package/dist/core/ConfigurationService.js.map +1 -1
- package/dist/core/handlers/FileProcessingHandler.js +5 -0
- package/dist/core/handlers/FileProcessingHandler.js.map +1 -1
- package/dist/core/reviewOrchestrator.js +61 -1
- package/dist/core/reviewOrchestrator.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime/cliEntry.js +57 -4
- package/dist/runtime/cliEntry.js.map +1 -1
- package/dist/runtime/fileCollector.d.ts +10 -1
- package/dist/runtime/fileCollector.js +217 -2
- package/dist/runtime/fileCollector.js.map +1 -1
- package/dist/runtime/reporting/markdownReportBuilder.d.ts +2 -0
- package/dist/runtime/reporting/markdownReportBuilder.js +57 -0
- package/dist/runtime/reporting/markdownReportBuilder.js.map +1 -1
- package/dist/runtime/reviewPipeline.d.ts +22 -3
- package/dist/runtime/reviewPipeline.js +46 -7
- package/dist/runtime/reviewPipeline.js.map +1 -1
- package/dist/runtime/runAiCodeReview.d.ts +19 -1
- package/dist/runtime/runAiCodeReview.js +243 -8
- package/dist/runtime/runAiCodeReview.js.map +1 -1
- package/dist/runtime/ui/RuntimeApp.js +15 -4
- package/dist/runtime/ui/RuntimeApp.js.map +1 -1
- package/dist/runtime/ui/screens/ProgressScreen.d.ts +6 -1
- package/dist/runtime/ui/screens/ProgressScreen.js +28 -2
- package/dist/runtime/ui/screens/ProgressScreen.js.map +1 -1
- package/dist/runtime/ui/screens/ResultsScreen.js +8 -1
- package/dist/runtime/ui/screens/ResultsScreen.js.map +1 -1
- package/dist/types/review.d.ts +60 -0
- package/dist/utils/detection/frameworkDetector.js +55 -0
- package/dist/utils/detection/frameworkDetector.js.map +1 -1
- package/dist/utils/promptTemplateManager.js +1 -0
- package/dist/utils/promptTemplateManager.js.map +1 -1
- package/package.json +13 -10
- package/.cr-aia.yml +0 -23
- package/.crignore +0 -0
- package/src/analysis/FindingsExtractor.ts +0 -431
- package/src/analysis/ai-detection/analyzers/BaseAnalyzer.ts +0 -267
- package/src/analysis/ai-detection/analyzers/DocumentationAnalyzer.ts +0 -622
- package/src/analysis/ai-detection/analyzers/GitHistoryAnalyzer.ts +0 -430
- package/src/analysis/ai-detection/core/AIDetectionEngine.ts +0 -467
- package/src/analysis/ai-detection/types/DetectionTypes.ts +0 -406
- package/src/analysis/ai-detection/utils/SubmissionConverter.ts +0 -390
- package/src/analysis/context/ReviewContext.ts +0 -378
- package/src/analysis/context/index.ts +0 -7
- package/src/analysis/index.ts +0 -8
- package/src/analysis/tokens/TokenAnalysisFormatter.ts +0 -154
- package/src/analysis/tokens/TokenAnalyzer.ts +0 -747
- package/src/analysis/tokens/index.ts +0 -8
- package/src/clients/base/abstractClient.ts +0 -190
- package/src/clients/base/httpClient.ts +0 -160
- package/src/clients/base/index.ts +0 -12
- package/src/clients/base/modelDetection.ts +0 -107
- package/src/clients/base/responseProcessor.ts +0 -586
- package/src/clients/factory/clientFactory.ts +0 -55
- package/src/clients/factory/index.ts +0 -8
- package/src/clients/implementations/index.ts +0 -8
- package/src/clients/implementations/openRouterClient.ts +0 -411
- package/src/clients/openRouterClient.ts +0 -863
- package/src/clients/openRouterClientWrapper.ts +0 -44
- package/src/clients/utils/directoryStructure.ts +0 -52
- package/src/clients/utils/index.ts +0 -11
- package/src/clients/utils/languageDetection.ts +0 -44
- package/src/clients/utils/promptFormatter.ts +0 -105
- package/src/clients/utils/promptLoader.ts +0 -53
- package/src/clients/utils/tokenCounter.ts +0 -297
- package/src/core/ApiClientSelector.ts +0 -37
- package/src/core/ConfigurationService.ts +0 -591
- package/src/core/ConsolidationService.ts +0 -423
- package/src/core/InteractiveDisplayManager.ts +0 -81
- package/src/core/OutputManager.ts +0 -275
- package/src/core/ReviewGenerator.ts +0 -140
- package/src/core/fileDiscovery.ts +0 -237
- package/src/core/handlers/EstimationHandler.ts +0 -104
- package/src/core/handlers/FileProcessingHandler.ts +0 -204
- package/src/core/handlers/OutputHandler.ts +0 -125
- package/src/core/handlers/ReviewExecutor.ts +0 -104
- package/src/core/reviewOrchestrator.ts +0 -333
- package/src/core/utils/ModelInfoUtils.ts +0 -56
- package/src/formatters/outputFormatter.ts +0 -62
- package/src/formatters/utils/IssueFormatters.ts +0 -83
- package/src/formatters/utils/JsonFormatter.ts +0 -77
- package/src/formatters/utils/MarkdownFormatters.ts +0 -609
- package/src/formatters/utils/MetadataFormatter.ts +0 -269
- package/src/formatters/utils/ModelInfoExtractor.ts +0 -115
- package/src/index.ts +0 -28
- package/src/plugins/PluginInterface.ts +0 -50
- package/src/plugins/PluginManager.ts +0 -126
- package/src/prompts/PromptManager.ts +0 -69
- package/src/prompts/cache/PromptCache.ts +0 -50
- package/src/prompts/promptText/common/variables/css-frameworks.json +0 -33
- package/src/prompts/promptText/common/variables/framework-versions.json +0 -45
- package/src/prompts/promptText/frameworks/react/comprehensive.hbs +0 -19
- package/src/prompts/promptText/languages/css/comprehensive.hbs +0 -18
- package/src/prompts/promptText/languages/generic/comprehensive.hbs +0 -20
- package/src/prompts/promptText/languages/html/comprehensive.hbs +0 -18
- package/src/prompts/promptText/languages/javascript/comprehensive.hbs +0 -18
- package/src/prompts/promptText/languages/python/comprehensive.hbs +0 -18
- package/src/prompts/promptText/languages/typescript/comprehensive.hbs +0 -18
- package/src/runtime/auth/service.ts +0 -58
- package/src/runtime/auth/session.ts +0 -103
- package/src/runtime/auth/types.ts +0 -11
- package/src/runtime/cliEntry.ts +0 -196
- package/src/runtime/debug/logManager.ts +0 -37
- package/src/runtime/errors.ts +0 -13
- package/src/runtime/fileCollector.ts +0 -222
- package/src/runtime/manifest.ts +0 -64
- package/src/runtime/openrouterProxy.ts +0 -45
- package/src/runtime/preprod/webCheck.ts +0 -104
- package/src/runtime/proxyConfig.ts +0 -94
- package/src/runtime/proxyEnvironment.ts +0 -71
- package/src/runtime/reportMerge.ts +0 -102
- package/src/runtime/reporting/markdownReportBuilder.ts +0 -138
- package/src/runtime/reporting/reportDataCollector.ts +0 -234
- package/src/runtime/reporting/summaryGenerator.ts +0 -86
- package/src/runtime/reviewPipeline.ts +0 -161
- package/src/runtime/runAiCodeReview.ts +0 -153
- package/src/runtime/runtimeConfig.ts +0 -5
- package/src/runtime/ui/Layout.tsx +0 -57
- package/src/runtime/ui/RuntimeApp.tsx +0 -233
- package/src/runtime/ui/inkModules.ts +0 -73
- package/src/runtime/ui/screens/AuthScreen.tsx +0 -128
- package/src/runtime/ui/screens/ModeSelection.tsx +0 -185
- package/src/runtime/ui/screens/ProgressScreen.tsx +0 -62
- package/src/runtime/ui/screens/ResultsScreen.tsx +0 -83
- package/src/strategies/ArchitecturalReviewStrategy.ts +0 -54
- package/src/strategies/CodingTestReviewStrategy.ts +0 -920
- package/src/strategies/ConsolidatedReviewStrategy.ts +0 -59
- package/src/strategies/ExtractPatternsReviewStrategy.ts +0 -64
- package/src/strategies/MultiPassReviewStrategy.ts +0 -785
- package/src/strategies/ReviewStrategy.ts +0 -64
- package/src/strategies/StrategyFactory.ts +0 -79
- package/src/strategies/index.ts +0 -14
- package/src/tokenizers/baseTokenizer.ts +0 -61
- package/src/tokenizers/gptTokenizer.ts +0 -27
- package/src/tokenizers/index.ts +0 -8
- package/src/types/apiResponses.ts +0 -40
- package/src/types/cli.ts +0 -24
- package/src/types/common.ts +0 -39
- package/src/types/configuration.ts +0 -201
- package/src/types/handlebars.d.ts +0 -5
- package/src/types/patch.d.ts +0 -25
- package/src/types/review.ts +0 -294
- package/src/types/reviewContext.d.ts +0 -65
- package/src/types/reviewSchema.ts +0 -181
- package/src/types/structuredReview.ts +0 -167
- package/src/types/tokenAnalysis.ts +0 -56
- package/src/utils/FileReader.ts +0 -93
- package/src/utils/FileWriter.ts +0 -76
- package/src/utils/PathGenerator.ts +0 -97
- package/src/utils/api/apiUtils.ts +0 -14
- package/src/utils/api/index.ts +0 -1
- package/src/utils/apiErrorHandler.ts +0 -287
- package/src/utils/ciDataCollector.ts +0 -252
- package/src/utils/codingTestConfigLoader.ts +0 -466
- package/src/utils/dependencies/aiDependencyAnalyzer.ts +0 -454
- package/src/utils/detection/frameworkDetector.ts +0 -879
- package/src/utils/detection/index.ts +0 -10
- package/src/utils/detection/projectTypeDetector.ts +0 -518
- package/src/utils/diagramGenerator.ts +0 -206
- package/src/utils/errorLogger.ts +0 -60
- package/src/utils/estimationUtils.ts +0 -407
- package/src/utils/fileFilters.ts +0 -373
- package/src/utils/fileSystem.ts +0 -57
- package/src/utils/index.ts +0 -36
- package/src/utils/logger.ts +0 -290
- package/src/utils/pathValidator.ts +0 -98
- package/src/utils/priorityFilter.ts +0 -59
- package/src/utils/projectDocs.ts +0 -189
- package/src/utils/promptPaths.ts +0 -29
- package/src/utils/promptTemplateManager.ts +0 -157
- package/src/utils/review/consolidateReview.ts +0 -553
- package/src/utils/review/fixDisplay.ts +0 -100
- package/src/utils/review/fixImplementation.ts +0 -61
- package/src/utils/review/index.ts +0 -36
- package/src/utils/review/interactiveProcessing.ts +0 -294
- package/src/utils/review/progressTracker.ts +0 -296
- package/src/utils/review/reviewExtraction.ts +0 -382
- package/src/utils/review/types.ts +0 -46
- package/src/utils/reviewActionHandler.ts +0 -18
- package/src/utils/reviewParser.ts +0 -253
- package/src/utils/sanitizer.ts +0 -238
- package/src/utils/smartFileSelector.ts +0 -255
- package/src/utils/templateLoader.ts +0 -514
- package/src/utils/treeGenerator.ts +0 -153
- package/tsconfig.build.json +0 -14
- package/tsconfig.json +0 -59
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import type { ReviewTotals } from './reviewPipeline';
|
|
2
|
-
|
|
3
|
-
export interface StructuredIssue {
|
|
4
|
-
priority?: string;
|
|
5
|
-
filePath?: string;
|
|
6
|
-
lineNumbers?: string;
|
|
7
|
-
type?: string;
|
|
8
|
-
description?: string;
|
|
9
|
-
suggestedFix?: string;
|
|
10
|
-
title?: string;
|
|
11
|
-
impact?: string;
|
|
12
|
-
[key: string]: unknown;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface StructuredDataReport {
|
|
16
|
-
structuredData?: {
|
|
17
|
-
issues?: StructuredIssue[];
|
|
18
|
-
};
|
|
19
|
-
totals?: Record<string, number>;
|
|
20
|
-
metadata?: Record<string, unknown>;
|
|
21
|
-
costInfo?: Record<string, unknown>;
|
|
22
|
-
modelUsed?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface MergeResult {
|
|
26
|
-
findings: Record<string, unknown>[];
|
|
27
|
-
totals: ReviewTotals;
|
|
28
|
-
metadata?: Record<string, unknown>;
|
|
29
|
-
costInfo?: Record<string, unknown>;
|
|
30
|
-
modelUsed?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function mergeReports(reports: StructuredDataReport[]): MergeResult {
|
|
34
|
-
if (reports.length === 0) {
|
|
35
|
-
return { findings: [], totals: { critical: 0, high: 0, medium: 0, low: 0 } };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (reports.length === 1) {
|
|
39
|
-
return normalizeReport(reports[0]);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const allFindings: Record<string, unknown>[] = [];
|
|
43
|
-
const totals: ReviewTotals = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
44
|
-
|
|
45
|
-
for (const report of reports) {
|
|
46
|
-
const normalized = normalizeReport(report);
|
|
47
|
-
if (normalized.findings) {
|
|
48
|
-
allFindings.push(...(normalized.findings as Record<string, unknown>[]));
|
|
49
|
-
}
|
|
50
|
-
if (normalized.totals) {
|
|
51
|
-
totals.critical += normalized.totals.critical || 0;
|
|
52
|
-
totals.high += normalized.totals.high || 0;
|
|
53
|
-
totals.medium += normalized.totals.medium || 0;
|
|
54
|
-
totals.low += normalized.totals.low || 0;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
findings: allFindings,
|
|
60
|
-
totals,
|
|
61
|
-
metadata: {
|
|
62
|
-
mergedFrom: reports.length,
|
|
63
|
-
timestamp: new Date().toISOString(),
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function normalizeReport(report: StructuredDataReport): MergeResult {
|
|
69
|
-
const findings: Record<string, unknown>[] = [];
|
|
70
|
-
const totals: ReviewTotals = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
71
|
-
|
|
72
|
-
if (report.structuredData?.issues) {
|
|
73
|
-
for (const issue of report.structuredData.issues) {
|
|
74
|
-
const priority = issue.priority?.toLowerCase() || 'medium';
|
|
75
|
-
const finding = {
|
|
76
|
-
file: issue.filePath,
|
|
77
|
-
line: parseInt(issue.lineNumbers?.split('-')[0] || '0', 10),
|
|
78
|
-
rule: issue.type || 'general',
|
|
79
|
-
severity: priority,
|
|
80
|
-
msg: issue.description,
|
|
81
|
-
fix_hint: issue.suggestedFix,
|
|
82
|
-
title: issue.title,
|
|
83
|
-
impact: issue.impact,
|
|
84
|
-
raw: issue,
|
|
85
|
-
};
|
|
86
|
-
findings.push(finding);
|
|
87
|
-
|
|
88
|
-
if (priority === 'critical') totals.critical += 1;
|
|
89
|
-
else if (priority === 'high') totals.high += 1;
|
|
90
|
-
else if (priority === 'medium') totals.medium += 1;
|
|
91
|
-
else totals.low += 1;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
findings,
|
|
97
|
-
totals,
|
|
98
|
-
metadata: report.metadata,
|
|
99
|
-
costInfo: report.costInfo,
|
|
100
|
-
modelUsed: report.modelUsed,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import type { FileIssueGroup, TokenStats } from './reportDataCollector';
|
|
2
|
-
import type { ReviewTotals } from '../reviewPipeline';
|
|
3
|
-
|
|
4
|
-
export const SUMMARY_PLACEHOLDER = '<!--REPORT_SUMMARY_PLACEHOLDER-->';
|
|
5
|
-
|
|
6
|
-
export interface MarkdownReportContext {
|
|
7
|
-
repoName: string;
|
|
8
|
-
generatedAt: Date;
|
|
9
|
-
durationSeconds: number;
|
|
10
|
-
filesReviewed: number;
|
|
11
|
-
totals: ReviewTotals;
|
|
12
|
-
tokenStats?: TokenStats;
|
|
13
|
-
estimatedCostUSD?: number;
|
|
14
|
-
issueGroups: FileIssueGroup[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function pad(num: number): string {
|
|
18
|
-
return num.toString().padStart(2, '0');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function formatEuropeanTimestamp(date: Date): string {
|
|
22
|
-
const day = pad(date.getDate());
|
|
23
|
-
const month = pad(date.getMonth() + 1);
|
|
24
|
-
const year = date.getFullYear();
|
|
25
|
-
const hours = pad(date.getHours());
|
|
26
|
-
const minutes = pad(date.getMinutes());
|
|
27
|
-
return `${day}.${month}.${year} ${hours}.${minutes}`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function formatDuration(seconds: number): string {
|
|
31
|
-
const hrs = Math.floor(seconds / 3600);
|
|
32
|
-
const mins = Math.floor((seconds % 3600) / 60);
|
|
33
|
-
const secs = seconds % 60;
|
|
34
|
-
return `${pad(hrs)}:${pad(mins)}:${pad(secs)}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function formatCost(amount?: number): string | undefined {
|
|
38
|
-
if (typeof amount !== 'number' || Number.isNaN(amount)) {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
return `$${amount.toFixed(amount < 1 ? 4 : 2)} USD`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function buildStatsSection(ctx: MarkdownReportContext): string {
|
|
45
|
-
const stats: string[] = ['### Stats'];
|
|
46
|
-
|
|
47
|
-
const cost = formatCost(ctx.estimatedCostUSD);
|
|
48
|
-
if (cost) {
|
|
49
|
-
stats.push(`- Spend: ${cost}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
stats.push(`- Duration: ${formatDuration(ctx.durationSeconds)}`);
|
|
53
|
-
stats.push(`- Files reviewed: ${ctx.filesReviewed}`);
|
|
54
|
-
|
|
55
|
-
if (ctx.tokenStats) {
|
|
56
|
-
stats.push(
|
|
57
|
-
`- Total tokens: ${ctx.tokenStats.totalTokens.toLocaleString()} (input: ${ctx.tokenStats.inputTokens.toLocaleString()} / output: ${ctx.tokenStats.outputTokens.toLocaleString()})`,
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
stats.push(
|
|
62
|
-
`- Issues: Critical ${ctx.totals.critical} · High ${ctx.totals.high} · Medium ${ctx.totals.medium} · Low ${ctx.totals.low}`,
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
return stats.join('\n');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function formatIssueTitle(issue: FileIssueGroup['issues'][number]): string {
|
|
69
|
-
const capitalized = issue.severity.charAt(0).toUpperCase() + issue.severity.slice(1);
|
|
70
|
-
const linePart = issue.lineLabel ? ` (lines ${issue.lineLabel})` : '';
|
|
71
|
-
const title = issue.title || 'Issue';
|
|
72
|
-
return `##### [${capitalized}] ${title}${linePart}`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function buildIssueBlock(group: FileIssueGroup): string {
|
|
76
|
-
const lines: string[] = [`#### ${group.filePath}`];
|
|
77
|
-
|
|
78
|
-
for (const issue of group.issues) {
|
|
79
|
-
lines.push(formatIssueTitle(issue));
|
|
80
|
-
|
|
81
|
-
if (issue.description) {
|
|
82
|
-
lines.push(`- **Problem:** ${issue.description.trim()}`);
|
|
83
|
-
}
|
|
84
|
-
if (issue.impact) {
|
|
85
|
-
lines.push(`- **Impact:** ${issue.impact.trim()}`);
|
|
86
|
-
}
|
|
87
|
-
if (issue.suggestedFix) {
|
|
88
|
-
lines.push(`- **Recommendation:** ${issue.suggestedFix.trim()}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
lines.push('');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return lines.join('\n').trimEnd();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function buildIssuesSection(groups: FileIssueGroup[]): string {
|
|
98
|
-
if (groups.length === 0) {
|
|
99
|
-
return '### Issues\n_No actionable issues reported in this review._';
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const blocks = groups.map((group) => buildIssueBlock(group));
|
|
103
|
-
return `### Issues\n\n${blocks.join('\n\n---\n\n')}`;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function buildMarkdownReport(ctx: MarkdownReportContext): string {
|
|
107
|
-
const parts: string[] = [];
|
|
108
|
-
const timestamp = formatEuropeanTimestamp(ctx.generatedAt);
|
|
109
|
-
|
|
110
|
-
parts.push(`# Code Review Report — ${timestamp}`);
|
|
111
|
-
parts.push(`**Repository:** ${ctx.repoName}`);
|
|
112
|
-
parts.push('\n---\n');
|
|
113
|
-
|
|
114
|
-
parts.push(buildStatsSection(ctx));
|
|
115
|
-
parts.push('\n\n---\n');
|
|
116
|
-
|
|
117
|
-
parts.push('### Summary');
|
|
118
|
-
parts.push(SUMMARY_PLACEHOLDER);
|
|
119
|
-
parts.push('\n---\n');
|
|
120
|
-
|
|
121
|
-
parts.push(buildIssuesSection(ctx.issueGroups));
|
|
122
|
-
parts.push('\n\n---\n');
|
|
123
|
-
parts.push('_Generated by cr-aia_');
|
|
124
|
-
|
|
125
|
-
return parts.join('\n\n');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function injectSummary(markdown: string, summary: string): string {
|
|
129
|
-
if (!markdown.includes(SUMMARY_PLACEHOLDER)) {
|
|
130
|
-
return markdown;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const formattedSummary = summary.trim() ? summary.trim() : '_Summary unavailable._';
|
|
134
|
-
return markdown.replace(
|
|
135
|
-
SUMMARY_PLACEHOLDER,
|
|
136
|
-
`>${formattedSummary.replace(/\n+/g, ' ')}`,
|
|
137
|
-
);
|
|
138
|
-
}
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
import type { StructuredDataReport, StructuredIssue } from '../reportMerge';
|
|
2
|
-
import type { ReviewTotals } from '../reviewPipeline';
|
|
3
|
-
|
|
4
|
-
export type IssueSeverity = 'critical' | 'high' | 'medium' | 'low';
|
|
5
|
-
|
|
6
|
-
export interface NormalizedIssue {
|
|
7
|
-
filePath: string;
|
|
8
|
-
severity: IssueSeverity;
|
|
9
|
-
title?: string;
|
|
10
|
-
description?: string;
|
|
11
|
-
impact?: string;
|
|
12
|
-
suggestedFix?: string;
|
|
13
|
-
lineLabel?: string;
|
|
14
|
-
lineStart?: number;
|
|
15
|
-
lineEnd?: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface FileIssueGroup {
|
|
19
|
-
filePath: string;
|
|
20
|
-
issues: NormalizedIssue[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface TokenStats {
|
|
24
|
-
totalTokens: number;
|
|
25
|
-
inputTokens: number;
|
|
26
|
-
outputTokens: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface CollectedReportData {
|
|
30
|
-
totals: ReviewTotals;
|
|
31
|
-
groupedIssues: FileIssueGroup[];
|
|
32
|
-
tokenStats?: TokenStats;
|
|
33
|
-
estimatedCostUSD?: number;
|
|
34
|
-
issueCount: number;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const SEVERITY_ORDER: Record<IssueSeverity, number> = {
|
|
38
|
-
critical: 0,
|
|
39
|
-
high: 1,
|
|
40
|
-
medium: 2,
|
|
41
|
-
low: 3,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
function normalizeSeverity(priority?: string): IssueSeverity {
|
|
45
|
-
const normalized = priority?.toLowerCase().trim();
|
|
46
|
-
if (normalized === 'critical') return 'critical';
|
|
47
|
-
if (normalized === 'high') return 'high';
|
|
48
|
-
if (normalized === 'low') return 'low';
|
|
49
|
-
return 'medium';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function parseLineLabel(lineNumbers?: string): {
|
|
53
|
-
label?: string;
|
|
54
|
-
start?: number;
|
|
55
|
-
end?: number;
|
|
56
|
-
} {
|
|
57
|
-
if (!lineNumbers) {
|
|
58
|
-
return {};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const trimmed = lineNumbers.trim();
|
|
62
|
-
if (!trimmed) {
|
|
63
|
-
return {};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const match = trimmed.match(/^(\d+)(?:\s*-\s*(\d+))?$/);
|
|
67
|
-
if (!match) {
|
|
68
|
-
return { label: trimmed };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const start = Number.parseInt(match[1], 10);
|
|
72
|
-
const end = match[2] ? Number.parseInt(match[2], 10) : start;
|
|
73
|
-
const label = start === end ? `${start}` : `${start}-${end}`;
|
|
74
|
-
|
|
75
|
-
return { label, start, end };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function toNumber(value: unknown): number | undefined {
|
|
79
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
80
|
-
return value;
|
|
81
|
-
}
|
|
82
|
-
if (typeof value === 'string') {
|
|
83
|
-
const parsed = Number.parseFloat(value);
|
|
84
|
-
if (!Number.isNaN(parsed)) {
|
|
85
|
-
return parsed;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function extractIssues(reports: StructuredDataReport[]): NormalizedIssue[] {
|
|
92
|
-
const issues: NormalizedIssue[] = [];
|
|
93
|
-
|
|
94
|
-
for (const report of reports) {
|
|
95
|
-
let structuredIssues: StructuredIssue[] | undefined = report.structuredData?.issues;
|
|
96
|
-
|
|
97
|
-
if (!structuredIssues && typeof report.structuredData === 'string') {
|
|
98
|
-
try {
|
|
99
|
-
const parsed = JSON.parse(report.structuredData);
|
|
100
|
-
structuredIssues = parsed?.issues;
|
|
101
|
-
} catch {
|
|
102
|
-
structuredIssues = undefined;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (!Array.isArray(structuredIssues)) {
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
for (const issue of structuredIssues) {
|
|
111
|
-
const filePath = issue.filePath?.trim() || 'Unspecified file';
|
|
112
|
-
const severity = normalizeSeverity(issue.priority);
|
|
113
|
-
const { label, start, end } = parseLineLabel(issue.lineNumbers);
|
|
114
|
-
|
|
115
|
-
issues.push({
|
|
116
|
-
filePath,
|
|
117
|
-
severity,
|
|
118
|
-
title: issue.title,
|
|
119
|
-
description: issue.description,
|
|
120
|
-
impact: issue.impact,
|
|
121
|
-
suggestedFix: issue.suggestedFix,
|
|
122
|
-
lineLabel: label,
|
|
123
|
-
lineStart: start,
|
|
124
|
-
lineEnd: end,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return issues;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function groupIssuesByFile(issues: NormalizedIssue[]): FileIssueGroup[] {
|
|
133
|
-
const groups = new Map<string, NormalizedIssue[]>();
|
|
134
|
-
|
|
135
|
-
for (const issue of issues) {
|
|
136
|
-
if (!groups.has(issue.filePath)) {
|
|
137
|
-
groups.set(issue.filePath, []);
|
|
138
|
-
}
|
|
139
|
-
groups.get(issue.filePath)!.push(issue);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const sortedGroups = Array.from(groups.entries())
|
|
143
|
-
.map(([filePath, fileIssues]) => {
|
|
144
|
-
fileIssues.sort((a, b) => {
|
|
145
|
-
const severityDiff = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
|
|
146
|
-
if (severityDiff !== 0) {
|
|
147
|
-
return severityDiff;
|
|
148
|
-
}
|
|
149
|
-
if (a.lineStart && b.lineStart) {
|
|
150
|
-
return a.lineStart - b.lineStart;
|
|
151
|
-
}
|
|
152
|
-
return (a.title || '').localeCompare(b.title || '');
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
return { filePath, issues: fileIssues };
|
|
156
|
-
})
|
|
157
|
-
.sort((a, b) => a.filePath.localeCompare(b.filePath));
|
|
158
|
-
|
|
159
|
-
return sortedGroups;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function aggregateCostInfo(reports: StructuredDataReport[]): {
|
|
163
|
-
tokenStats?: TokenStats;
|
|
164
|
-
estimatedCostUSD?: number;
|
|
165
|
-
} {
|
|
166
|
-
let inputTokens = 0;
|
|
167
|
-
let outputTokens = 0;
|
|
168
|
-
let estimatedCost = 0;
|
|
169
|
-
let hasCostInfo = false;
|
|
170
|
-
|
|
171
|
-
for (const report of reports) {
|
|
172
|
-
const costInfo = (report.costInfo || (report as any).costInfo) as Record<string, unknown> | undefined;
|
|
173
|
-
if (!costInfo) continue;
|
|
174
|
-
|
|
175
|
-
const input = toNumber(costInfo.inputTokens);
|
|
176
|
-
const output = toNumber(costInfo.outputTokens);
|
|
177
|
-
const total = toNumber(costInfo.totalTokens);
|
|
178
|
-
const estimate = toNumber(costInfo.estimatedCost ?? costInfo.cost);
|
|
179
|
-
|
|
180
|
-
if (input) {
|
|
181
|
-
inputTokens += input;
|
|
182
|
-
hasCostInfo = true;
|
|
183
|
-
}
|
|
184
|
-
if (output) {
|
|
185
|
-
outputTokens += output;
|
|
186
|
-
hasCostInfo = true;
|
|
187
|
-
}
|
|
188
|
-
if (estimate) {
|
|
189
|
-
estimatedCost += estimate;
|
|
190
|
-
hasCostInfo = true;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (!input && !output && total && !Number.isNaN(total)) {
|
|
194
|
-
// If total tokens is provided without split, distribute evenly.
|
|
195
|
-
inputTokens += total / 2;
|
|
196
|
-
outputTokens += total / 2;
|
|
197
|
-
hasCostInfo = true;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (!hasCostInfo) {
|
|
202
|
-
return {};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
tokenStats: {
|
|
207
|
-
inputTokens: Math.round(inputTokens),
|
|
208
|
-
outputTokens: Math.round(outputTokens),
|
|
209
|
-
totalTokens: Math.round(inputTokens + outputTokens),
|
|
210
|
-
},
|
|
211
|
-
estimatedCostUSD: estimatedCost || undefined,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export function collectReportData(reports: StructuredDataReport[]): CollectedReportData {
|
|
216
|
-
const issues = extractIssues(reports);
|
|
217
|
-
const groupedIssues = groupIssuesByFile(issues);
|
|
218
|
-
|
|
219
|
-
// Derive severity totals if missing (default zero).
|
|
220
|
-
const totals: ReviewTotals = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
221
|
-
for (const issue of issues) {
|
|
222
|
-
totals[issue.severity] += 1;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const { tokenStats, estimatedCostUSD } = aggregateCostInfo(reports);
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
totals,
|
|
229
|
-
groupedIssues,
|
|
230
|
-
tokenStats,
|
|
231
|
-
estimatedCostUSD,
|
|
232
|
-
issueCount: issues.length,
|
|
233
|
-
};
|
|
234
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { buildOpenRouterProxyHeaders, resolveOpenRouterProxyUrl, withProxyMetadata } from '../openrouterProxy';
|
|
2
|
-
import type { ReviewTotals } from '../reviewPipeline';
|
|
3
|
-
import logger from '../../utils/logger';
|
|
4
|
-
|
|
5
|
-
interface SummaryGeneratorOptions {
|
|
6
|
-
model?: string;
|
|
7
|
-
totals: ReviewTotals;
|
|
8
|
-
durationSeconds: number;
|
|
9
|
-
estimatedCostUSD?: number;
|
|
10
|
-
issueCount: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function buildFallbackSummary(options: SummaryGeneratorOptions): string {
|
|
14
|
-
const parts: string[] = [];
|
|
15
|
-
const { totals, durationSeconds, estimatedCostUSD, issueCount } = options;
|
|
16
|
-
const totalIssues = issueCount || totals.critical + totals.high + totals.medium + totals.low;
|
|
17
|
-
parts.push(
|
|
18
|
-
`Review completed in ${Math.round(durationSeconds)}s with ${totalIssues} tracked issues ` +
|
|
19
|
-
`(critical ${totals.critical}, high ${totals.high}, medium ${totals.medium}, low ${totals.low}).`,
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
if (typeof estimatedCostUSD === 'number') {
|
|
23
|
-
parts.push(`Approximate spend: $${estimatedCostUSD.toFixed(estimatedCostUSD < 1 ? 4 : 2)} USD.`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return parts.join(' ');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function generateReportSummary(
|
|
30
|
-
markdownDraft: string,
|
|
31
|
-
options: SummaryGeneratorOptions,
|
|
32
|
-
): Promise<string> {
|
|
33
|
-
const summaryModel = process.env.CR_AIA_SUMMARY_MODEL || options.model;
|
|
34
|
-
if (!summaryModel) {
|
|
35
|
-
return buildFallbackSummary(options);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const [, modelName] = summaryModel.includes(':')
|
|
39
|
-
? summaryModel.split(':')
|
|
40
|
-
: ['openrouter', summaryModel];
|
|
41
|
-
|
|
42
|
-
const systemPrompt = `You are an engineering lead summarizing a comprehensive code review report.
|
|
43
|
-
Provide 2-3 sentences capturing:
|
|
44
|
-
- Overall health of the reviewed codebase
|
|
45
|
-
- Highest-risk themes or repeating issues
|
|
46
|
-
- Notable spend/time observations if they appear.
|
|
47
|
-
Keep it factual and concise.`;
|
|
48
|
-
|
|
49
|
-
const payload = withProxyMetadata({
|
|
50
|
-
model: modelName,
|
|
51
|
-
temperature: 0.2,
|
|
52
|
-
max_tokens: 200,
|
|
53
|
-
messages: [
|
|
54
|
-
{ role: 'system', content: systemPrompt },
|
|
55
|
-
{ role: 'user', content: markdownDraft },
|
|
56
|
-
],
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const response = await fetch(resolveOpenRouterProxyUrl(), {
|
|
61
|
-
method: 'POST',
|
|
62
|
-
headers: buildOpenRouterProxyHeaders(),
|
|
63
|
-
body: JSON.stringify(payload),
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
if (!response.ok) {
|
|
67
|
-
const errorData = await response.text();
|
|
68
|
-
logger.warn(`AI summary generation failed (${response.status}): ${errorData}`);
|
|
69
|
-
return buildFallbackSummary(options);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const data = await response.json();
|
|
73
|
-
const content = data?.choices?.[0]?.message?.content?.trim();
|
|
74
|
-
if (content) {
|
|
75
|
-
return content;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
logger.warn('AI summary response missing content, falling back to deterministic summary.');
|
|
79
|
-
return buildFallbackSummary(options);
|
|
80
|
-
} catch (error) {
|
|
81
|
-
logger.warn(
|
|
82
|
-
`Unable to generate AI summary: ${error instanceof Error ? error.message : String(error)}`,
|
|
83
|
-
);
|
|
84
|
-
return buildFallbackSummary(options);
|
|
85
|
-
}
|
|
86
|
-
}
|