archicore 0.4.4 → 0.4.5

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.
@@ -51,6 +51,7 @@ export interface ProjectContext {
51
51
  languages: string[];
52
52
  framework?: string;
53
53
  };
54
+ allFiles: string[];
54
55
  }
55
56
  export declare class ContextBuilder {
56
57
  private symbols;
@@ -141,6 +141,17 @@ export class ContextBuilder {
141
141
  Logger.debug(`Building context for: "${userQuery}"`);
142
142
  const intent = this.detectIntent(userQuery);
143
143
  const entities = this.extractEntities(userQuery);
144
+ // Собираем список ВСЕХ файлов из графа
145
+ const allFiles = [];
146
+ for (const [filePath] of this.graph.nodes) {
147
+ allFiles.push(filePath);
148
+ }
149
+ // Также добавляем файлы из fileContents которых может не быть в графе
150
+ for (const [filePath] of this.fileContents) {
151
+ if (!allFiles.includes(filePath)) {
152
+ allFiles.push(filePath);
153
+ }
154
+ }
144
155
  const context = {
145
156
  intent,
146
157
  query: userQuery,
@@ -149,6 +160,7 @@ export class ContextBuilder {
149
160
  dependencies: [],
150
161
  issues: [],
151
162
  projectStats: this.projectStats,
163
+ allFiles: allFiles.slice(0, 500), // Лимит 500 файлов для prompt
152
164
  };
153
165
  // В зависимости от намерения собираем нужный контекст
154
166
  switch (intent) {
@@ -184,8 +184,9 @@ export function analyzeHttpError(status, responseBody) {
184
184
  }
185
185
  /**
186
186
  * Выполнение fetch с timeout и retry
187
+ * Показывает прогресс retry пользователю
187
188
  */
188
- async function fetchWithRetry(url, options, timeout = UPLOAD_TIMEOUT, maxRetries = MAX_RETRIES) {
189
+ async function fetchWithRetry(url, options, timeout = UPLOAD_TIMEOUT, maxRetries = MAX_RETRIES, showRetryProgress = true) {
189
190
  let lastError = null;
190
191
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
191
192
  try {
@@ -200,16 +201,24 @@ async function fetchWithRetry(url, options, timeout = UPLOAD_TIMEOUT, maxRetries
200
201
  }
201
202
  catch (error) {
202
203
  lastError = error instanceof Error ? error : new Error(String(error));
203
- // Не повторяем для определённых ошибок (кроме AbortError - таймаут можно повторить)
204
+ // Не повторяем для определённых ошибок
204
205
  const errorStr = String(error);
205
206
  if (errorStr.includes('ENOTFOUND') ||
206
207
  errorStr.includes('CERT')) {
207
208
  throw error;
208
209
  }
209
- // Экспоненциальная задержка перед повтором (увеличена для нестабильных соединений)
210
+ // Экспоненциальная задержка перед повтором
210
211
  if (attempt < maxRetries) {
211
- // 2s, 4s, 8s, 16s, 32s, до 60s max
212
212
  const delay = Math.min(2000 * Math.pow(2, attempt - 1), 60000);
213
+ // Показываем пользователю что идёт retry
214
+ if (showRetryProgress) {
215
+ const errorType = errorStr.includes('fetch failed') || errorStr.includes('ECONNREFUSED')
216
+ ? 'Connection failed'
217
+ : errorStr.includes('timeout') || errorStr.includes('abort')
218
+ ? 'Timeout'
219
+ : 'Network error';
220
+ console.log(` ⚠ ${errorType}. Retry ${attempt}/${maxRetries} in ${delay / 1000}s...`);
221
+ }
213
222
  debugLog(` fetchWithRetry: waiting ${delay / 1000}s before attempt ${attempt + 1}...`);
214
223
  await new Promise(resolve => setTimeout(resolve, delay));
215
224
  }
@@ -323,42 +323,56 @@ 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. ПРИВЯЗКА К РЕАЛЬНОЙ КОДОВОЙ БАЗЕ:
328
- - ЗАПРЕЩЕНО давать общие советы из интернета (типа "используйте vue-router lazy loading")
329
- - ОБЯЗАТЕЛЬНО сначала проверь что технология РЕАЛЬНО используется в проекте
330
- - Если технология НЕ используется - скажи это ПЕРВЫМ: "❌ В вашем проекте НЕ используется X"
331
- - ЗАТЕМ предложи решение для РЕАЛЬНОЙ архитектуры проекта
326
+ ###ABSOLUTE RULES - КРИТИЧЕСКИ ВАЖНО###
332
327
 
333
- 2. ТОЧНОСТЬ ПРИ ПОИСКЕ (100% ТРЕБОВАНИЕ):
334
- - При вопросе "где используется X" - покажи ВСЕ найденные файлы с путями
335
- - ВСЕГДА указывай: "Найдено в N файлах: [список]"
336
- - Если показано не все файлы - ОБЯЗАТЕЛЬНО укажи: "Показано N из M найденных"
337
- - НИКОГДА не говори "не используется" если показаны не все результаты
328
+ 🚨 ГЛАВНОЕ ПРАВИЛО: НЕ ВЫДУМЫВАЙ НИЧЕГО!
329
+ - ВСЯ информация ТОЛЬКО из секций PROJECT FILES и SMART CONTEXT ниже
330
+ - Если файла НЕТ в этих секциях - его НЕ СУЩЕСТВУЕТ
331
+ - Если технологии НЕТ в PROJECT STACK - она НЕ используется
332
+ - НИКОГДА не говори "возможно есть", "скорее всего", "обычно находится"
333
+ - Говори ТОЛЬКО о том, что ВИДИШЬ в данных ниже
338
334
 
339
- 3. ЗАПРЕТ НА ВЫДУМЫВАНИЕ:
340
- - ТОЛЬКО файлы из "PROJECT FILES" секции существуют
341
- - НИКОГДА не выдумывай пути, классы, функции
342
- - Если нет данных - скажи "Нет данных в индексе" или "Проект не проиндексирован"
335
+ 📁 ПРАВИЛО ФАЙЛОВ:
336
+ - Упоминай ТОЛЬКО файлы из секции PROJECT FILES
337
+ - Каждый путь ДОЛЖЕН быть скопирован из PROJECT FILES
338
+ - Если спрашивают о файле которого нет в PROJECT FILES: " Файл X не найден в индексе проекта"
339
+ - ЗАПРЕЩЕНО выдумывать пути типа "src/api/payments/" если их нет в данных
343
340
 
344
- 4. ФОРМАТ ОТВЕТА:
345
- - Начинай с проверки: " Используется" или "❌ НЕ используется"
346
- - Далее конкретика: файлы, строки, примеры ТОЛЬКО из PROJECT FILES
347
- - В конце: рекомендации для КОНКРЕТНОЙ архитектуры проекта
341
+ 🔍 ПРАВИЛО ПОИСКА:
342
+ - "Найдено в N файлах" - только если реально нашёл в PROJECT FILES
343
+ - "Не найдено" - если НЕТ в PROJECT FILES (не "возможно где-то есть")
344
+ - Копируй пути ТОЧНО как они указаны в PROJECT FILES
348
345
 
349
- Примеры ПРАВИЛЬНЫХ ответов:
350
- Q: "Как оптимизировать vue-router?"
351
- A: "❌ В вашем проекте НЕ используется vue-router.
352
- Ваш стек: vanilla JS + Express.
353
- Для оптимизации загрузки рекомендую:
354
- [конкретные советы для вашей архитектуры]"
346
+ 💡 ПРАВИЛО РЕКОМЕНДАЦИЙ:
347
+ - Рекомендации ТОЛЬКО для технологий из PROJECT STACK
348
+ - Не предлагай Stripe если его нет в зависимостях
349
+ - Не предлагай создавать папки которых нет
355
350
 
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 найденных файлов."`;
351
+ ФОРМАТ ОТВЕТА:
352
+ 1. Сначала: Найдено / Не найдено (с доказательством из PROJECT FILES)
353
+ 2. Потом: конкретные файлы и строки из PROJECT FILES
354
+ 3. В конце: рекомендации только для реального стека проекта
355
+
356
+ ПРИМЕР ПРАВИЛЬНОГО ОТВЕТА:
357
+ Q: "Как работает интеграция с Revolut?"
358
+ A: "❌ Интеграция с Revolut НЕ найдена в проекте.
359
+
360
+ Поиск в PROJECT FILES:
361
+ - Слово 'revolut' не найдено ни в одном файле
362
+ - Слово 'payment' не найдено в конфигах
363
+
364
+ Технологии из PROJECT STACK:
365
+ - Frontend: Vue.js
366
+ - Backend: PHP (Bitrix)
367
+
368
+ Если нужна интеграция с Revolut - потребуется разработка с нуля."
369
+
370
+ ПРИМЕР НЕПРАВИЛЬНОГО ОТВЕТА (ЗАПРЕЩЕНО):
371
+ ❌ "Интеграция находится в src/api/payments/revolut.js" <- ВЫДУМКА!
372
+ ❌ "Возможно используется Stripe" <- ВЫДУМКА без доказательств!
373
+ ❌ "Обычно платежи в src/payments/" <- ВЫДУМКА!
374
+
375
+ ###END ABSOLUTE RULES###`;
362
376
  // Добавляем метаданные проекта (стек технологий)
363
377
  if (context?.projectMetadata) {
364
378
  prompt += '\n\n###PROJECT STACK (РЕАЛЬНЫЕ технологии проекта)###\n';
@@ -457,16 +471,58 @@ A: "✅ Компонент Comments найден в 3 файлах:
457
471
  }
458
472
  prompt += '\n###END SMART CONTEXT###\n';
459
473
  }
474
+ // Добавляем список ВСЕХ файлов проекта (для справки что существует)
475
+ if (context?.smartContext?.projectStats) {
476
+ prompt += `\n\n###ALL PROJECT FILES (полный список существующих файлов)###\n`;
477
+ prompt += `Всего файлов: ${context.smartContext.projectStats.totalFiles}\n`;
478
+ prompt += `Всего символов: ${context.smartContext.projectStats.totalSymbols}\n`;
479
+ prompt += `Языки: ${context.smartContext.projectStats.languages.join(', ')}\n`;
480
+ // Добавляем полный список файлов
481
+ if (context.smartContext.allFiles && context.smartContext.allFiles.length > 0) {
482
+ prompt += `\n📁 СУЩЕСТВУЮЩИЕ ФАЙЛЫ ПРОЕКТА (${context.smartContext.allFiles.length}):\n`;
483
+ prompt += `⚠️ ЭТО ЕДИНСТВЕННЫЕ ФАЙЛЫ! Других НЕ существует!\n\n`;
484
+ // Группируем по директориям
485
+ const byDir = new Map();
486
+ for (const file of context.smartContext.allFiles) {
487
+ const cleanPath = sanitizePath(file);
488
+ const parts = cleanPath.split('/');
489
+ const dir = parts.slice(0, -1).join('/') || '/';
490
+ if (!byDir.has(dir))
491
+ byDir.set(dir, []);
492
+ byDir.get(dir).push(parts[parts.length - 1]);
493
+ }
494
+ // Выводим по директориям (до 100 файлов)
495
+ let fileCount = 0;
496
+ for (const [dir, files] of byDir) {
497
+ if (fileCount >= 100) {
498
+ prompt += `... и ещё ${context.smartContext.allFiles.length - fileCount} файлов\n`;
499
+ break;
500
+ }
501
+ prompt += `📂 ${dir}/\n`;
502
+ for (const file of files.slice(0, 10)) {
503
+ prompt += ` └─ ${file}\n`;
504
+ fileCount++;
505
+ }
506
+ if (files.length > 10) {
507
+ prompt += ` └─ ... (ещё ${files.length - 10} файлов в этой папке)\n`;
508
+ fileCount += files.length - 10;
509
+ }
510
+ }
511
+ }
512
+ // Добавляем список релевантных файлов для текущего запроса
513
+ if (context.smartContext.relevantFiles && context.smartContext.relevantFiles.length > 0) {
514
+ prompt += `\n📍 РЕЛЕВАНТНЫЕ файлы для текущего запроса:\n`;
515
+ for (const file of context.smartContext.relevantFiles.slice(0, 20)) {
516
+ prompt += `- ${sanitizePath(file.path)}\n`;
517
+ }
518
+ }
519
+ prompt += `\n###END ALL PROJECT FILES###\n`;
520
+ }
460
521
  if (context?.semanticMemory && context.semanticMemory.length > 0) {
461
522
  const totalResults = context.semanticMemory.length;
462
- const maxResults = Math.min(totalResults, 30); // Увеличено с 10 до 30
463
- prompt += `\n\n###PROJECT FILES (${maxResults} из ${totalResults} найденных)###\n`;
464
- prompt += `⚠️ ВНИМАНИЕ: Показаны только ${maxResults} наиболее релевантных файлов из ${totalResults}.\n`;
465
- if (totalResults > maxResults) {
466
- prompt += `⚠️ КРИТИЧНО: Есть еще ${totalResults - maxResults} файлов, которые могут содержать искомое.\n`;
467
- prompt += `⚠️ ПРИ ОТВЕТЕ ОБЯЗАТЕЛЬНО УКАЖИ: "Найдено в ${maxResults} файлах, возможно есть еще в ${totalResults - maxResults} файлах"\n`;
468
- }
469
- prompt += '\n';
523
+ const maxResults = Math.min(totalResults, 30);
524
+ prompt += `\n\n###PROJECT FILES CONTENT (содержимое ${maxResults} файлов)###\n`;
525
+ prompt += `⚠️ ЭТО РЕАЛЬНЫЙ КОД ИЗ ПРОЕКТА - используй его как доказательство!\n\n`;
470
526
  for (const result of context.semanticMemory.slice(0, maxResults)) {
471
527
  const cleanPath = sanitizePath(result.chunk.metadata.filePath);
472
528
  const lineInfo = result.chunk.metadata.startLine > 0
@@ -474,19 +530,18 @@ A: "✅ Компонент Comments найден в 3 файлах:
474
530
  : '';
475
531
  prompt += `\n### Файл: ${cleanPath}${lineInfo}\n`;
476
532
  prompt += `Символы: ${result.chunk.metadata.symbols.join(', ') || 'N/A'}\n`;
477
- prompt += `Тип: ${result.chunk.metadata.type}\n`;
478
- // Используем полный контент из chunk.content, не обрезанный context
479
533
  const codeContent = result.chunk.content || result.context;
480
- // Ограничиваем до 3000 символов на файл чтобы не переполнить контекст
481
534
  const truncatedCode = codeContent.length > 3000
482
535
  ? codeContent.substring(0, 3000) + '\n... (truncated)'
483
536
  : codeContent;
484
537
  prompt += `\`\`\`\n${truncatedCode}\n\`\`\`\n`;
485
538
  }
486
- prompt += '\n###END PROJECT FILES###';
539
+ prompt += '\n###END PROJECT FILES CONTENT###';
487
540
  }
488
541
  else if (!context?.smartContext) {
489
- prompt += '\n\n###PROJECT FILES: EMPTY###\nNo code indexed. Respond: "Проект не проиндексирован. Запустите индексацию."';
542
+ prompt += '\n\n###PROJECT FILES: EMPTY###\n';
543
+ prompt += 'Проект не проиндексирован или данные не загружены.\n';
544
+ prompt += 'Отвечай: "Проект не проиндексирован. Запустите /index для индексации."';
490
545
  }
491
546
  return prompt;
492
547
  }
@@ -276,6 +276,7 @@ export interface LLMContext {
276
276
  languages: string[];
277
277
  framework?: string;
278
278
  };
279
+ allFiles?: string[];
279
280
  };
280
281
  }
281
282
  export interface LLMResponse {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archicore",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "AI Software Architect - code analysis, impact prediction, semantic search",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",