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,609 @@
1
+ /**
2
+ * GitHub Service for ArchiCore
3
+ *
4
+ * Handles OAuth, API calls, webhooks, and repository management
5
+ */
6
+ import { randomBytes, createHash, createHmac, createCipheriv, createDecipheriv } from 'crypto';
7
+ import { readFile, writeFile, mkdir } from 'fs/promises';
8
+ import { join } from 'path';
9
+ import { Logger } from '../utils/logger.js';
10
+ const DATA_DIR = '.archicore';
11
+ const CONNECTIONS_FILE = 'github-connections.json';
12
+ const REPOSITORIES_FILE = 'github-repositories.json';
13
+ // Encryption key (in production, use env variable)
14
+ const ENCRYPTION_KEY = process.env.GITHUB_ENCRYPTION_KEY || 'archicore-github-key-32bytes!!';
15
+ export class GitHubService {
16
+ dataDir;
17
+ connections = [];
18
+ repositories = [];
19
+ initialized = false;
20
+ clientId;
21
+ clientSecret;
22
+ redirectUri;
23
+ webhookBaseUrl;
24
+ constructor(dataDir = DATA_DIR) {
25
+ this.dataDir = dataDir;
26
+ this.clientId = process.env.GITHUB_CLIENT_ID || '';
27
+ this.clientSecret = process.env.GITHUB_CLIENT_SECRET || '';
28
+ this.redirectUri = process.env.GITHUB_REDIRECT_URI || 'http://localhost:3000/api/github/callback';
29
+ this.webhookBaseUrl = process.env.ARCHICORE_WEBHOOK_URL || 'http://localhost:3000/api/github/webhook';
30
+ }
31
+ async ensureInitialized() {
32
+ if (this.initialized)
33
+ return;
34
+ try {
35
+ await mkdir(this.dataDir, { recursive: true });
36
+ }
37
+ catch { }
38
+ // Load connections
39
+ try {
40
+ const path = join(this.dataDir, CONNECTIONS_FILE);
41
+ const data = await readFile(path, 'utf-8');
42
+ const parsed = JSON.parse(data);
43
+ this.connections = parsed.connections || [];
44
+ }
45
+ catch {
46
+ this.connections = [];
47
+ }
48
+ // Load repositories
49
+ try {
50
+ const path = join(this.dataDir, REPOSITORIES_FILE);
51
+ const data = await readFile(path, 'utf-8');
52
+ const parsed = JSON.parse(data);
53
+ this.repositories = parsed.repositories || [];
54
+ }
55
+ catch {
56
+ this.repositories = [];
57
+ }
58
+ this.initialized = true;
59
+ }
60
+ async saveConnections() {
61
+ const path = join(this.dataDir, CONNECTIONS_FILE);
62
+ await writeFile(path, JSON.stringify({ connections: this.connections }, null, 2));
63
+ }
64
+ async saveRepositories() {
65
+ const path = join(this.dataDir, REPOSITORIES_FILE);
66
+ await writeFile(path, JSON.stringify({ repositories: this.repositories }, null, 2));
67
+ }
68
+ // ===== ENCRYPTION =====
69
+ encrypt(text) {
70
+ const iv = randomBytes(16);
71
+ const key = createHash('sha256').update(ENCRYPTION_KEY).digest();
72
+ const cipher = createCipheriv('aes-256-cbc', key, iv);
73
+ let encrypted = cipher.update(text, 'utf8', 'hex');
74
+ encrypted += cipher.final('hex');
75
+ return iv.toString('hex') + ':' + encrypted;
76
+ }
77
+ decrypt(encrypted) {
78
+ const parts = encrypted.split(':');
79
+ const iv = Buffer.from(parts[0], 'hex');
80
+ const key = createHash('sha256').update(ENCRYPTION_KEY).digest();
81
+ const decipher = createDecipheriv('aes-256-cbc', key, iv);
82
+ let decrypted = decipher.update(parts[1], 'hex', 'utf8');
83
+ decrypted += decipher.final('utf8');
84
+ return decrypted;
85
+ }
86
+ // ===== OAUTH =====
87
+ /**
88
+ * Generate OAuth authorization URL
89
+ */
90
+ getAuthorizationUrl(state) {
91
+ const scopes = ['repo', 'read:user', 'user:email', 'write:repo_hook'];
92
+ const params = new URLSearchParams({
93
+ client_id: this.clientId,
94
+ redirect_uri: this.redirectUri,
95
+ scope: scopes.join(' '),
96
+ state,
97
+ allow_signup: 'true'
98
+ });
99
+ return `https://github.com/login/oauth/authorize?${params.toString()}`;
100
+ }
101
+ /**
102
+ * Exchange authorization code for access token
103
+ */
104
+ async exchangeCodeForToken(code) {
105
+ const response = await fetch('https://github.com/login/oauth/access_token', {
106
+ method: 'POST',
107
+ headers: {
108
+ 'Accept': 'application/json',
109
+ 'Content-Type': 'application/json'
110
+ },
111
+ body: JSON.stringify({
112
+ client_id: this.clientId,
113
+ client_secret: this.clientSecret,
114
+ code,
115
+ redirect_uri: this.redirectUri
116
+ })
117
+ });
118
+ if (!response.ok) {
119
+ throw new Error('Failed to exchange code for token');
120
+ }
121
+ const data = await response.json();
122
+ if (data.error) {
123
+ throw new Error(data.error_description || data.error);
124
+ }
125
+ return data;
126
+ }
127
+ /**
128
+ * Get authenticated GitHub user
129
+ */
130
+ async getGitHubUser(accessToken) {
131
+ const response = await fetch('https://api.github.com/user', {
132
+ headers: {
133
+ 'Authorization': `Bearer ${accessToken}`,
134
+ 'Accept': 'application/vnd.github.v3+json',
135
+ 'User-Agent': 'ArchiCore'
136
+ }
137
+ });
138
+ if (!response.ok) {
139
+ throw new Error('Failed to get GitHub user');
140
+ }
141
+ return response.json();
142
+ }
143
+ /**
144
+ * Save GitHub connection for user
145
+ */
146
+ async saveConnection(userId, tokenResponse, githubUser) {
147
+ await this.ensureInitialized();
148
+ // Remove existing connection for this user
149
+ this.connections = this.connections.filter(c => c.userId !== userId);
150
+ const connection = {
151
+ id: 'ghc_' + randomBytes(12).toString('hex'),
152
+ userId,
153
+ githubUserId: githubUser.id,
154
+ githubUsername: githubUser.login,
155
+ accessToken: this.encrypt(tokenResponse.access_token),
156
+ refreshToken: tokenResponse.refresh_token ? this.encrypt(tokenResponse.refresh_token) : undefined,
157
+ tokenExpiresAt: tokenResponse.expires_in
158
+ ? new Date(Date.now() + tokenResponse.expires_in * 1000)
159
+ : undefined,
160
+ scope: tokenResponse.scope.split(','),
161
+ createdAt: new Date(),
162
+ updatedAt: new Date()
163
+ };
164
+ this.connections.push(connection);
165
+ await this.saveConnections();
166
+ Logger.info(`GitHub connected for user ${userId}: ${githubUser.login}`);
167
+ return connection;
168
+ }
169
+ /**
170
+ * Get connection for user
171
+ */
172
+ async getConnection(userId) {
173
+ await this.ensureInitialized();
174
+ return this.connections.find(c => c.userId === userId) || null;
175
+ }
176
+ /**
177
+ * Get decrypted access token
178
+ */
179
+ async getAccessToken(userId) {
180
+ const connection = await this.getConnection(userId);
181
+ if (!connection)
182
+ return null;
183
+ return this.decrypt(connection.accessToken);
184
+ }
185
+ /**
186
+ * Disconnect GitHub
187
+ */
188
+ async disconnect(userId) {
189
+ await this.ensureInitialized();
190
+ const connection = this.connections.find(c => c.userId === userId);
191
+ if (!connection)
192
+ return false;
193
+ // Remove all connected repositories
194
+ const userRepos = this.repositories.filter(r => r.userId === userId);
195
+ for (const repo of userRepos) {
196
+ if (repo.webhookId) {
197
+ try {
198
+ await this.deleteWebhook(userId, repo.fullName, repo.webhookId);
199
+ }
200
+ catch (e) {
201
+ Logger.warn(`Failed to delete webhook for ${repo.fullName}`);
202
+ }
203
+ }
204
+ }
205
+ this.repositories = this.repositories.filter(r => r.userId !== userId);
206
+ this.connections = this.connections.filter(c => c.userId !== userId);
207
+ await this.saveConnections();
208
+ await this.saveRepositories();
209
+ Logger.info(`GitHub disconnected for user ${userId}`);
210
+ return true;
211
+ }
212
+ // ===== REPOSITORIES =====
213
+ /**
214
+ * List user's GitHub repositories
215
+ */
216
+ async listUserRepositories(userId) {
217
+ const accessToken = await this.getAccessToken(userId);
218
+ if (!accessToken) {
219
+ throw new Error('GitHub not connected');
220
+ }
221
+ const repos = [];
222
+ let page = 1;
223
+ const perPage = 100;
224
+ while (true) {
225
+ const response = await fetch(`https://api.github.com/user/repos?per_page=${perPage}&page=${page}&sort=updated`, {
226
+ headers: {
227
+ 'Authorization': `Bearer ${accessToken}`,
228
+ 'Accept': 'application/vnd.github.v3+json',
229
+ 'User-Agent': 'ArchiCore'
230
+ }
231
+ });
232
+ if (!response.ok) {
233
+ throw new Error('Failed to list repositories');
234
+ }
235
+ const data = await response.json();
236
+ repos.push(...data);
237
+ if (data.length < perPage)
238
+ break;
239
+ page++;
240
+ }
241
+ return repos;
242
+ }
243
+ /**
244
+ * Get repository details
245
+ */
246
+ async getRepository(userId, fullName) {
247
+ const accessToken = await this.getAccessToken(userId);
248
+ if (!accessToken) {
249
+ throw new Error('GitHub not connected');
250
+ }
251
+ const response = await fetch(`https://api.github.com/repos/${fullName}`, {
252
+ headers: {
253
+ 'Authorization': `Bearer ${accessToken}`,
254
+ 'Accept': 'application/vnd.github.v3+json',
255
+ 'User-Agent': 'ArchiCore'
256
+ }
257
+ });
258
+ if (!response.ok) {
259
+ throw new Error('Failed to get repository');
260
+ }
261
+ return response.json();
262
+ }
263
+ /**
264
+ * List repository branches
265
+ */
266
+ async listBranches(userId, fullName) {
267
+ const accessToken = await this.getAccessToken(userId);
268
+ if (!accessToken) {
269
+ throw new Error('GitHub not connected');
270
+ }
271
+ const response = await fetch(`https://api.github.com/repos/${fullName}/branches`, {
272
+ headers: {
273
+ 'Authorization': `Bearer ${accessToken}`,
274
+ 'Accept': 'application/vnd.github.v3+json',
275
+ 'User-Agent': 'ArchiCore'
276
+ }
277
+ });
278
+ if (!response.ok) {
279
+ throw new Error('Failed to list branches');
280
+ }
281
+ return response.json();
282
+ }
283
+ /**
284
+ * Connect repository to ArchiCore
285
+ */
286
+ async connectRepository(userId, githubRepoId, options = {}) {
287
+ await this.ensureInitialized();
288
+ const accessToken = await this.getAccessToken(userId);
289
+ if (!accessToken) {
290
+ throw new Error('GitHub not connected');
291
+ }
292
+ // Get repository details
293
+ const repos = await this.listUserRepositories(userId);
294
+ const githubRepo = repos.find(r => r.id === githubRepoId);
295
+ if (!githubRepo) {
296
+ throw new Error('Repository not found or access denied');
297
+ }
298
+ // Check if already connected
299
+ const existing = this.repositories.find(r => r.userId === userId && r.githubRepoId === githubRepoId);
300
+ if (existing) {
301
+ throw new Error('Repository already connected');
302
+ }
303
+ // Generate webhook secret
304
+ const webhookSecret = randomBytes(32).toString('hex');
305
+ // Create webhook
306
+ let webhookId;
307
+ try {
308
+ const webhook = await this.createWebhook(userId, githubRepo.full_name, webhookSecret, ['push', 'pull_request']);
309
+ webhookId = webhook.id;
310
+ }
311
+ catch (e) {
312
+ Logger.warn(`Failed to create webhook for ${githubRepo.full_name}: ${e}`);
313
+ }
314
+ const connection = await this.getConnection(userId);
315
+ const connectedRepo = {
316
+ id: 'repo_' + randomBytes(12).toString('hex'),
317
+ connectionId: connection.id,
318
+ userId,
319
+ projectId: options.projectId,
320
+ githubRepoId,
321
+ fullName: githubRepo.full_name,
322
+ name: githubRepo.name,
323
+ owner: githubRepo.owner.login,
324
+ private: githubRepo.private,
325
+ defaultBranch: githubRepo.default_branch,
326
+ cloneUrl: githubRepo.clone_url,
327
+ webhookId,
328
+ webhookSecret: webhookId ? this.encrypt(webhookSecret) : undefined,
329
+ autoAnalyze: options.autoAnalyze ?? true,
330
+ analyzePRs: options.analyzePRs ?? true,
331
+ status: 'active',
332
+ createdAt: new Date(),
333
+ updatedAt: new Date()
334
+ };
335
+ this.repositories.push(connectedRepo);
336
+ await this.saveRepositories();
337
+ Logger.info(`Repository connected: ${githubRepo.full_name}`);
338
+ return connectedRepo;
339
+ }
340
+ /**
341
+ * Disconnect repository
342
+ */
343
+ async disconnectRepository(userId, repoId) {
344
+ await this.ensureInitialized();
345
+ const repo = this.repositories.find(r => r.id === repoId && r.userId === userId);
346
+ if (!repo)
347
+ return false;
348
+ // Delete webhook
349
+ if (repo.webhookId) {
350
+ try {
351
+ await this.deleteWebhook(userId, repo.fullName, repo.webhookId);
352
+ }
353
+ catch (e) {
354
+ Logger.warn(`Failed to delete webhook: ${e}`);
355
+ }
356
+ }
357
+ this.repositories = this.repositories.filter(r => r.id !== repoId);
358
+ await this.saveRepositories();
359
+ Logger.info(`Repository disconnected: ${repo.fullName}`);
360
+ return true;
361
+ }
362
+ /**
363
+ * Get connected repositories for user
364
+ */
365
+ async getConnectedRepositories(userId) {
366
+ await this.ensureInitialized();
367
+ return this.repositories.filter(r => r.userId === userId);
368
+ }
369
+ /**
370
+ * Get connected repository by ID
371
+ */
372
+ async getConnectedRepository(repoId) {
373
+ await this.ensureInitialized();
374
+ return this.repositories.find(r => r.id === repoId) || null;
375
+ }
376
+ // ===== WEBHOOKS =====
377
+ /**
378
+ * Create webhook on repository
379
+ */
380
+ async createWebhook(userId, fullName, secret, events) {
381
+ const accessToken = await this.getAccessToken(userId);
382
+ if (!accessToken) {
383
+ throw new Error('GitHub not connected');
384
+ }
385
+ const response = await fetch(`https://api.github.com/repos/${fullName}/hooks`, {
386
+ method: 'POST',
387
+ headers: {
388
+ 'Authorization': `Bearer ${accessToken}`,
389
+ 'Accept': 'application/vnd.github.v3+json',
390
+ 'User-Agent': 'ArchiCore',
391
+ 'Content-Type': 'application/json'
392
+ },
393
+ body: JSON.stringify({
394
+ name: 'web',
395
+ active: true,
396
+ events,
397
+ config: {
398
+ url: this.webhookBaseUrl,
399
+ content_type: 'json',
400
+ secret,
401
+ insecure_ssl: '0'
402
+ }
403
+ })
404
+ });
405
+ if (!response.ok) {
406
+ const error = await response.json();
407
+ throw new Error(error.message || 'Failed to create webhook');
408
+ }
409
+ return response.json();
410
+ }
411
+ /**
412
+ * Delete webhook from repository
413
+ */
414
+ async deleteWebhook(userId, fullName, webhookId) {
415
+ const accessToken = await this.getAccessToken(userId);
416
+ if (!accessToken)
417
+ return;
418
+ await fetch(`https://api.github.com/repos/${fullName}/hooks/${webhookId}`, {
419
+ method: 'DELETE',
420
+ headers: {
421
+ 'Authorization': `Bearer ${accessToken}`,
422
+ 'Accept': 'application/vnd.github.v3+json',
423
+ 'User-Agent': 'ArchiCore'
424
+ }
425
+ });
426
+ }
427
+ /**
428
+ * Verify webhook signature
429
+ */
430
+ verifyWebhookSignature(payload, signature, secret) {
431
+ const hmac = createHmac('sha256', secret);
432
+ const digest = 'sha256=' + hmac.update(payload).digest('hex');
433
+ return signature === digest;
434
+ }
435
+ /**
436
+ * Find repository by webhook payload
437
+ */
438
+ async findRepositoryByWebhook(fullName) {
439
+ await this.ensureInitialized();
440
+ return this.repositories.find(r => r.fullName === fullName) || null;
441
+ }
442
+ // ===== PULL REQUESTS =====
443
+ /**
444
+ * Get pull request details
445
+ */
446
+ async getPullRequest(userId, fullName, prNumber) {
447
+ const accessToken = await this.getAccessToken(userId);
448
+ if (!accessToken) {
449
+ throw new Error('GitHub not connected');
450
+ }
451
+ const response = await fetch(`https://api.github.com/repos/${fullName}/pulls/${prNumber}`, {
452
+ headers: {
453
+ 'Authorization': `Bearer ${accessToken}`,
454
+ 'Accept': 'application/vnd.github.v3+json',
455
+ 'User-Agent': 'ArchiCore'
456
+ }
457
+ });
458
+ if (!response.ok) {
459
+ throw new Error('Failed to get pull request');
460
+ }
461
+ return response.json();
462
+ }
463
+ /**
464
+ * Get pull request files
465
+ */
466
+ async getPullRequestFiles(userId, fullName, prNumber) {
467
+ const accessToken = await this.getAccessToken(userId);
468
+ if (!accessToken) {
469
+ throw new Error('GitHub not connected');
470
+ }
471
+ const response = await fetch(`https://api.github.com/repos/${fullName}/pulls/${prNumber}/files`, {
472
+ headers: {
473
+ 'Authorization': `Bearer ${accessToken}`,
474
+ 'Accept': 'application/vnd.github.v3+json',
475
+ 'User-Agent': 'ArchiCore'
476
+ }
477
+ });
478
+ if (!response.ok) {
479
+ throw new Error('Failed to get pull request files');
480
+ }
481
+ return response.json();
482
+ }
483
+ /**
484
+ * Post comment on pull request
485
+ */
486
+ async postPRComment(userId, fullName, prNumber, body) {
487
+ const accessToken = await this.getAccessToken(userId);
488
+ if (!accessToken) {
489
+ throw new Error('GitHub not connected');
490
+ }
491
+ const response = await fetch(`https://api.github.com/repos/${fullName}/issues/${prNumber}/comments`, {
492
+ method: 'POST',
493
+ headers: {
494
+ 'Authorization': `Bearer ${accessToken}`,
495
+ 'Accept': 'application/vnd.github.v3+json',
496
+ 'User-Agent': 'ArchiCore',
497
+ 'Content-Type': 'application/json'
498
+ },
499
+ body: JSON.stringify({ body })
500
+ });
501
+ if (!response.ok) {
502
+ throw new Error('Failed to post comment');
503
+ }
504
+ return response.json();
505
+ }
506
+ /**
507
+ * Create PR review
508
+ */
509
+ async createPRReview(userId, fullName, prNumber, body, event) {
510
+ const accessToken = await this.getAccessToken(userId);
511
+ if (!accessToken) {
512
+ throw new Error('GitHub not connected');
513
+ }
514
+ const response = await fetch(`https://api.github.com/repos/${fullName}/pulls/${prNumber}/reviews`, {
515
+ method: 'POST',
516
+ headers: {
517
+ 'Authorization': `Bearer ${accessToken}`,
518
+ 'Accept': 'application/vnd.github.v3+json',
519
+ 'User-Agent': 'ArchiCore',
520
+ 'Content-Type': 'application/json'
521
+ },
522
+ body: JSON.stringify({ body, event })
523
+ });
524
+ if (!response.ok) {
525
+ throw new Error('Failed to create review');
526
+ }
527
+ return response.json();
528
+ }
529
+ // ===== FILE CONTENT =====
530
+ /**
531
+ * Get file content from repository
532
+ */
533
+ async getFileContent(userId, fullName, path, ref) {
534
+ const accessToken = await this.getAccessToken(userId);
535
+ if (!accessToken) {
536
+ throw new Error('GitHub not connected');
537
+ }
538
+ const url = new URL(`https://api.github.com/repos/${fullName}/contents/${path}`);
539
+ if (ref)
540
+ url.searchParams.set('ref', ref);
541
+ const response = await fetch(url.toString(), {
542
+ headers: {
543
+ 'Authorization': `Bearer ${accessToken}`,
544
+ 'Accept': 'application/vnd.github.v3+json',
545
+ 'User-Agent': 'ArchiCore'
546
+ }
547
+ });
548
+ if (!response.ok) {
549
+ throw new Error('Failed to get file content');
550
+ }
551
+ const data = await response.json();
552
+ return Buffer.from(data.content, 'base64').toString('utf-8');
553
+ }
554
+ /**
555
+ * Download repository as zip
556
+ */
557
+ async downloadRepository(userId, fullName, ref) {
558
+ const accessToken = await this.getAccessToken(userId);
559
+ if (!accessToken) {
560
+ throw new Error('GitHub not connected');
561
+ }
562
+ const url = `https://api.github.com/repos/${fullName}/zipball/${ref || 'HEAD'}`;
563
+ const response = await fetch(url, {
564
+ headers: {
565
+ 'Authorization': `Bearer ${accessToken}`,
566
+ 'Accept': 'application/vnd.github.v3+json',
567
+ 'User-Agent': 'ArchiCore'
568
+ }
569
+ });
570
+ if (!response.ok) {
571
+ throw new Error('Failed to download repository');
572
+ }
573
+ const arrayBuffer = await response.arrayBuffer();
574
+ return Buffer.from(arrayBuffer);
575
+ }
576
+ // ===== UTILITY =====
577
+ /**
578
+ * Check if GitHub is configured
579
+ */
580
+ isConfigured() {
581
+ return !!(this.clientId && this.clientSecret);
582
+ }
583
+ /**
584
+ * Update repository status
585
+ */
586
+ async updateRepositoryStatus(repoId, status, error) {
587
+ await this.ensureInitialized();
588
+ const repo = this.repositories.find(r => r.id === repoId);
589
+ if (repo) {
590
+ repo.status = status;
591
+ repo.error = error;
592
+ repo.updatedAt = new Date();
593
+ await this.saveRepositories();
594
+ }
595
+ }
596
+ /**
597
+ * Update last analyzed timestamp
598
+ */
599
+ async updateLastAnalyzed(repoId) {
600
+ await this.ensureInitialized();
601
+ const repo = this.repositories.find(r => r.id === repoId);
602
+ if (repo) {
603
+ repo.lastAnalyzedAt = new Date();
604
+ repo.updatedAt = new Date();
605
+ await this.saveRepositories();
606
+ }
607
+ }
608
+ }
609
+ //# sourceMappingURL=github-service.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Change Impact Engine
3
+ *
4
+ * Анализ влияния изменений на кодовую базу:
5
+ * - Определение затронутых компонентов
6
+ * - Оценка рисков
7
+ * - Генерация рекомендаций
8
+ * - Построение графа влияния
9
+ */
10
+ import { ChangeImpact, Change, DependencyGraph, Symbol, ArchitectureModel } from '../types/index.js';
11
+ export declare class ImpactEngine {
12
+ analyzeChange(change: Change, graph: DependencyGraph, symbols: Map<string, Symbol>, architecture?: ArchitectureModel): ChangeImpact;
13
+ private findAffectedNodes;
14
+ private getChangedNodes;
15
+ private traverseDependents;
16
+ private findDependents;
17
+ private calculateImpactLevel;
18
+ private generateImpactReason;
19
+ private assessRisks;
20
+ private generateRecommendations;
21
+ private buildImpactGraph;
22
+ generateReport(impact: ChangeImpact): string;
23
+ private groupByImpactLevel;
24
+ }
25
+ //# sourceMappingURL=index.d.ts.map