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.
Files changed (200) hide show
  1. package/dist/analysis/static/wpPhpcsRunner.d.ts +11 -0
  2. package/dist/analysis/static/wpPhpcsRunner.js +219 -0
  3. package/dist/analysis/static/wpPhpcsRunner.js.map +1 -0
  4. package/dist/clients/implementations/openRouterClient.js +2 -2
  5. package/dist/clients/implementations/openRouterClient.js.map +1 -1
  6. package/dist/clients/openRouterClient.js +2 -2
  7. package/dist/clients/openRouterClient.js.map +1 -1
  8. package/dist/clients/utils/promptFormatter.d.ts +3 -2
  9. package/dist/clients/utils/promptFormatter.js +82 -24
  10. package/dist/clients/utils/promptFormatter.js.map +1 -1
  11. package/dist/core/ConfigurationService.d.ts +21 -0
  12. package/dist/core/ConfigurationService.js +39 -0
  13. package/dist/core/ConfigurationService.js.map +1 -1
  14. package/dist/core/handlers/FileProcessingHandler.js +5 -0
  15. package/dist/core/handlers/FileProcessingHandler.js.map +1 -1
  16. package/dist/core/reviewOrchestrator.js +61 -1
  17. package/dist/core/reviewOrchestrator.js.map +1 -1
  18. package/dist/index.d.ts +0 -1
  19. package/dist/index.js +0 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/runtime/cliEntry.js +57 -4
  22. package/dist/runtime/cliEntry.js.map +1 -1
  23. package/dist/runtime/fileCollector.d.ts +10 -1
  24. package/dist/runtime/fileCollector.js +217 -2
  25. package/dist/runtime/fileCollector.js.map +1 -1
  26. package/dist/runtime/reporting/markdownReportBuilder.d.ts +2 -0
  27. package/dist/runtime/reporting/markdownReportBuilder.js +57 -0
  28. package/dist/runtime/reporting/markdownReportBuilder.js.map +1 -1
  29. package/dist/runtime/reviewPipeline.d.ts +22 -3
  30. package/dist/runtime/reviewPipeline.js +46 -7
  31. package/dist/runtime/reviewPipeline.js.map +1 -1
  32. package/dist/runtime/runAiCodeReview.d.ts +19 -1
  33. package/dist/runtime/runAiCodeReview.js +243 -8
  34. package/dist/runtime/runAiCodeReview.js.map +1 -1
  35. package/dist/runtime/ui/RuntimeApp.js +15 -4
  36. package/dist/runtime/ui/RuntimeApp.js.map +1 -1
  37. package/dist/runtime/ui/screens/ProgressScreen.d.ts +6 -1
  38. package/dist/runtime/ui/screens/ProgressScreen.js +28 -2
  39. package/dist/runtime/ui/screens/ProgressScreen.js.map +1 -1
  40. package/dist/runtime/ui/screens/ResultsScreen.js +8 -1
  41. package/dist/runtime/ui/screens/ResultsScreen.js.map +1 -1
  42. package/dist/types/review.d.ts +60 -0
  43. package/dist/utils/detection/frameworkDetector.js +55 -0
  44. package/dist/utils/detection/frameworkDetector.js.map +1 -1
  45. package/dist/utils/promptTemplateManager.js +1 -0
  46. package/dist/utils/promptTemplateManager.js.map +1 -1
  47. package/package.json +13 -10
  48. package/.cr-aia.yml +0 -23
  49. package/.crignore +0 -0
  50. package/src/analysis/FindingsExtractor.ts +0 -431
  51. package/src/analysis/ai-detection/analyzers/BaseAnalyzer.ts +0 -267
  52. package/src/analysis/ai-detection/analyzers/DocumentationAnalyzer.ts +0 -622
  53. package/src/analysis/ai-detection/analyzers/GitHistoryAnalyzer.ts +0 -430
  54. package/src/analysis/ai-detection/core/AIDetectionEngine.ts +0 -467
  55. package/src/analysis/ai-detection/types/DetectionTypes.ts +0 -406
  56. package/src/analysis/ai-detection/utils/SubmissionConverter.ts +0 -390
  57. package/src/analysis/context/ReviewContext.ts +0 -378
  58. package/src/analysis/context/index.ts +0 -7
  59. package/src/analysis/index.ts +0 -8
  60. package/src/analysis/tokens/TokenAnalysisFormatter.ts +0 -154
  61. package/src/analysis/tokens/TokenAnalyzer.ts +0 -747
  62. package/src/analysis/tokens/index.ts +0 -8
  63. package/src/clients/base/abstractClient.ts +0 -190
  64. package/src/clients/base/httpClient.ts +0 -160
  65. package/src/clients/base/index.ts +0 -12
  66. package/src/clients/base/modelDetection.ts +0 -107
  67. package/src/clients/base/responseProcessor.ts +0 -586
  68. package/src/clients/factory/clientFactory.ts +0 -55
  69. package/src/clients/factory/index.ts +0 -8
  70. package/src/clients/implementations/index.ts +0 -8
  71. package/src/clients/implementations/openRouterClient.ts +0 -411
  72. package/src/clients/openRouterClient.ts +0 -863
  73. package/src/clients/openRouterClientWrapper.ts +0 -44
  74. package/src/clients/utils/directoryStructure.ts +0 -52
  75. package/src/clients/utils/index.ts +0 -11
  76. package/src/clients/utils/languageDetection.ts +0 -44
  77. package/src/clients/utils/promptFormatter.ts +0 -105
  78. package/src/clients/utils/promptLoader.ts +0 -53
  79. package/src/clients/utils/tokenCounter.ts +0 -297
  80. package/src/core/ApiClientSelector.ts +0 -37
  81. package/src/core/ConfigurationService.ts +0 -591
  82. package/src/core/ConsolidationService.ts +0 -423
  83. package/src/core/InteractiveDisplayManager.ts +0 -81
  84. package/src/core/OutputManager.ts +0 -275
  85. package/src/core/ReviewGenerator.ts +0 -140
  86. package/src/core/fileDiscovery.ts +0 -237
  87. package/src/core/handlers/EstimationHandler.ts +0 -104
  88. package/src/core/handlers/FileProcessingHandler.ts +0 -204
  89. package/src/core/handlers/OutputHandler.ts +0 -125
  90. package/src/core/handlers/ReviewExecutor.ts +0 -104
  91. package/src/core/reviewOrchestrator.ts +0 -333
  92. package/src/core/utils/ModelInfoUtils.ts +0 -56
  93. package/src/formatters/outputFormatter.ts +0 -62
  94. package/src/formatters/utils/IssueFormatters.ts +0 -83
  95. package/src/formatters/utils/JsonFormatter.ts +0 -77
  96. package/src/formatters/utils/MarkdownFormatters.ts +0 -609
  97. package/src/formatters/utils/MetadataFormatter.ts +0 -269
  98. package/src/formatters/utils/ModelInfoExtractor.ts +0 -115
  99. package/src/index.ts +0 -28
  100. package/src/plugins/PluginInterface.ts +0 -50
  101. package/src/plugins/PluginManager.ts +0 -126
  102. package/src/prompts/PromptManager.ts +0 -69
  103. package/src/prompts/cache/PromptCache.ts +0 -50
  104. package/src/prompts/promptText/common/variables/css-frameworks.json +0 -33
  105. package/src/prompts/promptText/common/variables/framework-versions.json +0 -45
  106. package/src/prompts/promptText/frameworks/react/comprehensive.hbs +0 -19
  107. package/src/prompts/promptText/languages/css/comprehensive.hbs +0 -18
  108. package/src/prompts/promptText/languages/generic/comprehensive.hbs +0 -20
  109. package/src/prompts/promptText/languages/html/comprehensive.hbs +0 -18
  110. package/src/prompts/promptText/languages/javascript/comprehensive.hbs +0 -18
  111. package/src/prompts/promptText/languages/python/comprehensive.hbs +0 -18
  112. package/src/prompts/promptText/languages/typescript/comprehensive.hbs +0 -18
  113. package/src/runtime/auth/service.ts +0 -58
  114. package/src/runtime/auth/session.ts +0 -103
  115. package/src/runtime/auth/types.ts +0 -11
  116. package/src/runtime/cliEntry.ts +0 -196
  117. package/src/runtime/debug/logManager.ts +0 -37
  118. package/src/runtime/errors.ts +0 -13
  119. package/src/runtime/fileCollector.ts +0 -222
  120. package/src/runtime/manifest.ts +0 -64
  121. package/src/runtime/openrouterProxy.ts +0 -45
  122. package/src/runtime/preprod/webCheck.ts +0 -104
  123. package/src/runtime/proxyConfig.ts +0 -94
  124. package/src/runtime/proxyEnvironment.ts +0 -71
  125. package/src/runtime/reportMerge.ts +0 -102
  126. package/src/runtime/reporting/markdownReportBuilder.ts +0 -138
  127. package/src/runtime/reporting/reportDataCollector.ts +0 -234
  128. package/src/runtime/reporting/summaryGenerator.ts +0 -86
  129. package/src/runtime/reviewPipeline.ts +0 -161
  130. package/src/runtime/runAiCodeReview.ts +0 -153
  131. package/src/runtime/runtimeConfig.ts +0 -5
  132. package/src/runtime/ui/Layout.tsx +0 -57
  133. package/src/runtime/ui/RuntimeApp.tsx +0 -233
  134. package/src/runtime/ui/inkModules.ts +0 -73
  135. package/src/runtime/ui/screens/AuthScreen.tsx +0 -128
  136. package/src/runtime/ui/screens/ModeSelection.tsx +0 -185
  137. package/src/runtime/ui/screens/ProgressScreen.tsx +0 -62
  138. package/src/runtime/ui/screens/ResultsScreen.tsx +0 -83
  139. package/src/strategies/ArchitecturalReviewStrategy.ts +0 -54
  140. package/src/strategies/CodingTestReviewStrategy.ts +0 -920
  141. package/src/strategies/ConsolidatedReviewStrategy.ts +0 -59
  142. package/src/strategies/ExtractPatternsReviewStrategy.ts +0 -64
  143. package/src/strategies/MultiPassReviewStrategy.ts +0 -785
  144. package/src/strategies/ReviewStrategy.ts +0 -64
  145. package/src/strategies/StrategyFactory.ts +0 -79
  146. package/src/strategies/index.ts +0 -14
  147. package/src/tokenizers/baseTokenizer.ts +0 -61
  148. package/src/tokenizers/gptTokenizer.ts +0 -27
  149. package/src/tokenizers/index.ts +0 -8
  150. package/src/types/apiResponses.ts +0 -40
  151. package/src/types/cli.ts +0 -24
  152. package/src/types/common.ts +0 -39
  153. package/src/types/configuration.ts +0 -201
  154. package/src/types/handlebars.d.ts +0 -5
  155. package/src/types/patch.d.ts +0 -25
  156. package/src/types/review.ts +0 -294
  157. package/src/types/reviewContext.d.ts +0 -65
  158. package/src/types/reviewSchema.ts +0 -181
  159. package/src/types/structuredReview.ts +0 -167
  160. package/src/types/tokenAnalysis.ts +0 -56
  161. package/src/utils/FileReader.ts +0 -93
  162. package/src/utils/FileWriter.ts +0 -76
  163. package/src/utils/PathGenerator.ts +0 -97
  164. package/src/utils/api/apiUtils.ts +0 -14
  165. package/src/utils/api/index.ts +0 -1
  166. package/src/utils/apiErrorHandler.ts +0 -287
  167. package/src/utils/ciDataCollector.ts +0 -252
  168. package/src/utils/codingTestConfigLoader.ts +0 -466
  169. package/src/utils/dependencies/aiDependencyAnalyzer.ts +0 -454
  170. package/src/utils/detection/frameworkDetector.ts +0 -879
  171. package/src/utils/detection/index.ts +0 -10
  172. package/src/utils/detection/projectTypeDetector.ts +0 -518
  173. package/src/utils/diagramGenerator.ts +0 -206
  174. package/src/utils/errorLogger.ts +0 -60
  175. package/src/utils/estimationUtils.ts +0 -407
  176. package/src/utils/fileFilters.ts +0 -373
  177. package/src/utils/fileSystem.ts +0 -57
  178. package/src/utils/index.ts +0 -36
  179. package/src/utils/logger.ts +0 -290
  180. package/src/utils/pathValidator.ts +0 -98
  181. package/src/utils/priorityFilter.ts +0 -59
  182. package/src/utils/projectDocs.ts +0 -189
  183. package/src/utils/promptPaths.ts +0 -29
  184. package/src/utils/promptTemplateManager.ts +0 -157
  185. package/src/utils/review/consolidateReview.ts +0 -553
  186. package/src/utils/review/fixDisplay.ts +0 -100
  187. package/src/utils/review/fixImplementation.ts +0 -61
  188. package/src/utils/review/index.ts +0 -36
  189. package/src/utils/review/interactiveProcessing.ts +0 -294
  190. package/src/utils/review/progressTracker.ts +0 -296
  191. package/src/utils/review/reviewExtraction.ts +0 -382
  192. package/src/utils/review/types.ts +0 -46
  193. package/src/utils/reviewActionHandler.ts +0 -18
  194. package/src/utils/reviewParser.ts +0 -253
  195. package/src/utils/sanitizer.ts +0 -238
  196. package/src/utils/smartFileSelector.ts +0 -255
  197. package/src/utils/templateLoader.ts +0 -514
  198. package/src/utils/treeGenerator.ts +0 -153
  199. package/tsconfig.build.json +0 -14
  200. package/tsconfig.json +0 -59
@@ -1,161 +0,0 @@
1
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2
- import { basename, isAbsolute, join, resolve } from 'node:path';
3
- import { tmpdir } from 'node:os';
4
- import { execa } from 'execa';
5
- import { collectFiles, type FileCollectionMode } from './fileCollector';
6
- import { loadManifest } from './manifest';
7
- import { runAiCodeReview } from './runAiCodeReview';
8
- import { ensureProxyEnvironmentInitialized } from './proxyEnvironment';
9
- import { mergeReports } from './reportMerge';
10
- import { collectReportData } from './reporting/reportDataCollector';
11
- import { buildMarkdownReport, injectSummary } from './reporting/markdownReportBuilder';
12
- import { generateReportSummary } from './reporting/summaryGenerator';
13
- import logger from '../utils/logger';
14
-
15
- export type ReviewStage = 'preparing' | 'collecting' | 'reviewing' | 'merging';
16
- export type ReviewMode = FileCollectionMode;
17
-
18
- export interface ReviewOptions {
19
- model: string;
20
- outDir: string;
21
- debug?: boolean;
22
- mode?: ReviewMode;
23
- preprodTargetUrl?: string;
24
- onStage?: (stage: ReviewStage, info?: { filesFound?: number }) => void;
25
- }
26
-
27
- export interface ReviewTotals {
28
- critical: number;
29
- high: number;
30
- medium: number;
31
- low: number;
32
- }
33
-
34
- export interface ReviewResult {
35
- totals: ReviewTotals;
36
- findings: any[];
37
- duration: number;
38
- repo: string;
39
- filesReviewed: number;
40
- reportPath?: string;
41
- }
42
-
43
- export async function executeReview(options: ReviewOptions): Promise<ReviewResult> {
44
- const { model, outDir, debug = false, onStage, mode = 'uncommitted' } = options;
45
-
46
- onStage?.('collecting');
47
- logger.info('Collecting files for review', { mode, workspace: process.cwd() });
48
- const files = await collectFiles({ mode });
49
- const fileCount = files.length;
50
-
51
- if (fileCount === 0) {
52
- throw new Error('No files to review');
53
- }
54
-
55
- onStage?.('collecting', { filesFound: fileCount });
56
- logger.info('File collection complete', { fileCount });
57
-
58
- const workspaceRoot = process.cwd();
59
- const repoRoot = await resolveRepoRoot();
60
- const manifest = loadManifest(workspaceRoot);
61
- const tempDir = mkdtempSync(join(tmpdir(), 'cr-aia-'));
62
- const rawOutDir = mkdtempSync(join(tmpdir(), 'cr-aia-raw-'));
63
- const manifestPath = join(tempDir, 'ai-code-review.json');
64
- writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
65
-
66
- ensureProxyEnvironmentInitialized(repoRoot);
67
-
68
- const finalOutDir = resolveOutputDirectory(workspaceRoot, outDir);
69
- mkdirSync(finalOutDir, { recursive: true });
70
-
71
- const start = Date.now();
72
-
73
- try {
74
- onStage?.('reviewing', { filesFound: fileCount });
75
- const reports = await runAiCodeReview(files, {
76
- provider: 'openrouter',
77
- type: 'comprehensive',
78
- outDir: rawOutDir,
79
- format: 'json',
80
- model,
81
- configPath: manifestPath,
82
- debug,
83
- });
84
-
85
- onStage?.('merging');
86
- const merged = mergeReports(reports || []);
87
- const totals =
88
- (merged?.totals as ReviewTotals) || ({ critical: 0, high: 0, medium: 0, low: 0 } as ReviewTotals);
89
- const findings = (merged?.findings as any[]) || [];
90
-
91
- const repo = basename(workspaceRoot) || 'workspace';
92
- const duration = Math.round((Date.now() - start) / 1000);
93
- const reportTimestamp = new Date();
94
-
95
- const collected = collectReportData(reports || []);
96
- const severityTotals = totals || collected.totals;
97
-
98
- const markdownDraft = buildMarkdownReport({
99
- repoName: repo,
100
- generatedAt: reportTimestamp,
101
- durationSeconds: duration,
102
- filesReviewed: fileCount,
103
- totals: severityTotals,
104
- tokenStats: collected.tokenStats,
105
- estimatedCostUSD: collected.estimatedCostUSD,
106
- issueGroups: collected.groupedIssues,
107
- });
108
-
109
- const summary = await generateReportSummary(markdownDraft, {
110
- model,
111
- totals: severityTotals,
112
- durationSeconds: duration,
113
- estimatedCostUSD: collected.estimatedCostUSD,
114
- issueCount: collected.issueCount,
115
- });
116
-
117
- const finalMarkdown = injectSummary(markdownDraft, summary);
118
- const reportFileName = formatReportFileName(repo, reportTimestamp);
119
- const reportPath = join(finalOutDir, reportFileName);
120
- writeFileSync(reportPath, finalMarkdown, 'utf-8');
121
-
122
- return {
123
- totals: severityTotals,
124
- findings,
125
- duration,
126
- repo,
127
- filesReviewed: fileCount,
128
- reportPath,
129
- };
130
- } finally {
131
- rmSync(tempDir, { recursive: true, force: true });
132
- rmSync(rawOutDir, { recursive: true, force: true });
133
- }
134
- }
135
-
136
- async function resolveRepoRoot(): Promise<string> {
137
- try {
138
- const { stdout } = await execa('git', ['rev-parse', '--show-toplevel']);
139
- return stdout.trim();
140
- } catch {
141
- return process.cwd();
142
- }
143
- }
144
-
145
- function resolveOutputDirectory(root: string, outputDir: string): string {
146
- if (isAbsolute(outputDir)) {
147
- return outputDir;
148
- }
149
- return resolve(root, outputDir);
150
- }
151
-
152
- function formatReportFileName(repo: string, date: Date): string {
153
- const sanitizedRepo = repo.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/-+/g, '-');
154
- const pad = (value: number) => value.toString().padStart(2, '0');
155
- const day = pad(date.getDate());
156
- const month = pad(date.getMonth() + 1);
157
- const year = date.getFullYear();
158
- const hours = pad(date.getHours());
159
- const minutes = pad(date.getMinutes());
160
- return `cr-${sanitizedRepo}-${day}-${month}-${year}-${hours}-${minutes}.md`;
161
- }
@@ -1,153 +0,0 @@
1
- import { existsSync, mkdirSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
- import { join, relative, resolve } from 'node:path';
3
- import { execa } from 'execa';
4
- import type { OutputFormat } from '../types/common';
5
- import type { ReviewOptions, ReviewType } from '../types/review';
6
- import { orchestrateReview } from '../core/reviewOrchestrator';
7
- import { RUNTIME_CONFIG } from './runtimeConfig';
8
-
9
- export interface AiReviewOptions {
10
- provider: 'openrouter';
11
- type: string;
12
- outDir: string;
13
- format: 'json' | 'md';
14
- model?: string;
15
- configPath?: string;
16
- debug?: boolean;
17
- }
18
-
19
- export async function runAiCodeReview(files: string[], opts: AiReviewOptions): Promise<any[]> {
20
- if (files.length === 0) return [];
21
-
22
- const workspaceRoot = process.cwd();
23
- let repoRoot: string;
24
- try {
25
- const { stdout } = await execa('git', ['rev-parse', '--show-toplevel']);
26
- repoRoot = stdout.trim();
27
- } catch {
28
- repoRoot = process.cwd();
29
- }
30
-
31
- const absOutDir = resolve(workspaceRoot, opts.outDir);
32
-
33
- if (!existsSync(absOutDir)) {
34
- mkdirSync(absOutDir, { recursive: true });
35
- }
36
-
37
- const outputFileMeta = new Map<string, { mtimeMs: number; size: number }>();
38
- if (existsSync(absOutDir)) {
39
- for (const file of readdirSync(absOutDir)) {
40
- if (!file.endsWith('.json')) continue;
41
- try {
42
- const stats = statSync(join(absOutDir, file));
43
- outputFileMeta.set(file, { mtimeMs: stats.mtimeMs, size: stats.size });
44
- } catch {
45
- // ignore stale files
46
- }
47
- }
48
- }
49
-
50
- function getAllFilesInDir(dirPath: string, repo: string): string[] {
51
- const entries = readdirSync(dirPath, { withFileTypes: true });
52
- const nested: string[] = [];
53
- for (const entry of entries) {
54
- const fullPath = join(dirPath, entry.name);
55
- const relPath = relative(repo, fullPath);
56
- if (relPath.includes('.git/') || relPath.includes('node_modules/')) {
57
- continue;
58
- }
59
-
60
- if (entry.isDirectory()) {
61
- const subFiles = getAllFilesInDir(fullPath, repo);
62
- nested.push(...subFiles);
63
- } else if (entry.isFile()) {
64
- nested.push(fullPath);
65
- }
66
- }
67
- return nested;
68
- }
69
-
70
- const expandedFiles: string[] = [];
71
- for (const file of files) {
72
- const absPath = resolve(file);
73
- try {
74
- const stats = statSync(absPath);
75
- if (stats.isDirectory()) {
76
- const dirFiles = getAllFilesInDir(absPath, repoRoot);
77
- expandedFiles.push(...dirFiles);
78
- } else if (stats.isFile()) {
79
- expandedFiles.push(absPath);
80
- }
81
- } catch {
82
- continue;
83
- }
84
- }
85
-
86
- const uniqueExpandedFiles = Array.from(new Set(expandedFiles));
87
- const relativeFilesSet = new Set<string>();
88
- for (const absPath of uniqueExpandedFiles) {
89
- try {
90
- const stats = statSync(absPath);
91
- if (!stats.isFile()) continue;
92
- const repoRelative = relative(repoRoot, absPath);
93
- if (!repoRelative || repoRelative.startsWith('..')) continue;
94
-
95
- const workspaceRelative = relative(workspaceRoot, absPath).replace(/\\/g, '/');
96
- if (!workspaceRelative || workspaceRelative.startsWith('..')) continue;
97
-
98
- relativeFilesSet.add(workspaceRelative);
99
- } catch {
100
- continue;
101
- }
102
- }
103
-
104
- const outputs: any[] = [];
105
-
106
- const collectOutputs = () => {
107
- if (!existsSync(absOutDir)) {
108
- return;
109
- }
110
- const jsonFiles = readdirSync(absOutDir).filter((f) => f.endsWith('.json'));
111
- for (const file of jsonFiles) {
112
- const filePath = join(absOutDir, file);
113
- try {
114
- const stats = statSync(filePath);
115
- const previous = outputFileMeta.get(file);
116
- if (previous && stats.mtimeMs <= previous.mtimeMs && stats.size === previous.size) {
117
- continue;
118
- }
119
- const content = JSON.parse(readFileSync(filePath, 'utf-8'));
120
- outputs.push(content);
121
- outputFileMeta.set(file, { mtimeMs: stats.mtimeMs, size: stats.size });
122
- } catch {
123
- continue;
124
- }
125
- }
126
- };
127
-
128
- if (relativeFilesSet.size === 0) {
129
- throw new Error('No files to review after filtering');
130
- }
131
-
132
- const targets = Array.from(relativeFilesSet);
133
- const mappedFormat: OutputFormat = opts.format === 'md' ? 'markdown' : 'json';
134
-
135
- const baseOptions: (ReviewOptions & { config?: string }) = {
136
- type: opts.type as ReviewType,
137
- output: mappedFormat,
138
- outputDir: absOutDir,
139
- model: opts.model || RUNTIME_CONFIG.DEFAULT_MODEL,
140
- debug: opts.debug ?? false,
141
- };
142
-
143
- if (opts.configPath) {
144
- baseOptions.config = opts.configPath;
145
- }
146
-
147
- for (const target of targets) {
148
- await orchestrateReview(target, baseOptions);
149
- collectOutputs();
150
- }
151
-
152
- return outputs;
153
- }
@@ -1,5 +0,0 @@
1
- export const RUNTIME_CONFIG = {
2
- DEFAULT_MODEL: 'openrouter:x-ai/grok-4-fast',
3
- } as const;
4
-
5
- export type RuntimeConfig = typeof RUNTIME_CONFIG;
@@ -1,57 +0,0 @@
1
- import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
2
- import { getInk } from './inkModules';
3
-
4
- interface LayoutValue {
5
- columns: number;
6
- rows: number;
7
- frameWidth: number;
8
- }
9
-
10
- const DEFAULT_COLUMNS = 80;
11
- const DEFAULT_ROWS = 24;
12
-
13
- const LayoutContext = createContext<LayoutValue>({
14
- columns: DEFAULT_COLUMNS,
15
- rows: DEFAULT_ROWS,
16
- frameWidth: DEFAULT_COLUMNS - 4,
17
- });
18
-
19
- export function LayoutProvider({ children }: { children: React.ReactNode }) {
20
- const { useStdout } = getInk();
21
- const { stdout } = useStdout();
22
- const [dimensions, setDimensions] = useState<LayoutValue>(() => ({
23
- columns: stdout?.columns ?? DEFAULT_COLUMNS,
24
- rows: stdout?.rows ?? DEFAULT_ROWS,
25
- frameWidth: Math.max(60, Math.min((stdout?.columns ?? DEFAULT_COLUMNS) - 4, 110)),
26
- }));
27
-
28
- useEffect(() => {
29
- if (!stdout) {
30
- return;
31
- }
32
-
33
- const handleResize = () => {
34
- const cols = stdout.columns ?? DEFAULT_COLUMNS;
35
- const rows = stdout.rows ?? DEFAULT_ROWS;
36
- setDimensions({
37
- columns: cols,
38
- rows,
39
- frameWidth: Math.max(60, Math.min(cols - 4, 110)),
40
- });
41
- };
42
-
43
- stdout.on('resize', handleResize);
44
- return () => {
45
- stdout.off('resize', handleResize);
46
- };
47
- }, [stdout]);
48
-
49
- const value = useMemo<LayoutValue>(() => dimensions, [dimensions]);
50
-
51
- return <LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>;
52
- }
53
-
54
- export function useLayout(): LayoutValue {
55
- return useContext(LayoutContext);
56
- }
57
-
@@ -1,233 +0,0 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
- import { LayoutProvider, useLayout } from './Layout';
3
- import { AuthScreen } from './screens/AuthScreen';
4
- import { ModeSelection } from './screens/ModeSelection';
5
- import { ProgressScreen } from './screens/ProgressScreen';
6
- import { ResultsScreen } from './screens/ResultsScreen';
7
- import { getInk, getInkSpinner } from './inkModules';
8
- import type { ReviewMode, ReviewResult, ReviewStage } from '../reviewPipeline';
9
- import { executeReview } from '../reviewPipeline';
10
- import { loadSession, getSessionToken } from '../auth/session';
11
- import type { SessionUser } from '../auth/types';
12
- import { RUNTIME_CONFIG } from '../runtimeConfig';
13
- import { MissingCrIgnoreError } from '../errors';
14
- import { triggerManualWebCheck } from '../preprod/webCheck';
15
- import { startDebugLogSession, type DebugLogSession } from '../debug/logManager';
16
- import logger from '../../utils/logger';
17
-
18
- type ScreenState = 'auth' | 'mode' | 'progress' | 'results';
19
-
20
- interface RuntimeAppProps {
21
- debug?: boolean;
22
- }
23
-
24
- interface ProgressState {
25
- stage: ReviewStage;
26
- filesFound?: number;
27
- }
28
-
29
- const defaultProgress: ProgressState = { stage: 'collecting' };
30
- const GENERIC_ERROR_MESSAGE =
31
- 'Something went wrong. Press D to enable debug mode and rerun to capture logs.';
32
- const PREPROD_ERROR_MESSAGE =
33
- 'Could not start the website check. Double-check the URL or try again later.';
34
-
35
- function AppBody({ debug = false }: RuntimeAppProps) {
36
- const { Box, Text, useApp, useInput } = getInk();
37
- const { exit } = useApp();
38
- const layout = useLayout();
39
- const [screen, setScreen] = useState<ScreenState>('auth');
40
- const [user, setUser] = useState<SessionUser | null>(null);
41
- const [progress, setProgress] = useState<ProgressState>(defaultProgress);
42
- const [results, setResults] = useState<ReviewResult | null>(null);
43
- const [debugEnabled, setDebugEnabled] = useState(false);
44
- const [lastDebugLogPath, setLastDebugLogPath] = useState<string | null>(null);
45
- type ErrorInfo = { type: 'generic' | 'crignore'; message: string; hint?: string };
46
- const [error, setError] = useState<ErrorInfo | null>(null);
47
- const combinedDebugFlag = useMemo(() => debug || debugEnabled, [debug, debugEnabled]);
48
-
49
- useEffect(() => {
50
- const sessionUser = loadSession();
51
- const token = getSessionToken();
52
- if (token) {
53
- process.env.CR_AIA_PROXY_SESSION_TOKEN = token;
54
- }
55
- if (sessionUser) {
56
- setUser(sessionUser);
57
- setScreen('mode');
58
- }
59
- }, []);
60
-
61
- useInput(
62
- useCallback(
63
- (input, key) => {
64
- if (key.ctrl && input === 'c') {
65
- exit();
66
- }
67
-
68
- if (screen === 'results' && key.return) {
69
- setScreen('mode');
70
- }
71
- },
72
- [exit, screen],
73
- ),
74
- );
75
-
76
- const handleAuth = useCallback((nextUser: SessionUser) => {
77
- setUser(nextUser);
78
- setScreen('mode');
79
- }, []);
80
-
81
- const handleToggleDebug = useCallback(() => {
82
- setDebugEnabled((prev) => {
83
- if (prev) {
84
- setLastDebugLogPath(null);
85
- }
86
- return !prev;
87
- });
88
- }, []);
89
-
90
- const handleRunReview = useCallback(async (mode: ReviewMode, extras?: { targetUrl?: string }) => {
91
- const trimmedUrl = extras?.targetUrl?.trim();
92
-
93
- if (mode === 'preprod' && !trimmedUrl) {
94
- setError({ type: 'generic', message: 'Pre-production mode requires a website URL.' });
95
- return;
96
- }
97
-
98
- setScreen('progress');
99
- setResults(null);
100
- setError(null);
101
- setProgress(mode === 'preprod' ? { stage: 'preparing' } : defaultProgress);
102
- setLastDebugLogPath(null);
103
-
104
- let debugSession: DebugLogSession | null = null;
105
- const finalizeDebugLogging = () => {
106
- if (debugSession) {
107
- debugSession.stop();
108
- setLastDebugLogPath(debugSession.filePath);
109
- } else {
110
- setLastDebugLogPath(null);
111
- }
112
- };
113
-
114
- if (debugEnabled) {
115
- try {
116
- debugSession = startDebugLogSession(process.cwd());
117
- } catch (logInitError) {
118
- const message =
119
- logInitError instanceof Error
120
- ? logInitError.message
121
- : 'Unable to initialize debug logging.';
122
- setError({ type: 'generic', message: `Debug logging disabled: ${message}` });
123
- }
124
- }
125
-
126
- if (mode === 'preprod' && trimmedUrl) {
127
- try {
128
- logger.info('Triggering manual website check', { url: trimmedUrl });
129
- await triggerManualWebCheck(trimmedUrl);
130
- } catch (webError) {
131
- logger.error('Pre-production web check failed', webError);
132
- setError({ type: 'generic', message: PREPROD_ERROR_MESSAGE });
133
- setScreen('mode');
134
- finalizeDebugLogging();
135
- return;
136
- }
137
- }
138
-
139
- try {
140
- logger.info('Starting code review run', { mode, workspace: process.cwd(), preprodTarget: trimmedUrl });
141
- setProgress({ stage: 'collecting' });
142
- const reviewResult = await executeReview({
143
- model: process.env.AI_CODE_REVIEW_MODEL || RUNTIME_CONFIG.DEFAULT_MODEL,
144
- outDir: './reports',
145
- debug: combinedDebugFlag,
146
- mode,
147
- preprodTargetUrl: trimmedUrl,
148
- onStage: (stage, info) => setProgress({ stage, filesFound: info?.filesFound }),
149
- });
150
-
151
- logger.info('Code review run complete', {
152
- mode,
153
- durationSeconds: reviewResult.duration,
154
- filesReviewed: reviewResult.filesReviewed,
155
- });
156
- setResults(reviewResult);
157
- setScreen('results');
158
- } catch (reviewError) {
159
- logger.error('Code review run failed', reviewError);
160
- if (reviewError instanceof MissingCrIgnoreError) {
161
- setError({
162
- type: 'crignore',
163
- message: 'cr-aia needs a .crignore file in this repo.',
164
- hint: `Create ${reviewError.crIgnorePath} (can be empty, gitignore syntax) and rerun.`,
165
- });
166
- } else {
167
- setError({ type: 'generic', message: GENERIC_ERROR_MESSAGE });
168
- }
169
- setScreen('mode');
170
- } finally {
171
- finalizeDebugLogging();
172
- }
173
- }, [combinedDebugFlag, debugEnabled]);
174
-
175
- return (
176
- <Box width={layout.frameWidth} flexDirection="column" gap={1}>
177
- {screen === 'auth' && <AuthScreen onAuth={handleAuth} />}
178
- {screen === 'mode' && user && (
179
- <ModeSelection
180
- onSelect={(mode, payload) => handleRunReview(mode, payload)}
181
- debugEnabled={debugEnabled}
182
- onToggleDebug={handleToggleDebug}
183
- />
184
- )}
185
- {screen === 'progress' && <ProgressScreen stage={progress.stage} filesFound={progress.filesFound} />}
186
- {screen === 'results' && results && (
187
- <ResultsScreen result={results} debugLogPath={lastDebugLogPath} />
188
- )}
189
- {error && (
190
- <Box
191
- borderStyle="round"
192
- borderColor={error.type === 'crignore' ? 'yellow' : 'red'}
193
- paddingX={1}
194
- paddingY={1}
195
- >
196
- <Text color={error.type === 'crignore' ? 'yellow' : 'red'}>
197
- {error.message}
198
- {error.hint ? `\n${error.hint}` : ''}
199
- </Text>
200
- </Box>
201
- )}
202
- {lastDebugLogPath && screen !== 'results' && (
203
- <Box>
204
- <Text dimColor>Debug logs saved to: {lastDebugLogPath}</Text>
205
- </Box>
206
- )}
207
- <Box justifyContent="space-between">
208
- <Text dimColor>{user ? `Logged in as ${user.username}` : 'Authenticate to start reviewing'}</Text>
209
- <Text dimColor>Ctrl+C to exit</Text>
210
- </Box>
211
- </Box>
212
- );
213
- }
214
-
215
- export function RuntimeApp(props: RuntimeAppProps) {
216
- return (
217
- <LayoutProvider>
218
- <AppBody {...props} />
219
- </LayoutProvider>
220
- );
221
- }
222
-
223
- export function LoadingScreen() {
224
- const { Box, Text } = getInk();
225
- const Spinner = getInkSpinner();
226
- return (
227
- <Box flexDirection="column" paddingX={1} paddingY={1}>
228
- <Text>
229
- <Spinner type="dots" /> Preparing runtime environment...
230
- </Text>
231
- </Box>
232
- );
233
- }
@@ -1,73 +0,0 @@
1
- type InkModule = typeof import('ink');
2
- type SpinnerModule = typeof import('ink-spinner');
3
- type TextInputModule = typeof import('ink-text-input');
4
- type SelectInputModule = typeof import('ink-select-input');
5
-
6
- const dynamicImporter = new Function('specifier', 'return import(specifier);') as (
7
- specifier: string,
8
- ) => Promise<unknown>;
9
-
10
- let inkModule: InkModule | null = null;
11
- let spinnerModule: SpinnerModule['default'] | null = null;
12
- let textInputModule: TextInputModule['default'] | null = null;
13
- let selectInputModule: SelectInputModule['default'] | null = null;
14
-
15
- async function loadInk(): Promise<void> {
16
- if (!inkModule) {
17
- inkModule = (await dynamicImporter('ink')) as InkModule;
18
- }
19
- }
20
-
21
- async function loadSpinner(): Promise<void> {
22
- if (!spinnerModule) {
23
- const mod = (await dynamicImporter('ink-spinner')) as SpinnerModule;
24
- spinnerModule = mod.default;
25
- }
26
- }
27
-
28
- async function loadTextInput(): Promise<void> {
29
- if (!textInputModule) {
30
- const mod = (await dynamicImporter('ink-text-input')) as TextInputModule;
31
- textInputModule = mod.default;
32
- }
33
- }
34
-
35
- async function loadSelectInput(): Promise<void> {
36
- if (!selectInputModule) {
37
- const mod = (await dynamicImporter('ink-select-input')) as SelectInputModule;
38
- selectInputModule = mod.default;
39
- }
40
- }
41
-
42
- export async function preloadInkModules(): Promise<void> {
43
- await Promise.all([loadInk(), loadSpinner(), loadTextInput(), loadSelectInput()]);
44
- }
45
-
46
- export function getInk(): InkModule {
47
- if (!inkModule) {
48
- throw new Error('Ink module not loaded. Call preloadInkModules() before rendering UI.');
49
- }
50
- return inkModule;
51
- }
52
-
53
- export function getInkSpinner(): SpinnerModule['default'] {
54
- if (!spinnerModule) {
55
- throw new Error('Ink spinner module not loaded. Call preloadInkModules() before rendering UI.');
56
- }
57
- return spinnerModule;
58
- }
59
-
60
- export function getInkTextInput(): TextInputModule['default'] {
61
- if (!textInputModule) {
62
- throw new Error('Ink TextInput module not loaded. Call preloadInkModules() before rendering UI.');
63
- }
64
- return textInputModule;
65
- }
66
-
67
- export function getInkSelectInput(): SelectInputModule['default'] {
68
- if (!selectInputModule) {
69
- throw new Error('Ink SelectInput module not loaded. Call preloadInkModules() before rendering UI.');
70
- }
71
- return selectInputModule;
72
- }
73
-