@nitra/cursor 5.1.0 → 5.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.
- package/.claude-template/settings.template.json +22 -0
- package/.pi-template/extensions/n-cursor-adr/docs/index.md +15 -9
- package/CHANGELOG.md +12 -1
- package/bin/n-cursor.js +73 -16
- package/docs/stryker.config.md +6 -0
- package/docs/vitest.config.md +6 -0
- package/lib/docs/llm.md +29 -0
- package/lib/docs/omlx.md +32 -0
- package/lib/llm.mjs +137 -0
- package/lib/omlx.mjs +49 -4
- package/package.json +1 -1
- package/rules/abie/docs/fix.md +6 -0
- package/rules/abie/js/docs/applies.md +6 -0
- package/rules/abie/js/docs/env_dns.md +25 -22
- package/rules/abie/js/docs/firebase_hosting.md +6 -0
- package/rules/abie/js/docs/hc_pairing.md +21 -25
- package/rules/abie/js/docs/ua_http_route.md +27 -19
- package/rules/abie/js/docs/ua_node_selector.md +24 -19
- package/rules/abie/lib/docs/enabled.md +13 -7
- package/rules/abie/lib/docs/env-dns.md +9 -3
- package/rules/abie/lib/docs/hc-yaml.md +6 -0
- package/rules/abie/lib/docs/http-route.md +6 -0
- package/rules/abie/lib/docs/k8s-tree.md +6 -0
- package/rules/abie/lib/docs/kustomization-patches.md +6 -0
- package/rules/abie/lib/docs/overlay-paths.md +6 -0
- package/rules/abie/lib/docs/yaml.md +6 -0
- package/rules/adr/docs/fix.md +6 -0
- package/rules/adr/js/docs/hooks.md +29 -244
- package/rules/bun/docs/fix.md +6 -0
- package/rules/bun/js/docs/layout.md +37 -375
- package/rules/capacitor/docs/fix.md +22 -108
- package/rules/capacitor/js/docs/platforms.md +62 -268
- package/rules/changelog/docs/fix.md +6 -0
- package/rules/changelog/lib/docs/package-manifest.md +6 -0
- package/rules/ci4/docs/fix.md +23 -165
- package/rules/ci4/js/docs/marksman_config.md +9 -1
- package/rules/docker/docs/fix.md +6 -0
- package/rules/docker/js/docs/lint.md +55 -239
- package/rules/docker/lib/docs/docker-hadolint.md +6 -0
- package/rules/docker/lib/docs/docker-mirror.md +6 -0
- package/rules/docker/lib/docs/docker-native-addon.md +6 -0
- package/rules/docker/lib/docs/docker-nginx-user.md +6 -0
- package/rules/docker/lint/docs/lint.md +9 -1
- package/rules/efes/docs/fix.md +6 -0
- package/rules/ga/lint/docs/lint.md +6 -0
- package/rules/graphql/docs/fix.md +6 -0
- package/rules/graphql/lib/docs/graphql-gql-scan.md +6 -0
- package/rules/image-avif/docs/fix.md +6 -0
- package/rules/image-avif/js/docs/avif_generation.md +6 -0
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +9 -3
- package/rules/js-bun-redis/lib/docs/redis-imports.md +6 -0
- package/rules/js-lint/js/docs/utils_imports.md +6 -0
- package/rules/js-lint-ci/docs/fix.md +7 -1
- package/rules/js-mssql/docs/fix.md +6 -0
- package/rules/js-mssql/lib/docs/mssql-pool-scan.md +6 -0
- package/rules/js-run/docs/fix.md +6 -0
- package/rules/js-run/lib/docs/bunyan-imports.md +6 -0
- package/rules/js-run/lib/docs/check-env-scan.md +6 -0
- package/rules/js-run/lib/docs/conn-file-rules.md +6 -0
- package/rules/js-run/lib/docs/conn-imports-scan.md +6 -0
- package/rules/js-run/lib/docs/promise-settimeout-scan.md +6 -0
- package/rules/js-run/lib/docs/temporal-scan.md +6 -0
- package/rules/k8s/docs/fix.md +6 -0
- package/rules/k8s/lint/docs/lint.md +6 -0
- package/rules/nginx-default-tpl/docs/fix.md +6 -0
- package/rules/npm-module/js/docs/header_doc_pointer.md +7 -0
- package/rules/npm-module/js/header_doc_pointer.mjs +2 -8
- package/rules/php/docs/fix.md +6 -0
- package/rules/php/lint/docs/lint.md +6 -0
- package/rules/python/docs/fix.md +6 -0
- package/rules/python/lint/docs/lint.md +6 -0
- package/rules/rego/lint/docs/lint.md +6 -0
- package/rules/release/docs/change.md +6 -0
- package/rules/release/docs/fix.md +6 -0
- package/rules/release/docs/release.md +6 -0
- package/rules/release/lib/docs/aggregate.md +6 -0
- package/rules/release/lib/docs/change-file.md +6 -0
- package/rules/release/lib/docs/fallback.md +6 -0
- package/rules/rust/lib/docs/has-cargo-toml.md +6 -0
- package/rules/security/docs/fix.md +7 -1
- package/rules/security/js/docs/lint.md +6 -0
- package/rules/style-lint/docs/fix.md +6 -0
- package/rules/tauri/docs/fix.md +6 -0
- package/rules/test/docs/fix.md +6 -0
- package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +6 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +6 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +6 -0
- package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +6 -0
- package/rules/text/docs/fix.md +6 -0
- package/rules/text/lint/docs/lint.md +6 -0
- package/rules/text/lint/docs/run-dotenv-linter.md +6 -0
- package/rules/text/lint/docs/run-shellcheck.md +6 -0
- package/rules/text/lint/docs/run-v8r.md +6 -0
- package/rules/vue/lib/docs/vue-forbidden-imports.md +6 -0
- package/scripts/coverage-classify/cache.mjs +1 -1
- package/scripts/coverage-classify/docs/apply.md +6 -0
- package/scripts/coverage-classify/docs/cache.md +6 -0
- package/scripts/coverage-classify/docs/prompt.md +6 -0
- package/scripts/coverage-classify/docs/verdict-schema.md +6 -0
- package/scripts/coverage-classify/prompt.mjs +1 -1
- package/scripts/coverage-fix-extract.mjs +1 -1
- package/scripts/coverage-fix.mjs +2 -1
- package/scripts/docs/auto-skills.md +6 -0
- package/scripts/docs/build-agents-commands.md +7 -1
- package/scripts/docs/cli-entry.md +6 -0
- package/scripts/docs/coverage-fix-extract.md +6 -0
- package/scripts/docs/coverage-fix.md +6 -0
- package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +6 -0
- package/scripts/docs/lint-cli.md +6 -0
- package/scripts/docs/post-tool-use-fix.md +6 -0
- package/scripts/docs/rename-yaml-extensions.md +6 -0
- package/scripts/docs/skills-cli.md +6 -0
- package/scripts/docs/sync-setup-bun-deps-action.md +6 -0
- package/scripts/docs/upgrade-nitra-cursor-and-install.md +6 -0
- package/scripts/docs/worktree-cli.md +6 -0
- package/scripts/lib/docs/assert-project-root.md +6 -0
- package/scripts/lib/docs/check-mdc-template-refs.md +6 -0
- package/scripts/lib/docs/check-reporter.md +6 -0
- package/scripts/lib/docs/diff-added-lines.md +6 -0
- package/scripts/lib/docs/discover-check-rules-from-cursor.md +6 -0
- package/scripts/lib/docs/discover-checkable-rules.md +6 -0
- package/scripts/lib/docs/ensure-tool.md +6 -0
- package/scripts/lib/docs/generated-markdown.md +6 -0
- package/scripts/lib/docs/gha-workflow.md +6 -0
- package/scripts/lib/docs/inline-template-links.md +6 -0
- package/scripts/lib/docs/list-rule-ids.md +6 -0
- package/scripts/lib/docs/load-cursor-config.md +6 -0
- package/scripts/lib/docs/mirror-parity.md +6 -0
- package/scripts/lib/docs/read-n-cursor-config-lite.md +6 -0
- package/scripts/lib/docs/resolve-target-files.md +6 -0
- package/scripts/lib/docs/root-notice.md +6 -0
- package/scripts/lib/docs/rule-meta-helpers.md +6 -0
- package/scripts/lib/docs/rule-meta.md +6 -0
- package/scripts/lib/docs/run-conftest-batch.md +6 -0
- package/scripts/lib/docs/run-lint-step.md +6 -0
- package/scripts/lib/docs/run-rule-cli.md +6 -0
- package/scripts/lib/docs/run-rule.md +6 -0
- package/scripts/lib/docs/run-standard-lint.md +6 -0
- package/scripts/lib/docs/run-standard-rule.md +6 -0
- package/scripts/lib/docs/skill-meta.md +6 -0
- package/scripts/lib/docs/template.md +6 -0
- package/scripts/lib/docs/timing-summary.md +6 -0
- package/scripts/lib/docs/workspaces.md +6 -0
- package/scripts/lib/docs/worktree-notice.md +6 -0
- package/scripts/lib/docs/worktree.md +6 -0
- package/scripts/lib/mirror-parity.mjs +1 -1
- package/scripts/lib/root-notice.mjs +1 -1
- package/scripts/lib/worktree-notice.mjs +5 -5
- package/scripts/lib/worktree.mjs +1 -1
- package/scripts/sync-claude-config.mjs +3 -0
- package/scripts/utils/docs/ast-scan-utils.md +6 -0
- package/scripts/utils/docs/ensure-gitignore-entries.md +6 -0
- package/scripts/utils/docs/find-package-json-paths.md +6 -0
- package/scripts/utils/docs/lock-cache-dir.md +6 -0
- package/scripts/utils/docs/pass.md +6 -0
- package/scripts/utils/docs/resolve-cargo-manifest.md +6 -0
- package/scripts/utils/docs/resolve-cmd.md +6 -0
- package/scripts/utils/docs/resolve-js-root.md +6 -0
- package/scripts/utils/docs/test-helpers.md +6 -0
- package/scripts/utils/docs/walk-cache.md +6 -0
- package/scripts/utils/docs/walkDir.md +6 -0
- package/scripts/utils/docs/worktree-fingerprint.md +6 -0
- package/scripts/utils/resolve-js-root.mjs +1 -1
- package/skills/doc-aggregate/SKILL.md +129 -0
- package/skills/doc-aggregate/js/docgen-ignore.mjs +9 -0
- package/skills/{docgen → doc-aggregate}/js/docgen-scan.mjs +22 -67
- package/skills/doc-aggregate/js/docs/docgen-ignore.md +21 -0
- package/skills/doc-files/SKILL.md +100 -0
- package/skills/doc-files/js/docgen-crc.mjs +164 -0
- package/skills/{docgen → doc-files}/js/docgen-extract-anchors.mjs +20 -11
- package/skills/{docgen → doc-files}/js/docgen-extract.mjs +15 -9
- package/skills/doc-files/js/docgen-files-batch.mjs +181 -0
- package/skills/doc-files/js/docgen-gen.mjs +291 -0
- package/skills/{docgen → doc-files}/js/docgen-prompts.mjs +43 -40
- package/skills/doc-files/js/docgen-scan.mjs +298 -0
- package/skills/doc-files/js/docs/docgen-crc.md +32 -0
- package/skills/doc-files/js/docs/docgen-extract-anchors.md +27 -0
- package/skills/doc-files/js/docs/docgen-extract.md +29 -0
- package/skills/doc-files/js/docs/docgen-files-batch.md +25 -0
- package/skills/doc-files/js/docs/docgen-gen.md +30 -0
- package/skills/doc-files/js/docs/docgen-prompts.md +32 -0
- package/skills/doc-files/js/docs/docgen-scan.md +25 -0
- package/skills/doc-files/meta.json +1 -0
- package/skills/fix/js/docs/llm-worker.md +6 -0
- package/skills/fix/js/docs/orchestrator.md +6 -0
- package/skills/fix/js/llm-worker.mjs +3 -3
- package/skills/fix/js/orchestrator.mjs +1 -1
- package/skills/start-check/js/check.mjs +5 -3
- package/skills/start-check/js/docs/check.md +6 -0
- package/skills/docgen/SKILL.md +0 -224
- package/skills/docgen/bench/etalon/firebase_hosting.md +0 -19
- package/skills/docgen/bench/etalon/k8s-tree.md +0 -24
- package/skills/docgen/bench/etalon/overlay-paths.md +0 -24
- package/skills/docgen/js/docgen-batch-omlx.mjs +0 -82
- package/skills/docgen/js/docgen-batch.mjs +0 -95
- package/skills/docgen/js/docgen-compare-pi-vs-direct.mjs +0 -95
- package/skills/docgen/js/docgen-gen.mjs +0 -306
- package/skills/docgen/js/docs/docgen-extract.md +0 -28
- package/skills/docgen/js/docs/docgen-gen.md +0 -41
- package/skills/docgen/js/docs/docgen-ignore.md +0 -24
- package/skills/docgen/js/docs/docgen-prompts.md +0 -24
- package/skills/docgen/js/docs/docgen-scan.md +0 -48
- /package/skills/{docgen → doc-aggregate}/meta.json +0 -0
- /package/skills/{docgen → doc-files}/js/docgen-ignore.mjs +0 -0
package/skills/docgen/SKILL.md
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: docgen
|
|
3
|
-
description: >-
|
|
4
|
-
Обходить проєкт і для кожного кодового файлу (js/mjs/ts/vue/py) пише лаконічну поведінкову українську md-документацію у теку docs/ поряд із кодом — диспатчить окремого субагента на кожен файл, за правилами adr/ci4
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# docgen — генерація документації по файлах
|
|
8
|
-
|
|
9
|
-
## Мета
|
|
10
|
-
|
|
11
|
-
Для кожного кодового файлу проєкту створити лаконічну поведінкову `.md`-документацію у теці `docs/`
|
|
12
|
-
**поряд із самим файлом** (`<dir>/docs/<stem>.md`). Документацію пише **окремий субагент**
|
|
13
|
-
на кожен файл — не один прохід, а батч-диспатч. Джерело правди стилю — правила `adr` і
|
|
14
|
-
`ci4` (`docs/explanation`/`docs/adr`-каталоги з тих правил **не застосовуємо** — доку
|
|
15
|
-
кладемо локально поряд із кодом).
|
|
16
|
-
|
|
17
|
-
Документація — **трирівнева**, рівні виконуються строго послідовно:
|
|
18
|
-
|
|
19
|
-
1. **Tier 1 — файли**: `<dir>/docs/<stem>.md`, субагент на файл (нижче).
|
|
20
|
-
2. **Tier 2 — module-summary**: `<module_root>/docs/ARCHITECTURE.md`, субагент на модуль.
|
|
21
|
-
3. **Tier 3 — доменні доки**: `docs/<домен>.md` у кореневій `docs/`, субагент-синтезатор
|
|
22
|
-
виділяє бізнес-домени й пише файл на кожен домен.
|
|
23
|
-
|
|
24
|
-
Агрегат ніколи не випереджає джерело: Tier 2 — лише після завершення всього Tier 1,
|
|
25
|
-
Tier 3 — лише після завершення всього Tier 2.
|
|
26
|
-
|
|
27
|
-
## ⚠️ Паралелізм
|
|
28
|
-
|
|
29
|
-
Диспатч субагентів — **батчами по 5 одночасно**. Не запускати весь список одразу
|
|
30
|
-
(перевантаження). Кожен субагент пише свій окремий файл — спільного стану немає, гонок
|
|
31
|
-
за файли немає.
|
|
32
|
-
|
|
33
|
-
Рівні строго послідовні: Tier 2 стартує лише після завершення всього Tier 1, Tier 3 —
|
|
34
|
-
лише після всього Tier 2. Усередині Tier 1 і Tier 2 — батчі по 5. Tier 3 — один субагент.
|
|
35
|
-
|
|
36
|
-
## Передумова
|
|
37
|
-
|
|
38
|
-
- Поточна директорія — корінь проєкту, який документуємо.
|
|
39
|
-
- Доступний `npx @nitra/cursor` (пакет `@nitra/cursor` встановлено або через npx).
|
|
40
|
-
|
|
41
|
-
## Workflow
|
|
42
|
-
|
|
43
|
-
### Крок 1: Зібрати список файлів
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
npx @nitra/cursor docgen scan
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Команда друкує JSON-масив об'єктів. Усі шляхи в ньому — відносні до кореня проєкту:
|
|
50
|
-
|
|
51
|
-
```json
|
|
52
|
-
[{ "sourcePath": "src/lib/foo.js", "docPath": "src/lib/docs/foo.md", "exists": false }]
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Розпарси JSON.
|
|
56
|
-
|
|
57
|
-
### Крок 2: Відфільтрувати вже описані
|
|
58
|
-
|
|
59
|
-
За замовчуванням **пропусти** елементи з `"exists": true`. Перегенеровуй їх лише якщо
|
|
60
|
-
користувач явно попросив `--overwrite` (тоді обробляй усі). `--overwrite` — **не** прапор
|
|
61
|
-
`docgen scan`: scanner лише лістить файли, а рішення «пропустити чи перегенерувати» приймаєш
|
|
62
|
-
ти тут, фільтруючи за полем `exists`.
|
|
63
|
-
|
|
64
|
-
Якщо після фільтра список порожній — зупинись:
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
✓ Усі кодові файли вже мають документацію. Нічого робити.
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Запам'ятай `total = довжина відфільтрованого списку`.
|
|
71
|
-
|
|
72
|
-
### Крок 3: Диспатч субагентів батчами по 5
|
|
73
|
-
|
|
74
|
-
Розбий список на батчі по 5 елементів. Для кожного батчу запусти **до 5 субагентів
|
|
75
|
-
одночасно (в одному повідомленні)**, дочекайся завершення батчу, переходь до наступного.
|
|
76
|
-
|
|
77
|
-
Промпт кожного субагента (підстав `sourcePath` і `docPath`):
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
Напиши лаконічну технічну документацію для одного файлу коду — орієнтовану на поведінку, не реалізацію.
|
|
81
|
-
|
|
82
|
-
ФАЙЛ-ДЖЕРЕЛО: <sourcePath>
|
|
83
|
-
ЗАПИСАТИ В: <docPath>
|
|
84
|
-
|
|
85
|
-
Кроки:
|
|
86
|
-
1. Прочитай файл <sourcePath> повністю.
|
|
87
|
-
2. Створи теку для <docPath>, якщо її немає.
|
|
88
|
-
3. Запиши markdown-документ у <docPath> за правилами нижче.
|
|
89
|
-
|
|
90
|
-
Правила документа (за adr/ci4):
|
|
91
|
-
- Мова — УКРАЇНСЬКА для всього тексту (заголовки, абзаци, таблиці). Code identifiers,
|
|
92
|
-
шляхи, імена API, команди — лишай як у коді (зазвичай ASCII).
|
|
93
|
-
- ЧИСТИЙ Markdown. Жодних HTML-обгорток (<div>/<span>/класів) — токен-ефективність.
|
|
94
|
-
- ФОКУС НА ПОВЕДІНЦІ, не реалізації. Пиши ЩО і НАВІЩО, а не як саме це зроблено.
|
|
95
|
-
- НЕ перелічуй модулі стандартної бібліотеки (node:fs, node:path, node:crypto, python stdlib
|
|
96
|
-
тощо) — вони не несуть бізнес-значення. Зовнішні залежності (npm-пакети, внутрішні модулі)
|
|
97
|
-
згадуй лише якщо їхня роль не очевидна з контексту.
|
|
98
|
-
- НЕ перелічуй внутрішні назви допоміжних функцій/змінних — описуй їхню роль і поведінку.
|
|
99
|
-
Імена публічних exports згадуй лише коли export — справжня точка інтеграції, яку кличуть
|
|
100
|
-
ззовні. Для дрібних/листкових модулів з однією відповідальністю опиши роль поведінково,
|
|
101
|
-
БЕЗ сигнатур, таблиць типів і переліку параметрів — це деталі реалізації.
|
|
102
|
-
- Контекстна незалежність: кожна секція самодостатня. Уникай «як вище», «ця функція», «той сервіс».
|
|
103
|
-
- Секції (включай лише доречні — порожніх не вигадуй):
|
|
104
|
-
## Огляд — 1-3 речення: що файл робить і навіщо він існує (роль у системі). Згадай
|
|
105
|
-
ключову семантику, якщо вона визначає сенс файлу (opt-in/gate, кеш, ідемпотентність тощо).
|
|
106
|
-
## Поведінка — покроковий алгоритм у бізнес-термінах (не деталі реалізації).
|
|
107
|
-
Нумерований список: що відбувається, умови, гілки логіки. Якщо файл керується
|
|
108
|
-
конфігом чи форматом даних — наведи короткий приклад (тільки реальний з коду).
|
|
109
|
-
## Публічний API — ЛИШЕ якщо модуль має нетривіальну зовнішню поверхню, яку називають
|
|
110
|
-
споживачі. Для кожного export: назва + що робить. Без сигнатур і таблиць типів.
|
|
111
|
-
Не дублюй ## Поведінка. Для модуля з однією функцією-предикатом цю секцію пропусти.
|
|
112
|
-
## Де використовується — де в системі цей файл вживається (якщо відомо з коду).
|
|
113
|
-
## Гарантії поведінки — інваріанти й крайові випадки: що гарантовано (read-only,
|
|
114
|
-
не кидає винятків, fail-safe-значення за замовчуванням, безпечна обробка поганих даних)
|
|
115
|
-
і що стається при відсутніх ресурсах чи некоректному вводі. Пропусти, якщо таких гарантій немає.
|
|
116
|
-
- Для .vue додай ## Інтерфейс компонента — props (типи, defaults), emits, slots, реактивний стан.
|
|
117
|
-
- НЕ вигадуй деталей, яких немає в коді.
|
|
118
|
-
- Мета — Behavior Test: читач розуміє, що робить файл і як він вписується в систему,
|
|
119
|
-
без потреби читати реалізацію.
|
|
120
|
-
|
|
121
|
-
Поверни лише підтвердження, що файл <docPath> записано.
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Крок 4: Tier 2 — module-summary
|
|
125
|
-
|
|
126
|
-
Після завершення **всіх** батчів Tier 1 зібрати список модулів:
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
npx @nitra/cursor docgen modules
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Команда друкує JSON-масив:
|
|
133
|
-
|
|
134
|
-
```json
|
|
135
|
-
[
|
|
136
|
-
{
|
|
137
|
-
"moduleRoot": "/abs/npm/rules/adr",
|
|
138
|
-
"relRoot": "npm/rules/adr",
|
|
139
|
-
"slug": "npm-rules-adr",
|
|
140
|
-
"docPath": "/abs/npm/rules/adr/docs/ARCHITECTURE.md",
|
|
141
|
-
"members": ["npm/rules/adr/index.mjs"],
|
|
142
|
-
"exists": false
|
|
143
|
-
}
|
|
144
|
-
]
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
module-summary **завжди регенерується** (це агрегат — поле `exists` ігноруй). Розбий модулі на батчі по 5 і диспатч субагентів. Промпт кожного (підстав `relRoot`, `docPath`, `members`):
|
|
148
|
-
|
|
149
|
-
```
|
|
150
|
-
Напиши module-summary для одного логічного модуля.
|
|
151
|
-
|
|
152
|
-
МОДУЛЬ: <relRoot>
|
|
153
|
-
ЗАПИСАТИ В: <docPath>
|
|
154
|
-
ФАЙЛИ МОДУЛЯ (members): <members>
|
|
155
|
-
|
|
156
|
-
Кроки:
|
|
157
|
-
1. Прочитай файлові доки членів модуля. <member> — sourcePath відносно кореня проєкту
|
|
158
|
-
(= поточний CWD); його файлова дока — <CWD>/<dir>/docs/<stem>.md. За потреби зазирни
|
|
159
|
-
в самі файли.
|
|
160
|
-
2. Створи теку для <docPath>, якщо її немає.
|
|
161
|
-
3. Запиши markdown у <docPath> за тими ж правилами стилю, що й файлова дока
|
|
162
|
-
(українська, чистий Markdown, контекстна незалежність, без HTML).
|
|
163
|
-
|
|
164
|
-
Секції module-summary:
|
|
165
|
-
## Огляд модуля — призначення модуля <relRoot>, його роль у проєкті.
|
|
166
|
-
## Ключові файли — список із кліковими посиланнями (відносними до розташування цього
|
|
167
|
-
ARCHITECTURE.md) на члени модуля та їхні файлові доки.
|
|
168
|
-
## Публічний API — що модуль експортує назовні.
|
|
169
|
-
## Внутрішній потік — як компоненти модуля взаємодіють.
|
|
170
|
-
## Підмодулі — вкладені модулі, якщо є.
|
|
171
|
-
|
|
172
|
-
Поверни лише підтвердження, що файл <docPath> записано.
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Крок 5: Tier 3 — доменні доки
|
|
176
|
-
|
|
177
|
-
Після завершення **всіх** module-summary диспатч **одного** субагента-синтезатора.
|
|
178
|
-
У промпт підстав конкретний перелік шляхів module-summary (<module_root>/docs/ARCHITECTURE.md
|
|
179
|
-
кожного модуля з виводу `docgen modules`), а не інструкцію їх шукати. Промпт:
|
|
180
|
-
|
|
181
|
-
```
|
|
182
|
-
Синтезуй доменну документацію бізнес-процесів проєкту.
|
|
183
|
-
|
|
184
|
-
ДЖЕРЕЛА (module-summary, читай усі): <перелік шляхів ARCHITECTURE.md, підставлений вище>
|
|
185
|
-
|
|
186
|
-
Кроки:
|
|
187
|
-
1. Прочитай усі module-summary.
|
|
188
|
-
2. Виділи бізнес-домени та процеси (можуть перетинати межі модулів). Доменів може бути багато.
|
|
189
|
-
3. Для КОЖНОГО домену запиши окремий файл docs/<домен>.md у кореневій docs/:
|
|
190
|
-
- назва файлу — короткий kebab-slug домену;
|
|
191
|
-
- не перезаписуй файлові доки кореневих файлів у docs/ (напр. app.md, eslint.config.md):
|
|
192
|
-
якщо слаґ домену збігається з іменем такого файлу — додай суфікс -domain
|
|
193
|
-
(напр. app-domain.md). Інакше пиши docs/<домен>.md як є;
|
|
194
|
-
- опиши бізнес-процес домену з кліковими відносними посиланнями на module-summary, конкретні файли й директорії.
|
|
195
|
-
|
|
196
|
-
Правила стилю — ті ж (українська, чистий Markdown, контекстна незалежність, без HTML).
|
|
197
|
-
|
|
198
|
-
Поверни перелік створених файлів docs/<домен>.md.
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Крок 6: Підсумок
|
|
202
|
-
|
|
203
|
-
Після всіх батчів виведи:
|
|
204
|
-
|
|
205
|
-
```
|
|
206
|
-
✓ docgen завершено.
|
|
207
|
-
Tier 1 (файли): описано <N>, пропущено <S>, помилок <E>.
|
|
208
|
-
Tier 2 (модулі): <M> module-summary.
|
|
209
|
-
Tier 3 (домени): <D> доменних доків у docs/.
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
Перелічи файли з помилками (субагент впав або не записав `docPath`), якщо такі є.
|
|
213
|
-
Помилка одного файлу не зупиняє решту — обробляй усі батчі до кінця.
|
|
214
|
-
|
|
215
|
-
## Нотатки
|
|
216
|
-
|
|
217
|
-
- Не комітити автоматично — користувач вирішує, коли комітити згенеровану доку.
|
|
218
|
-
- Scanner ігнорує `node_modules`, `dist`, `.git`, `__pycache__`, `coverage`, `.cursor`,
|
|
219
|
-
`.claude`, усі теки `docs/`, а також `*.test.*` / `*.spec.*` / `*.d.ts`.
|
|
220
|
-
Кореневий repo `docs/` — system-wide only: file-level docs туди не пишуться, і Tier 1
|
|
221
|
-
має трактувати цей корінь як повністю нецільовий.
|
|
222
|
-
- Список glob-ів для ignore живе в окремому snippet-модулі
|
|
223
|
-
`npm/skills/docgen/js/docgen-ignore.mjs` (`DOCGEN_IGNORE_GLOBS`).
|
|
224
|
-
Scanner лише читає цей список.
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# firebase_hosting.mjs
|
|
2
|
-
|
|
3
|
-
## Огляд
|
|
4
|
-
|
|
5
|
-
Перевірка-концерн правила abie: у підкаталогах першого рівня репозиторію не повинно бути артефактів Firebase Hosting (`.firebaserc`, `firebase.json`, `.firebase/`), бо `abie.mdc` забороняє Firebase Hosting. Сам корінь репозиторію не перевіряється — там ці імена можуть належати суміжним проєктам.
|
|
6
|
-
|
|
7
|
-
## Поведінка
|
|
8
|
-
|
|
9
|
-
1. Прочитати список елементів кореня репозиторію. Якщо каталог не читається — зафіксувати помилку (fail) і завершитися.
|
|
10
|
-
2. Відібрати підкаталоги першого рівня, пропустивши `.git` і `node_modules`.
|
|
11
|
-
3. У кожному такому підкаталозі перевірити наявність заборонених імен: файлів `.firebaserc`, `firebase.json` і каталогу `.firebase/`. Кожна знахідка — окремий fail.
|
|
12
|
-
4. Якщо жодного порушення не знайдено — зафіксувати pass.
|
|
13
|
-
5. Повернути підсумковий exit-код.
|
|
14
|
-
|
|
15
|
-
## Гарантії поведінки
|
|
16
|
-
|
|
17
|
-
- Read-only: лише перелічує й перевіряє існування шляхів.
|
|
18
|
-
- Помилка читання кореня не валить процес винятком, а стає fail.
|
|
19
|
-
- Перевіряється лише перший рівень; корінь і глибші рівні поза охопленням.
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# k8s-tree.mjs
|
|
2
|
-
|
|
3
|
-
## Огляд
|
|
4
|
-
|
|
5
|
-
Обхід Kubernetes-дерева для перевірок abie з кешуванням на час одного прогону. Знаходить YAML під сегментом `k8s/` і визначає каталоги з `Deployment`. Перший виклик платить за обхід; наступні концерни прогону беруть із кешу.
|
|
6
|
-
|
|
7
|
-
## Поведінка
|
|
8
|
-
|
|
9
|
-
1. Пошук маніфестів: рекурсивно обійти дерево, відібравши `.yaml`/`.yml` під сегментом `k8s/`; `.github/` свідомо пропускається. Результат відсортований.
|
|
10
|
-
2. Каталоги з Deployment: розпарсити передані YAML, відібрати `kind: Deployment`, зібрати унікальні каталоги.
|
|
11
|
-
3. Кешування: обидві операції кешуються module-level singleton-ом за ключем із входів; повтор без I/O.
|
|
12
|
-
4. Пошкоджені YAML за замовчуванням мовчки пропускаються; репортер передає викликач.
|
|
13
|
-
|
|
14
|
-
## Публічний API
|
|
15
|
-
|
|
16
|
-
- `findK8sYamlFiles` — відсортований список YAML під `k8s/` (з кешем, пропуск `.github/`).
|
|
17
|
-
- `collectDeploymentDirs` — множина каталогів із Deployment (кеш, опц. репортер помилок).
|
|
18
|
-
|
|
19
|
-
## Гарантії поведінки
|
|
20
|
-
|
|
21
|
-
- Read-only щодо проєкту.
|
|
22
|
-
- Стійкість до пошкоджених YAML: помилкові документи пропускаються, обхід не переривається.
|
|
23
|
-
- Детермінований вивід (стабільне сортування).
|
|
24
|
-
- Кеш у межах прогону; повторні виклики безкоштовні.
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# overlay-paths.mjs
|
|
2
|
-
|
|
3
|
-
## Огляд
|
|
4
|
-
|
|
5
|
-
Набір чистих path-хелперів для overlay-перевірок правила abie: класифікація шляхів (ua-overlay проти base-шару), виведення каталогу пакета з overlay-шляху, умовні питання правила (чи потрібен HTTPRoute, чи є Deployment). Уся логіка — над рядками/шляхами та перевіркою існування файлів; YAML не парситься.
|
|
6
|
-
|
|
7
|
-
## Поведінка
|
|
8
|
-
|
|
9
|
-
- ua-overlay: шлях закінчується на `ua/kustomization.yaml`; base-шар — за сегментом `base/`.
|
|
10
|
-
- Каталог пакета: з `…/k8s/ua/kustomization.yaml` виділяється батько `k8s/`; без збігу — немає результату.
|
|
11
|
-
- HTTPRoute-gate: вимога лише для Vite-пакетів (є `vite.config.{js,mjs,ts}`).
|
|
12
|
-
- Deployment: чи хоч один каталог із Deployment лежить у `k8s/` цього пакета.
|
|
13
|
-
- base-шар: yaml під `<пакет>/k8s/` і не в `ua/`.
|
|
14
|
-
- Шляхи нормалізуються до posix (`\`→`/`).
|
|
15
|
-
|
|
16
|
-
## Публічний API
|
|
17
|
-
|
|
18
|
-
- `isUaKustomizationPath`, `abiePackageDirFromK8sOverlay`, `abieOverlayRequiresHttpRouteByVite`, `abieOverlayK8sTreeHasDeployment`, `isAbieK8sBaseYamlPath`, `isK8sYamlInAbiePackageExcludingUaOverlay`.
|
|
19
|
-
|
|
20
|
-
## Гарантії поведінки
|
|
21
|
-
|
|
22
|
-
- Read-only, без побічних ефектів.
|
|
23
|
-
- Невідповідність шаблону → негативний/порожній результат, не виняток.
|
|
24
|
-
- Незалежність від ОС (розділювачі зводяться до `/`).
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Тимчасовий A/B-batch: docgen Tier 1 через omlx (gemma-4-e2b 4bit на MLX)
|
|
3
|
-
* замість pi/ollama. Перезаписує всі docs/<stem>.md для файлів з sym<4,
|
|
4
|
-
* НЕ ескалює в cloud. Призначення — порівняння якості omlx vs попередньої версії.
|
|
5
|
-
*
|
|
6
|
-
* Запуск: node npm/skills/docgen/js/docgen-batch-omlx.mjs [--limit N] [--from N]
|
|
7
|
-
* --limit N — обробити перші N файлів зі списку sym<4
|
|
8
|
-
* --from N — почати з індексу N (для дозапуску)
|
|
9
|
-
*/
|
|
10
|
-
import { readFileSync, mkdirSync, writeFileSync } from 'node:fs'
|
|
11
|
-
import { dirname, join, resolve } from 'node:path'
|
|
12
|
-
import { fileURLToPath } from 'node:url'
|
|
13
|
-
import { execSync } from 'node:child_process'
|
|
14
|
-
import { env } from 'node:process'
|
|
15
|
-
import { generateDoc } from './docgen-gen.mjs'
|
|
16
|
-
import { extractFacts } from './docgen-extract.mjs'
|
|
17
|
-
|
|
18
|
-
const ROOT = resolve(fileURLToPath(import.meta.url), '../../../../..')
|
|
19
|
-
|
|
20
|
-
const args = process.argv.slice(2)
|
|
21
|
-
const limitIdx = args.indexOf('--limit')
|
|
22
|
-
const limit = limitIdx !== -1 ? Number(args[limitIdx + 1]) : Infinity
|
|
23
|
-
const fromIdx = args.indexOf('--from')
|
|
24
|
-
const from = fromIdx !== -1 ? Number(args[fromIdx + 1]) : 0
|
|
25
|
-
|
|
26
|
-
env.N_CURSOR_DOCGEN_BACKEND = 'omlx'
|
|
27
|
-
|
|
28
|
-
const scanOut = execSync('node npm/bin/n-cursor.js docgen scan', { cwd: ROOT, encoding: 'utf8' })
|
|
29
|
-
const all = JSON.parse(scanOut)
|
|
30
|
-
|
|
31
|
-
const local = []
|
|
32
|
-
for (const f of all) {
|
|
33
|
-
try {
|
|
34
|
-
const src = readFileSync(join(ROOT, f.sourcePath), 'utf8')
|
|
35
|
-
const facts = extractFacts(src, join(ROOT, f.sourcePath))
|
|
36
|
-
const sym = (facts.internalSymbols ?? []).length
|
|
37
|
-
if (sym < 4) local.push({ ...f, sym })
|
|
38
|
-
} catch {
|
|
39
|
-
/* пропускаємо нечитані */
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const slice = local.slice(from, from + limit)
|
|
44
|
-
console.log(`📋 Файлів sym<4 у проєкті: ${local.length}; обробляємо: ${slice.length} (from=${from}, limit=${limit === Infinity ? 'усе' : limit})`)
|
|
45
|
-
console.log(`🤖 Бекенд: omlx → ${env.N_CURSOR_DOCGEN_OMLX_URL ?? 'http://127.0.0.1:8000/v1/chat/completions'}`)
|
|
46
|
-
|
|
47
|
-
const stats = { ok: 0, err: 0, totalMs: 0, scores: [], errors: [] }
|
|
48
|
-
|
|
49
|
-
for (let i = 0; i < slice.length; i++) {
|
|
50
|
-
const f = slice[i]
|
|
51
|
-
const t0 = Date.now()
|
|
52
|
-
const pct = Math.round(((i + 1) / slice.length) * 100)
|
|
53
|
-
process.stdout.write(` [${i + 1}/${slice.length} ${pct}%] sym=${f.sym} ${f.sourcePath} ... `)
|
|
54
|
-
try {
|
|
55
|
-
const result = await generateDoc(join(ROOT, f.sourcePath), {
|
|
56
|
-
symThreshold: 999, // не уходити в cloud за sym
|
|
57
|
-
cloudModel: null // повністю вимкнути cloud-fallback навіть при low det-score
|
|
58
|
-
})
|
|
59
|
-
const docAbs = join(ROOT, f.docPath)
|
|
60
|
-
mkdirSync(dirname(docAbs), { recursive: true })
|
|
61
|
-
writeFileSync(docAbs, result.md)
|
|
62
|
-
const ms = Date.now() - t0
|
|
63
|
-
stats.ok++
|
|
64
|
-
stats.totalMs += ms
|
|
65
|
-
stats.scores.push(result.score ?? 0)
|
|
66
|
-
process.stdout.write(`✓ ${Math.round(ms / 1000)}s score=${result.score ?? '?'} tier=${result.tier}\n`)
|
|
67
|
-
} catch (error) {
|
|
68
|
-
stats.err++
|
|
69
|
-
stats.errors.push({ path: f.sourcePath, msg: error.message })
|
|
70
|
-
process.stdout.write(`✗ ${error.message}\n`)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const avgScore = stats.scores.length ? Math.round(stats.scores.reduce((a, b) => a + b, 0) / stats.scores.length) : 0
|
|
75
|
-
console.log(`\n${'─'.repeat(60)}`)
|
|
76
|
-
console.log(`✓ OK: ${stats.ok} ✗ Err: ${stats.err}`)
|
|
77
|
-
console.log(` Сумарний час: ${Math.round(stats.totalMs / 1000)}s; середній на файл: ${stats.ok ? Math.round(stats.totalMs / stats.ok / 1000) : 0}s`)
|
|
78
|
-
console.log(` Середній det-score: ${avgScore}`)
|
|
79
|
-
if (stats.errors.length) {
|
|
80
|
-
console.log('Помилки:')
|
|
81
|
-
for (const e of stats.errors) console.log(` - ${e.path}: ${e.msg}`)
|
|
82
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Batch docgen для відсутніх файлів проєкту.
|
|
3
|
-
* sym < 4 → gemma3:4b orchestrated (local)
|
|
4
|
-
* sym ≥ 4 → Claude Sonnet (cloud, via generateDoc pre-routing)
|
|
5
|
-
*/
|
|
6
|
-
import { readFileSync, mkdirSync, writeFileSync } from 'node:fs'
|
|
7
|
-
import { dirname, join, resolve } from 'node:path'
|
|
8
|
-
import { fileURLToPath } from 'node:url'
|
|
9
|
-
import { generateDoc } from './docgen-gen.mjs'
|
|
10
|
-
import { extractFacts } from './docgen-extract.mjs'
|
|
11
|
-
import { execSync } from 'node:child_process'
|
|
12
|
-
|
|
13
|
-
const ROOT = resolve(fileURLToPath(import.meta.url), '../../../../..')
|
|
14
|
-
|
|
15
|
-
// 1. Отримати список відсутніх файлів
|
|
16
|
-
const scanOut = execSync('node npm/bin/n-cursor.js docgen scan', { cwd: ROOT, encoding: 'utf8' })
|
|
17
|
-
const allFiles = JSON.parse(scanOut)
|
|
18
|
-
const missing = allFiles.filter(x => !x.exists)
|
|
19
|
-
|
|
20
|
-
console.log(`\n📋 Файлів для генерації: ${missing.length}`)
|
|
21
|
-
|
|
22
|
-
// 2. Розкласти по тирах
|
|
23
|
-
const cloud = [],
|
|
24
|
-
local = []
|
|
25
|
-
for (const f of missing) {
|
|
26
|
-
try {
|
|
27
|
-
const src = readFileSync(join(ROOT, f.sourcePath), 'utf8')
|
|
28
|
-
const facts = extractFacts(src, join(ROOT, f.sourcePath))
|
|
29
|
-
const sym = (facts.internalSymbols ?? []).length
|
|
30
|
-
if (sym >= 4) cloud.push({ ...f, sym })
|
|
31
|
-
else local.push({ ...f, sym })
|
|
32
|
-
} catch {
|
|
33
|
-
local.push({ ...f, sym: 0 })
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
console.log(` Local (sym<4): ${local.length}`)
|
|
38
|
-
console.log(` Cloud (sym≥4): ${cloud.length}`)
|
|
39
|
-
|
|
40
|
-
const stats = { ok: 0, err: 0, localOk: 0, cloudOk: 0, errors: [] }
|
|
41
|
-
|
|
42
|
-
// 3. Cloud файли (sym≥4) — generateDoc auto-routes до Claude
|
|
43
|
-
console.log('\n☁️ Cloud tier...')
|
|
44
|
-
for (const f of cloud) {
|
|
45
|
-
const t0 = Date.now()
|
|
46
|
-
try {
|
|
47
|
-
const result = await generateDoc(join(ROOT, f.sourcePath), { symThreshold: 4 })
|
|
48
|
-
const docAbs = join(ROOT, f.docPath)
|
|
49
|
-
mkdirSync(dirname(docAbs), { recursive: true })
|
|
50
|
-
writeFileSync(docAbs, result.md)
|
|
51
|
-
stats.ok++
|
|
52
|
-
stats.cloudOk++
|
|
53
|
-
console.log(` ✓ ${f.sourcePath} (sym=${f.sym}, ${Math.round((Date.now() - t0) / 1000)}s)`)
|
|
54
|
-
} catch (error) {
|
|
55
|
-
stats.err++
|
|
56
|
-
stats.errors.push(f.sourcePath)
|
|
57
|
-
console.error(` ✗ ${f.sourcePath}: ${error.message}`)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// 4. Local файли (sym<4) — gemma3:4b orchestrated
|
|
62
|
-
console.log('\n💻 Local tier...')
|
|
63
|
-
let done = 0
|
|
64
|
-
for (const f of local) {
|
|
65
|
-
done++
|
|
66
|
-
const t0 = Date.now()
|
|
67
|
-
const pct = Math.round((done / local.length) * 100)
|
|
68
|
-
process.stdout.write(` [${done}/${local.length} ${pct}%] ${f.sourcePath} ... `)
|
|
69
|
-
try {
|
|
70
|
-
const result = await generateDoc(join(ROOT, f.sourcePath), {
|
|
71
|
-
mode: 'orchestrated',
|
|
72
|
-
symThreshold: 999 // force local
|
|
73
|
-
})
|
|
74
|
-
const docAbs = join(ROOT, f.docPath)
|
|
75
|
-
mkdirSync(dirname(docAbs), { recursive: true })
|
|
76
|
-
writeFileSync(docAbs, result.md)
|
|
77
|
-
stats.ok++
|
|
78
|
-
stats.localOk++
|
|
79
|
-
process.stdout.write(`✓ ${Math.round((Date.now() - t0) / 1000)}s score=${result.score ?? '?'}\n`)
|
|
80
|
-
} catch (error) {
|
|
81
|
-
stats.err++
|
|
82
|
-
stats.errors.push(f.sourcePath)
|
|
83
|
-
process.stdout.write(`✗ ${error.message}\n`)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 5. Підсумок
|
|
88
|
-
console.log(`\n${'─'.repeat(50)}`)
|
|
89
|
-
console.log(`✓ OK: ${stats.ok} ✗ Err: ${stats.err}`)
|
|
90
|
-
console.log(` 💻 Local (gemma3:4b): ${stats.localOk} файлів`)
|
|
91
|
-
console.log(` ☁️ Cloud (Claude/pi): ${stats.cloudOk} файлів`)
|
|
92
|
-
if (stats.errors.length > 0) {
|
|
93
|
-
console.log('Помилки:')
|
|
94
|
-
for (const e of stats.errors) console.log(` - ${e}`)
|
|
95
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A/B: docgen Tier 1 через pi cli (з omlx-провайдером у ~/.pi/agent/models.json)
|
|
3
|
-
* vs прямий callOmlxMessages (`N_CURSOR_DOCGEN_BACKEND=omlx`).
|
|
4
|
-
*
|
|
5
|
-
* Однаковий 8-сет файлів, однаковий оркестратор (E1+E2+E3+E4), різний backend.
|
|
6
|
-
* Пише в /tmp/docgen-compare/{pi,direct}/<idx>-<stem>.md і збирає метрики.
|
|
7
|
-
*
|
|
8
|
-
* Запуск: node npm/skills/docgen/js/docgen-compare-pi-vs-direct.mjs [--from N] [--limit N]
|
|
9
|
-
*/
|
|
10
|
-
import { readFileSync, mkdirSync, writeFileSync, existsSync } from 'node:fs'
|
|
11
|
-
import { join, resolve, basename } from 'node:path'
|
|
12
|
-
import { fileURLToPath } from 'node:url'
|
|
13
|
-
import { execSync } from 'node:child_process'
|
|
14
|
-
import { env } from 'node:process'
|
|
15
|
-
import { generateDoc } from './docgen-gen.mjs'
|
|
16
|
-
import { extractFacts } from './docgen-extract.mjs'
|
|
17
|
-
|
|
18
|
-
const ROOT = resolve(fileURLToPath(import.meta.url), '../../../../..')
|
|
19
|
-
const TMP = '/tmp/docgen-compare'
|
|
20
|
-
|
|
21
|
-
const args = process.argv.slice(2)
|
|
22
|
-
const limitIdx = args.indexOf('--limit')
|
|
23
|
-
const limit = limitIdx !== -1 ? Number(args[limitIdx + 1]) : 8
|
|
24
|
-
const fromIdx = args.indexOf('--from')
|
|
25
|
-
const from = fromIdx !== -1 ? Number(args[fromIdx + 1]) : 1
|
|
26
|
-
|
|
27
|
-
const scanOut = execSync('node npm/bin/n-cursor.js docgen scan', { cwd: ROOT, encoding: 'utf8' })
|
|
28
|
-
const all = JSON.parse(scanOut)
|
|
29
|
-
|
|
30
|
-
const local = []
|
|
31
|
-
for (const f of all) {
|
|
32
|
-
try {
|
|
33
|
-
const src = readFileSync(join(ROOT, f.sourcePath), 'utf8')
|
|
34
|
-
const facts = extractFacts(src, join(ROOT, f.sourcePath))
|
|
35
|
-
const sym = (facts.internalSymbols ?? []).length
|
|
36
|
-
if (sym < 4) local.push({ ...f, sym })
|
|
37
|
-
} catch {}
|
|
38
|
-
}
|
|
39
|
-
const slice = local.slice(from, from + limit)
|
|
40
|
-
|
|
41
|
-
mkdirSync(join(TMP, 'pi'), { recursive: true })
|
|
42
|
-
mkdirSync(join(TMP, 'direct'), { recursive: true })
|
|
43
|
-
|
|
44
|
-
async function runBackendAsync(kind) {
|
|
45
|
-
if (kind === 'direct') env.N_CURSOR_DOCGEN_BACKEND = 'omlx'
|
|
46
|
-
else delete env.N_CURSOR_DOCGEN_BACKEND
|
|
47
|
-
const out = { ok: 0, err: 0, totalMs: 0, scores: [], lengths: [], errors: [], times: [] }
|
|
48
|
-
console.log(`\n══════ Backend: ${kind} ══════`)
|
|
49
|
-
for (let i = 0; i < slice.length; i++) {
|
|
50
|
-
const f = slice[i]
|
|
51
|
-
const t0 = Date.now()
|
|
52
|
-
const stem = basename(f.sourcePath).replace(/\.[^.]+$/, '')
|
|
53
|
-
const destFile = join(TMP, kind, `${String(i + 1).padStart(2, '0')}-${stem}.md`)
|
|
54
|
-
process.stdout.write(` [${i + 1}/${slice.length}] sym=${f.sym} ${f.sourcePath} ... `)
|
|
55
|
-
try {
|
|
56
|
-
const r = await generateDoc(join(ROOT, f.sourcePath), { symThreshold: 999, cloudModel: null })
|
|
57
|
-
writeFileSync(destFile, r.md)
|
|
58
|
-
const ms = Date.now() - t0
|
|
59
|
-
out.ok++
|
|
60
|
-
out.totalMs += ms
|
|
61
|
-
out.times.push(ms)
|
|
62
|
-
out.scores.push(r.score ?? 0)
|
|
63
|
-
out.lengths.push(r.md.length)
|
|
64
|
-
process.stdout.write(`✓ ${Math.round(ms / 1000)}s score=${r.score ?? '?'} chars=${r.md.length}\n`)
|
|
65
|
-
} catch (error) {
|
|
66
|
-
out.err++
|
|
67
|
-
out.errors.push({ path: f.sourcePath, msg: error.message })
|
|
68
|
-
process.stdout.write(`✗ ${error.message}\n`)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return out
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const direct = await runBackendAsync('direct')
|
|
75
|
-
const pi = await runBackendAsync('pi')
|
|
76
|
-
|
|
77
|
-
function avg(a) { return a.length ? Math.round(a.reduce((x, y) => x + y, 0) / a.length) : 0 }
|
|
78
|
-
function median(a) {
|
|
79
|
-
if (!a.length) return 0
|
|
80
|
-
const s = a.toSorted((x, y) => x - y)
|
|
81
|
-
return s[Math.floor(s.length / 2)]
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const report = {
|
|
85
|
-
files: slice.map(f => f.sourcePath),
|
|
86
|
-
direct: { ok: direct.ok, err: direct.err, avgMs: avg(direct.times), medianMs: median(direct.times), avgScore: avg(direct.scores), avgChars: avg(direct.lengths), totalSec: Math.round(direct.totalMs / 1000) },
|
|
87
|
-
pi: { ok: pi.ok, err: pi.err, avgMs: avg(pi.times), medianMs: median(pi.times), avgScore: avg(pi.scores), avgChars: avg(pi.lengths), totalSec: Math.round(pi.totalMs / 1000) }
|
|
88
|
-
}
|
|
89
|
-
writeFileSync(join(TMP, 'report.json'), JSON.stringify(report, null, 2))
|
|
90
|
-
|
|
91
|
-
console.log(`\n${'─'.repeat(60)}\nA/B SUMMARY (${slice.length} файлів, той самий оркестратор)\n${'─'.repeat(60)}`)
|
|
92
|
-
console.log(`Backend | ok | err | avg s | median s | avg score | avg chars | total s`)
|
|
93
|
-
console.log(`direct (curl) | ${direct.ok} | ${direct.err} | ${Math.round(report.direct.avgMs / 1000)} | ${Math.round(report.direct.medianMs / 1000)} | ${report.direct.avgScore} | ${report.direct.avgChars} | ${report.direct.totalSec}`)
|
|
94
|
-
console.log(`pi cli | ${pi.ok} | ${pi.err} | ${Math.round(report.pi.avgMs / 1000)} | ${Math.round(report.pi.medianMs / 1000)} | ${report.pi.avgScore} | ${report.pi.avgChars} | ${report.pi.totalSec}`)
|
|
95
|
-
console.log(`\nФайли: ${TMP}/{direct,pi}/<idx>-<stem>.md\nReport: ${TMP}/report.json`)
|