viberadar 0.3.80 → 0.3.82

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.
@@ -1167,6 +1167,72 @@ function buildWriteE2eTestPrompt(feat, plan, modules) {
1167
1167
  `- Следуй существующим паттернам проекта`,
1168
1168
  ].filter(Boolean).join('\n');
1169
1169
  }
1170
+ // ─── Documentation prompt builders ───────────────────────────────────────────
1171
+ function buildGenerateDocsPrompt(feat, modules, projectRoot) {
1172
+ const sourceFiles = modules
1173
+ .filter(m => m.featureKeys.includes(feat.key) && m.type !== 'test' && !m.isInfra)
1174
+ .map(m => '- ' + m.relativePath.replace(/\\/g, '/'));
1175
+ return [
1176
+ `Сгенерируй исчерпывающую пользовательскую документацию для фичи "${feat.label}".`,
1177
+ ``,
1178
+ feat.description ? `Описание фичи: ${feat.description}` : '',
1179
+ ``,
1180
+ `Файлы фичи (${sourceFiles.length}):`,
1181
+ ...sourceFiles,
1182
+ ``,
1183
+ `Задача:`,
1184
+ `1. Прочитай каждый файл фичи`,
1185
+ `2. Разберись в архитектуре: как файлы связаны, какие API/функции они экспортируют`,
1186
+ `3. Напиши документацию в формате Markdown`,
1187
+ ``,
1188
+ `Обязательные секции документа:`,
1189
+ `## Overview — что делает фича, зачем нужна, какую задачу решает`,
1190
+ `## Architecture — ключевые компоненты, data flow, связи между файлами`,
1191
+ `## Usage — как использовать (API, функции, endpoints, компоненты)`,
1192
+ `## Configuration — конфигурация, env-переменные, настройки`,
1193
+ `## Key Files — список файлов с кратким описанием роли каждого`,
1194
+ `## Dependencies — внешние пакеты и внутренние зависимости`,
1195
+ ``,
1196
+ `Требования:`,
1197
+ `- Документация должна быть исчерпывающей, понятной для нового разработчика`,
1198
+ `- Описывай каждый публичный API/экспорт`,
1199
+ `- Приводи примеры использования`,
1200
+ `- Запиши результат в файл: docs/features/${feat.key}.md`,
1201
+ `- Создай директорию docs/features/ если её нет`,
1202
+ ].filter(Boolean).join('\n');
1203
+ }
1204
+ function buildUpdateDocsPrompt(feat, modules, changedFiles, currentDoc, projectRoot) {
1205
+ const allSourceFiles = modules
1206
+ .filter(m => m.featureKeys.includes(feat.key) && m.type !== 'test' && !m.isInfra)
1207
+ .map(m => '- ' + m.relativePath.replace(/\\/g, '/'));
1208
+ return [
1209
+ `Обнови документацию фичи "${feat.label}".`,
1210
+ ``,
1211
+ `Текущая документация:`,
1212
+ '```markdown',
1213
+ currentDoc,
1214
+ '```',
1215
+ ``,
1216
+ `Файлы, изменённые после последнего обновления документации (${changedFiles.length}):`,
1217
+ ...changedFiles.map(f => '- ' + f.replace(/\\/g, '/')),
1218
+ ``,
1219
+ `Все файлы фичи (${allSourceFiles.length}):`,
1220
+ ...allSourceFiles,
1221
+ ``,
1222
+ `Задача:`,
1223
+ `1. Прочитай каждый изменённый файл`,
1224
+ `2. Определи, что изменилось по сравнению с текущей документацией`,
1225
+ `3. Обнови соответствующие секции документа`,
1226
+ `4. Сохрани структуру и стиль существующей документации`,
1227
+ `5. Если добавлены новые файлы — добавь их в секцию Key Files`,
1228
+ `6. Если удалены файлы — убери их из документации`,
1229
+ ``,
1230
+ `Требования:`,
1231
+ `- Не переписывай весь документ, обнови только устаревшие секции`,
1232
+ `- Сохрани существующие примеры, если они всё ещё валидны`,
1233
+ `- Запиши обновлённый результат в файл: docs/features/${feat.key}.md`,
1234
+ ].join('\n');
1235
+ }
1170
1236
  // ─── Main server ──────────────────────────────────────────────────────────────
1171
1237
  function startServer({ data: initialData, port, projectRoot }) {
1172
1238
  return new Promise((resolve, reject) => {
@@ -1637,6 +1703,37 @@ function startServer({ data: initialData, port, projectRoot }) {
1637
1703
  return;
1638
1704
  }
1639
1705
  }
1706
+ else if (task === 'generate-docs') {
1707
+ if (!featureKey || !currentData.features) {
1708
+ failBeforeStart('Фича не найдена');
1709
+ return;
1710
+ }
1711
+ const feat = currentData.features.find(f => f.key === featureKey);
1712
+ if (!feat) {
1713
+ failBeforeStart(`Фича ${featureKey} не найдена`);
1714
+ return;
1715
+ }
1716
+ prompt = buildGenerateDocsPrompt(feat, currentData.modules, projectRoot);
1717
+ }
1718
+ else if (task === 'update-docs') {
1719
+ if (!featureKey || !currentData.features) {
1720
+ failBeforeStart('Фича не найдена');
1721
+ return;
1722
+ }
1723
+ const feat = currentData.features.find(f => f.key === featureKey);
1724
+ if (!feat) {
1725
+ failBeforeStart(`Фича ${featureKey} не найдена`);
1726
+ return;
1727
+ }
1728
+ const docStatus = currentData.documentation?.features.find(f => f.key === featureKey);
1729
+ let currentDoc = '';
1730
+ try {
1731
+ currentDoc = fs.readFileSync(path.join(projectRoot, 'docs', 'features', `${featureKey}.md`), 'utf-8');
1732
+ }
1733
+ catch { }
1734
+ const changedFiles = docStatus?.changedFilesSinceDoc || [];
1735
+ prompt = buildUpdateDocsPrompt(feat, currentData.modules, changedFiles, currentDoc, projectRoot);
1736
+ }
1640
1737
  else {
1641
1738
  prompt = buildMapUnmappedPrompt(currentData.modules, currentData.features || []);
1642
1739
  }
@@ -2205,6 +2302,7 @@ function startServer({ data: initialData, port, projectRoot }) {
2205
2302
  chokidar_1.default.watch([
2206
2303
  '**/*.{ts,tsx,js,jsx,vue,svelte}',
2207
2304
  'viberadar.config.json',
2305
+ 'docs/features/*.md',
2208
2306
  ], {
2209
2307
  cwd: projectRoot,
2210
2308
  ignored: [
@@ -2222,7 +2320,7 @@ function startServer({ data: initialData, port, projectRoot }) {
2222
2320
  const rawUrl = req.url ?? '/';
2223
2321
  const parsedUrl = new URL(rawUrl, 'http://127.0.0.1');
2224
2322
  const url = parsedUrl.pathname;
2225
- if (url === '/' || url === '/radar/qa' || url === '/radar/observability') {
2323
+ if (url === '/' || url === '/radar/qa' || url === '/radar/observability' || url === '/radar/docs') {
2226
2324
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
2227
2325
  res.end(DASHBOARD_HTML);
2228
2326
  return;
@@ -2750,6 +2848,50 @@ function startServer({ data: initialData, port, projectRoot }) {
2750
2848
  }
2751
2849
  return;
2752
2850
  }
2851
+ // ── Documentation API ─────────────────────────────────────────────────────
2852
+ if (url === '/api/docs/content' && req.method === 'GET') {
2853
+ const featureKey = parsedUrl.searchParams.get('feature');
2854
+ if (!featureKey) {
2855
+ res.writeHead(400, { 'Content-Type': 'application/json' });
2856
+ res.end(JSON.stringify({ error: 'Missing feature param' }));
2857
+ return;
2858
+ }
2859
+ const docPath = path.join(projectRoot, 'docs', 'features', `${featureKey}.md`);
2860
+ try {
2861
+ const content = fs.readFileSync(docPath, 'utf-8');
2862
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2863
+ res.end(JSON.stringify({ content, exists: true }));
2864
+ }
2865
+ catch {
2866
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2867
+ res.end(JSON.stringify({ content: null, exists: false }));
2868
+ }
2869
+ return;
2870
+ }
2871
+ if (url === '/api/docs/save' && req.method === 'POST') {
2872
+ let body = '';
2873
+ req.on('data', (chunk) => { body += chunk.toString(); });
2874
+ req.on('end', () => {
2875
+ try {
2876
+ const { featureKey, content } = JSON.parse(body);
2877
+ if (!featureKey || typeof content !== 'string') {
2878
+ res.writeHead(400, { 'Content-Type': 'application/json' });
2879
+ res.end(JSON.stringify({ error: 'Missing featureKey or content' }));
2880
+ return;
2881
+ }
2882
+ const docsDir = path.join(projectRoot, 'docs', 'features');
2883
+ fs.mkdirSync(docsDir, { recursive: true });
2884
+ fs.writeFileSync(path.join(docsDir, `${featureKey}.md`), content, 'utf-8');
2885
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2886
+ res.end(JSON.stringify({ ok: true }));
2887
+ }
2888
+ catch (err) {
2889
+ res.writeHead(500, { 'Content-Type': 'application/json' });
2890
+ res.end(JSON.stringify({ error: err.message }));
2891
+ }
2892
+ });
2893
+ return;
2894
+ }
2753
2895
  res.writeHead(404);
2754
2896
  res.end('Not found');
2755
2897
  });