archicore 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +530 -0
  2. package/dist/analyzers/dead-code.d.ts +95 -0
  3. package/dist/analyzers/dead-code.js +327 -0
  4. package/dist/analyzers/duplication.d.ts +90 -0
  5. package/dist/analyzers/duplication.js +344 -0
  6. package/dist/analyzers/security.d.ts +79 -0
  7. package/dist/analyzers/security.js +484 -0
  8. package/dist/architecture/index.d.ts +35 -0
  9. package/dist/architecture/index.js +249 -0
  10. package/dist/cli/commands/analyzers.d.ts +6 -0
  11. package/dist/cli/commands/analyzers.js +431 -0
  12. package/dist/cli/commands/export.d.ts +6 -0
  13. package/dist/cli/commands/export.js +78 -0
  14. package/dist/cli/commands/index.d.ts +8 -0
  15. package/dist/cli/commands/index.js +8 -0
  16. package/dist/cli/commands/init.d.ts +26 -0
  17. package/dist/cli/commands/init.js +140 -0
  18. package/dist/cli/commands/interactive.d.ts +7 -0
  19. package/dist/cli/commands/interactive.js +522 -0
  20. package/dist/cli/commands/projects.d.ts +6 -0
  21. package/dist/cli/commands/projects.js +249 -0
  22. package/dist/cli/index.d.ts +7 -0
  23. package/dist/cli/index.js +7 -0
  24. package/dist/cli/ui/box.d.ts +17 -0
  25. package/dist/cli/ui/box.js +62 -0
  26. package/dist/cli/ui/colors.d.ts +49 -0
  27. package/dist/cli/ui/colors.js +86 -0
  28. package/dist/cli/ui/index.d.ts +9 -0
  29. package/dist/cli/ui/index.js +9 -0
  30. package/dist/cli/ui/prompt.d.ts +34 -0
  31. package/dist/cli/ui/prompt.js +122 -0
  32. package/dist/cli/ui/spinner.d.ts +29 -0
  33. package/dist/cli/ui/spinner.js +80 -0
  34. package/dist/cli/ui/table.d.ts +33 -0
  35. package/dist/cli/ui/table.js +84 -0
  36. package/dist/cli/utils/config.d.ts +23 -0
  37. package/dist/cli/utils/config.js +73 -0
  38. package/dist/cli/utils/index.d.ts +6 -0
  39. package/dist/cli/utils/index.js +6 -0
  40. package/dist/cli/utils/session.d.ts +27 -0
  41. package/dist/cli/utils/session.js +117 -0
  42. package/dist/cli.d.ts +8 -0
  43. package/dist/cli.js +295 -0
  44. package/dist/code-index/ast-parser.d.ts +16 -0
  45. package/dist/code-index/ast-parser.js +330 -0
  46. package/dist/code-index/dependency-graph.d.ts +16 -0
  47. package/dist/code-index/dependency-graph.js +161 -0
  48. package/dist/code-index/index.d.ts +44 -0
  49. package/dist/code-index/index.js +124 -0
  50. package/dist/code-index/symbol-extractor.d.ts +13 -0
  51. package/dist/code-index/symbol-extractor.js +150 -0
  52. package/dist/export/index.d.ts +92 -0
  53. package/dist/export/index.js +676 -0
  54. package/dist/github/github-service.d.ts +146 -0
  55. package/dist/github/github-service.js +609 -0
  56. package/dist/impact-engine/index.d.ts +25 -0
  57. package/dist/impact-engine/index.js +284 -0
  58. package/dist/index.d.ts +60 -0
  59. package/dist/index.js +149 -0
  60. package/dist/metrics/index.d.ts +136 -0
  61. package/dist/metrics/index.js +525 -0
  62. package/dist/orchestrator/deepseek-optimizer.d.ts +67 -0
  63. package/dist/orchestrator/deepseek-optimizer.js +320 -0
  64. package/dist/orchestrator/index.d.ts +34 -0
  65. package/dist/orchestrator/index.js +305 -0
  66. package/dist/pr-guardian/index.d.ts +143 -0
  67. package/dist/pr-guardian/index.js +553 -0
  68. package/dist/refactoring/index.d.ts +108 -0
  69. package/dist/refactoring/index.js +580 -0
  70. package/dist/rules-engine/index.d.ts +129 -0
  71. package/dist/rules-engine/index.js +482 -0
  72. package/dist/semantic-memory/embedding-service.d.ts +24 -0
  73. package/dist/semantic-memory/embedding-service.js +120 -0
  74. package/dist/semantic-memory/index.d.ts +45 -0
  75. package/dist/semantic-memory/index.js +206 -0
  76. package/dist/semantic-memory/vector-store.d.ts +27 -0
  77. package/dist/semantic-memory/vector-store.js +166 -0
  78. package/dist/server/index.d.ts +28 -0
  79. package/dist/server/index.js +141 -0
  80. package/dist/server/middleware/api-auth.d.ts +43 -0
  81. package/dist/server/middleware/api-auth.js +256 -0
  82. package/dist/server/routes/admin.d.ts +5 -0
  83. package/dist/server/routes/admin.js +123 -0
  84. package/dist/server/routes/api.d.ts +7 -0
  85. package/dist/server/routes/api.js +362 -0
  86. package/dist/server/routes/auth.d.ts +16 -0
  87. package/dist/server/routes/auth.js +191 -0
  88. package/dist/server/routes/developer.d.ts +8 -0
  89. package/dist/server/routes/developer.js +439 -0
  90. package/dist/server/routes/github.d.ts +7 -0
  91. package/dist/server/routes/github.js +495 -0
  92. package/dist/server/routes/upload.d.ts +7 -0
  93. package/dist/server/routes/upload.js +196 -0
  94. package/dist/server/services/api-key-service.d.ts +81 -0
  95. package/dist/server/services/api-key-service.js +281 -0
  96. package/dist/server/services/auth-service.d.ts +40 -0
  97. package/dist/server/services/auth-service.js +315 -0
  98. package/dist/server/services/project-service.d.ts +123 -0
  99. package/dist/server/services/project-service.js +533 -0
  100. package/dist/server/services/token-service.d.ts +107 -0
  101. package/dist/server/services/token-service.js +416 -0
  102. package/dist/server/services/upload-service.d.ts +93 -0
  103. package/dist/server/services/upload-service.js +464 -0
  104. package/dist/types/api.d.ts +188 -0
  105. package/dist/types/api.js +86 -0
  106. package/dist/types/github.d.ts +335 -0
  107. package/dist/types/github.js +5 -0
  108. package/dist/types/index.d.ts +265 -0
  109. package/dist/types/index.js +32 -0
  110. package/dist/types/user.d.ts +69 -0
  111. package/dist/types/user.js +42 -0
  112. package/dist/utils/file-utils.d.ts +20 -0
  113. package/dist/utils/file-utils.js +163 -0
  114. package/dist/utils/logger.d.ts +17 -0
  115. package/dist/utils/logger.js +41 -0
  116. package/dist/watcher/index.d.ts +125 -0
  117. package/dist/watcher/index.js +397 -0
  118. package/package.json +71 -0
@@ -0,0 +1,143 @@
1
+ /**
2
+ * PR Guardian - Pull Request Analysis System
3
+ *
4
+ * Анализ Pull Request'ов:
5
+ * - Оценка риска изменений
6
+ * - Проверка архитектурных правил
7
+ * - Рекомендации ревьюверам
8
+ * - Автоматические проверки качества
9
+ */
10
+ import { DependencyGraph, Symbol } from '../types/index.js';
11
+ export interface PRAnalysisRequest {
12
+ changedFiles: ChangedFile[];
13
+ baseBranch: string;
14
+ targetBranch: string;
15
+ author: string;
16
+ title: string;
17
+ description?: string;
18
+ }
19
+ export interface ChangedFile {
20
+ path: string;
21
+ status: 'added' | 'modified' | 'deleted' | 'renamed';
22
+ additions: number;
23
+ deletions: number;
24
+ oldContent?: string;
25
+ newContent?: string;
26
+ }
27
+ export interface PRAnalysisResult {
28
+ score: PRScore;
29
+ riskLevel: RiskLevel;
30
+ summary: PRSummary;
31
+ checks: PRCheck[];
32
+ suggestions: PRSuggestion[];
33
+ impactAnalysis: ImpactAnalysis;
34
+ reviewerGuidance: ReviewerGuidance;
35
+ }
36
+ export interface PRScore {
37
+ overall: number;
38
+ security: number;
39
+ architecture: number;
40
+ complexity: number;
41
+ testCoverage: number;
42
+ documentation: number;
43
+ }
44
+ export type RiskLevel = 'low' | 'medium' | 'high' | 'critical';
45
+ export interface PRSummary {
46
+ totalFiles: number;
47
+ additions: number;
48
+ deletions: number;
49
+ affectedModules: string[];
50
+ criticalFilesChanged: boolean;
51
+ breakingChanges: boolean;
52
+ estimatedReviewTime: number;
53
+ }
54
+ export interface PRCheck {
55
+ name: string;
56
+ status: 'passed' | 'failed' | 'warning';
57
+ message: string;
58
+ details?: string;
59
+ }
60
+ export interface PRSuggestion {
61
+ type: 'improvement' | 'concern' | 'question';
62
+ priority: 'low' | 'medium' | 'high';
63
+ file?: string;
64
+ line?: number;
65
+ message: string;
66
+ suggestion?: string;
67
+ }
68
+ export interface ImpactAnalysis {
69
+ directlyAffected: string[];
70
+ transitivelyAffected: string[];
71
+ potentialBreaking: string[];
72
+ testFilesNeeded: string[];
73
+ }
74
+ export interface ReviewerGuidance {
75
+ focusAreas: FocusArea[];
76
+ suggestedReviewers: string[];
77
+ estimatedComplexity: 'simple' | 'moderate' | 'complex';
78
+ needsSecurityReview: boolean;
79
+ needsArchitectureReview: boolean;
80
+ }
81
+ export interface FocusArea {
82
+ file: string;
83
+ reason: string;
84
+ priority: 'high' | 'medium' | 'low';
85
+ }
86
+ export declare class PRGuardian {
87
+ private securityAnalyzer;
88
+ private duplicationDetector;
89
+ constructor();
90
+ /**
91
+ * Полный анализ PR
92
+ */
93
+ analyze(request: PRAnalysisRequest, graph: DependencyGraph, symbols: Map<string, Symbol>): Promise<PRAnalysisResult>;
94
+ /**
95
+ * Быстрая проверка (для CI)
96
+ */
97
+ quickCheck(request: PRAnalysisRequest): Promise<{
98
+ passed: boolean;
99
+ riskLevel: RiskLevel;
100
+ blockers: string[];
101
+ }>;
102
+ /**
103
+ * Расчёт сводки PR
104
+ */
105
+ private calculateSummary;
106
+ /**
107
+ * Детекция breaking changes
108
+ */
109
+ private detectBreakingChanges;
110
+ /**
111
+ * Анализ влияния изменений
112
+ */
113
+ private analyzeImpact;
114
+ /**
115
+ * Запуск проверок
116
+ */
117
+ private runChecks;
118
+ /**
119
+ * Анализ безопасности изменённого кода
120
+ */
121
+ private analyzeChangedSecurity;
122
+ /**
123
+ * Анализ дублирования в изменениях
124
+ */
125
+ private analyzeChangedDuplication;
126
+ /**
127
+ * Генерация предложений
128
+ */
129
+ private generateSuggestions;
130
+ /**
131
+ * Расчёт оценок
132
+ */
133
+ private calculateScore;
134
+ /**
135
+ * Определение уровня риска
136
+ */
137
+ private determineRiskLevel;
138
+ /**
139
+ * Генерация руководства для ревьюверов
140
+ */
141
+ private generateReviewerGuidance;
142
+ }
143
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,553 @@
1
+ /**
2
+ * PR Guardian - Pull Request Analysis System
3
+ *
4
+ * Анализ Pull Request'ов:
5
+ * - Оценка риска изменений
6
+ * - Проверка архитектурных правил
7
+ * - Рекомендации ревьюверам
8
+ * - Автоматические проверки качества
9
+ */
10
+ import { SecurityAnalyzer } from '../analyzers/security.js';
11
+ import { DuplicationDetector } from '../analyzers/duplication.js';
12
+ import { Logger } from '../utils/logger.js';
13
+ // Критические файлы, требующие особого внимания
14
+ const CRITICAL_FILE_PATTERNS = [
15
+ /^(package|composer|Cargo|go\.mod|requirements).*\.(json|lock|toml|txt)$/,
16
+ /\.(env|config|secrets?)(\.\w+)?$/,
17
+ /auth|security|crypto|password|token/i,
18
+ /^(\.github|\.gitlab|\.circleci)/,
19
+ /^(Dockerfile|docker-compose|k8s|kubernetes)/i,
20
+ /migrations?\//i
21
+ ];
22
+ // Файлы, которые обычно не нужно ревьювить детально
23
+ const LOW_PRIORITY_PATTERNS = [
24
+ /\.(md|txt|rst)$/,
25
+ /\.(snap|json)$/, // Snapshots, lock files
26
+ /\.test\.(ts|js|tsx|jsx)$/,
27
+ /\.spec\.(ts|js|tsx|jsx)$/,
28
+ /__tests__\//,
29
+ /\.d\.ts$/
30
+ ];
31
+ export class PRGuardian {
32
+ securityAnalyzer;
33
+ duplicationDetector;
34
+ constructor() {
35
+ this.securityAnalyzer = new SecurityAnalyzer();
36
+ this.duplicationDetector = new DuplicationDetector();
37
+ }
38
+ /**
39
+ * Полный анализ PR
40
+ */
41
+ async analyze(request, graph, symbols) {
42
+ Logger.progress('Analyzing pull request...');
43
+ // 1. Базовая сводка
44
+ const summary = this.calculateSummary(request);
45
+ // 2. Анализ влияния
46
+ const impactAnalysis = this.analyzeImpact(request, graph);
47
+ // 3. Запускаем проверки
48
+ const checks = await this.runChecks(request, graph, symbols);
49
+ // 4. Анализ безопасности изменённого кода
50
+ const securityResult = await this.analyzeChangedSecurity(request);
51
+ // 5. Проверка дублирования
52
+ const duplicationResult = await this.analyzeChangedDuplication(request);
53
+ // 6. Генерируем предложения
54
+ const suggestions = this.generateSuggestions(request, checks, securityResult, duplicationResult, impactAnalysis);
55
+ // 7. Рассчитываем оценки
56
+ const score = this.calculateScore(checks, securityResult, summary);
57
+ // 8. Определяем уровень риска
58
+ const riskLevel = this.determineRiskLevel(score, summary, impactAnalysis);
59
+ // 9. Руководство для ревьюверов
60
+ const reviewerGuidance = this.generateReviewerGuidance(request, impactAnalysis, score, riskLevel);
61
+ Logger.success(`PR analysis complete. Risk level: ${riskLevel}`);
62
+ return {
63
+ score,
64
+ riskLevel,
65
+ summary,
66
+ checks,
67
+ suggestions,
68
+ impactAnalysis,
69
+ reviewerGuidance
70
+ };
71
+ }
72
+ /**
73
+ * Быстрая проверка (для CI)
74
+ */
75
+ async quickCheck(request) {
76
+ const summary = this.calculateSummary(request);
77
+ const securityResult = await this.analyzeChangedSecurity(request);
78
+ const blockers = [];
79
+ // Проверяем критические проблемы безопасности
80
+ if (securityResult.summary.critical > 0) {
81
+ blockers.push(`${securityResult.summary.critical} critical security issues found`);
82
+ }
83
+ // Проверяем захардкоженные секреты
84
+ if (securityResult.secrets.length > 0) {
85
+ blockers.push(`${securityResult.secrets.length} hardcoded secrets detected`);
86
+ }
87
+ // Слишком большой PR
88
+ if (summary.totalFiles > 50) {
89
+ blockers.push('PR is too large (>50 files). Consider splitting.');
90
+ }
91
+ // Изменения в критических файлах без описания
92
+ if (summary.criticalFilesChanged && !request.description) {
93
+ blockers.push('Critical files changed but no description provided');
94
+ }
95
+ const riskLevel = blockers.length > 0 ? 'critical' :
96
+ summary.criticalFilesChanged ? 'high' :
97
+ summary.totalFiles > 20 ? 'medium' : 'low';
98
+ return {
99
+ passed: blockers.length === 0,
100
+ riskLevel,
101
+ blockers
102
+ };
103
+ }
104
+ /**
105
+ * Расчёт сводки PR
106
+ */
107
+ calculateSummary(request) {
108
+ const additions = request.changedFiles.reduce((sum, f) => sum + f.additions, 0);
109
+ const deletions = request.changedFiles.reduce((sum, f) => sum + f.deletions, 0);
110
+ // Определяем затронутые модули
111
+ const modules = new Set();
112
+ for (const file of request.changedFiles) {
113
+ const parts = file.path.split('/');
114
+ if (parts.length > 1) {
115
+ modules.add(parts[0]);
116
+ if (parts.length > 2) {
117
+ modules.add(`${parts[0]}/${parts[1]}`);
118
+ }
119
+ }
120
+ }
121
+ // Проверяем критические файлы
122
+ const criticalFilesChanged = request.changedFiles.some(f => CRITICAL_FILE_PATTERNS.some(pattern => pattern.test(f.path)));
123
+ // Проверяем breaking changes (удаление публичных экспортов)
124
+ const breakingChanges = request.changedFiles.some(f => f.status === 'deleted' ||
125
+ (f.oldContent && f.newContent && this.detectBreakingChanges(f.oldContent, f.newContent)));
126
+ // Оценка времени ревью (примерно)
127
+ const estimatedReviewTime = Math.ceil(request.changedFiles.length * 2 +
128
+ (additions + deletions) / 50 +
129
+ (criticalFilesChanged ? 15 : 0) +
130
+ (breakingChanges ? 10 : 0));
131
+ return {
132
+ totalFiles: request.changedFiles.length,
133
+ additions,
134
+ deletions,
135
+ affectedModules: Array.from(modules),
136
+ criticalFilesChanged,
137
+ breakingChanges,
138
+ estimatedReviewTime
139
+ };
140
+ }
141
+ /**
142
+ * Детекция breaking changes
143
+ */
144
+ detectBreakingChanges(oldContent, newContent) {
145
+ // Ищем удалённые экспорты
146
+ const exportPattern = /export\s+(?:const|let|var|function|class|interface|type|enum)\s+(\w+)/g;
147
+ const oldExports = new Set();
148
+ let match;
149
+ while ((match = exportPattern.exec(oldContent)) !== null) {
150
+ oldExports.add(match[1]);
151
+ }
152
+ exportPattern.lastIndex = 0;
153
+ const newExports = new Set();
154
+ while ((match = exportPattern.exec(newContent)) !== null) {
155
+ newExports.add(match[1]);
156
+ }
157
+ // Проверяем, удалены ли экспорты
158
+ for (const exp of oldExports) {
159
+ if (!newExports.has(exp)) {
160
+ return true;
161
+ }
162
+ }
163
+ return false;
164
+ }
165
+ /**
166
+ * Анализ влияния изменений
167
+ */
168
+ analyzeImpact(request, graph) {
169
+ const changedPaths = new Set(request.changedFiles.map(f => f.path));
170
+ const transitivelyAffected = new Set();
171
+ const potentialBreaking = new Set();
172
+ // Находим файлы, зависящие от изменённых
173
+ for (const [filePath, edges] of graph.edges) {
174
+ for (const edge of edges) {
175
+ // Нормализуем путь для сравнения
176
+ const targetPath = edge.to.replace(/^\.\//, '').replace(/\.\w+$/, '');
177
+ for (const changed of changedPaths) {
178
+ const changedNorm = changed.replace(/\.\w+$/, '');
179
+ if (targetPath.includes(changedNorm) || changedNorm.includes(targetPath)) {
180
+ if (!changedPaths.has(filePath)) {
181
+ transitivelyAffected.add(filePath);
182
+ // Если изменённый файл удалён, это breaking
183
+ const changedFile = request.changedFiles.find(f => f.path === changed);
184
+ if (changedFile?.status === 'deleted') {
185
+ potentialBreaking.add(filePath);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
192
+ // Определяем тесты для запуска
193
+ const testFilesNeeded = new Set();
194
+ const allAffected = new Set([...changedPaths, ...transitivelyAffected]);
195
+ for (const [nodeId, node] of graph.nodes) {
196
+ const isTest = /\.(test|spec)\.(ts|js|tsx|jsx)$/.test(node.filePath);
197
+ if (!isTest)
198
+ continue;
199
+ // Проверяем, тестирует ли этот файл затронутые модули
200
+ const edges = graph.edges.get(nodeId) || [];
201
+ for (const edge of edges) {
202
+ for (const affected of allAffected) {
203
+ if (edge.to.includes(affected.replace(/\.\w+$/, ''))) {
204
+ testFilesNeeded.add(node.filePath);
205
+ break;
206
+ }
207
+ }
208
+ }
209
+ }
210
+ return {
211
+ directlyAffected: Array.from(changedPaths),
212
+ transitivelyAffected: Array.from(transitivelyAffected),
213
+ potentialBreaking: Array.from(potentialBreaking),
214
+ testFilesNeeded: Array.from(testFilesNeeded)
215
+ };
216
+ }
217
+ /**
218
+ * Запуск проверок
219
+ */
220
+ async runChecks(request, _graph, _symbols) {
221
+ const checks = [];
222
+ // 1. Размер PR
223
+ const totalChanges = request.changedFiles.reduce((sum, f) => sum + f.additions + f.deletions, 0);
224
+ if (request.changedFiles.length > 30 || totalChanges > 1000) {
225
+ checks.push({
226
+ name: 'PR Size',
227
+ status: 'warning',
228
+ message: 'Large PR detected',
229
+ details: `${request.changedFiles.length} files, ${totalChanges} lines changed. Consider splitting.`
230
+ });
231
+ }
232
+ else {
233
+ checks.push({
234
+ name: 'PR Size',
235
+ status: 'passed',
236
+ message: 'PR size is acceptable'
237
+ });
238
+ }
239
+ // 2. Наличие тестов
240
+ const hasTests = request.changedFiles.some(f => /\.(test|spec)\.(ts|js|tsx|jsx)$/.test(f.path));
241
+ const hasCodeChanges = request.changedFiles.some(f => /\.(ts|js|tsx|jsx)$/.test(f.path) &&
242
+ !/\.(test|spec)\./.test(f.path));
243
+ if (hasCodeChanges && !hasTests) {
244
+ checks.push({
245
+ name: 'Test Coverage',
246
+ status: 'warning',
247
+ message: 'No test files changed',
248
+ details: 'Consider adding tests for the changed code'
249
+ });
250
+ }
251
+ else if (hasTests) {
252
+ checks.push({
253
+ name: 'Test Coverage',
254
+ status: 'passed',
255
+ message: 'Tests are included'
256
+ });
257
+ }
258
+ // 3. Документация для публичных API
259
+ const publicApiChanged = request.changedFiles.some(f => /index\.(ts|js)$/.test(f.path) ||
260
+ f.path.includes('api/') ||
261
+ f.path.includes('public/'));
262
+ const hasDocChanges = request.changedFiles.some(f => /\.(md|rst|txt)$/.test(f.path) ||
263
+ f.path.toLowerCase().includes('readme') ||
264
+ f.path.toLowerCase().includes('doc'));
265
+ if (publicApiChanged && !hasDocChanges) {
266
+ checks.push({
267
+ name: 'Documentation',
268
+ status: 'warning',
269
+ message: 'Public API changed without documentation update',
270
+ details: 'Consider updating relevant documentation'
271
+ });
272
+ }
273
+ else {
274
+ checks.push({
275
+ name: 'Documentation',
276
+ status: 'passed',
277
+ message: 'Documentation status OK'
278
+ });
279
+ }
280
+ // 4. Конфигурационные файлы
281
+ const configFiles = request.changedFiles.filter(f => CRITICAL_FILE_PATTERNS.some(p => p.test(f.path)));
282
+ if (configFiles.length > 0) {
283
+ checks.push({
284
+ name: 'Critical Files',
285
+ status: 'warning',
286
+ message: `${configFiles.length} critical/config files changed`,
287
+ details: configFiles.map(f => f.path).join(', ')
288
+ });
289
+ }
290
+ else {
291
+ checks.push({
292
+ name: 'Critical Files',
293
+ status: 'passed',
294
+ message: 'No critical files changed'
295
+ });
296
+ }
297
+ // 5. Проверка naming conventions
298
+ const badNaming = request.changedFiles.filter(f => {
299
+ const fileName = f.path.split('/').pop() || '';
300
+ // Проверяем snake_case vs camelCase consistency
301
+ return /[A-Z]/.test(fileName) && fileName.includes('_');
302
+ });
303
+ if (badNaming.length > 0) {
304
+ checks.push({
305
+ name: 'Naming Conventions',
306
+ status: 'warning',
307
+ message: 'Inconsistent file naming detected',
308
+ details: badNaming.map(f => f.path).join(', ')
309
+ });
310
+ }
311
+ else {
312
+ checks.push({
313
+ name: 'Naming Conventions',
314
+ status: 'passed',
315
+ message: 'File naming is consistent'
316
+ });
317
+ }
318
+ return checks;
319
+ }
320
+ /**
321
+ * Анализ безопасности изменённого кода
322
+ */
323
+ async analyzeChangedSecurity(request) {
324
+ const fileContents = new Map();
325
+ for (const file of request.changedFiles) {
326
+ if (file.newContent) {
327
+ fileContents.set(file.path, file.newContent);
328
+ }
329
+ }
330
+ return this.securityAnalyzer.analyze(fileContents);
331
+ }
332
+ /**
333
+ * Анализ дублирования в изменениях
334
+ */
335
+ async analyzeChangedDuplication(request) {
336
+ const fileContents = new Map();
337
+ for (const file of request.changedFiles) {
338
+ if (file.newContent) {
339
+ fileContents.set(file.path, file.newContent);
340
+ }
341
+ }
342
+ return this.duplicationDetector.analyze(fileContents);
343
+ }
344
+ /**
345
+ * Генерация предложений
346
+ */
347
+ generateSuggestions(request, checks, securityResult, duplicationResult, impactAnalysis) {
348
+ const suggestions = [];
349
+ // Предложения на основе проверок
350
+ for (const check of checks) {
351
+ if (check.status === 'failed' || check.status === 'warning') {
352
+ suggestions.push({
353
+ type: 'concern',
354
+ priority: check.status === 'failed' ? 'high' : 'medium',
355
+ message: check.message,
356
+ suggestion: check.details
357
+ });
358
+ }
359
+ }
360
+ // Предложения по безопасности
361
+ for (const vuln of securityResult.vulnerabilities.slice(0, 5)) {
362
+ suggestions.push({
363
+ type: 'concern',
364
+ priority: vuln.severity === 'critical' ? 'high' : 'medium',
365
+ file: vuln.file,
366
+ line: vuln.line,
367
+ message: `Security: ${vuln.type}`,
368
+ suggestion: vuln.remediation
369
+ });
370
+ }
371
+ // Предложения по секретам
372
+ for (const secret of securityResult.secrets) {
373
+ suggestions.push({
374
+ type: 'concern',
375
+ priority: 'high',
376
+ file: secret.file,
377
+ line: secret.line,
378
+ message: `Hardcoded secret detected: ${secret.type}`,
379
+ suggestion: 'Move to environment variables or secret manager'
380
+ });
381
+ }
382
+ // Предложения по дублированию
383
+ for (const clone of duplicationResult.clones.slice(0, 3)) {
384
+ suggestions.push({
385
+ type: 'improvement',
386
+ priority: 'medium',
387
+ file: clone.instances[0]?.file,
388
+ line: clone.instances[0]?.startLine,
389
+ message: `Code duplication detected (${clone.linesCount} lines, ${clone.instances.length} instances)`,
390
+ suggestion: clone.suggestion
391
+ });
392
+ }
393
+ // Предложения по breaking changes
394
+ if (impactAnalysis.potentialBreaking.length > 0) {
395
+ suggestions.push({
396
+ type: 'concern',
397
+ priority: 'high',
398
+ message: `Potential breaking changes affecting ${impactAnalysis.potentialBreaking.length} files`,
399
+ suggestion: 'Ensure backward compatibility or update all affected files'
400
+ });
401
+ }
402
+ // Вопросы для больших изменений
403
+ const addedFiles = request.changedFiles.filter(f => f.status === 'added');
404
+ if (addedFiles.length > 5) {
405
+ suggestions.push({
406
+ type: 'question',
407
+ priority: 'medium',
408
+ message: `${addedFiles.length} new files added`,
409
+ suggestion: 'Is this new functionality? Should it be a separate PR/module?'
410
+ });
411
+ }
412
+ return suggestions;
413
+ }
414
+ /**
415
+ * Расчёт оценок
416
+ */
417
+ calculateScore(checks, securityResult, summary) {
418
+ // Security score
419
+ const securityScore = Math.max(0, 100 -
420
+ securityResult.summary.critical * 30 -
421
+ securityResult.summary.high * 15 -
422
+ securityResult.summary.medium * 5 -
423
+ securityResult.secrets.length * 25);
424
+ // Architecture score (based on checks)
425
+ const failedChecks = checks.filter(c => c.status === 'failed').length;
426
+ const warningChecks = checks.filter(c => c.status === 'warning').length;
427
+ const architectureScore = Math.max(0, 100 - failedChecks * 20 - warningChecks * 10);
428
+ // Complexity score (based on size)
429
+ const complexityScore = Math.max(0, 100 -
430
+ Math.min(50, summary.totalFiles) -
431
+ Math.min(30, (summary.additions + summary.deletions) / 100) -
432
+ (summary.breakingChanges ? 20 : 0));
433
+ // Test coverage score
434
+ const testScore = checks.find(c => c.name === 'Test Coverage')?.status === 'passed' ? 100 : 50;
435
+ // Documentation score
436
+ const docScore = checks.find(c => c.name === 'Documentation')?.status === 'passed' ? 100 : 60;
437
+ // Overall score
438
+ const overall = Math.round(securityScore * 0.3 +
439
+ architectureScore * 0.25 +
440
+ complexityScore * 0.2 +
441
+ testScore * 0.15 +
442
+ docScore * 0.1);
443
+ return {
444
+ overall,
445
+ security: Math.round(securityScore),
446
+ architecture: Math.round(architectureScore),
447
+ complexity: Math.round(complexityScore),
448
+ testCoverage: Math.round(testScore),
449
+ documentation: Math.round(docScore)
450
+ };
451
+ }
452
+ /**
453
+ * Определение уровня риска
454
+ */
455
+ determineRiskLevel(score, summary, impactAnalysis) {
456
+ // Critical risk
457
+ if (score.security < 50 || impactAnalysis.potentialBreaking.length > 5) {
458
+ return 'critical';
459
+ }
460
+ // High risk
461
+ if (score.overall < 60 ||
462
+ summary.criticalFilesChanged ||
463
+ summary.breakingChanges ||
464
+ impactAnalysis.transitivelyAffected.length > 20) {
465
+ return 'high';
466
+ }
467
+ // Medium risk
468
+ if (score.overall < 80 ||
469
+ summary.totalFiles > 15 ||
470
+ impactAnalysis.transitivelyAffected.length > 10) {
471
+ return 'medium';
472
+ }
473
+ return 'low';
474
+ }
475
+ /**
476
+ * Генерация руководства для ревьюверов
477
+ */
478
+ generateReviewerGuidance(request, impactAnalysis, score, _riskLevel) {
479
+ const focusAreas = [];
480
+ // Критические файлы - высокий приоритет
481
+ for (const file of request.changedFiles) {
482
+ if (CRITICAL_FILE_PATTERNS.some(p => p.test(file.path))) {
483
+ focusAreas.push({
484
+ file: file.path,
485
+ reason: 'Critical/configuration file',
486
+ priority: 'high'
487
+ });
488
+ }
489
+ }
490
+ // Файлы с большим количеством изменений
491
+ const largeChanges = request.changedFiles
492
+ .filter(f => f.additions + f.deletions > 100)
493
+ .filter(f => !LOW_PRIORITY_PATTERNS.some(p => p.test(f.path)));
494
+ for (const file of largeChanges) {
495
+ focusAreas.push({
496
+ file: file.path,
497
+ reason: `Large changes (${file.additions + file.deletions} lines)`,
498
+ priority: 'high'
499
+ });
500
+ }
501
+ // Файлы, влияющие на многие другие
502
+ const highImpactFiles = impactAnalysis.directlyAffected.filter(filePath => {
503
+ const affected = impactAnalysis.transitivelyAffected.filter(t => t.includes(filePath.replace(/\.\w+$/, '')));
504
+ return affected.length > 5;
505
+ });
506
+ for (const file of highImpactFiles.slice(0, 5)) {
507
+ if (!focusAreas.some(f => f.file === file)) {
508
+ focusAreas.push({
509
+ file,
510
+ reason: 'High impact - many files depend on this',
511
+ priority: 'medium'
512
+ });
513
+ }
514
+ }
515
+ // Определяем сложность
516
+ let estimatedComplexity;
517
+ if (request.changedFiles.length <= 3 && score.overall >= 80) {
518
+ estimatedComplexity = 'simple';
519
+ }
520
+ else if (request.changedFiles.length <= 10 && score.overall >= 60) {
521
+ estimatedComplexity = 'moderate';
522
+ }
523
+ else {
524
+ estimatedComplexity = 'complex';
525
+ }
526
+ // Нужен ли security review
527
+ const needsSecurityReview = score.security < 80 ||
528
+ request.changedFiles.some(f => /auth|security|crypto|password|token|secret/i.test(f.path));
529
+ // Нужен ли architecture review
530
+ const needsArchitectureReview = score.architecture < 80 ||
531
+ request.changedFiles.some(f => /index\.(ts|js)$/.test(f.path) ||
532
+ f.path.includes('types/') ||
533
+ f.path.includes('interfaces/')) ||
534
+ impactAnalysis.transitivelyAffected.length > 10;
535
+ // Рекомендуемые ревьюверы (заглушка - в реальности это из git history)
536
+ const suggestedReviewers = [];
537
+ const modules = [...new Set(request.changedFiles.map(f => f.path.split('/')[0]))];
538
+ if (modules.length > 0) {
539
+ suggestedReviewers.push(`Owner of ${modules[0]} module`);
540
+ }
541
+ if (needsSecurityReview) {
542
+ suggestedReviewers.push('Security team member');
543
+ }
544
+ return {
545
+ focusAreas: focusAreas.slice(0, 10),
546
+ suggestedReviewers,
547
+ estimatedComplexity,
548
+ needsSecurityReview,
549
+ needsArchitectureReview
550
+ };
551
+ }
552
+ }
553
+ //# sourceMappingURL=index.js.map