archicore 0.1.8 → 0.2.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 (42) hide show
  1. package/dist/cli/commands/auth.d.ts +4 -0
  2. package/dist/cli/commands/auth.js +58 -0
  3. package/dist/cli/commands/index.d.ts +2 -1
  4. package/dist/cli/commands/index.js +2 -1
  5. package/dist/cli/commands/interactive.js +107 -59
  6. package/dist/cli/ui/index.d.ts +1 -0
  7. package/dist/cli/ui/index.js +1 -0
  8. package/dist/cli/ui/progress.d.ts +57 -0
  9. package/dist/cli/ui/progress.js +149 -0
  10. package/dist/cli/utils/error-handler.d.ts +40 -0
  11. package/dist/cli/utils/error-handler.js +234 -0
  12. package/dist/cli/utils/index.d.ts +2 -0
  13. package/dist/cli/utils/index.js +3 -0
  14. package/dist/cli/utils/project-selector.d.ts +33 -0
  15. package/dist/cli/utils/project-selector.js +148 -0
  16. package/dist/cli.js +3 -1
  17. package/dist/code-index/ast-parser.d.ts +1 -1
  18. package/dist/code-index/ast-parser.js +9 -3
  19. package/dist/code-index/index.d.ts +1 -1
  20. package/dist/code-index/index.js +2 -2
  21. package/dist/github/github-service.js +8 -1
  22. package/dist/orchestrator/index.js +29 -1
  23. package/dist/semantic-memory/embedding-service.d.ts +4 -1
  24. package/dist/semantic-memory/embedding-service.js +58 -11
  25. package/dist/semantic-memory/index.d.ts +1 -1
  26. package/dist/semantic-memory/index.js +54 -7
  27. package/dist/server/config.d.ts +52 -0
  28. package/dist/server/config.js +88 -0
  29. package/dist/server/index.d.ts +3 -0
  30. package/dist/server/index.js +115 -10
  31. package/dist/server/routes/admin.js +3 -3
  32. package/dist/server/routes/api.js +199 -2
  33. package/dist/server/routes/auth.js +79 -5
  34. package/dist/server/routes/device-auth.js +1 -1
  35. package/dist/server/routes/github.js +33 -0
  36. package/dist/server/services/auth-service.d.ts +5 -0
  37. package/dist/server/services/auth-service.js +10 -0
  38. package/dist/server/services/project-service.d.ts +15 -1
  39. package/dist/server/services/project-service.js +185 -26
  40. package/dist/types/user.d.ts +2 -2
  41. package/dist/types/user.js +13 -4
  42. package/package.json +9 -1
@@ -5,7 +5,7 @@ import { Router } from 'express';
5
5
  import { AuthService } from '../services/auth-service.js';
6
6
  import { Logger } from '../../utils/logger.js';
7
7
  export const authRouter = Router();
8
- const authService = new AuthService();
8
+ const authService = AuthService.getInstance();
9
9
  // Middleware to check authentication
10
10
  export async function authMiddleware(req, res, next) {
11
11
  const authHeader = req.headers.authorization;
@@ -91,7 +91,7 @@ authRouter.post('/logout', authMiddleware, async (req, res) => {
91
91
  });
92
92
  /**
93
93
  * GET /api/auth/me
94
- * Get current user info
94
+ * Get current user info with usage and limits
95
95
  */
96
96
  authRouter.get('/me', authMiddleware, async (req, res) => {
97
97
  try {
@@ -99,9 +99,27 @@ authRouter.get('/me', authMiddleware, async (req, res) => {
99
99
  res.status(401).json({ error: 'Not authenticated' });
100
100
  return;
101
101
  }
102
+ // Get fresh user data with usage
103
+ const freshUser = await authService.getUser(req.user.id);
104
+ if (!freshUser) {
105
+ res.status(404).json({ error: 'User not found' });
106
+ return;
107
+ }
102
108
  // Remove sensitive data
103
- const { passwordHash, ...user } = req.user;
104
- res.json({ user });
109
+ const { passwordHash, ...user } = freshUser;
110
+ // Get tier limits
111
+ const { TIER_LIMITS } = await import('../../types/user.js');
112
+ const limits = TIER_LIMITS[user.tier];
113
+ res.json({
114
+ user,
115
+ limits: {
116
+ requestsPerDay: limits.requestsPerDay,
117
+ fullAnalysisPerDay: limits.fullAnalysisPerDay,
118
+ projectsPerDay: limits.projectsPerDay,
119
+ maxProjectSizeMB: limits.maxProjectSizeMB
120
+ },
121
+ usage: user.usage
122
+ });
105
123
  }
106
124
  catch (error) {
107
125
  Logger.error('Get user error:', error);
@@ -138,6 +156,53 @@ authRouter.put('/me', authMiddleware, async (req, res) => {
138
156
  res.status(500).json({ error: 'Failed to update profile' });
139
157
  }
140
158
  });
159
+ /**
160
+ * POST /api/auth/avatar
161
+ * Upload user avatar (base64 image)
162
+ */
163
+ authRouter.post('/avatar', authMiddleware, async (req, res) => {
164
+ try {
165
+ const { avatar } = req.body;
166
+ if (!req.user) {
167
+ res.status(401).json({ error: 'Not authenticated' });
168
+ return;
169
+ }
170
+ // Validate avatar - must be a valid base64 image data URL
171
+ if (!avatar || typeof avatar !== 'string') {
172
+ res.status(400).json({ error: 'Avatar is required' });
173
+ return;
174
+ }
175
+ // Security: Only allow specific image formats
176
+ const validImagePattern = /^data:image\/(png|jpeg|jpg|gif|webp);base64,/;
177
+ if (!validImagePattern.test(avatar)) {
178
+ res.status(400).json({ error: 'Invalid image format. Only PNG, JPEG, GIF, and WebP are allowed.' });
179
+ return;
180
+ }
181
+ // Limit avatar size (max 500KB base64 = ~375KB actual image)
182
+ const maxBase64Size = 500 * 1024;
183
+ if (avatar.length > maxBase64Size) {
184
+ res.status(400).json({ error: 'Avatar image is too large. Maximum size is 500KB.' });
185
+ return;
186
+ }
187
+ // Additional security: Check for script injection in base64
188
+ const base64Data = avatar.split(',')[1];
189
+ if (!base64Data || /<script|javascript:|onerror|onload/i.test(base64Data)) {
190
+ res.status(400).json({ error: 'Invalid image data' });
191
+ return;
192
+ }
193
+ // Update user avatar
194
+ const updated = await authService.updateUser(req.user.id, { avatar });
195
+ if (!updated) {
196
+ res.status(400).json({ error: 'Failed to update avatar' });
197
+ return;
198
+ }
199
+ res.json({ success: true, avatar });
200
+ }
201
+ catch (error) {
202
+ Logger.error('Avatar upload error:', error);
203
+ res.status(500).json({ error: 'Failed to upload avatar' });
204
+ }
205
+ });
141
206
  /**
142
207
  * POST /api/auth/oauth/:provider
143
208
  * OAuth login (GitHub, Google)
@@ -164,7 +229,7 @@ authRouter.post('/oauth/:provider', async (req, res) => {
164
229
  });
165
230
  /**
166
231
  * GET /api/auth/usage
167
- * Get current user's usage stats
232
+ * Get current user's usage stats with limits
168
233
  */
169
234
  authRouter.get('/usage', authMiddleware, async (req, res) => {
170
235
  try {
@@ -177,9 +242,18 @@ authRouter.get('/usage', authMiddleware, async (req, res) => {
177
242
  res.status(404).json({ error: 'User not found' });
178
243
  return;
179
244
  }
245
+ // Get tier limits
246
+ const { TIER_LIMITS } = await import('../../types/user.js');
247
+ const limits = TIER_LIMITS[user.tier];
180
248
  res.json({
181
249
  tier: user.tier,
182
250
  usage: user.usage,
251
+ limits: {
252
+ requestsPerDay: limits.requestsPerDay,
253
+ fullAnalysisPerDay: limits.fullAnalysisPerDay,
254
+ projectsPerDay: limits.projectsPerDay,
255
+ maxProjectSizeMB: limits.maxProjectSizeMB
256
+ },
183
257
  subscription: user.subscription
184
258
  });
185
259
  }
@@ -7,7 +7,7 @@ import { Router } from 'express';
7
7
  import { randomBytes } from 'crypto';
8
8
  import { AuthService } from '../services/auth-service.js';
9
9
  const router = Router();
10
- const authService = new AuthService();
10
+ const authService = AuthService.getInstance();
11
11
  const pendingAuths = new Map();
12
12
  // Generate random codes
13
13
  function generateDeviceCode() {
@@ -10,12 +10,15 @@ import { join } from 'path';
10
10
  import AdmZip from 'adm-zip';
11
11
  import { GitHubService } from '../../github/github-service.js';
12
12
  import { ProjectService } from '../services/project-service.js';
13
+ import { AuthService } from '../services/auth-service.js';
13
14
  import { authMiddleware } from './auth.js';
14
15
  import { Logger } from '../../utils/logger.js';
16
+ import { TIER_LIMITS } from '../../types/user.js';
15
17
  export const githubRouter = Router();
16
18
  // Services
17
19
  const githubService = new GitHubService();
18
20
  const projectService = new ProjectService();
21
+ const authService = AuthService.getInstance();
19
22
  // Store OAuth states (in production, use Redis)
20
23
  const oauthStates = new Map();
21
24
  // ===== OAUTH FLOW =====
@@ -76,8 +79,12 @@ githubRouter.get('/callback', async (req, res) => {
76
79
  oauthStates.delete(state);
77
80
  // Exchange code for token
78
81
  const tokenResponse = await githubService.exchangeCodeForToken(code);
82
+ // Log granted scopes for debugging
83
+ Logger.info(`GitHub OAuth scopes granted: ${tokenResponse.scope}`);
84
+ Logger.info(`GitHub token type: ${tokenResponse.token_type}`);
79
85
  // Get GitHub user info
80
86
  const githubUser = await githubService.getGitHubUser(tokenResponse.access_token);
87
+ Logger.info(`GitHub user connected: ${githubUser.login}`);
81
88
  // Save connection
82
89
  await githubService.saveConnection(stateData.userId, tokenResponse, githubUser);
83
90
  // Redirect to app with success
@@ -268,6 +275,22 @@ githubRouter.post('/repositories/connect', authMiddleware, async (req, res) => {
268
275
  Logger.progress(`Downloading repository: ${connectedRepo.fullName} (branch: ${targetBranch})`);
269
276
  const zipBuffer = await githubService.downloadRepository(req.user.id, connectedRepo.fullName, targetBranch);
270
277
  Logger.info(`Downloaded ZIP: ${zipBuffer.length} bytes`);
278
+ // Check project size limit
279
+ const user = await authService.getUser(req.user.id);
280
+ if (user) {
281
+ const limits = TIER_LIMITS[user.tier];
282
+ const projectSizeMB = zipBuffer.length / (1024 * 1024);
283
+ if (projectSizeMB > limits.maxProjectSizeMB) {
284
+ res.status(413).json({
285
+ error: 'Project size limit exceeded',
286
+ message: `Project size (${projectSizeMB.toFixed(1)}MB) exceeds your plan limit (${limits.maxProjectSizeMB}MB). Upgrade to a higher tier for larger projects.`,
287
+ size: projectSizeMB,
288
+ limit: limits.maxProjectSizeMB,
289
+ tier: user.tier
290
+ });
291
+ return;
292
+ }
293
+ }
271
294
  // Create projects directory
272
295
  const projectsDir = process.env.PROJECTS_DIR || join('.archicore', 'projects');
273
296
  await mkdir(projectsDir, { recursive: true });
@@ -322,6 +345,16 @@ githubRouter.post('/repositories/connect', authMiddleware, async (req, res) => {
322
345
  }
323
346
  }
324
347
  Logger.success(`Downloaded and extracted to: ${actualPath}`);
348
+ // Check project creation limit
349
+ const usageResult = await authService.checkAndUpdateUsage(req.user.id, 'project');
350
+ if (!usageResult.allowed) {
351
+ res.status(429).json({
352
+ error: 'Project limit reached',
353
+ message: `You have reached your daily project limit (${usageResult.limit})`,
354
+ usage: { used: usageResult.limit, limit: usageResult.limit, remaining: 0 }
355
+ });
356
+ return;
357
+ }
325
358
  const project = await projectService.createProject(projectName || connectedRepo.name, actualPath, req.user.id);
326
359
  projectId = project.id;
327
360
  // Update connected repo with project ID and save to file
@@ -3,11 +3,16 @@
3
3
  */
4
4
  import { User, SubscriptionTier, AuthResponse } from '../../types/user.js';
5
5
  export declare class AuthService {
6
+ private static instance;
6
7
  private dataDir;
7
8
  private users;
8
9
  private sessions;
9
10
  private initialized;
10
11
  constructor(dataDir?: string);
12
+ /**
13
+ * Get singleton instance of AuthService
14
+ */
15
+ static getInstance(): AuthService;
11
16
  private ensureInitialized;
12
17
  private createDefaultAdmin;
13
18
  private saveUsers;
@@ -10,6 +10,7 @@ const DATA_DIR = '.archicore';
10
10
  const USERS_FILE = 'users.json';
11
11
  const SESSIONS_FILE = 'sessions.json';
12
12
  export class AuthService {
13
+ static instance = null;
13
14
  dataDir;
14
15
  users = [];
15
16
  sessions = [];
@@ -17,6 +18,15 @@ export class AuthService {
17
18
  constructor(dataDir = DATA_DIR) {
18
19
  this.dataDir = dataDir;
19
20
  }
21
+ /**
22
+ * Get singleton instance of AuthService
23
+ */
24
+ static getInstance() {
25
+ if (!AuthService.instance) {
26
+ AuthService.instance = new AuthService();
27
+ }
28
+ return AuthService.instance;
29
+ }
20
30
  async ensureInitialized() {
21
31
  if (this.initialized)
22
32
  return;
@@ -28,6 +28,20 @@ export interface ProjectStats {
28
28
  nodesCount: number;
29
29
  edgesCount: number;
30
30
  }
31
+ /**
32
+ * Progress callback type for indexing
33
+ */
34
+ export interface IndexingProgress {
35
+ step: 'scanning' | 'parsing' | 'symbols' | 'graph' | 'embeddings' | 'complete' | 'error';
36
+ stepNumber: number;
37
+ totalSteps: number;
38
+ current: number;
39
+ total: number;
40
+ message: string;
41
+ percentage: number;
42
+ }
43
+ export type ProgressCallback = (progress: IndexingProgress) => void;
44
+ export declare function subscribeToProgress(projectId: string, callback: ProgressCallback): () => void;
31
45
  export declare class ProjectService {
32
46
  private projects;
33
47
  private projectData;
@@ -159,7 +173,7 @@ export declare class ProjectService {
159
173
  */
160
174
  private getFileContents;
161
175
  /**
162
- * Генерация документации по коду проекта
176
+ * Генерация документации по коду проекта с использованием AI
163
177
  */
164
178
  generateDocumentation(projectId: string, options?: {
165
179
  format?: string;
@@ -22,6 +22,66 @@ import { DuplicationDetector } from '../../analyzers/duplication.js';
22
22
  import { SecurityAnalyzer } from '../../analyzers/security.js';
23
23
  import { RefactoringEngine } from '../../refactoring/index.js';
24
24
  import { GitHubService } from '../../github/github-service.js';
25
+ /**
26
+ * Sanitize file paths to hide server directories
27
+ * Converts absolute paths to relative project paths
28
+ */
29
+ function sanitizePath(filePath) {
30
+ if (!filePath)
31
+ return filePath;
32
+ // Common server path patterns to strip
33
+ const serverPatterns = [
34
+ /^\/scripts\/archicore\/.archicore\/projects\/[^/]+\/[^/]+\//,
35
+ /^\/scripts\/archicore\/.archicore\/projects\/[^/]+\//,
36
+ /^\/home\/[^/]+\/[^/]+\/.archicore\/projects\/[^/]+\//,
37
+ /^C:\\[^\\]+\\[^\\]+\\.archicore\\projects\\[^\\]+\\/i,
38
+ /^\/var\/[^/]+\/.archicore\/projects\/[^/]+\//,
39
+ /^.*\.archicore[\/\\]projects[\/\\][^\/\\]+[\/\\][^\/\\]+[\/\\]/,
40
+ /^.*\.archicore[\/\\]projects[\/\\][^\/\\]+[\/\\]/
41
+ ];
42
+ for (const pattern of serverPatterns) {
43
+ if (pattern.test(filePath)) {
44
+ return filePath.replace(pattern, '');
45
+ }
46
+ }
47
+ // If path contains .archicore/projects, strip everything before the actual project path
48
+ const archiMatch = filePath.match(/\.archicore[\/\\]projects[\/\\][^\/\\]+[\/\\](?:[^\/\\]+[\/\\])?(.+)/);
49
+ if (archiMatch) {
50
+ return archiMatch[1];
51
+ }
52
+ return filePath;
53
+ }
54
+ // Store active progress callbacks for SSE
55
+ const progressCallbacks = new Map();
56
+ export function subscribeToProgress(projectId, callback) {
57
+ if (!progressCallbacks.has(projectId)) {
58
+ progressCallbacks.set(projectId, []);
59
+ }
60
+ progressCallbacks.get(projectId).push(callback);
61
+ // Return unsubscribe function
62
+ return () => {
63
+ const callbacks = progressCallbacks.get(projectId);
64
+ if (callbacks) {
65
+ const index = callbacks.indexOf(callback);
66
+ if (index > -1) {
67
+ callbacks.splice(index, 1);
68
+ }
69
+ }
70
+ };
71
+ }
72
+ function emitProgress(projectId, progress) {
73
+ const callbacks = progressCallbacks.get(projectId);
74
+ if (callbacks) {
75
+ for (const cb of callbacks) {
76
+ try {
77
+ cb(progress);
78
+ }
79
+ catch (err) {
80
+ // Ignore callback errors
81
+ }
82
+ }
83
+ }
84
+ }
25
85
  // Singleton instance
26
86
  let instance = null;
27
87
  export class ProjectService {
@@ -173,24 +233,54 @@ export class ProjectService {
173
233
  }
174
234
  project.status = 'indexing';
175
235
  await this.saveProjects();
236
+ const totalSteps = 5;
237
+ const sendProgress = (step, stepNumber, current, total, message) => {
238
+ const basePercentage = ((stepNumber - 1) / totalSteps) * 100;
239
+ const stepPercentage = total > 0 ? (current / total) * (100 / totalSteps) : 0;
240
+ const percentage = Math.min(100, Math.round(basePercentage + stepPercentage));
241
+ emitProgress(projectId, {
242
+ step,
243
+ stepNumber,
244
+ totalSteps,
245
+ current,
246
+ total,
247
+ message,
248
+ percentage
249
+ });
250
+ };
176
251
  try {
177
252
  const data = await this.getProjectData(projectId);
178
253
  Logger.progress(`Indexing project: ${project.name}`);
254
+ sendProgress('scanning', 1, 0, 1, 'Scanning project files...');
179
255
  // 1. Парсинг AST
180
- const asts = await data.codeIndex.parseProject();
256
+ sendProgress('parsing', 1, 0, 1, 'Parsing source files...');
257
+ const asts = await data.codeIndex.parseProject((current, total, file) => {
258
+ sendProgress('parsing', 1, current, total, `Parsing: ${file.split(/[/\\]/).pop()}`);
259
+ });
181
260
  data.asts = asts;
182
261
  // 2. Извлечение символов
262
+ sendProgress('symbols', 2, 0, 1, 'Extracting symbols...');
183
263
  const symbols = data.codeIndex.extractSymbols(asts);
184
264
  data.symbols = symbols;
265
+ sendProgress('symbols', 2, 1, 1, `Extracted ${symbols.size} symbols`);
185
266
  // 3. Построение графа зависимостей
267
+ sendProgress('graph', 3, 0, 1, 'Building dependency graph...');
186
268
  const graph = data.codeIndex.buildDependencyGraph(asts, symbols);
187
269
  data.graph = graph;
270
+ sendProgress('graph', 3, 1, 1, `Built graph: ${graph.nodes.size} nodes`);
188
271
  // 4. Индексация в семантическую память (если доступна)
189
272
  if (data.semanticMemory) {
273
+ sendProgress('embeddings', 4, 0, symbols.size, 'Initializing vector store...');
190
274
  await data.semanticMemory.initialize();
191
- await data.semanticMemory.indexSymbols(symbols, asts);
275
+ await data.semanticMemory.indexSymbols(symbols, asts, (current, total) => {
276
+ sendProgress('embeddings', 4, current, total, `Generating embeddings: ${current}/${total}`);
277
+ });
278
+ }
279
+ else {
280
+ sendProgress('embeddings', 4, 1, 1, 'Skipping embeddings (no API key)');
192
281
  }
193
282
  // 5. Загрузка архитектурных знаний
283
+ sendProgress('complete', 5, 0, 1, 'Loading architecture...');
194
284
  await data.architecture.load();
195
285
  // Обновляем статус и статистику
196
286
  const stats = {
@@ -203,12 +293,22 @@ export class ProjectService {
203
293
  project.lastIndexedAt = new Date().toISOString();
204
294
  project.stats = stats;
205
295
  await this.saveProjects();
296
+ sendProgress('complete', 5, 1, 1, 'Indexing complete!');
206
297
  Logger.success(`Project indexed: ${stats.filesCount} files, ${stats.symbolsCount} symbols`);
207
298
  return { success: true, stats };
208
299
  }
209
300
  catch (error) {
210
301
  project.status = 'error';
211
302
  await this.saveProjects();
303
+ emitProgress(projectId, {
304
+ step: 'error',
305
+ stepNumber: 0,
306
+ totalSteps,
307
+ current: 0,
308
+ total: 0,
309
+ message: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
310
+ percentage: 0
311
+ });
212
312
  Logger.error('Indexing failed:', error);
213
313
  throw error;
214
314
  }
@@ -758,7 +858,7 @@ export class ProjectService {
758
858
  return fileContents;
759
859
  }
760
860
  /**
761
- * Генерация документации по коду проекта
861
+ * Генерация документации по коду проекта с использованием AI
762
862
  */
763
863
  async generateDocumentation(projectId, options = {}) {
764
864
  const project = this.projects.get(projectId);
@@ -773,24 +873,91 @@ export class ProjectService {
773
873
  // Собираем информацию о проекте
774
874
  const stats = project.stats;
775
875
  const symbols = data.symbols ? Array.from(data.symbols.values()) : [];
776
- // graph is available via data.graph if needed for more detailed docs
777
876
  // Группируем символы по типу (kind)
778
877
  const classes = symbols.filter(s => s.kind === 'class');
779
878
  const functions = symbols.filter(s => s.kind === 'function');
780
879
  const interfaces = symbols.filter(s => s.kind === 'interface');
781
- // Формируем структуру файлов
782
- const files = fileContents ? Array.from(fileContents.keys()) : [];
880
+ // Формируем структуру файлов (с очисткой путей)
881
+ const files = fileContents ? Array.from(fileContents.keys()).map(sanitizePath) : [];
783
882
  const fileStructure = this.buildFileStructure(files);
883
+ // Собираем код для анализа AI (первые 10 важных файлов)
884
+ const codeSnippets = [];
885
+ if (fileContents) {
886
+ const importantFiles = Array.from(fileContents.entries())
887
+ .filter(([path]) => !path.includes('node_modules') && !path.includes('.git'))
888
+ .slice(0, 10);
889
+ for (const [filePath, content] of importantFiles) {
890
+ const cleanPath = sanitizePath(filePath);
891
+ const truncatedContent = content.substring(0, 2000);
892
+ codeSnippets.push(`### ${cleanPath}\n\`\`\`\n${truncatedContent}\n\`\`\``);
893
+ }
894
+ }
895
+ // Используем AI для генерации подробной документации
896
+ const orchestrator = data.orchestrator;
897
+ let aiDocumentation = '';
898
+ if (orchestrator.isAvailable()) {
899
+ const langInstruction = language === 'ru'
900
+ ? 'ВАЖНО: Отвечай ТОЛЬКО на русском языке!'
901
+ : 'Respond in English.';
902
+ const prompt = {
903
+ system: `You are a technical documentation writer. Generate comprehensive project documentation.
904
+ ${langInstruction}
905
+
906
+ The documentation should include:
907
+ 1. Project Overview - what the project does, its purpose
908
+ 2. Architecture Description - how components are organized
909
+ 3. Key Components - main classes and their responsibilities
910
+ 4. Main Functions - important functions and what they do
911
+ 5. Data Flow - how data moves through the system
912
+ 6. Usage Examples - how to use the main features
913
+
914
+ Be specific and detailed. Analyze the actual code provided to understand the project's purpose.`,
915
+ user: `Generate detailed documentation for project "${project.name}".
916
+
917
+ Project Statistics:
918
+ - Files: ${stats?.filesCount || files.length}
919
+ - Symbols: ${stats?.symbolsCount || symbols.length}
920
+ - Classes: ${classes.length}
921
+ - Functions: ${functions.length}
922
+ - Interfaces: ${interfaces.length}
923
+
924
+ File Structure:
925
+ ${fileStructure}
926
+
927
+ Classes: ${classes.slice(0, 15).map(c => `${c.name} (${sanitizePath(c.filePath)})`).join(', ') || 'None'}
928
+
929
+ Key Functions: ${functions.slice(0, 20).map(f => f.name).join(', ') || 'None'}
930
+
931
+ Interfaces: ${interfaces.slice(0, 10).map(i => i.name).join(', ') || 'None'}
932
+
933
+ Code Samples:
934
+ ${codeSnippets.slice(0, 5).join('\n\n')}
935
+
936
+ Generate comprehensive markdown documentation based on this analysis.`
937
+ };
938
+ try {
939
+ const response = await orchestrator.query(prompt);
940
+ aiDocumentation = response.content;
941
+ }
942
+ catch (err) {
943
+ Logger.warn('AI documentation generation failed, using basic template');
944
+ }
945
+ }
784
946
  // Генерируем документацию
785
947
  let documentation = '';
786
948
  if (format === 'markdown') {
787
949
  const title = language === 'ru' ? 'Документация проекта' : 'Project Documentation';
788
- const overview = language === 'ru' ? 'Обзор' : 'Overview';
789
- const structure = language === 'ru' ? 'Структура проекта' : 'Project Structure';
790
- const classesTitle = language === 'ru' ? 'Классы' : 'Classes';
791
- const functionsTitle = language === 'ru' ? 'Функции' : 'Functions';
792
- const interfacesTitle = language === 'ru' ? 'Интерфейсы' : 'Interfaces';
793
- documentation = `# ${title}: ${project.name}
950
+ if (aiDocumentation) {
951
+ // Используем AI-сгенерированную документацию
952
+ documentation = `# ${title}: ${project.name}\n\n${aiDocumentation}\n\n---\n*Generated by ArchiCore AI*`;
953
+ }
954
+ else {
955
+ // Fallback на базовый шаблон
956
+ const overview = language === 'ru' ? 'Обзор' : 'Overview';
957
+ const structure = language === 'ru' ? 'Структура проекта' : 'Project Structure';
958
+ const classesTitle = language === 'ru' ? 'Классы' : 'Classes';
959
+ const functionsTitle = language === 'ru' ? 'Функции' : 'Functions';
960
+ documentation = `# ${title}: ${project.name}
794
961
 
795
962
  ## ${overview}
796
963
 
@@ -810,37 +977,29 @@ ${classes.length > 0 ? `## ${classesTitle}
810
977
 
811
978
  ${classes.slice(0, 20).map(c => `### ${c.name}
812
979
 
813
- - **File:** \`${c.filePath}\`
980
+ - **File:** \`${sanitizePath(c.filePath)}\`
814
981
  - **Line:** ${c.location?.startLine || 'N/A'}
815
982
  `).join('\n')}
816
983
  ` : ''}
817
984
 
818
- ${interfaces.length > 0 ? `## ${interfacesTitle}
819
-
820
- ${interfaces.slice(0, 20).map(i => `### ${i.name}
821
-
822
- - **File:** \`${i.filePath}\`
823
- - **Line:** ${i.location?.startLine || 'N/A'}
824
- `).join('\n')}
825
- ` : ''}
826
-
827
985
  ${functions.length > 0 ? `## ${functionsTitle}
828
986
 
829
- ${functions.slice(0, 30).map(f => `- \`${f.name}\` - ${f.filePath}:${f.location?.startLine || 'N/A'}`).join('\n')}
987
+ ${functions.slice(0, 30).map(f => `- \`${f.name}\` - ${sanitizePath(f.filePath)}:${f.location?.startLine || 'N/A'}`).join('\n')}
830
988
  ` : ''}
831
989
 
832
990
  ---
833
991
  *Generated by ArchiCore*
834
992
  `;
993
+ }
835
994
  }
836
995
  else {
837
996
  // JSON format
838
997
  documentation = JSON.stringify({
839
998
  project: project.name,
840
999
  stats,
841
- classes: classes.map(c => ({ name: c.name, file: c.filePath, line: c.location?.startLine })),
842
- functions: functions.map(f => ({ name: f.name, file: f.filePath, line: f.location?.startLine })),
843
- interfaces: interfaces.map(i => ({ name: i.name, file: i.filePath, line: i.location?.startLine })),
1000
+ classes: classes.map(c => ({ name: c.name, file: sanitizePath(c.filePath), line: c.location?.startLine })),
1001
+ functions: functions.map(f => ({ name: f.name, file: sanitizePath(f.filePath), line: f.location?.startLine })),
1002
+ interfaces: interfaces.map(i => ({ name: i.name, file: sanitizePath(i.filePath), line: i.location?.startLine })),
844
1003
  files
845
1004
  }, null, 2);
846
1005
  }
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * User and Subscription Types for ArchiCore
3
3
  */
4
- export type SubscriptionTier = 'free' | 'mid' | 'pro' | 'admin';
4
+ export type SubscriptionTier = 'free' | 'mid' | 'pro' | 'enterprise' | 'admin';
5
5
  export interface TierLimits {
6
6
  requestsPerDay: number;
7
7
  fullAnalysisPerDay: number;
8
8
  projectsPerDay: number;
9
- maxFileSizeMB: number;
9
+ maxProjectSizeMB: number;
10
10
  retentionDays: number;
11
11
  price: number;
12
12
  priceYearly: number;
@@ -6,7 +6,7 @@ export const TIER_LIMITS = {
6
6
  requestsPerDay: 10,
7
7
  fullAnalysisPerDay: 1,
8
8
  projectsPerDay: 3,
9
- maxFileSizeMB: 10,
9
+ maxProjectSizeMB: 30,
10
10
  retentionDays: 7,
11
11
  price: 0,
12
12
  priceYearly: 0
@@ -15,7 +15,7 @@ export const TIER_LIMITS = {
15
15
  requestsPerDay: 50,
16
16
  fullAnalysisPerDay: 5,
17
17
  projectsPerDay: 10,
18
- maxFileSizeMB: 30,
18
+ maxProjectSizeMB: 50,
19
19
  retentionDays: 14,
20
20
  price: 39,
21
21
  priceYearly: 390 // ~2 months free
@@ -24,16 +24,25 @@ export const TIER_LIMITS = {
24
24
  requestsPerDay: 150,
25
25
  fullAnalysisPerDay: 15,
26
26
  projectsPerDay: 20,
27
- maxFileSizeMB: 60,
27
+ maxProjectSizeMB: 80,
28
28
  retentionDays: 30,
29
29
  price: 99,
30
30
  priceYearly: 990 // ~2 months free
31
31
  },
32
+ enterprise: {
33
+ requestsPerDay: Infinity,
34
+ fullAnalysisPerDay: Infinity,
35
+ projectsPerDay: Infinity,
36
+ maxProjectSizeMB: 500,
37
+ retentionDays: 365,
38
+ price: -1, // Custom pricing
39
+ priceYearly: -1
40
+ },
32
41
  admin: {
33
42
  requestsPerDay: Infinity,
34
43
  fullAnalysisPerDay: Infinity,
35
44
  projectsPerDay: Infinity,
36
- maxFileSizeMB: 500,
45
+ maxProjectSizeMB: 1000,
37
46
  retentionDays: 365,
38
47
  price: 0,
39
48
  priceYearly: 0