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
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Review context for maintaining state between review passes.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a class for maintaining context between multiple review passes.
|
|
5
|
+
* It stores information about the review, including file metadata, important code elements,
|
|
6
|
+
* and findings from previous passes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { FileInfo } from '../../types/review';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Type of code element tracked in the context
|
|
13
|
+
*/
|
|
14
|
+
export enum CodeElementType {
|
|
15
|
+
Function = 'function',
|
|
16
|
+
Class = 'class',
|
|
17
|
+
Interface = 'interface',
|
|
18
|
+
Variable = 'variable',
|
|
19
|
+
Import = 'import',
|
|
20
|
+
ExportedItem = 'exported',
|
|
21
|
+
Component = 'component',
|
|
22
|
+
EntryPoint = 'entryPoint',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A code element tracked in the context
|
|
27
|
+
*/
|
|
28
|
+
export interface CodeElement {
|
|
29
|
+
/** Type of code element */
|
|
30
|
+
type: CodeElementType;
|
|
31
|
+
/** Name of the code element */
|
|
32
|
+
name: string;
|
|
33
|
+
/** File where the element is defined */
|
|
34
|
+
file: string;
|
|
35
|
+
/** Short description or signature of the element */
|
|
36
|
+
signature?: string;
|
|
37
|
+
/** Importance score (higher = more important) */
|
|
38
|
+
importance: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A review finding from a previous pass
|
|
43
|
+
*/
|
|
44
|
+
export interface ReviewFinding {
|
|
45
|
+
/** Type of finding (e.g., 'bug', 'security', 'performance') */
|
|
46
|
+
type: string;
|
|
47
|
+
/** Short description of the finding */
|
|
48
|
+
description: string;
|
|
49
|
+
/** File where the finding was located */
|
|
50
|
+
file?: string;
|
|
51
|
+
/** Severity of the finding (higher = more severe) */
|
|
52
|
+
severity: number;
|
|
53
|
+
/** Pass number where this finding was identified */
|
|
54
|
+
passNumber: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Summary of a file from previous review passes
|
|
59
|
+
*/
|
|
60
|
+
export interface FileSummary {
|
|
61
|
+
/** Path to the file */
|
|
62
|
+
path: string;
|
|
63
|
+
/** File type or extension */
|
|
64
|
+
type: string;
|
|
65
|
+
/** Short description of the file purpose */
|
|
66
|
+
description: string;
|
|
67
|
+
/** Key elements in this file (e.g., classes, functions) */
|
|
68
|
+
keyElements: string[];
|
|
69
|
+
/** Pass number when this summary was created */
|
|
70
|
+
passNumber: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Context for multi-pass reviews
|
|
75
|
+
*/
|
|
76
|
+
export class ReviewContext {
|
|
77
|
+
/** Project name */
|
|
78
|
+
private projectName: string;
|
|
79
|
+
/** Review type */
|
|
80
|
+
private reviewType: string;
|
|
81
|
+
/** All files involved in the review */
|
|
82
|
+
private allFiles: string[];
|
|
83
|
+
/** Current pass number */
|
|
84
|
+
private currentPass: number;
|
|
85
|
+
/** Important code elements tracked across passes */
|
|
86
|
+
private codeElements: Map<string, CodeElement>;
|
|
87
|
+
/** Findings from previous passes */
|
|
88
|
+
private findings: ReviewFinding[];
|
|
89
|
+
/** File summaries from previous passes */
|
|
90
|
+
private fileSummaries: Map<string, FileSummary>;
|
|
91
|
+
/** General notes about the codebase */
|
|
92
|
+
private generalNotes: string[];
|
|
93
|
+
/** Timestamp when context was created */
|
|
94
|
+
private createdAt: Date;
|
|
95
|
+
/** Timestamp of last update */
|
|
96
|
+
private updatedAt: Date;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Create a new review context
|
|
100
|
+
* @param projectName Name of the project
|
|
101
|
+
* @param reviewType Type of review
|
|
102
|
+
* @param files Files involved in the review
|
|
103
|
+
*/
|
|
104
|
+
constructor(projectName: string, reviewType: string, files: FileInfo[]) {
|
|
105
|
+
this.projectName = projectName;
|
|
106
|
+
this.reviewType = reviewType;
|
|
107
|
+
this.allFiles = files.map((f) => f.path);
|
|
108
|
+
this.currentPass = 0;
|
|
109
|
+
this.codeElements = new Map();
|
|
110
|
+
this.findings = [];
|
|
111
|
+
this.fileSummaries = new Map();
|
|
112
|
+
this.generalNotes = [];
|
|
113
|
+
this.createdAt = new Date();
|
|
114
|
+
this.updatedAt = new Date();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Start a new review pass
|
|
119
|
+
* @returns Updated pass number
|
|
120
|
+
*/
|
|
121
|
+
public startPass(): number {
|
|
122
|
+
this.currentPass++;
|
|
123
|
+
this.updatedAt = new Date();
|
|
124
|
+
return this.currentPass;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get the current pass number
|
|
129
|
+
* @returns Current pass number
|
|
130
|
+
*/
|
|
131
|
+
public getCurrentPass(): number {
|
|
132
|
+
return this.currentPass;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Add a code element to the context
|
|
137
|
+
* @param element Code element to add
|
|
138
|
+
*/
|
|
139
|
+
public addCodeElement(element: CodeElement): void {
|
|
140
|
+
const key = `${element.type}:${element.file}:${element.name}`;
|
|
141
|
+
this.codeElements.set(key, element);
|
|
142
|
+
this.updatedAt = new Date();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get all tracked code elements
|
|
147
|
+
* @returns Array of code elements
|
|
148
|
+
*/
|
|
149
|
+
public getCodeElements(): CodeElement[] {
|
|
150
|
+
return Array.from(this.codeElements.values());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get code elements of a specific type
|
|
155
|
+
* @param type Type of code elements to get
|
|
156
|
+
* @returns Array of code elements of the specified type
|
|
157
|
+
*/
|
|
158
|
+
public getCodeElementsByType(type: CodeElementType): CodeElement[] {
|
|
159
|
+
return this.getCodeElements().filter((el) => el.type === type);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get code elements in a specific file
|
|
164
|
+
* @param filePath Path of the file
|
|
165
|
+
* @returns Array of code elements in the file
|
|
166
|
+
*/
|
|
167
|
+
public getCodeElementsInFile(filePath: string): CodeElement[] {
|
|
168
|
+
return this.getCodeElements().filter((el) => el.file === filePath);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Add a review finding
|
|
173
|
+
* @param finding Review finding to add
|
|
174
|
+
*/
|
|
175
|
+
public addFinding(finding: ReviewFinding): void {
|
|
176
|
+
this.findings.push({
|
|
177
|
+
...finding,
|
|
178
|
+
passNumber: this.currentPass,
|
|
179
|
+
});
|
|
180
|
+
this.updatedAt = new Date();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get all findings
|
|
185
|
+
* @returns Array of all findings
|
|
186
|
+
*/
|
|
187
|
+
public getFindings(): ReviewFinding[] {
|
|
188
|
+
return [...this.findings];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Add or update a file summary
|
|
193
|
+
* @param summary File summary to add
|
|
194
|
+
*/
|
|
195
|
+
public addFileSummary(summary: FileSummary): void {
|
|
196
|
+
this.fileSummaries.set(summary.path, {
|
|
197
|
+
...summary,
|
|
198
|
+
passNumber: this.currentPass,
|
|
199
|
+
});
|
|
200
|
+
this.updatedAt = new Date();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get summary for a specific file
|
|
205
|
+
* @param filePath Path of the file
|
|
206
|
+
* @returns File summary or undefined if not found
|
|
207
|
+
*/
|
|
208
|
+
public getFileSummary(filePath: string): FileSummary | undefined {
|
|
209
|
+
return this.fileSummaries.get(filePath);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get summaries for all files
|
|
214
|
+
* @returns Array of file summaries
|
|
215
|
+
*/
|
|
216
|
+
public getAllFileSummaries(): FileSummary[] {
|
|
217
|
+
return Array.from(this.fileSummaries.values());
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Add a general note about the codebase
|
|
222
|
+
* @param note Note to add
|
|
223
|
+
*/
|
|
224
|
+
public addGeneralNote(note: string): void {
|
|
225
|
+
this.generalNotes.push(note);
|
|
226
|
+
this.updatedAt = new Date();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get all general notes
|
|
231
|
+
* @returns Array of general notes
|
|
232
|
+
*/
|
|
233
|
+
public getGeneralNotes(): string[] {
|
|
234
|
+
return [...this.generalNotes];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Generate a contextual prompt for the next pass
|
|
239
|
+
* @param files Files to include in the next pass
|
|
240
|
+
* @param maxContextLength Maximum length of context in characters
|
|
241
|
+
* @returns Formatted context string for inclusion in the next prompt
|
|
242
|
+
*/
|
|
243
|
+
public generateNextPassContext(files: string[], maxContextLength = 2000): string {
|
|
244
|
+
let context = `
|
|
245
|
+
### Review Context (Pass ${this.currentPass})
|
|
246
|
+
|
|
247
|
+
Project: ${this.projectName}
|
|
248
|
+
Review Type: ${this.reviewType}
|
|
249
|
+
Files in this pass: ${files.length} / ${this.allFiles.length}
|
|
250
|
+
|
|
251
|
+
`;
|
|
252
|
+
|
|
253
|
+
// Add important findings from previous passes
|
|
254
|
+
const importantFindings = this.findings.sort((a, b) => b.severity - a.severity).slice(0, 5);
|
|
255
|
+
|
|
256
|
+
if (importantFindings.length > 0) {
|
|
257
|
+
context += '#### Key Findings from Previous Passes\n\n';
|
|
258
|
+
importantFindings.forEach((finding) => {
|
|
259
|
+
context += `- [${finding.type.toUpperCase()}] ${finding.description}${finding.file ? ` (in ${finding.file})` : ''}\n`;
|
|
260
|
+
});
|
|
261
|
+
context += '\n';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Add summaries of files that are related but not in this pass
|
|
265
|
+
const relatedFiles = this.getAllFileSummaries()
|
|
266
|
+
.filter((summary) => !files.includes(summary.path))
|
|
267
|
+
.slice(0, 5);
|
|
268
|
+
|
|
269
|
+
if (relatedFiles.length > 0) {
|
|
270
|
+
context += '#### Related Files (Not in This Pass)\n\n';
|
|
271
|
+
relatedFiles.forEach((file) => {
|
|
272
|
+
context += `- ${file.path}: ${file.description}\n`;
|
|
273
|
+
if (file.keyElements.length > 0) {
|
|
274
|
+
context += ` Key elements: ${file.keyElements.join(', ')}\n`;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
context += '\n';
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Add important code elements relevant to this pass
|
|
281
|
+
const relevantElements = this.getCodeElements()
|
|
282
|
+
.filter((el) => files.includes(el.file) || el.importance > 7)
|
|
283
|
+
.sort((a, b) => b.importance - a.importance)
|
|
284
|
+
.slice(0, 10);
|
|
285
|
+
|
|
286
|
+
if (relevantElements.length > 0) {
|
|
287
|
+
context += '#### Important Code Elements\n\n';
|
|
288
|
+
relevantElements.forEach((element) => {
|
|
289
|
+
context += `- ${element.type} \`${element.name}\`${element.signature ? `: ${element.signature}` : ''} (in ${element.file})\n`;
|
|
290
|
+
});
|
|
291
|
+
context += '\n';
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Add general notes
|
|
295
|
+
if (this.generalNotes.length > 0) {
|
|
296
|
+
context += '#### General Notes\n\n';
|
|
297
|
+
this.generalNotes.slice(0, 3).forEach((note) => {
|
|
298
|
+
context += `- ${note}\n`;
|
|
299
|
+
});
|
|
300
|
+
context += '\n';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Truncate if too long
|
|
304
|
+
if (context.length > maxContextLength) {
|
|
305
|
+
context = `${context.substring(0, maxContextLength - 3)}...`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return context;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Serialize the context to JSON
|
|
313
|
+
* @returns JSON representation of the context
|
|
314
|
+
*/
|
|
315
|
+
public toJSON(): object {
|
|
316
|
+
return {
|
|
317
|
+
projectName: this.projectName,
|
|
318
|
+
reviewType: this.reviewType,
|
|
319
|
+
currentPass: this.currentPass,
|
|
320
|
+
codeElements: Array.from(this.codeElements.values()),
|
|
321
|
+
findings: this.findings,
|
|
322
|
+
fileSummaries: Array.from(this.fileSummaries.values()),
|
|
323
|
+
generalNotes: this.generalNotes,
|
|
324
|
+
createdAt: this.createdAt.toISOString(),
|
|
325
|
+
updatedAt: this.updatedAt.toISOString(),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Create a review context from JSON
|
|
331
|
+
* @param json JSON object
|
|
332
|
+
* @returns New ReviewContext instance
|
|
333
|
+
*/
|
|
334
|
+
public static fromJSON(json: Record<string, unknown>): ReviewContext {
|
|
335
|
+
const context = new ReviewContext(
|
|
336
|
+
json.projectName as string,
|
|
337
|
+
json.reviewType as string,
|
|
338
|
+
(json.allFiles as any[]) || [],
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
context.currentPass = (json.currentPass as number) || 0;
|
|
342
|
+
|
|
343
|
+
// Restore code elements
|
|
344
|
+
if (Array.isArray(json.codeElements)) {
|
|
345
|
+
json.codeElements.forEach((element: CodeElement) => {
|
|
346
|
+
context.addCodeElement(element);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Restore findings
|
|
351
|
+
if (Array.isArray(json.findings)) {
|
|
352
|
+
context.findings = json.findings;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Restore file summaries
|
|
356
|
+
if (Array.isArray(json.fileSummaries)) {
|
|
357
|
+
json.fileSummaries.forEach((summary: FileSummary) => {
|
|
358
|
+
context.fileSummaries.set(summary.path, summary);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Restore general notes
|
|
363
|
+
if (Array.isArray(json.generalNotes)) {
|
|
364
|
+
context.generalNotes = json.generalNotes;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Restore timestamps
|
|
368
|
+
if (json.createdAt) {
|
|
369
|
+
context.createdAt = new Date(json.createdAt as string);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (json.updatedAt) {
|
|
373
|
+
context.updatedAt = new Date(json.updatedAt as string);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return context;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Formatter for token analysis results.
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for formatting token analysis results into
|
|
5
|
+
* human-readable strings and other formats for display or logging.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { FileTokenAnalysis, TokenAnalysisResult } from './TokenAnalyzer';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Format a number with commas as thousands separators
|
|
12
|
+
* @param num Number to format
|
|
13
|
+
* @returns Formatted number string
|
|
14
|
+
*/
|
|
15
|
+
function formatNumber(num: number): string {
|
|
16
|
+
return num.toLocaleString();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Format a file size in bytes to a human-readable string
|
|
21
|
+
* @param bytes Size in bytes
|
|
22
|
+
* @returns Formatted size string (e.g., "1.23 KB")
|
|
23
|
+
*/
|
|
24
|
+
function formatFileSize(bytes: number): string {
|
|
25
|
+
if (bytes < 1024) {
|
|
26
|
+
return `${bytes} B`;
|
|
27
|
+
}
|
|
28
|
+
if (bytes < 1024 * 1024) {
|
|
29
|
+
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
30
|
+
}
|
|
31
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Format token analysis result as a human-readable string
|
|
36
|
+
* @param analysis Token analysis result
|
|
37
|
+
* @param modelName Name of the model used
|
|
38
|
+
* @param includedFiles Whether to include detailed file listing
|
|
39
|
+
* @returns Formatted string representation of the analysis
|
|
40
|
+
*/
|
|
41
|
+
export function formatTokenAnalysis(
|
|
42
|
+
analysis: TokenAnalysisResult,
|
|
43
|
+
modelName: string,
|
|
44
|
+
includeFiles = false,
|
|
45
|
+
): string {
|
|
46
|
+
// Extract provider and model from both colon and slash formats
|
|
47
|
+
// Handle formats: "provider:model", "provider/model", or just "model"
|
|
48
|
+
let provider: string | undefined;
|
|
49
|
+
let model: string;
|
|
50
|
+
|
|
51
|
+
if (modelName.includes(':')) {
|
|
52
|
+
// Traditional format: "provider:model"
|
|
53
|
+
[provider, model] = modelName.split(':', 2);
|
|
54
|
+
} else if (modelName.includes('/')) {
|
|
55
|
+
// OpenRouter format: "provider/model"
|
|
56
|
+
[provider, model] = modelName.split('/', 2);
|
|
57
|
+
} else {
|
|
58
|
+
// Just model name
|
|
59
|
+
provider = undefined;
|
|
60
|
+
model = modelName;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const displayModel = model || modelName;
|
|
64
|
+
const displayProvider = provider
|
|
65
|
+
? `${provider.charAt(0).toUpperCase() + provider.slice(1)}`
|
|
66
|
+
: 'Unknown';
|
|
67
|
+
|
|
68
|
+
let output = `
|
|
69
|
+
=== Token Analysis Report ===
|
|
70
|
+
|
|
71
|
+
Provider: ${displayProvider}
|
|
72
|
+
Model: ${displayModel}
|
|
73
|
+
Files: ${formatNumber(analysis.fileCount)} (${formatFileSize(analysis.totalSizeInBytes)})
|
|
74
|
+
|
|
75
|
+
Token Information:
|
|
76
|
+
Content Tokens: ${formatNumber(analysis.totalTokens)}
|
|
77
|
+
Prompt Overhead: ${formatNumber(analysis.promptOverheadTokens)}
|
|
78
|
+
Total Estimated Tokens: ${formatNumber(analysis.estimatedTotalTokens)}
|
|
79
|
+
Context Window Size: ${formatNumber(analysis.contextWindowSize)}
|
|
80
|
+
|
|
81
|
+
Context Utilization:
|
|
82
|
+
${((analysis.estimatedTotalTokens / analysis.contextWindowSize) * 100).toFixed(2)}% of context window used
|
|
83
|
+
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
// Add chunking information if recommended
|
|
87
|
+
if (analysis.chunkingRecommendation.chunkingRecommended) {
|
|
88
|
+
output += `
|
|
89
|
+
Multi-Pass Analysis:
|
|
90
|
+
Chunking Required: Yes
|
|
91
|
+
Reason: ${analysis.chunkingRecommendation.reason}
|
|
92
|
+
Estimated Passes: ${formatNumber(analysis.estimatedPassesNeeded)}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
// Add chunk details
|
|
96
|
+
analysis.chunkingRecommendation.recommendedChunks.forEach((chunk, index) => {
|
|
97
|
+
output += `
|
|
98
|
+
Chunk ${index + 1}:
|
|
99
|
+
Files: ${formatNumber(chunk.files.length)}
|
|
100
|
+
Estimated Tokens: ${formatNumber(chunk.estimatedTokenCount)}
|
|
101
|
+
Priority: ${chunk.priority}
|
|
102
|
+
`;
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
output += `
|
|
106
|
+
Multi-Pass Analysis:
|
|
107
|
+
Chunking Required: No
|
|
108
|
+
Reason: ${analysis.chunkingRecommendation.reason}
|
|
109
|
+
`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Add file details if requested
|
|
113
|
+
if (includeFiles) {
|
|
114
|
+
output += `
|
|
115
|
+
File Details:
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
// Sort files by token count (largest first)
|
|
119
|
+
const sortedFiles = [...analysis.files].sort((a, b) => b.tokenCount - a.tokenCount);
|
|
120
|
+
|
|
121
|
+
sortedFiles.forEach((file) => {
|
|
122
|
+
output += ` ${file.relativePath}:
|
|
123
|
+
Tokens: ${formatNumber(file.tokenCount)}
|
|
124
|
+
Size: ${formatFileSize(file.sizeInBytes)}
|
|
125
|
+
Tokens/Byte: ${file.tokensPerByte.toFixed(2)}
|
|
126
|
+
`;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return output;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Format token analysis result as JSON
|
|
135
|
+
* @param analysis Token analysis result
|
|
136
|
+
* @returns JSON string representation of the analysis
|
|
137
|
+
*/
|
|
138
|
+
export function formatTokenAnalysisAsJson(analysis: TokenAnalysisResult): string {
|
|
139
|
+
return JSON.stringify(analysis, null, 2);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Format a single file token analysis as a string
|
|
144
|
+
* @param fileAnalysis File token analysis
|
|
145
|
+
* @returns Formatted string representation of the file analysis
|
|
146
|
+
*/
|
|
147
|
+
export function formatFileTokenAnalysis(fileAnalysis: FileTokenAnalysis): string {
|
|
148
|
+
return `
|
|
149
|
+
File: ${fileAnalysis.relativePath}
|
|
150
|
+
Tokens: ${formatNumber(fileAnalysis.tokenCount)}
|
|
151
|
+
Size: ${formatFileSize(fileAnalysis.sizeInBytes)}
|
|
152
|
+
Tokens/Byte: ${fileAnalysis.tokensPerByte.toFixed(2)}
|
|
153
|
+
`;
|
|
154
|
+
}
|