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.
- package/.cr-aia.yml +23 -0
- package/.crignore +0 -0
- package/dist/index.js +27 -0
- package/package.json +85 -0
- package/src/analysis/FindingsExtractor.ts +431 -0
- package/src/analysis/ai-detection/analyzers/BaseAnalyzer.ts +267 -0
- package/src/analysis/ai-detection/analyzers/DocumentationAnalyzer.ts +622 -0
- package/src/analysis/ai-detection/analyzers/GitHistoryAnalyzer.ts +430 -0
- package/src/analysis/ai-detection/core/AIDetectionEngine.ts +467 -0
- package/src/analysis/ai-detection/types/DetectionTypes.ts +406 -0
- package/src/analysis/ai-detection/utils/SubmissionConverter.ts +390 -0
- package/src/analysis/context/ReviewContext.ts +378 -0
- package/src/analysis/context/index.ts +7 -0
- package/src/analysis/index.ts +8 -0
- package/src/analysis/tokens/TokenAnalysisFormatter.ts +154 -0
- package/src/analysis/tokens/TokenAnalyzer.ts +747 -0
- package/src/analysis/tokens/index.ts +8 -0
- package/src/clients/base/abstractClient.ts +190 -0
- package/src/clients/base/httpClient.ts +160 -0
- package/src/clients/base/index.ts +12 -0
- package/src/clients/base/modelDetection.ts +107 -0
- package/src/clients/base/responseProcessor.ts +586 -0
- package/src/clients/factory/clientFactory.ts +55 -0
- package/src/clients/factory/index.ts +8 -0
- package/src/clients/implementations/index.ts +8 -0
- package/src/clients/implementations/openRouterClient.ts +411 -0
- package/src/clients/openRouterClient.ts +863 -0
- package/src/clients/openRouterClientWrapper.ts +44 -0
- package/src/clients/utils/directoryStructure.ts +52 -0
- package/src/clients/utils/index.ts +11 -0
- package/src/clients/utils/languageDetection.ts +44 -0
- package/src/clients/utils/promptFormatter.ts +105 -0
- package/src/clients/utils/promptLoader.ts +53 -0
- package/src/clients/utils/tokenCounter.ts +297 -0
- package/src/core/ApiClientSelector.ts +37 -0
- package/src/core/ConfigurationService.ts +591 -0
- package/src/core/ConsolidationService.ts +423 -0
- package/src/core/InteractiveDisplayManager.ts +81 -0
- package/src/core/OutputManager.ts +275 -0
- package/src/core/ReviewGenerator.ts +140 -0
- package/src/core/fileDiscovery.ts +237 -0
- package/src/core/handlers/EstimationHandler.ts +104 -0
- package/src/core/handlers/FileProcessingHandler.ts +204 -0
- package/src/core/handlers/OutputHandler.ts +125 -0
- package/src/core/handlers/ReviewExecutor.ts +104 -0
- package/src/core/reviewOrchestrator.ts +333 -0
- package/src/core/utils/ModelInfoUtils.ts +56 -0
- package/src/formatters/outputFormatter.ts +62 -0
- package/src/formatters/utils/IssueFormatters.ts +83 -0
- package/src/formatters/utils/JsonFormatter.ts +77 -0
- package/src/formatters/utils/MarkdownFormatters.ts +609 -0
- package/src/formatters/utils/MetadataFormatter.ts +269 -0
- package/src/formatters/utils/ModelInfoExtractor.ts +115 -0
- package/src/index.ts +27 -0
- package/src/plugins/PluginInterface.ts +50 -0
- package/src/plugins/PluginManager.ts +126 -0
- package/src/prompts/PromptManager.ts +69 -0
- package/src/prompts/cache/PromptCache.ts +50 -0
- package/src/prompts/promptText/common/variables/css-frameworks.json +33 -0
- package/src/prompts/promptText/common/variables/framework-versions.json +45 -0
- package/src/prompts/promptText/frameworks/react/comprehensive.hbs +19 -0
- package/src/prompts/promptText/languages/css/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/generic/comprehensive.hbs +20 -0
- package/src/prompts/promptText/languages/html/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/javascript/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/python/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/typescript/comprehensive.hbs +18 -0
- package/src/runtime/auth/service.ts +58 -0
- package/src/runtime/auth/session.ts +103 -0
- package/src/runtime/auth/types.ts +11 -0
- package/src/runtime/cliEntry.ts +196 -0
- package/src/runtime/errors.ts +13 -0
- package/src/runtime/fileCollector.ts +188 -0
- package/src/runtime/manifest.ts +64 -0
- package/src/runtime/openrouterProxy.ts +45 -0
- package/src/runtime/proxyConfig.ts +94 -0
- package/src/runtime/proxyEnvironment.ts +71 -0
- package/src/runtime/reportMerge.ts +102 -0
- package/src/runtime/reporting/markdownReportBuilder.ts +138 -0
- package/src/runtime/reporting/reportDataCollector.ts +234 -0
- package/src/runtime/reporting/summaryGenerator.ts +86 -0
- package/src/runtime/reviewPipeline.ts +155 -0
- package/src/runtime/runAiCodeReview.ts +153 -0
- package/src/runtime/runtimeConfig.ts +5 -0
- package/src/runtime/ui/Layout.tsx +57 -0
- package/src/runtime/ui/RuntimeApp.tsx +150 -0
- package/src/runtime/ui/inkModules.ts +73 -0
- package/src/runtime/ui/screens/AuthScreen.tsx +128 -0
- package/src/runtime/ui/screens/ModeSelection.tsx +52 -0
- package/src/runtime/ui/screens/ProgressScreen.tsx +55 -0
- package/src/runtime/ui/screens/ResultsScreen.tsx +76 -0
- package/src/strategies/ArchitecturalReviewStrategy.ts +54 -0
- package/src/strategies/CodingTestReviewStrategy.ts +920 -0
- package/src/strategies/ConsolidatedReviewStrategy.ts +59 -0
- package/src/strategies/ExtractPatternsReviewStrategy.ts +64 -0
- package/src/strategies/MultiPassReviewStrategy.ts +785 -0
- package/src/strategies/ReviewStrategy.ts +64 -0
- package/src/strategies/StrategyFactory.ts +79 -0
- package/src/strategies/index.ts +14 -0
- package/src/tokenizers/baseTokenizer.ts +61 -0
- package/src/tokenizers/gptTokenizer.ts +27 -0
- package/src/tokenizers/index.ts +8 -0
- package/src/types/apiResponses.ts +40 -0
- package/src/types/cli.ts +24 -0
- package/src/types/common.ts +39 -0
- package/src/types/configuration.ts +201 -0
- package/src/types/handlebars.d.ts +5 -0
- package/src/types/patch.d.ts +25 -0
- package/src/types/review.ts +294 -0
- package/src/types/reviewContext.d.ts +65 -0
- package/src/types/reviewSchema.ts +181 -0
- package/src/types/structuredReview.ts +167 -0
- package/src/types/tokenAnalysis.ts +56 -0
- package/src/utils/FileReader.ts +93 -0
- package/src/utils/FileWriter.ts +76 -0
- package/src/utils/PathGenerator.ts +97 -0
- package/src/utils/api/apiUtils.ts +14 -0
- package/src/utils/api/index.ts +1 -0
- package/src/utils/apiErrorHandler.ts +287 -0
- package/src/utils/ciDataCollector.ts +252 -0
- package/src/utils/codingTestConfigLoader.ts +466 -0
- package/src/utils/dependencies/aiDependencyAnalyzer.ts +454 -0
- package/src/utils/detection/frameworkDetector.ts +879 -0
- package/src/utils/detection/index.ts +10 -0
- package/src/utils/detection/projectTypeDetector.ts +518 -0
- package/src/utils/diagramGenerator.ts +206 -0
- package/src/utils/errorLogger.ts +60 -0
- package/src/utils/estimationUtils.ts +407 -0
- package/src/utils/fileFilters.ts +373 -0
- package/src/utils/fileSystem.ts +57 -0
- package/src/utils/index.ts +36 -0
- package/src/utils/logger.ts +240 -0
- package/src/utils/pathValidator.ts +98 -0
- package/src/utils/priorityFilter.ts +59 -0
- package/src/utils/projectDocs.ts +189 -0
- package/src/utils/promptPaths.ts +29 -0
- package/src/utils/promptTemplateManager.ts +157 -0
- package/src/utils/review/consolidateReview.ts +553 -0
- package/src/utils/review/fixDisplay.ts +100 -0
- package/src/utils/review/fixImplementation.ts +61 -0
- package/src/utils/review/index.ts +36 -0
- package/src/utils/review/interactiveProcessing.ts +294 -0
- package/src/utils/review/progressTracker.ts +296 -0
- package/src/utils/review/reviewExtraction.ts +382 -0
- package/src/utils/review/types.ts +46 -0
- package/src/utils/reviewActionHandler.ts +18 -0
- package/src/utils/reviewParser.ts +253 -0
- package/src/utils/sanitizer.ts +238 -0
- package/src/utils/smartFileSelector.ts +255 -0
- package/src/utils/templateLoader.ts +514 -0
- package/src/utils/treeGenerator.ts +153 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +59 -0
package/.cr-aia.yml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
proxy_base_url: https://cr.ai.enki.si
|
|
2
|
+
proxy_http_referer:
|
|
3
|
+
proxy_x_title: cr-aia
|
|
4
|
+
output:
|
|
5
|
+
format: markdown
|
|
6
|
+
dir: ./code-review-reports
|
|
7
|
+
review:
|
|
8
|
+
interactive: false
|
|
9
|
+
include_tests: false
|
|
10
|
+
include_project_docs: true
|
|
11
|
+
include_dependency_analysis: true
|
|
12
|
+
trace_code: false
|
|
13
|
+
use_ts_prune: false
|
|
14
|
+
use_eslint: false
|
|
15
|
+
auto_fix: false
|
|
16
|
+
prompt_all: false
|
|
17
|
+
confirm: true
|
|
18
|
+
api:
|
|
19
|
+
model: openrouter:fast-model
|
|
20
|
+
test_api: false
|
|
21
|
+
system:
|
|
22
|
+
debug: false
|
|
23
|
+
log_level: info
|
package/.crignore
ADDED
|
File without changes
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const logger_1 = __importDefault(require("./utils/logger"));
|
|
7
|
+
const proxyEnvironment_1 = require("./runtime/proxyEnvironment");
|
|
8
|
+
const cliEntry_1 = require("./runtime/cliEntry");
|
|
9
|
+
// Minimal bootstrap that wires the runtime CLI into the proxy-enabled environment.
|
|
10
|
+
async function bootstrap() {
|
|
11
|
+
try {
|
|
12
|
+
(0, proxyEnvironment_1.ensureProxyEnvironmentInitialized)(process.cwd());
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
logger_1.default.warn(`Failed to load proxy configuration: ${error instanceof Error ? error.message : String(error)}`);
|
|
16
|
+
}
|
|
17
|
+
const handled = await (0, cliEntry_1.handleRuntimeCliEntry)();
|
|
18
|
+
if (!handled) {
|
|
19
|
+
logger_1.default.error('Unknown command. Run `cr-aia --help` for usage.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
bootstrap().catch((error) => {
|
|
24
|
+
logger_1.default.error(`Fatal runtime error: ${error instanceof Error ? error.message : String(error)}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codereview-aia",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "codereview-aia CLI",
|
|
5
|
+
"author": "rrretsuf",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": "./dist/index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"clean": "rm -rf dist .tsbuildinfo",
|
|
10
|
+
"build": "pnpm run clean && tsc -p tsconfig.json",
|
|
11
|
+
"build:prod": "pnpm run build",
|
|
12
|
+
"dev": "ts-node --transpile-only src/index.ts",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"lint": "tsc --noEmit",
|
|
15
|
+
"test": "pnpm run lint"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=20.0.0",
|
|
19
|
+
"pnpm": ">=8.0.0"
|
|
20
|
+
},
|
|
21
|
+
"packageManager": "pnpm@8.15.0",
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"registry": "https://registry.npmjs.org/",
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@langchain/core": "^0.3.44",
|
|
28
|
+
"@supabase/supabase-js": "^2.39.0",
|
|
29
|
+
"chalk": "^4.1.2",
|
|
30
|
+
"cr-aia": "^0.1.3",
|
|
31
|
+
"dompurify": "^3.2.5",
|
|
32
|
+
"dotenv": "^16.3.1",
|
|
33
|
+
"execa": "^8.0.1",
|
|
34
|
+
"glob": "^10.3.10",
|
|
35
|
+
"gpt-tokenizer": "^2.9.0",
|
|
36
|
+
"handlebars": "^4.7.8",
|
|
37
|
+
"i18next": "^24.2.3",
|
|
38
|
+
"i18next-fs-backend": "^2.6.0",
|
|
39
|
+
"i18next-icu": "^2.3.0",
|
|
40
|
+
"ink": "^6.4.0",
|
|
41
|
+
"ink-select-input": "^6.2.0",
|
|
42
|
+
"ink-spinner": "^5.0.0",
|
|
43
|
+
"ink-text-input": "^6.0.0",
|
|
44
|
+
"intl-messageformat": "^10.7.16",
|
|
45
|
+
"jsdom": "^26.0.0",
|
|
46
|
+
"langchain": "^0.3.21",
|
|
47
|
+
"mem0ai": "^2.1.34",
|
|
48
|
+
"picocolors": "^1.0.0",
|
|
49
|
+
"react": "^19.2.0",
|
|
50
|
+
"sqlite3": "^5.1.7",
|
|
51
|
+
"yaml": "^2.8.0",
|
|
52
|
+
"yargs": "^17.7.2",
|
|
53
|
+
"zod": "^3.24.2"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"react": "*"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@biomejs/biome": "^2.0.6",
|
|
60
|
+
"@types/dompurify": "^3.0.5",
|
|
61
|
+
"@types/handlebars": "^4.1.0",
|
|
62
|
+
"@types/js-yaml": "^4.0.9",
|
|
63
|
+
"@types/jsdom": "^21.1.7",
|
|
64
|
+
"@types/node": "^20.17.32",
|
|
65
|
+
"@types/react": "^19.2.3",
|
|
66
|
+
"@types/yargs": "^17.0.33",
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
|
68
|
+
"@typescript-eslint/parser": "^6.15.0",
|
|
69
|
+
"@vitest/ui": "^3.2.1",
|
|
70
|
+
"esbuild": "^0.25.3",
|
|
71
|
+
"eslint": "^8.56.0",
|
|
72
|
+
"js-yaml": "^4.1.0",
|
|
73
|
+
"ts-node": "^10.9.2",
|
|
74
|
+
"tsconfig-paths": "^4.2.0",
|
|
75
|
+
"typescript": "^5.8.3",
|
|
76
|
+
"vitest": "^3.2.1"
|
|
77
|
+
},
|
|
78
|
+
"overrides": {
|
|
79
|
+
"esbuild": "^0.25.3"
|
|
80
|
+
},
|
|
81
|
+
"resolutions": {
|
|
82
|
+
"esbuild": "^0.25.3",
|
|
83
|
+
"jsdom": "^26.0.0"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Findings Extractor for Code Reviews
|
|
3
|
+
*
|
|
4
|
+
* This service extracts, categorizes, and analyzes findings from code review
|
|
5
|
+
* results. It provides utilities for grading, recommendation generation,
|
|
6
|
+
* and finding prioritization.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import logger from '../utils/logger';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Categorized findings from code review
|
|
13
|
+
*/
|
|
14
|
+
export interface CategorizedFindings {
|
|
15
|
+
high: Set<string>;
|
|
16
|
+
medium: Set<string>;
|
|
17
|
+
low: Set<string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Grade information with justification
|
|
22
|
+
*/
|
|
23
|
+
export interface GradeInfo {
|
|
24
|
+
grade: string;
|
|
25
|
+
justification: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Service for extracting and analyzing findings from code reviews
|
|
30
|
+
*/
|
|
31
|
+
export class FindingsExtractor {
|
|
32
|
+
/**
|
|
33
|
+
* Extract findings from review passes
|
|
34
|
+
* @param passes Array of review results with content
|
|
35
|
+
* @returns Categorized findings
|
|
36
|
+
*/
|
|
37
|
+
extractFindingsFromPasses(passes: Array<{ content: string }>): CategorizedFindings {
|
|
38
|
+
const highPriorityFindings = new Set<string>();
|
|
39
|
+
const mediumPriorityFindings = new Set<string>();
|
|
40
|
+
const lowPriorityFindings = new Set<string>();
|
|
41
|
+
|
|
42
|
+
for (const pass of passes) {
|
|
43
|
+
if (!pass.content) continue;
|
|
44
|
+
|
|
45
|
+
const issueTexts = this.extractIssueTexts(pass.content);
|
|
46
|
+
|
|
47
|
+
for (const issue of issueTexts) {
|
|
48
|
+
const lowerIssue = issue.toLowerCase();
|
|
49
|
+
|
|
50
|
+
// Categorize by priority keywords
|
|
51
|
+
if (this.isHighPriority(lowerIssue)) {
|
|
52
|
+
highPriorityFindings.add(issue);
|
|
53
|
+
} else if (this.isMediumPriority(lowerIssue)) {
|
|
54
|
+
mediumPriorityFindings.add(issue);
|
|
55
|
+
} else {
|
|
56
|
+
lowPriorityFindings.add(issue);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
logger.debug(
|
|
62
|
+
`Extracted findings: ${highPriorityFindings.size} high, ${mediumPriorityFindings.size} medium, ${lowPriorityFindings.size} low`,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
high: highPriorityFindings,
|
|
67
|
+
medium: mediumPriorityFindings,
|
|
68
|
+
low: lowPriorityFindings,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Extract individual issue texts from content
|
|
74
|
+
* @param content Review content
|
|
75
|
+
* @returns Array of issue descriptions
|
|
76
|
+
*/
|
|
77
|
+
extractIssueTexts(content: string): string[] {
|
|
78
|
+
const issues: string[] = [];
|
|
79
|
+
|
|
80
|
+
// Extract bullet points and numbered items
|
|
81
|
+
const bulletRegex = /^[\s]*[-*•]\s+(.+)$/gm;
|
|
82
|
+
const numberedRegex = /^[\s]*\d+\.\s+(.+)$/gm;
|
|
83
|
+
const dashRegex = /^[\s]*-\s+(.+)$/gm;
|
|
84
|
+
|
|
85
|
+
let match;
|
|
86
|
+
|
|
87
|
+
// Extract bullet points
|
|
88
|
+
while ((match = bulletRegex.exec(content)) !== null) {
|
|
89
|
+
const issue = match[1].trim();
|
|
90
|
+
if (issue.length > 10) {
|
|
91
|
+
// Filter out very short items
|
|
92
|
+
issues.push(issue);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Extract numbered items
|
|
97
|
+
while ((match = numberedRegex.exec(content)) !== null) {
|
|
98
|
+
const issue = match[1].trim();
|
|
99
|
+
if (issue.length > 10) {
|
|
100
|
+
issues.push(issue);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Extract dash items (alternative format)
|
|
105
|
+
while ((match = dashRegex.exec(content)) !== null) {
|
|
106
|
+
const issue = match[1].trim();
|
|
107
|
+
if (issue.length > 10) {
|
|
108
|
+
issues.push(issue);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Remove duplicates
|
|
113
|
+
return [...new Set(issues)];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Check if an issue is high priority
|
|
118
|
+
* @param issue Issue text (lowercase)
|
|
119
|
+
* @returns True if high priority
|
|
120
|
+
*/
|
|
121
|
+
private isHighPriority(issue: string): boolean {
|
|
122
|
+
const highPriorityKeywords = [
|
|
123
|
+
'security',
|
|
124
|
+
'vulnerability',
|
|
125
|
+
'critical',
|
|
126
|
+
'error',
|
|
127
|
+
'bug',
|
|
128
|
+
'crash',
|
|
129
|
+
'memory leak',
|
|
130
|
+
'sql injection',
|
|
131
|
+
'xss',
|
|
132
|
+
'csrf',
|
|
133
|
+
'injection',
|
|
134
|
+
'authentication',
|
|
135
|
+
'authorization',
|
|
136
|
+
'privilege escalation',
|
|
137
|
+
'data breach',
|
|
138
|
+
'sensitive data',
|
|
139
|
+
'password',
|
|
140
|
+
'token',
|
|
141
|
+
'deadlock',
|
|
142
|
+
'race condition',
|
|
143
|
+
'null pointer',
|
|
144
|
+
'buffer overflow',
|
|
145
|
+
];
|
|
146
|
+
return highPriorityKeywords.some((keyword) => issue.includes(keyword));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check if an issue is medium priority
|
|
151
|
+
* @param issue Issue text (lowercase)
|
|
152
|
+
* @returns True if medium priority
|
|
153
|
+
*/
|
|
154
|
+
private isMediumPriority(issue: string): boolean {
|
|
155
|
+
const mediumPriorityKeywords = [
|
|
156
|
+
'warning',
|
|
157
|
+
'deprecated',
|
|
158
|
+
'inefficient',
|
|
159
|
+
'refactor',
|
|
160
|
+
'improve',
|
|
161
|
+
'optimization',
|
|
162
|
+
'maintainability',
|
|
163
|
+
'readability',
|
|
164
|
+
'complexity',
|
|
165
|
+
'duplication',
|
|
166
|
+
'coupling',
|
|
167
|
+
'cohesion',
|
|
168
|
+
'design pattern',
|
|
169
|
+
'architecture',
|
|
170
|
+
'structure',
|
|
171
|
+
'organization',
|
|
172
|
+
'naming',
|
|
173
|
+
'documentation',
|
|
174
|
+
'comment',
|
|
175
|
+
'test coverage',
|
|
176
|
+
'error handling',
|
|
177
|
+
];
|
|
178
|
+
return mediumPriorityKeywords.some((keyword) => issue.includes(keyword));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Calculate grade based on findings for a specific category
|
|
183
|
+
* @param findings Categorized findings
|
|
184
|
+
* @param category Category name (for logging)
|
|
185
|
+
* @returns Grade information
|
|
186
|
+
*/
|
|
187
|
+
calculateGrade(findings: CategorizedFindings, category: string): GradeInfo {
|
|
188
|
+
const totalIssues = findings.high.size + findings.medium.size + findings.low.size;
|
|
189
|
+
|
|
190
|
+
let grade: string;
|
|
191
|
+
let justification: string;
|
|
192
|
+
|
|
193
|
+
if (findings.high.size > 5) {
|
|
194
|
+
grade = 'D';
|
|
195
|
+
justification = `Multiple critical issues (${findings.high.size}) require immediate attention`;
|
|
196
|
+
} else if (findings.high.size > 2) {
|
|
197
|
+
grade = 'C';
|
|
198
|
+
justification = `Several critical issues (${findings.high.size}) need to be addressed`;
|
|
199
|
+
} else if (findings.high.size > 0) {
|
|
200
|
+
grade = 'C+';
|
|
201
|
+
justification = `Some critical issues (${findings.high.size}) present`;
|
|
202
|
+
} else if (findings.medium.size > 10) {
|
|
203
|
+
grade = 'C+';
|
|
204
|
+
justification = `Many medium-priority issues (${findings.medium.size}) affect code quality`;
|
|
205
|
+
} else if (findings.medium.size > 5) {
|
|
206
|
+
grade = 'B';
|
|
207
|
+
justification = `Several medium-priority issues (${findings.medium.size}) could be improved`;
|
|
208
|
+
} else if (findings.medium.size > 2) {
|
|
209
|
+
grade = 'B+';
|
|
210
|
+
justification = `Some medium-priority issues (${findings.medium.size}) noted`;
|
|
211
|
+
} else if (totalIssues > 5) {
|
|
212
|
+
grade = 'A-';
|
|
213
|
+
justification = `Minor issues (${totalIssues}) but overall good quality`;
|
|
214
|
+
} else if (totalIssues > 0) {
|
|
215
|
+
grade = 'A';
|
|
216
|
+
justification = `Very few issues (${totalIssues}), high quality code`;
|
|
217
|
+
} else {
|
|
218
|
+
grade = 'A+';
|
|
219
|
+
justification = 'Excellent code quality with no significant issues';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
logger.debug(`Grade for ${category}: ${grade} (${justification})`);
|
|
223
|
+
return { grade, justification };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Calculate overall grade based on findings
|
|
228
|
+
* @param findings Categorized findings
|
|
229
|
+
* @returns Overall grade string
|
|
230
|
+
*/
|
|
231
|
+
calculateOverallGrade(findings: CategorizedFindings): string {
|
|
232
|
+
const gradeInfo = this.calculateGrade(findings, 'overall');
|
|
233
|
+
return gradeInfo.grade;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get justification for grade
|
|
238
|
+
* @param findings Categorized findings
|
|
239
|
+
* @param category Category name
|
|
240
|
+
* @returns Grade justification string
|
|
241
|
+
*/
|
|
242
|
+
getGradeJustification(findings: CategorizedFindings, category: string): string {
|
|
243
|
+
const gradeInfo = this.calculateGrade(findings, category);
|
|
244
|
+
return gradeInfo.justification;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Generate recommendations based on findings
|
|
249
|
+
* @param findings Categorized findings
|
|
250
|
+
* @param hasErrors Whether there are compilation errors
|
|
251
|
+
* @returns Array of recommendation strings
|
|
252
|
+
*/
|
|
253
|
+
generateRecommendations(findings: CategorizedFindings, hasErrors: boolean): string[] {
|
|
254
|
+
const recommendations: string[] = [];
|
|
255
|
+
|
|
256
|
+
if (hasErrors) {
|
|
257
|
+
recommendations.push('Fix compilation errors before proceeding with other improvements');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (findings.high.size > 0) {
|
|
261
|
+
recommendations.push(
|
|
262
|
+
`Address ${findings.high.size} high-priority security and critical issues immediately`,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
if (findings.high.size > 3) {
|
|
266
|
+
recommendations.push(
|
|
267
|
+
'Consider conducting a security audit given the number of critical issues',
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (findings.medium.size > 8) {
|
|
273
|
+
recommendations.push(
|
|
274
|
+
`Review and address ${findings.medium.size} medium-priority maintainability issues`,
|
|
275
|
+
);
|
|
276
|
+
recommendations.push('Consider refactoring to improve code structure and maintainability');
|
|
277
|
+
} else if (findings.medium.size > 3) {
|
|
278
|
+
recommendations.push(
|
|
279
|
+
`Address ${findings.medium.size} medium-priority issues to improve code quality`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (findings.low.size > 15) {
|
|
284
|
+
recommendations.push(
|
|
285
|
+
`Consider addressing ${findings.low.size} low-priority style and minor issues`,
|
|
286
|
+
);
|
|
287
|
+
recommendations.push('Implement automated linting and formatting tools');
|
|
288
|
+
} else if (findings.low.size > 8) {
|
|
289
|
+
recommendations.push(`Review ${findings.low.size} low-priority issues when time permits`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Add positive recommendations for good code
|
|
293
|
+
if (findings.high.size === 0 && findings.medium.size < 3) {
|
|
294
|
+
recommendations.push('Code quality is good. Continue following current best practices.');
|
|
295
|
+
|
|
296
|
+
if (findings.low.size === 0) {
|
|
297
|
+
recommendations.push(
|
|
298
|
+
'Excellent code quality. Consider sharing best practices with the team.',
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Add specific recommendations based on issue types
|
|
304
|
+
if (this.hasSecurityIssues(findings)) {
|
|
305
|
+
recommendations.push('Implement security code review process and security testing');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (this.hasPerformanceIssues(findings)) {
|
|
309
|
+
recommendations.push('Consider performance profiling and optimization');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (this.hasTestingIssues(findings)) {
|
|
313
|
+
recommendations.push('Improve test coverage and add more comprehensive tests');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return recommendations;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Check if findings contain security-related issues
|
|
321
|
+
* @param findings Categorized findings
|
|
322
|
+
* @returns True if security issues are present
|
|
323
|
+
*/
|
|
324
|
+
private hasSecurityIssues(findings: CategorizedFindings): boolean {
|
|
325
|
+
const allFindings = [...findings.high, ...findings.medium, ...findings.low];
|
|
326
|
+
const securityKeywords = [
|
|
327
|
+
'security',
|
|
328
|
+
'vulnerability',
|
|
329
|
+
'injection',
|
|
330
|
+
'xss',
|
|
331
|
+
'csrf',
|
|
332
|
+
'authentication',
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
return allFindings.some((finding) =>
|
|
336
|
+
securityKeywords.some((keyword) => finding.toLowerCase().includes(keyword)),
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Check if findings contain performance-related issues
|
|
342
|
+
* @param findings Categorized findings
|
|
343
|
+
* @returns True if performance issues are present
|
|
344
|
+
*/
|
|
345
|
+
private hasPerformanceIssues(findings: CategorizedFindings): boolean {
|
|
346
|
+
const allFindings = [...findings.high, ...findings.medium, ...findings.low];
|
|
347
|
+
const performanceKeywords = [
|
|
348
|
+
'performance',
|
|
349
|
+
'slow',
|
|
350
|
+
'inefficient',
|
|
351
|
+
'optimization',
|
|
352
|
+
'memory',
|
|
353
|
+
'cpu',
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
return allFindings.some((finding) =>
|
|
357
|
+
performanceKeywords.some((keyword) => finding.toLowerCase().includes(keyword)),
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Check if findings contain testing-related issues
|
|
363
|
+
* @param findings Categorized findings
|
|
364
|
+
* @returns True if testing issues are present
|
|
365
|
+
*/
|
|
366
|
+
private hasTestingIssues(findings: CategorizedFindings): boolean {
|
|
367
|
+
const allFindings = [...findings.high, ...findings.medium, ...findings.low];
|
|
368
|
+
const testingKeywords = [
|
|
369
|
+
'test',
|
|
370
|
+
'coverage',
|
|
371
|
+
'mock',
|
|
372
|
+
'assertion',
|
|
373
|
+
'unit test',
|
|
374
|
+
'integration test',
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
return allFindings.some((finding) =>
|
|
378
|
+
testingKeywords.some((keyword) => finding.toLowerCase().includes(keyword)),
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Format findings for display in reports
|
|
384
|
+
* @param findings Categorized findings
|
|
385
|
+
* @param maxPerCategory Maximum items to show per category
|
|
386
|
+
* @returns Formatted findings string
|
|
387
|
+
*/
|
|
388
|
+
formatFindings(findings: CategorizedFindings, maxPerCategory = 5): string {
|
|
389
|
+
const sections: string[] = [];
|
|
390
|
+
|
|
391
|
+
if (findings.high.size > 0) {
|
|
392
|
+
sections.push(`**High Priority (${findings.high.size}):**`);
|
|
393
|
+
sections.push(
|
|
394
|
+
...Array.from(findings.high)
|
|
395
|
+
.slice(0, maxPerCategory)
|
|
396
|
+
.map((f) => `- ${f}`),
|
|
397
|
+
);
|
|
398
|
+
if (findings.high.size > maxPerCategory) {
|
|
399
|
+
sections.push(`- ... and ${findings.high.size - maxPerCategory} more`);
|
|
400
|
+
}
|
|
401
|
+
sections.push('');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (findings.medium.size > 0) {
|
|
405
|
+
sections.push(`**Medium Priority (${findings.medium.size}):**`);
|
|
406
|
+
sections.push(
|
|
407
|
+
...Array.from(findings.medium)
|
|
408
|
+
.slice(0, maxPerCategory)
|
|
409
|
+
.map((f) => `- ${f}`),
|
|
410
|
+
);
|
|
411
|
+
if (findings.medium.size > maxPerCategory) {
|
|
412
|
+
sections.push(`- ... and ${findings.medium.size - maxPerCategory} more`);
|
|
413
|
+
}
|
|
414
|
+
sections.push('');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (findings.low.size > 0) {
|
|
418
|
+
sections.push(`**Low Priority (${findings.low.size}):**`);
|
|
419
|
+
sections.push(
|
|
420
|
+
...Array.from(findings.low)
|
|
421
|
+
.slice(0, Math.min(maxPerCategory, 3))
|
|
422
|
+
.map((f) => `- ${f}`),
|
|
423
|
+
);
|
|
424
|
+
if (findings.low.size > 3) {
|
|
425
|
+
sections.push(`- ... and ${findings.low.size - 3} more`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return sections.join('\n');
|
|
430
|
+
}
|
|
431
|
+
}
|