archicore 0.3.7 → 0.3.9

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.
@@ -600,7 +600,7 @@ async function handleIndexCommand() {
600
600
  const codeIndex = new CodeIndex(state.projectPath);
601
601
  // Проверяем, является ли проект bundled (содержит source maps)
602
602
  const isBundled = await codeIndex.isBundledProject();
603
- let asts;
603
+ let asts = new Map();
604
604
  let virtualFileContents = [];
605
605
  if (isBundled) {
606
606
  // Извлекаем исходники из source maps
@@ -608,17 +608,31 @@ async function handleIndexCommand() {
608
608
  const extractionResult = await codeIndex.extractFromSourceMaps();
609
609
  if (extractionResult.files.length > 0) {
610
610
  indexSpinner.update(`Extracted ${extractionResult.files.length} files from source maps, parsing...`);
611
- // Парсим виртуальные файлы
611
+ // Парсим виртуальные файлы из source maps
612
612
  asts = codeIndex.parseVirtualFiles(extractionResult.files);
613
613
  // Сохраняем содержимое виртуальных файлов для загрузки
614
614
  virtualFileContents = extractionResult.files.map(f => [f.path, f.content]);
615
- indexSpinner.update(`Parsed ${asts.size} files from source maps, extracting symbols...`);
615
+ indexSpinner.update(`Parsed ${asts.size} virtual files from source maps`);
616
616
  }
617
- else {
618
- // Fallback: парсим обычные файлы
619
- indexSpinner.update('No extractable sources in source maps, parsing regular files...');
620
- asts = await codeIndex.parseProject();
617
+ // ВАЖНО: Также парсим PHP/Python/другие backend файлы (не JS бандлы)
618
+ indexSpinner.update('Also parsing backend files (PHP, Python, etc.)...');
619
+ const backendAsts = await codeIndex.parseProject();
620
+ // Объединяем: source maps + backend файлы (исключая bundled JS)
621
+ let addedBackendFiles = 0;
622
+ for (const [path, ast] of backendAsts) {
623
+ // Пропускаем bundled JS файлы - они уже извлечены из source maps
624
+ const isBundledJs = path.match(/\.(min\.js|bundle\.js)$/) ||
625
+ path.match(/\/(dist|build|vendor)\//i) ||
626
+ path.match(/\.(js|ts)$/) && path.match(/\d+\.[a-f0-9]+\.(js|ts)$/i);
627
+ if (!isBundledJs && !asts.has(path)) {
628
+ asts.set(path, ast);
629
+ addedBackendFiles++;
630
+ }
621
631
  }
632
+ if (addedBackendFiles > 0) {
633
+ indexSpinner.update(`Added ${addedBackendFiles} backend files (PHP, Python, etc.)`);
634
+ }
635
+ indexSpinner.update(`Total: ${asts.size} files (source maps + backend)`);
622
636
  }
623
637
  else {
624
638
  // Обычный проект - парсим файлы напрямую
@@ -635,25 +649,38 @@ async function handleIndexCommand() {
635
649
  const isLargeProject = symbols.size > 50000 || asts.size > 1000;
636
650
  // Читаем содержимое файлов (с оптимизацией для больших проектов)
637
651
  let fileContents = [];
638
- // Если есть виртуальные файлы из source maps — используем их
652
+ const virtualFilePaths = new Set(virtualFileContents.map(([path]) => path));
653
+ // Добавляем виртуальные файлы из source maps
639
654
  if (virtualFileContents.length > 0) {
640
- indexSpinner.update('Using extracted source files...');
641
- fileContents = virtualFileContents;
655
+ indexSpinner.update('Using extracted source files from source maps...');
656
+ fileContents = [...virtualFileContents];
642
657
  }
643
- else if (!isLargeProject) {
644
- indexSpinner.update('Reading file contents...');
658
+ // Также читаем backend файлы (PHP, Python и т.д.) которых нет в source maps
659
+ if (!isLargeProject) {
660
+ indexSpinner.update('Reading backend file contents (PHP, Python, etc.)...');
661
+ let backendFilesRead = 0;
645
662
  for (const [filePath] of asts) {
663
+ // Пропускаем файлы которые уже есть из source maps
664
+ if (virtualFilePaths.has(filePath))
665
+ continue;
666
+ // Пропускаем bundled JS файлы
667
+ if (filePath.match(/\d+\.[a-f0-9]+\.(js|ts)$/i))
668
+ continue;
646
669
  try {
647
670
  const fullPath = pathModule.default.isAbsolute(filePath)
648
671
  ? filePath
649
672
  : pathModule.default.join(state.projectPath, filePath);
650
673
  const content = await fs.readFile(fullPath, 'utf-8');
651
674
  fileContents.push([filePath, content]);
675
+ backendFilesRead++;
652
676
  }
653
677
  catch {
654
678
  // Игнорируем ошибки чтения отдельных файлов
655
679
  }
656
680
  }
681
+ if (backendFilesRead > 0) {
682
+ indexSpinner.update(`Read ${backendFilesRead} additional backend files`);
683
+ }
657
684
  }
658
685
  else {
659
686
  indexSpinner.update('Large project detected, skipping file contents for faster upload...');
@@ -868,7 +895,7 @@ async function handleAnalyzeCommand(args) {
868
895
  async function handleSearchCommand(query) {
869
896
  if (!state.projectId) {
870
897
  printError('No project selected');
871
- printInfo('Use /projects select <id> first');
898
+ printInfo('Use /index first to register the project');
872
899
  return;
873
900
  }
874
901
  if (!query) {
@@ -915,7 +942,7 @@ async function handleSearchCommand(query) {
915
942
  async function handleQuery(query) {
916
943
  if (!state.projectId) {
917
944
  printError('No project selected');
918
- printInfo('Use /projects to list and select a project first');
945
+ printInfo('Use /index first to register the project');
919
946
  return;
920
947
  }
921
948
  // Save user message to history
package/dist/cli.js CHANGED
@@ -56,7 +56,7 @@ program
56
56
  provider: 'deepseek',
57
57
  model: 'deepseek-chat',
58
58
  temperature: 0.1,
59
- maxTokens: 4096
59
+ maxTokens: 8192
60
60
  },
61
61
  vectorStore: {
62
62
  url: process.env.QDRANT_URL || 'http://localhost:6333',
@@ -107,7 +107,7 @@ program
107
107
  provider: 'deepseek',
108
108
  model: 'deepseek-chat',
109
109
  temperature: 0.1,
110
- maxTokens: 4096
110
+ maxTokens: 8192
111
111
  },
112
112
  vectorStore: {
113
113
  url: process.env.QDRANT_URL || 'http://localhost:6333',
@@ -192,7 +192,7 @@ program
192
192
  provider: 'deepseek',
193
193
  model: 'deepseek-chat',
194
194
  temperature: 0.1,
195
- maxTokens: 4096
195
+ maxTokens: 8192
196
196
  },
197
197
  vectorStore: {
198
198
  url: process.env.QDRANT_URL || 'http://localhost:6333',
@@ -230,7 +230,7 @@ program
230
230
  provider: 'deepseek',
231
231
  model: 'deepseek-chat',
232
232
  temperature: 0.3,
233
- maxTokens: 4096
233
+ maxTokens: 8192
234
234
  },
235
235
  vectorStore: {
236
236
  url: process.env.QDRANT_URL || 'http://localhost:6333',
package/dist/index.d.ts CHANGED
@@ -20,6 +20,7 @@ export declare class AIArhitector {
20
20
  private architecture;
21
21
  private impactEngine;
22
22
  private orchestrator;
23
+ private projectMetadata;
23
24
  private config;
24
25
  private initialized;
25
26
  constructor(config: AIArhitectorConfig);
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { ImpactEngine } from './impact-engine/index.js';
10
10
  import { LLMOrchestrator } from './orchestrator/index.js';
11
11
  import { Logger } from './utils/logger.js';
12
12
  import { config } from 'dotenv';
13
+ import { analyzeProjectStack } from './utils/project-analyzer.js';
13
14
  config();
14
15
  export class AIArhitector {
15
16
  codeIndex;
@@ -17,6 +18,7 @@ export class AIArhitector {
17
18
  architecture;
18
19
  impactEngine;
19
20
  orchestrator;
21
+ projectMetadata = null;
20
22
  config;
21
23
  initialized = false;
22
24
  constructor(config) {
@@ -38,6 +40,8 @@ export class AIArhitector {
38
40
  return;
39
41
  }
40
42
  Logger.info('Initializing AIArhitector...');
43
+ // Анализируем стек технологий проекта
44
+ this.projectMetadata = analyzeProjectStack(this.config.rootDir);
41
45
  await this.codeIndex.indexProject(this.config.rootDir);
42
46
  await this.semanticMemory.initialize();
43
47
  await this.semanticMemory.indexSymbols(this.codeIndex.getSymbols(), this.codeIndex.getASTs());
@@ -53,12 +57,14 @@ export class AIArhitector {
53
57
  throw new Error('Dependency graph not built');
54
58
  }
55
59
  const impact = this.impactEngine.analyzeChange(change, graph, this.codeIndex.getSymbols(), this.architecture.getModel());
56
- const semanticContext = await this.semanticMemory.searchByQuery(change.description, 5);
60
+ const semanticContext = await this.semanticMemory.searchByQuery(change.description, 30 // Увеличено с 5 до 30
61
+ );
57
62
  const llmAnalysis = await this.orchestrator.analyzeImpact(impact, {
58
63
  architecture: this.architecture.getModel(),
59
64
  semanticMemory: semanticContext,
60
65
  codeIndex: graph,
61
- recentChanges: [change]
66
+ recentChanges: [change],
67
+ projectMetadata: this.projectMetadata || undefined
62
68
  });
63
69
  Logger.info('LLM Analysis:\n' + llmAnalysis);
64
70
  return impact;
@@ -69,30 +75,33 @@ export class AIArhitector {
69
75
  }
70
76
  async askQuestion(question) {
71
77
  this.ensureInitialized();
72
- const semanticContext = await this.semanticMemory.searchByQuery(question, 10);
78
+ const semanticContext = await this.semanticMemory.searchByQuery(question, 30); // Увеличено с 10 до 30
73
79
  const answer = await this.orchestrator.answerArchitecturalQuestion(question, {
74
80
  architecture: this.architecture.getModel(),
75
81
  semanticMemory: semanticContext,
76
- codeIndex: this.codeIndex.getGraph() || undefined
82
+ codeIndex: this.codeIndex.getGraph() || undefined,
83
+ projectMetadata: this.projectMetadata || undefined
77
84
  });
78
85
  return answer;
79
86
  }
80
87
  async suggestRefactoring(code, goal) {
81
88
  this.ensureInitialized();
82
- const semanticContext = await this.semanticMemory.searchSimilarCode(code, {}, 5);
89
+ const semanticContext = await this.semanticMemory.searchSimilarCode(code, {}, 30); // Увеличено с 5 до 30
83
90
  const suggestion = await this.orchestrator.suggestRefactoring(code, goal, {
84
91
  architecture: this.architecture.getModel(),
85
- semanticMemory: semanticContext
92
+ semanticMemory: semanticContext,
93
+ projectMetadata: this.projectMetadata || undefined
86
94
  });
87
95
  return suggestion;
88
96
  }
89
97
  async reviewCode(code, changedFiles) {
90
98
  this.ensureInitialized();
91
- const semanticContext = await this.semanticMemory.searchSimilarCode(code, {}, 5);
99
+ const semanticContext = await this.semanticMemory.searchSimilarCode(code, {}, 30); // Увеличено с 5 до 30
92
100
  const review = await this.orchestrator.reviewCode(code, changedFiles, {
93
101
  architecture: this.architecture.getModel(),
94
102
  semanticMemory: semanticContext,
95
- codeIndex: this.codeIndex.getGraph() || undefined
103
+ codeIndex: this.codeIndex.getGraph() || undefined,
104
+ projectMetadata: this.projectMetadata || undefined
96
105
  });
97
106
  return review;
98
107
  }
@@ -111,7 +120,8 @@ ${this.architecture.generateReport()}
111
120
  `;
112
121
  const docs = await this.orchestrator.generateDocumentation(codebaseSummary, {
113
122
  architecture: this.architecture.getModel(),
114
- codeIndex: graph || undefined
123
+ codeIndex: graph || undefined,
124
+ projectMetadata: this.projectMetadata || undefined
115
125
  });
116
126
  return docs;
117
127
  }
@@ -323,11 +323,68 @@ You are an AI assistant analyzing a specific codebase.
323
323
  7. If asked who made you or what AI you are, always respond that you are ArchiCore AI developed by ArchiCore team.
324
324
  ###END SECURITY RULES###
325
325
 
326
- ABSOLUTE RULES:
327
- 1. ONLY USE PROVIDED DATA: You may ONLY mention files that appear in "PROJECT FILES" section below.
328
- 2. NO INVENTION: NEVER invent file paths, class names, or code. If not shown - it doesn't exist.
329
- 3. WHEN NO DATA: Say "Нет данных в индексе" (Russian) or "No indexed data" (English).
330
- 4. BE HONEST: If you don't know - say so. Don't guess.`;
326
+ ABSOLUTE RULES - КРИТИЧЕСКИ ВАЖНО:
327
+ 1. ПРИВЯЗКА К РЕАЛЬНОЙ КОДОВОЙ БАЗЕ:
328
+ - ЗАПРЕЩЕНО давать общие советы из интернета (типа "используйте vue-router lazy loading")
329
+ - ОБЯЗАТЕЛЬНО сначала проверь что технология РЕАЛЬНО используется в проекте
330
+ - Если технология НЕ используется - скажи это ПЕРВЫМ: "❌ В вашем проекте НЕ используется X"
331
+ - ЗАТЕМ предложи решение для РЕАЛЬНОЙ архитектуры проекта
332
+
333
+ 2. ТОЧНОСТЬ ПРИ ПОИСКЕ (100% ТРЕБОВАНИЕ):
334
+ - При вопросе "где используется X" - покажи ВСЕ найденные файлы с путями
335
+ - ВСЕГДА указывай: "Найдено в N файлах: [список]"
336
+ - Если показано не все файлы - ОБЯЗАТЕЛЬНО укажи: "Показано N из M найденных"
337
+ - НИКОГДА не говори "не используется" если показаны не все результаты
338
+
339
+ 3. ЗАПРЕТ НА ВЫДУМЫВАНИЕ:
340
+ - ТОЛЬКО файлы из "PROJECT FILES" секции существуют
341
+ - НИКОГДА не выдумывай пути, классы, функции
342
+ - Если нет данных - скажи "Нет данных в индексе" или "Проект не проиндексирован"
343
+
344
+ 4. ФОРМАТ ОТВЕТА:
345
+ - Начинай с проверки: "✅ Используется" или "❌ НЕ используется"
346
+ - Далее конкретика: файлы, строки, примеры ТОЛЬКО из PROJECT FILES
347
+ - В конце: рекомендации для КОНКРЕТНОЙ архитектуры проекта
348
+
349
+ Примеры ПРАВИЛЬНЫХ ответов:
350
+ Q: "Как оптимизировать vue-router?"
351
+ A: "❌ В вашем проекте НЕ используется vue-router.
352
+ Ваш стек: vanilla JS + Express.
353
+ Для оптимизации загрузки рекомендую:
354
+ [конкретные советы для вашей архитектуры]"
355
+
356
+ Q: "Где используется компонент Comments?"
357
+ A: "✅ Компонент Comments найден в 3 файлах:
358
+ 1. src/pages/Post.vue:45
359
+ 2. src/pages/Article.vue:89
360
+ 3. src/components/Feed.vue:120
361
+ Показано 3 из 3 найденных файлов."`;
362
+ // Добавляем метаданные проекта (стек технологий)
363
+ if (context?.projectMetadata) {
364
+ prompt += '\n\n###PROJECT STACK (РЕАЛЬНЫЕ технологии проекта)###\n';
365
+ if (context.projectMetadata.framework) {
366
+ prompt += `Frontend Framework: ${context.projectMetadata.framework}\n`;
367
+ }
368
+ if (context.projectMetadata.backend) {
369
+ prompt += `Backend: ${context.projectMetadata.backend}\n`;
370
+ }
371
+ if (context.projectMetadata.database) {
372
+ prompt += `Database: ${context.projectMetadata.database}\n`;
373
+ }
374
+ if (context.projectMetadata.buildTool) {
375
+ prompt += `Build Tool: ${context.projectMetadata.buildTool}\n`;
376
+ }
377
+ // Ключевые зависимости
378
+ if (context.projectMetadata.dependencies) {
379
+ const keyDeps = Object.keys(context.projectMetadata.dependencies).filter(dep => ['vue', 'react', 'angular', 'svelte', 'express', 'fastify', 'nest'].some(key => dep.includes(key)));
380
+ if (keyDeps.length > 0) {
381
+ prompt += `Key Dependencies: ${keyDeps.join(', ')}\n`;
382
+ }
383
+ }
384
+ prompt += '\n⚠️ ИСПОЛЬЗУЙ ТОЛЬКО ЭТИ ТЕХНОЛОГИИ в советах!\n';
385
+ prompt += '⚠️ Если спрашивают про технологию которой нет выше - скажи что НЕ используется!\n';
386
+ prompt += '###END PROJECT STACK###\n';
387
+ }
331
388
  if (context?.architecture?.boundedContexts && context.architecture.boundedContexts.length > 0) {
332
389
  prompt += '\n\n**Defined Architecture:**\n';
333
390
  for (const bc of context.architecture.boundedContexts) {
@@ -335,8 +392,16 @@ ABSOLUTE RULES:
335
392
  }
336
393
  }
337
394
  if (context?.semanticMemory && context.semanticMemory.length > 0) {
338
- prompt += '\n\n###PROJECT FILES (ONLY reference these - nothing else exists)###\n';
339
- for (const result of context.semanticMemory.slice(0, 10)) {
395
+ const totalResults = context.semanticMemory.length;
396
+ const maxResults = Math.min(totalResults, 30); // Увеличено с 10 до 30
397
+ prompt += `\n\n###PROJECT FILES (${maxResults} из ${totalResults} найденных)###\n`;
398
+ prompt += `⚠️ ВНИМАНИЕ: Показаны только ${maxResults} наиболее релевантных файлов из ${totalResults}.\n`;
399
+ if (totalResults > maxResults) {
400
+ prompt += `⚠️ КРИТИЧНО: Есть еще ${totalResults - maxResults} файлов, которые могут содержать искомое.\n`;
401
+ prompt += `⚠️ ПРИ ОТВЕТЕ ОБЯЗАТЕЛЬНО УКАЖИ: "Найдено в ${maxResults} файлах, возможно есть еще в ${totalResults - maxResults} файлах"\n`;
402
+ }
403
+ prompt += '\n';
404
+ for (const result of context.semanticMemory.slice(0, maxResults)) {
340
405
  const cleanPath = sanitizePath(result.chunk.metadata.filePath);
341
406
  prompt += `\nФайл: ${cleanPath}\n`;
342
407
  prompt += `${result.context}\n`;
@@ -136,7 +136,12 @@ router.get('/verify/:userCode', (req, res) => {
136
136
  * Authorize device (called from web after user logs in)
137
137
  */
138
138
  router.post('/authorize', async (req, res) => {
139
- const { userCode, userId, accessToken, action } = req.body;
139
+ const { userCode, userId, action } = req.body;
140
+ // Get access token from cookie or Authorization header (not from body!)
141
+ // Web frontend sends credentials: 'include' which sends the httpOnly cookie
142
+ const accessToken = req.cookies?.archicore_token ||
143
+ req.headers.authorization?.substring(7) ||
144
+ req.body.accessToken; // Fallback to body for backwards compatibility
140
145
  if (!userCode) {
141
146
  res.status(400).json({ error: 'invalid_request', message: 'Missing user_code' });
142
147
  return;
@@ -178,7 +178,7 @@ export class ProjectService {
178
178
  provider: 'deepseek',
179
179
  model: process.env.DEEPSEEK_MODEL || 'deepseek-chat',
180
180
  temperature: 0.1,
181
- maxTokens: 4096,
181
+ maxTokens: 8192,
182
182
  baseURL: 'https://api.deepseek.com'
183
183
  };
184
184
  // SemanticMemory - поддержка Jina (бесплатно) или OpenAI
@@ -420,12 +420,20 @@ export class ProjectService {
420
420
  }));
421
421
  }
422
422
  async askArchitect(projectId, question, language = 'en') {
423
+ const project = this.projects.get(projectId);
424
+ if (!project) {
425
+ throw new Error(`Project not found: ${projectId}`);
426
+ }
423
427
  const data = await this.getProjectData(projectId);
428
+ // Анализируем стек технологий проекта
429
+ const { analyzeProjectStack } = await import('../../utils/project-analyzer.js');
430
+ const projectMetadata = analyzeProjectStack(project.path);
424
431
  // Ищем релевантный контекст (если есть семантическая память)
425
432
  let searchResults = [];
426
433
  if (data.semanticMemory) {
427
434
  try {
428
- searchResults = await data.semanticMemory.searchByQuery(question, 5);
435
+ // Увеличиваем лимит результатов поиска с 5 до 30
436
+ searchResults = await data.semanticMemory.searchByQuery(question, 30);
429
437
  Logger.debug(`Semantic search returned ${searchResults.length} results`);
430
438
  }
431
439
  catch (err) {
@@ -435,7 +443,7 @@ export class ProjectService {
435
443
  // Fallback: если семантический поиск не работает, даём контекст из графа
436
444
  if (searchResults.length === 0 && data.graph) {
437
445
  Logger.info('Using graph fallback for context');
438
- const files = Array.from(data.graph.nodes.values()).slice(0, 10);
446
+ const files = Array.from(data.graph.nodes.values()).slice(0, 30); // Увеличено с 10 до 30
439
447
  for (const file of files) {
440
448
  searchResults.push({
441
449
  chunk: {
@@ -458,7 +466,7 @@ export class ProjectService {
458
466
  }
459
467
  // Добавляем информацию о символах если есть
460
468
  if (data.symbols && data.symbols.size > 0) {
461
- const symbolsList = Array.from(data.symbols.values()).slice(0, 20);
469
+ const symbolsList = Array.from(data.symbols.values()).slice(0, 50); // Увеличено с 20 до 50
462
470
  for (const sym of symbolsList) {
463
471
  const existing = searchResults.find(r => r.chunk.metadata.filePath === sym.filePath);
464
472
  if (existing) {
@@ -466,11 +474,12 @@ export class ProjectService {
466
474
  }
467
475
  }
468
476
  }
469
- Logger.debug(`Passing ${searchResults.length} context items to AI`);
477
+ Logger.debug(`Passing ${searchResults.length} context items + project metadata to AI`);
470
478
  const answer = await data.orchestrator.answerArchitecturalQuestion(question, {
471
479
  architecture: data.architecture.getModel(),
472
480
  semanticMemory: searchResults,
473
- language
481
+ language,
482
+ projectMetadata: projectMetadata || undefined
474
483
  });
475
484
  return answer;
476
485
  }
@@ -218,12 +218,21 @@ export interface LLMPrompt {
218
218
  user: string;
219
219
  context?: LLMContext;
220
220
  }
221
+ export interface ProjectMetadata {
222
+ framework?: string;
223
+ dependencies?: Record<string, string>;
224
+ devDependencies?: Record<string, string>;
225
+ buildTool?: string;
226
+ backend?: string;
227
+ database?: string;
228
+ }
221
229
  export interface LLMContext {
222
230
  codeIndex?: Partial<DependencyGraph>;
223
231
  semanticMemory?: SemanticSearchResult[];
224
232
  architecture?: Partial<ArchitectureModel>;
225
233
  recentChanges?: Change[];
226
234
  language?: 'en' | 'ru';
235
+ projectMetadata?: ProjectMetadata;
227
236
  }
228
237
  export interface LLMResponse {
229
238
  content: string;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Project Analyzer - анализ package.json для определения стека технологий
3
+ */
4
+ import { ProjectMetadata } from '../types/index.js';
5
+ /**
6
+ * Анализирует package.json проекта и определяет используемые технологии
7
+ */
8
+ export declare function analyzeProjectStack(projectPath: string): ProjectMetadata | null;
9
+ /**
10
+ * Создает человекочитаемое описание стека проекта
11
+ */
12
+ export declare function describeProjectStack(metadata: ProjectMetadata | null): string;
13
+ //# sourceMappingURL=project-analyzer.d.ts.map
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Project Analyzer - анализ package.json для определения стека технологий
3
+ */
4
+ import { readFileSync, existsSync } from 'fs';
5
+ import { join } from 'path';
6
+ import { Logger } from './logger.js';
7
+ /**
8
+ * Анализирует package.json проекта и определяет используемые технологии
9
+ */
10
+ export function analyzeProjectStack(projectPath) {
11
+ try {
12
+ const packageJsonPath = join(projectPath, 'package.json');
13
+ if (!existsSync(packageJsonPath)) {
14
+ Logger.warn('package.json not found in project');
15
+ return null;
16
+ }
17
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
18
+ const deps = packageJson.dependencies || {};
19
+ const devDeps = packageJson.devDependencies || {};
20
+ const allDeps = { ...deps, ...devDeps };
21
+ const metadata = {
22
+ dependencies: deps,
23
+ devDependencies: devDeps
24
+ };
25
+ // Определяем frontend framework
26
+ if (allDeps['vue'] || allDeps['@vue/cli']) {
27
+ metadata.framework = 'Vue.js';
28
+ }
29
+ else if (allDeps['react'] || allDeps['react-dom']) {
30
+ metadata.framework = 'React';
31
+ }
32
+ else if (allDeps['@angular/core']) {
33
+ metadata.framework = 'Angular';
34
+ }
35
+ else if (allDeps['svelte']) {
36
+ metadata.framework = 'Svelte';
37
+ }
38
+ else if (allDeps['next']) {
39
+ metadata.framework = 'Next.js (React)';
40
+ }
41
+ else if (allDeps['nuxt']) {
42
+ metadata.framework = 'Nuxt.js (Vue)';
43
+ }
44
+ else if (allDeps['@remix-run/react']) {
45
+ metadata.framework = 'Remix (React)';
46
+ }
47
+ else {
48
+ // Проверяем есть ли хоть что-то frontend
49
+ const hasFrontend = ['jquery', 'bootstrap', 'tailwindcss', '@mui/material'].some(dep => allDeps[dep]);
50
+ metadata.framework = hasFrontend ? 'Vanilla JS (no framework)' : 'None (backend only)';
51
+ }
52
+ // Определяем backend framework
53
+ if (allDeps['express']) {
54
+ metadata.backend = 'Express';
55
+ }
56
+ else if (allDeps['fastify']) {
57
+ metadata.backend = 'Fastify';
58
+ }
59
+ else if (allDeps['@nestjs/core']) {
60
+ metadata.backend = 'NestJS';
61
+ }
62
+ else if (allDeps['koa']) {
63
+ metadata.backend = 'Koa';
64
+ }
65
+ else if (allDeps['hapi']) {
66
+ metadata.backend = 'Hapi';
67
+ }
68
+ // Определяем database
69
+ if (allDeps['pg'] || allDeps['postgres']) {
70
+ metadata.database = 'PostgreSQL';
71
+ }
72
+ else if (allDeps['mongodb'] || allDeps['mongoose']) {
73
+ metadata.database = 'MongoDB';
74
+ }
75
+ else if (allDeps['mysql'] || allDeps['mysql2']) {
76
+ metadata.database = 'MySQL';
77
+ }
78
+ else if (allDeps['sqlite3'] || allDeps['better-sqlite3']) {
79
+ metadata.database = 'SQLite';
80
+ }
81
+ else if (allDeps['redis'] || allDeps['ioredis']) {
82
+ metadata.database = 'Redis';
83
+ }
84
+ // Определяем build tool
85
+ if (allDeps['vite'] || devDeps['vite']) {
86
+ metadata.buildTool = 'Vite';
87
+ }
88
+ else if (allDeps['webpack'] || devDeps['webpack']) {
89
+ metadata.buildTool = 'Webpack';
90
+ }
91
+ else if (allDeps['rollup'] || devDeps['rollup']) {
92
+ metadata.buildTool = 'Rollup';
93
+ }
94
+ else if (allDeps['parcel'] || devDeps['parcel']) {
95
+ metadata.buildTool = 'Parcel';
96
+ }
97
+ else if (allDeps['esbuild'] || devDeps['esbuild']) {
98
+ metadata.buildTool = 'esbuild';
99
+ }
100
+ Logger.info(`Project stack detected: ${metadata.framework || 'Unknown'} + ${metadata.backend || 'Unknown'}`);
101
+ return metadata;
102
+ }
103
+ catch (error) {
104
+ Logger.error('Failed to analyze project stack:', error);
105
+ return null;
106
+ }
107
+ }
108
+ /**
109
+ * Создает человекочитаемое описание стека проекта
110
+ */
111
+ export function describeProjectStack(metadata) {
112
+ if (!metadata) {
113
+ return 'Unknown stack (package.json not found)';
114
+ }
115
+ const parts = [];
116
+ if (metadata.framework && metadata.framework !== 'None (backend only)') {
117
+ parts.push(`Frontend: ${metadata.framework}`);
118
+ }
119
+ if (metadata.backend) {
120
+ parts.push(`Backend: ${metadata.backend}`);
121
+ }
122
+ if (metadata.database) {
123
+ parts.push(`Database: ${metadata.database}`);
124
+ }
125
+ if (metadata.buildTool) {
126
+ parts.push(`Build: ${metadata.buildTool}`);
127
+ }
128
+ return parts.length > 0 ? parts.join(', ') : 'Minimal project (no major frameworks)';
129
+ }
130
+ //# sourceMappingURL=project-analyzer.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archicore",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "AI Software Architect - code analysis, impact prediction, semantic search",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",