@nitra/cursor 12.2.0 → 12.3.1
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/CHANGELOG.md +12 -0
- package/package.json +2 -2
- package/rules/doc-files/js/docgen-crc.mjs +4 -31
- package/rules/doc-files/js/docgen-extract-anchors.mjs +1 -15
- package/rules/doc-files/js/docgen-files-batch.mjs +1 -12
- package/rules/doc-files/js/docgen-judge-measure.mjs +10 -18
- package/rules/doc-files/js/docs/docgen-crc.md +1 -1
- package/rules/doc-files/js/docs/docgen-extract-anchors.md +1 -1
- package/rules/doc-files/js/docs/docgen-files-batch.md +1 -1
- package/rules/doc-files/js/docs/docgen-judge-measure.md +1 -1
- package/rules/doc-files/js/docs/lint.md +1 -1
- package/rules/doc-files/js/lint.mjs +1 -16
- package/rules/lint/js/docs/orchestrate.md +2 -2
- package/rules/lint/js/orchestrate.mjs +8 -17
- package/rules/text/lint/cspell-fix.mjs +2 -1
- package/rules/text/lint/docs/cspell-fix.md +15 -9
- package/scripts/lib/adr/docs/normalize-cli.md +15 -15
- package/scripts/lib/adr/normalize-cli.mjs +3 -2
- package/scripts/lib/fix/analyze-escalation.mjs +35 -0
- package/scripts/lib/fix/docs/analyze-escalation.md +3 -1
- package/skills/doc-aggregate/js/docgen-ignore.mjs +1 -8
- package/skills/doc-aggregate/js/docs/docgen-ignore.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [12.3.1] - 2026-06-20
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- npm-module конформність: module-level JSDoc → pointer (`/** @see ./docs/… */`) у docgen-crc/extract-anchors/files-batch/judge-measure, doc-files/lint, lint/orchestrate, doc-aggregate/docgen-ignore; doc-CRC перештамповано; eslint --fix-стан прийнято як канон
|
|
8
|
+
|
|
9
|
+
## [12.3.0] - 2026-06-19
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- lint --full: резюме викликів моделей у stdout (локальна / cloud-min / cloud-avg) через reportRunStats/summarizeCalls
|
|
14
|
+
|
|
3
15
|
## [12.2.0] - 2026-06-19
|
|
4
16
|
|
|
5
17
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitra/cursor",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.3.1",
|
|
4
4
|
"description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -68,4 +68,4 @@
|
|
|
68
68
|
"skills": "skills",
|
|
69
69
|
"extensions": ".pi-template/extensions"
|
|
70
70
|
}
|
|
71
|
-
}
|
|
71
|
+
}
|
|
@@ -1,34 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CRC32 джерела + YAML-frontmatter файлової документації.
|
|
3
|
-
*
|
|
4
|
-
* Кожна файлова дока несе у frontmatter контрольну суму байтів джерела на момент
|
|
5
|
-
* генерації. Це детермінований маркер застарілості: `crc32(поточне джерело)` звіряється
|
|
6
|
-
* з `crc` у доці — розбіжність (або відсутня дока) означає, що дока відстала від коду.
|
|
7
|
-
* CRC не залежить від git-стану (rebase, незакомічене, гілки), тож придатний і для
|
|
8
|
-
* per-edit hook (бачить лише змінений файл), і для повного сканування.
|
|
9
|
-
*
|
|
10
|
-
* Degraded-маркер (ADR 260610-2228): якщо локальний конвеєр не дотягнув до порогу
|
|
11
|
-
* якості, дока все одно пишеться, а frontmatter додатково несе `score` (det-оцінка)
|
|
12
|
-
* та `issues` (коди проблем). CRC при цьому свіжий — Stop-гейт не блокує задачі через
|
|
13
|
-
* слабкість моделі; борг видимий через `check --degraded` і автоматично доретраюється
|
|
14
|
-
* наступним `gen` (рівно один раз на версію джерела — далі `retried: true` у frontmatter).
|
|
15
|
-
*
|
|
16
|
-
* Frontmatter — єдиний дозволений виняток із правила «чистий Markdown без HTML»:
|
|
17
|
-
* це машинні метадані, не контент. Формат:
|
|
18
|
-
*
|
|
19
|
-
* ---
|
|
20
|
-
* docgen:
|
|
21
|
-
* source: src/lib/foo.js
|
|
22
|
-
* crc: a3f1c9e0
|
|
23
|
-
* model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
24
|
-
* score: 55
|
|
25
|
-
* issues: short-behavior,internal-name:bar
|
|
26
|
-
* ---
|
|
27
|
-
*
|
|
28
|
-
* `model` — повний id моделі-генератора (як повертає resolveModel, із префіксом
|
|
29
|
-
* провайдера). Пасивна метадата: маркер «віку» доки за моделлю на додачу до CRC
|
|
30
|
-
* джерела. На staleness НЕ впливає — звіряється лише `crc`.
|
|
31
|
-
*/
|
|
1
|
+
/** @see ./docs/docgen-crc.md */
|
|
32
2
|
import { existsSync, readFileSync } from 'node:fs'
|
|
33
3
|
import { basename, extname } from 'node:path'
|
|
34
4
|
import { crc32 as zlibCrc32 } from 'node:zlib'
|
|
@@ -173,6 +143,9 @@ export function buildDocFrontmatter(source, crc, quality = null, model = null) {
|
|
|
173
143
|
*/
|
|
174
144
|
const LEADING_H1_RE = /^#[^\n]*\n+/u
|
|
175
145
|
|
|
146
|
+
/**
|
|
147
|
+
*
|
|
148
|
+
*/
|
|
176
149
|
export function stampDoc(md, source, crc, quality = null, model = null) {
|
|
177
150
|
const { body } = parseDocFrontmatter(md)
|
|
178
151
|
const cleanBody = body.replace(LEADING_NEWLINES_RE, '').replace(LEADING_H1_RE, '')
|
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E1 (Fact-anchoring): детермінований витяг «анкорів» — конкретних фрагментів
|
|
3
|
-
* з коду, які LLM зобовʼязана згадати в документації, щоб не зісковзнути на
|
|
4
|
-
* generic-фрази.
|
|
5
|
-
*
|
|
6
|
-
* Категорії анкорів:
|
|
7
|
-
* - urls : усі https?://… у вихідному коді
|
|
8
|
-
* - magicStrings : export const X = '…' з непорожнім value (≤120 символів)
|
|
9
|
-
* - errorMarkers : суфікси повідомлень про помилки виду `(rule.mdc)`
|
|
10
|
-
* - configRefs : посилання на .json-конфіги проєкту (.n-cursor.json, …)
|
|
11
|
-
* - examples : ```…```-блоки у file-header JSDoc (першому коментарі файла)
|
|
12
|
-
*
|
|
13
|
-
* Всі регулярки — на сирому src без AST: дешево, безпечно, без false-positive
|
|
14
|
-
* критичної ваги (надмір — менша проблема, ніж пропуск).
|
|
15
|
-
*/
|
|
1
|
+
/** @see ./docs/docgen-extract-anchors.md */
|
|
16
2
|
|
|
17
3
|
const URL_RE = /https?:\/\/[^\s'"`)<>]+/g
|
|
18
4
|
// Після обрізання template-частини URL має лишитися host (R10).
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JS-оркестрація генерації файлових док (local-only, ADR 260610-2228).
|
|
3
|
-
*
|
|
4
|
-
* Уся черга/батчинг/CRC-штамп живуть тут, а не в контексті моделі — тому
|
|
5
|
-
* масовий перший прогін на сотні файлів не «заморює» агента. Конвеєр суто
|
|
6
|
-
* локальний: жодних cloud-ескалацій; якщо det-score нижче порогу — дока все
|
|
7
|
-
* одно пишеться з degraded-маркером (`score`/`issues` у frontmatter), а наступний
|
|
8
|
-
* `gen` автоматично доретраює такі доки (один раз на версію джерела — далі `retried:true`).
|
|
9
|
-
*
|
|
10
|
-
* Перед масовим прогоном — health-check omlx: memory-guard зайнятої 8GB машини
|
|
11
|
-
* означає «відклади прогін», а не сотні хибних «✗» у звіті.
|
|
12
|
-
*/
|
|
1
|
+
/** @see ./docs/docgen-files-batch.md */
|
|
13
2
|
import { readFileSync, readdirSync, mkdirSync, writeFileSync, existsSync, statSync } from 'node:fs'
|
|
14
3
|
import { basename, dirname, join, relative } from 'node:path'
|
|
15
4
|
|
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* docgen-judge-measure.mjs — Q4 офлайн-вимірювач (spec 2026-06-14-docgen-judge-design).
|
|
4
|
-
*
|
|
5
|
-
* Міряє false-positive rate детермінованого `scoreDoc`: серед доків, що ПРОЙШЛИ
|
|
6
|
-
* (score ≥ threshold), який % сильна хмарна модель-суддя класифікує як
|
|
7
|
-
* `generic`/`inaccurate`. Це число вирішує, чи будувати рантайм-judge-гейт.
|
|
8
|
-
*
|
|
9
|
-
* Генерація: локальна (N_LOCAL_MIN_MODEL, omlx/* → прямий HTTP) — реальний пайплайн.
|
|
10
|
-
* Суддя: openai-codex/gpt-5.4-mini (сильніша хмара, ніж генератор — інакше вимір беззмістовний).
|
|
11
|
-
* Обидва — через існуючий `../../../lib/llm.mjs callLlm` (маршрутизація за префіксом).
|
|
12
|
-
*
|
|
13
|
-
* Кеш на диску (за хешем контенту) → повторні прогони не регенерують і не пересуджують.
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* node docgen-judge-measure.mjs <file1> <file2> ...
|
|
17
|
-
* MEASURE_CACHE=/tmp/x N_CLOUD_MIN_MODEL=openai-codex/gpt-5.4 node docgen-judge-measure.mjs ...
|
|
18
|
-
*/
|
|
1
|
+
/** @see ./docs/docgen-judge-measure.md */
|
|
19
2
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs'
|
|
20
3
|
import { createHash } from 'node:crypto'
|
|
21
4
|
import { join } from 'node:path'
|
|
@@ -39,10 +22,16 @@ Prefer "inaccurate" over "generic" if any claim is wrong. Respond with ONLY a JS
|
|
|
39
22
|
|
|
40
23
|
const sha = s => createHash('sha256').update(s).digest('hex').slice(0, 16)
|
|
41
24
|
|
|
25
|
+
/**
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
42
28
|
function cacheGet(key) {
|
|
43
29
|
const p = join(CACHE_DIR, key + '.json')
|
|
44
30
|
return existsSync(p) ? JSON.parse(readFileSync(p, 'utf8')) : null
|
|
45
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
46
35
|
function cacheSet(key, val) {
|
|
47
36
|
if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true })
|
|
48
37
|
writeFileSync(join(CACHE_DIR, key + '.json'), JSON.stringify(val))
|
|
@@ -83,6 +72,9 @@ function judgeCached(src, doc) {
|
|
|
83
72
|
return { ...v, cached: false }
|
|
84
73
|
}
|
|
85
74
|
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
*/
|
|
86
78
|
function main() {
|
|
87
79
|
const files = process.argv.slice(2).filter(f => !f.startsWith('--'))
|
|
88
80
|
if (!files.length) {
|
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: docgen-crc.mjs
|
|
4
4
|
resource: npm/rules/doc-files/js/docgen-crc.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: cca0a79f
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
Детермінований маркер актуальності файлових док: контрольна сума джерела у frontmatter плюс опційний degraded-маркер якості. Єдине джерело правди про «дока свіжа/застаріла/неякісна» для генерації, перевірок і хуків.
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Адаптер агрегатора `n-cursor lint` для правила doc-files (opportunistic LLM-fix
|
|
3
|
-
* tier, спека docs/specs/2026-06-15-opportunistic-llm-fix-tier.md).
|
|
4
|
-
*
|
|
5
|
-
* Quick-фаза отримує список змінених файлів і мапить їх у пари в **обидва** боки:
|
|
6
|
-
* - змінене **джерело** (`.js/.mjs/.ts/.vue/.py/.rs`) → перевірка його доки `<dir>/docs/<stem>.md`;
|
|
7
|
-
* - змінена/видалена **дока** (`<dir>/docs/<stem>.md`) → перевірка відповідного джерела
|
|
8
|
-
* (той самий stem у каталозі над текою `docs`).
|
|
9
|
-
* Ci-фаза (files === undefined) проганяє повний скан дерева.
|
|
10
|
-
*
|
|
11
|
-
* Детект — `missing` ∪ `crc-mismatch` (детермінований CRC, 0 LLM-токенів); degraded не блокує.
|
|
12
|
-
* Поведінка за осями (правило має `meta.json: llmFix:true`):
|
|
13
|
-
* - `readOnly` (CI/hook): **лише детект** — нуль мутацій/LLM, exit 1 на stale (детермінований гейт);
|
|
14
|
-
* - fix-by-default + omlx **піднято**: opportunistic-генерація stale-доків → re-detect → 0 якщо полагоджено;
|
|
15
|
-
* - fix-by-default + omlx **недоступно**: fix пропущено (повідомлення) + exit 1 — гейт тримається, без false-green.
|
|
16
|
-
*/
|
|
1
|
+
/** @see ./docs/lint.md */
|
|
17
2
|
import { join, dirname, basename, extname } from 'node:path'
|
|
18
3
|
import { existsSync, readdirSync } from 'node:fs'
|
|
19
4
|
|
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: orchestrate.mjs
|
|
4
4
|
resource: npm/rules/lint/js/orchestrate.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 0ab5b22c
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
@@ -16,7 +16,7 @@ docgen:
|
|
|
16
16
|
selectLintRules вибирає і сортує ідентифікатори правил на основі їхнього обсягу дії (`per-file` або `full`) та прапорця `--full`.
|
|
17
17
|
runLint запускає оркестрацію лінтування: або виконує перевірку конформності для заданих правил, або ітерує по алфавітно відсортованих правилах (`runPerFileRules`), запускаючи лінтер для змінених файлів (за замовчуванням), або виконує перевірку конформності всього репозиторію при використанні прапорця `--full`.
|
|
18
18
|
**Fail-fast — лише в `--read-only`** (CI/детект): перший ненульовий код спиняє. У fix-режимі (default) ненульовий код per-file правила НЕ спиняє — проганяються всі правила й виконується крок виправлення (конформність-драбина), а повертається найгірший код.
|
|
19
|
-
У режимі `--full` без `--read-only` після конформність-фази (`runFullConformancePhase`) викликається escalation-аналітика (`analyze-escalation.mjs`): фіксує зсув escalation-логу до фази, після — аналізує записи саме цього прогону.
|
|
19
|
+
У режимі `--full` без `--read-only` після конформність-фази (`runFullConformancePhase`) друкується резюме викликів моделей за прогін (`reportRunStats`: локальна / cloud-min / cloud-avg) і викликається escalation-аналітика (`analyze-escalation.mjs`): фіксує зсув escalation-логу до фази, після — аналізує записи саме цього прогону. Жодне з цього не впливає на exit-код lint.
|
|
20
20
|
|
|
21
21
|
## Публічний API
|
|
22
22
|
|
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Оркестратор `n-cursor lint` — дві ортогональні осі (spec 2026-06-14-lint-rule-consolidation
|
|
3
|
-
* + компаньйон 2026-06-14-lint-orchestrator-fix-readonly-unification):
|
|
4
|
-
* - **scope** (`--full`): default = дельта vs origin (лише `per-file` правила);
|
|
5
|
-
* `--full` = весь репо (`per-file` ∪ `full` правила);
|
|
6
|
-
* - **behavior** (`--read-only`): default = fix; `--read-only` = лише детект без мутацій.
|
|
7
|
-
*
|
|
8
|
-
* Data-driven: сканує `rules/<id>/meta.json` за полем `lint` (`per-file`|`full`),
|
|
9
|
-
* викликає `rules/<id>/js/lint.mjs` → `lint(files, cwd, { readOnly })`:
|
|
10
|
-
* - default scope: `files` = змінені відносно origin (`collectChangedFilesSince`);
|
|
11
|
-
* - `--full`: `files = undefined` — весь проєкт.
|
|
12
|
-
* Порядок правил — алфавітний. Fail-fast **лише в `--read-only`** (CI/детект): перший
|
|
13
|
-
* ненульовий код спиняє. У fix-режимі (default) ненульовий код НЕ спиняє — проганяємо всі
|
|
14
|
-
* правила й доходимо до кроку виправлення (конформність-драбина), повертаючи найгірший код.
|
|
15
|
-
*/
|
|
1
|
+
/** @see ./docs/orchestrate.md */
|
|
16
2
|
import { existsSync, readdirSync } from 'node:fs'
|
|
17
3
|
import { dirname, join } from 'node:path'
|
|
18
4
|
import { fileURLToPath } from 'node:url'
|
|
@@ -122,10 +108,15 @@ async function runPerFileRules(ids, ctx) {
|
|
|
122
108
|
* @returns {Promise<number>} код конформності
|
|
123
109
|
*/
|
|
124
110
|
async function runFullConformancePhase(cwd, readOnly, log) {
|
|
125
|
-
const { escalationLogSize, maybeAnalyzeEscalation } = await import(
|
|
111
|
+
const { escalationLogSize, maybeAnalyzeEscalation, reportRunStats } = await import(
|
|
112
|
+
'../../../scripts/lib/fix/analyze-escalation.mjs'
|
|
113
|
+
)
|
|
126
114
|
const escOffset = readOnly ? 0 : escalationLogSize()
|
|
127
115
|
const conformanceCode = await runConformance(cwd, readOnly, log)
|
|
128
|
-
if (!readOnly)
|
|
116
|
+
if (!readOnly) {
|
|
117
|
+
reportRunStats(escOffset, log) // резюме викликів моделей (локальна / cloud-min / cloud-avg)
|
|
118
|
+
maybeAnalyzeEscalation(cwd, escOffset, log)
|
|
119
|
+
}
|
|
129
120
|
return conformanceCode
|
|
130
121
|
}
|
|
131
122
|
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* Гейт: валідні слова після дописування у словник зникають; нерозкласифіковані та
|
|
15
15
|
* typo лишаються → cspell повертає !=0 → exit 1 (людина доправляє одруки вручну).
|
|
16
16
|
*/
|
|
17
|
+
import { env } from 'node:process'
|
|
17
18
|
import { spawnSync } from 'node:child_process'
|
|
18
19
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
19
20
|
import { join } from 'node:path'
|
|
@@ -27,7 +28,7 @@ const UNKNOWN_WORD_RE = /Unknown word \(([^)]+)\)/u
|
|
|
27
28
|
const MAX_CLASSIFY_WORDS = 80
|
|
28
29
|
|
|
29
30
|
/** Локальна fix-модель (рішення: єдиний knob `N_LOCAL_MIN_MODEL`). */
|
|
30
|
-
const fixModel = () =>
|
|
31
|
+
const fixModel = () => env.N_LOCAL_MIN_MODEL || ''
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Запускає `cspell .` із захопленням виводу.
|
|
@@ -3,24 +3,30 @@ type: JS Module
|
|
|
3
3
|
title: cspell-fix.mjs
|
|
4
4
|
resource: npm/rules/text/lint/cspell-fix.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 7b40e8f9
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 95
|
|
9
|
+
issues: anchor-miss:meta.json,judge:inaccurate:0.98
|
|
10
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
9
11
|
---
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## Огляд
|
|
14
|
+
|
|
15
|
+
Цей модуль інтегрує cspell у ланцюжок lint-text для класифікації невідомих слів згідно зі схемою omlx. Він витягує невідомі слова, класифікує їх за допомогою LLM (detect $\rightarrow$ omlx-класифікація) та автоматично дописує валідні терміни до словника `.cspell.json`. Нерозкласифіковані та ймовірні одруки залишаються для ручного рев'ю. Процес є read-only, оскільки він лише класифікує знахідки, а не переписує файли.
|
|
12
16
|
|
|
13
17
|
## Поведінка
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
unknownWords витягує унікальні невідомі слова з виводу cspell.
|
|
20
|
+
appendWordsToDict дописує класифіковані валідні слова до файлу .cspell.json, оновлюючи словник.
|
|
21
|
+
runCspellText запускає cspell, класифікує знахідки за допомогою LLM (якщо увімкнено) та повторно перевіряє код, повертаючи 0 при чистоті або 1 при знахідках.
|
|
17
22
|
|
|
18
23
|
## Публічний API
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
unknownWords — Збирає унікальні слова, які не були знайдені у словнику cspell.
|
|
26
|
+
appendWordsToDict — Додає зібрані слова до файлу `.cspell.json#words` у відсортованому та унікальному вигляді для перегляду у Git.
|
|
27
|
+
runCspellText — Виконує перевірку тексту за допомогою cspell, класифікуючи слова та оновлюючи словник за новою схемою.
|
|
22
28
|
|
|
23
29
|
## Гарантії поведінки
|
|
24
30
|
|
|
25
|
-
-
|
|
26
|
-
-
|
|
31
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
32
|
+
- За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
|
|
@@ -3,26 +3,27 @@ type: JS Module
|
|
|
3
3
|
title: normalize-cli.mjs
|
|
4
4
|
resource: npm/scripts/lib/adr/normalize-cli.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 63a18347
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 90
|
|
9
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
9
10
|
---
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
## Огляд
|
|
13
|
+
|
|
14
|
+
Цей файл є CLI-обгорткою для локального нормалізатора ADR. Він зчитує шляхи до чернеток (`--batch <file>`) та список чистих ADR (`--clean <file>`), використовуючи директорію ADR (`--adr-dir <dir>`) для резолву шляхів. Обгортка запускає `normalizePipeline`, яка генерує JSON-контракт у форматі `{ "operations": [...] }` та виводить його у stdout для подальшого парсингу bash-скриптом. Прогрес та помилки записуються у stderr. Поведінка нормалізатора може бути змінена через змінні середовища: `ADR_NORMALIZE_ALLOW_CLOUD` контролює можливість хмарної ескалації, а `ADR_NORMALIZE_VOTES` визначає кількість голосів self-consistency для чистих ADR.
|
|
12
15
|
|
|
13
16
|
## Поведінка
|
|
14
17
|
|
|
15
|
-
1.
|
|
16
|
-
2.
|
|
17
|
-
3.
|
|
18
|
-
4.
|
|
19
|
-
5.
|
|
20
|
-
6.
|
|
21
|
-
7.
|
|
22
|
-
8.
|
|
23
|
-
9.
|
|
24
|
-
10. `runAdrNormalizeLocalCli` друкує JSON-об'єкт, що містить операції, у stdout.
|
|
25
|
-
11. `runAdrNormalizeLocalCli` завершує роботу з кодом успіху.
|
|
18
|
+
1. Викликати runAdrNormalizeLocalCli.
|
|
19
|
+
2. Зчитувати список шляхів до чернеток батчу з файлу, вказаного через аргумент `--batch`.
|
|
20
|
+
3. Зчитувати список імен чистих ADR (кандидатів до злиття) з файлу, вказаного через аргумент `--clean`, якщо він наданий.
|
|
21
|
+
4. Визначати директорію ADR, використовуючи аргумент `--adr-dir` або за замовчуванням `cwd/docs/adr`.
|
|
22
|
+
5. Зчитувати вміст кожної чернетки батчу, резолвя шляхи відносно `--adr-dir`.
|
|
23
|
+
6. Зчитувати значення змінних середовища `ADR_NORMALIZE_ALLOW_CLOUD` та `ADR_NORMALIZE_VOTES`.
|
|
24
|
+
7. Викликати внутрішній механізм нормалізації, передаючи зібрані чернетки, список чистих ADR, та конфігурацію, отриману з змінних середовища.
|
|
25
|
+
8. Виводити інформацію про прогрес та статистику в stderr.
|
|
26
|
+
9. Виводити JSON-об'єкт, що містить операції, у stdout.
|
|
26
27
|
|
|
27
28
|
## Публічний API
|
|
28
29
|
|
|
@@ -30,5 +31,4 @@ runAdrNormalizeLocalCli — запускає субкоманду, виводя
|
|
|
30
31
|
|
|
31
32
|
## Гарантії поведінки
|
|
32
33
|
|
|
33
|
-
- Read-only:
|
|
34
|
-
- Не звертається до мережі.
|
|
34
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* ADR_NORMALIZE_ALLOW_CLOUD=1 дозволити хмарну ескалацію tier-каскаду (default off)
|
|
17
17
|
* ADR_NORMALIZE_VOTES=N голосів self-consistency для clean-ребер (default 2)
|
|
18
18
|
*/
|
|
19
|
+
import { env } from 'node:process'
|
|
19
20
|
import { readFileSync } from 'node:fs'
|
|
20
21
|
import { basename, isAbsolute, join } from 'node:path'
|
|
21
22
|
import { normalizePipeline } from './normalize-pipeline.mjs'
|
|
@@ -57,8 +58,8 @@ export function runAdrNormalizeLocalCli(argv) {
|
|
|
57
58
|
})
|
|
58
59
|
const cleanList = args.clean ? readLines(args.clean).map((c) => basename(c)) : []
|
|
59
60
|
|
|
60
|
-
const allowCloud =
|
|
61
|
-
const votes = Number(
|
|
61
|
+
const allowCloud = env.ADR_NORMALIZE_ALLOW_CLOUD === '1'
|
|
62
|
+
const votes = Number(env.ADR_NORMALIZE_VOTES) || 2
|
|
62
63
|
|
|
63
64
|
const { operations, stats, trace } = normalizePipeline(drafts, cleanList, {
|
|
64
65
|
allowCloud,
|
|
@@ -107,6 +107,41 @@ export function readEscalationRecords(path, sinceOffset = 0) {
|
|
|
107
107
|
return out
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/** Маркер skip-запису avg-рунга (кеп вичерпано) — НЕ фактичний виклик моделі. */
|
|
111
|
+
const AVG_SKIP_MARKER = 'cloud-avg cap reached'
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Рахує фактичні виклики моделей за тирами (skip-записи avg-кепу не рахуються).
|
|
115
|
+
* @param {object[]} records записи рунгів
|
|
116
|
+
* @returns {{ local: number, cloudMin: number, cloudAvg: number }} лічильники викликів
|
|
117
|
+
*/
|
|
118
|
+
export function summarizeCalls(records) {
|
|
119
|
+
const stats = { local: 0, cloudMin: 0, cloudAvg: 0 }
|
|
120
|
+
for (const r of records) {
|
|
121
|
+
if (r.callError === AVG_SKIP_MARKER) continue
|
|
122
|
+
if (r.tier === 'cloud-avg') stats.cloudAvg++
|
|
123
|
+
else if (r.tier === 'cloud-min') stats.cloudMin++
|
|
124
|
+
else if (typeof r.tier === 'string' && r.tier.startsWith('local')) stats.local++
|
|
125
|
+
}
|
|
126
|
+
return stats
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Друкує резюме викликів моделей за цей прогін (локальна / cloud-min / cloud-avg).
|
|
131
|
+
* No-op, якщо викликів не було. Читає записи від `sinceOffset`.
|
|
132
|
+
* @param {number} sinceOffset байтовий зсув логу перед прогоном
|
|
133
|
+
* @param {(s: string) => void} log логер
|
|
134
|
+
* @returns {void}
|
|
135
|
+
*/
|
|
136
|
+
export function reportRunStats(sinceOffset, log) {
|
|
137
|
+
const { local, cloudMin, cloudAvg } = summarizeCalls(readEscalationRecords(escalationLogPath(), sinceOffset))
|
|
138
|
+
if (local + cloudMin + cloudAvg === 0) return
|
|
139
|
+
log(
|
|
140
|
+
`\n📊 LLM-виклики fix-конформності (цей прогін): ` +
|
|
141
|
+
`локальна ${local} · cloud-min ${cloudMin} · cloud-avg ${cloudAvg}\n`
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
110
145
|
/**
|
|
111
146
|
* Стискає запис до полів, важливих для аналізу (без ts/ms-шуму).
|
|
112
147
|
* @param {object} r сирий запис рунга
|
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: analyze-escalation.mjs
|
|
4
4
|
resource: npm/scripts/lib/fix/analyze-escalation.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 5a586df6
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
@@ -16,6 +16,8 @@ docgen:
|
|
|
16
16
|
|
|
17
17
|
## Публічний API
|
|
18
18
|
|
|
19
|
+
- `summarizeCalls(records)` — лічильники фактичних викликів за тирами `{ local, cloudMin, cloudAvg }` (skip-записи avg-кепу не рахуються).
|
|
20
|
+
- `reportRunStats(sinceOffset, log)` — друкує резюме викликів моделей за прогін (no-op, якщо викликів не було).
|
|
19
21
|
- `analysisEnabled()` — чи дозволено авто-аналіз (kill-switch `N_CURSOR_FIX_ANALYZE`).
|
|
20
22
|
- `escalationLogSize(path?)` — розмір логу в байтах (since-offset).
|
|
21
23
|
- `readEscalationRecords(path, sinceOffset?)` — записи від зсуву.
|
|
@@ -1,9 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export спільного списку ignore-глобів із правила doc-files.
|
|
3
|
-
*
|
|
4
|
-
* Канонічне джерело — `npm/rules/doc-files/js/docgen-ignore.mjs`: скіл doc-aggregate
|
|
5
|
-
* і правило doc-files мусять бачити однакове дерево кодових файлів, інакше агрегат
|
|
6
|
-
* посилатиметься на файли без док (або навпаки). Залежність спрямована
|
|
7
|
-
* doc-aggregate → doc-files за ADR про розбиття docgen.
|
|
8
|
-
*/
|
|
1
|
+
/** @see ./docs/docgen-ignore.md */
|
|
9
2
|
export * from '../../../rules/doc-files/js/docgen-ignore.mjs'
|
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: docgen-ignore.mjs
|
|
4
4
|
resource: npm/skills/doc-aggregate/js/docgen-ignore.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 3b579230
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
Re-export спільного списку ignore-глобів зі скіла doc-files: обидва скіли документації (пофайлові доки й агрегати) мусять бачити однакове дерево кодових файлів, інакше агрегат посилатиметься на файли без док або навпаки.
|