@nitra/cursor 12.0.3 → 12.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/CHANGELOG.md +13 -0
- package/bin/n-cursor.js +10 -1
- package/package.json +1 -1
- package/rules/lint/js/docs/orchestrate.md +4 -2
- package/rules/lint/js/orchestrate.mjs +61 -23
- package/rules/release/docs/release.md +14 -212
- package/rules/tool-surface/docs/fix.md +32 -0
- package/rules/tool-surface/docs/index.md +11 -0
- package/scripts/lib/adr/docs/normalize-pipeline.md +29 -20
- package/scripts/lib/docs/worktree-notice.md +14 -15
- package/scripts/lib/fix/analyze-escalation.mjs +315 -0
- package/scripts/lib/fix/docs/analyze-escalation.md +31 -0
- package/scripts/lib/fix/docs/escalation-log.md +27 -0
- package/scripts/lib/fix/docs/index.md +2 -0
- package/scripts/lib/fix/docs/llm-fix-apply.md +2 -2
- package/scripts/lib/fix/docs/llm-worker.md +8 -11
- package/scripts/lib/fix/docs/orchestrator.md +20 -11
- package/scripts/lib/fix/docs/t0.md +5 -6
- package/scripts/lib/fix/escalation-log.mjs +92 -0
- package/scripts/lib/fix/llm-fix-apply.mjs +7 -3
- package/scripts/lib/fix/llm-worker.mjs +56 -20
- package/scripts/lib/fix/orchestrator.mjs +158 -55
- package/scripts/lib/fix/t0.mjs +53 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [12.2.0] - 2026-06-19
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- lint fail-fast лише для --read-only: у fix-режимі per-file правило з ненульовим кодом не спиняє — прогін доходить до кроку виправлення (конформність-драбина). + applyChanges створює батьківську теку (mkdirSync) перед записом. + детермінований T0-патерн changelog-create-change-file (writeChange замість LLM)
|
|
8
|
+
|
|
9
|
+
## [12.1.0] - 2026-06-19
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- fix-конформність: драбина ескалації моделей (local-min → local-min+feedback → cloud-min → cloud-avg) із per-рунговим escalation-логом (.n-cursor/fix-escalation.jsonl, поле diagnosis) і кепом --max-avg; прибрано --max-iter/MODEL_HEAVY
|
|
14
|
+
- analyze-escalation: аналіз escalation-логу хмарною avg-моделлю (чанкінг + map-reduce) → markdown-звіт із пропозиціями нових T0-патернів / правок .mdc / змін скриптів; CLI n-cursor analyze-escalation і авто-хук наприкінці lint --full (kill-switch N_CURSOR_FIX_ANALYZE)
|
|
15
|
+
|
|
3
16
|
## [12.0.3] - 2026-06-18
|
|
4
17
|
|
|
5
18
|
### Fixed
|
package/bin/n-cursor.js
CHANGED
|
@@ -1599,6 +1599,15 @@ try {
|
|
|
1599
1599
|
|
|
1600
1600
|
break
|
|
1601
1601
|
}
|
|
1602
|
+
case 'analyze-escalation': {
|
|
1603
|
+
// n-cursor analyze-escalation — читає весь escalation-лог (.n-cursor/fix-escalation.jsonl),
|
|
1604
|
+
// чанкує й просить хмарну avg-модель запропонувати, як зменшити LLM-залежність fix-
|
|
1605
|
+
// конформності (нові T0-патерни / правки .mdc / зміни скриптів). Звіт → markdown.
|
|
1606
|
+
const { runEscalationAnalysisCli } = await import('../scripts/lib/fix/analyze-escalation.mjs')
|
|
1607
|
+
process.exitCode = await runEscalationAnalysisCli(args)
|
|
1608
|
+
|
|
1609
|
+
break
|
|
1610
|
+
}
|
|
1602
1611
|
case 'taze': {
|
|
1603
1612
|
// n-cursor taze diff — read-only semver-diff package.json ↔ package.json.taze-bak
|
|
1604
1613
|
// (root + воркспейси) для скілу n-taze: скрипт класифікує major-оновлення,
|
|
@@ -1697,7 +1706,7 @@ try {
|
|
|
1697
1706
|
default: {
|
|
1698
1707
|
console.error(`❌ Невідома команда: ${command}`)
|
|
1699
1708
|
console.error(
|
|
1700
|
-
` Очікується: (без аргументів) синхронізація правил, rename-yaml-extensions, post-tool-use-fix, adr-normalize-local, lint, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text, lint-doc-files, fix-doc-files, coverage, coverage-fix, taze, start-check, change, release, skill, trace, doc-aggregate`
|
|
1709
|
+
` Очікується: (без аргументів) синхронізація правил, rename-yaml-extensions, post-tool-use-fix, adr-normalize-local, lint, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text, lint-doc-files, fix-doc-files, coverage, coverage-fix, analyze-escalation, taze, start-check, change, release, skill, trace, doc-aggregate`
|
|
1701
1710
|
)
|
|
1702
1711
|
process.exitCode = 1
|
|
1703
1712
|
}
|
package/package.json
CHANGED
|
@@ -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: f4eb439d
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
@@ -14,7 +14,9 @@ docgen:
|
|
|
14
14
|
|
|
15
15
|
Поведінка
|
|
16
16
|
selectLintRules вибирає і сортує ідентифікатори правил на основі їхнього обсягу дії (`per-file` або `full`) та прапорця `--full`.
|
|
17
|
-
runLint запускає оркестрацію лінтування: або виконує перевірку конформності для заданих правил, або ітерує по алфавітно відсортованих
|
|
17
|
+
runLint запускає оркестрацію лінтування: або виконує перевірку конформності для заданих правил, або ітерує по алфавітно відсортованих правилах (`runPerFileRules`), запускаючи лінтер для змінених файлів (за замовчуванням), або виконує перевірку конформності всього репозиторію при використанні прапорця `--full`.
|
|
18
|
+
**Fail-fast — лише в `--read-only`** (CI/детект): перший ненульовий код спиняє. У fix-режимі (default) ненульовий код per-file правила НЕ спиняє — проганяються всі правила й виконується крок виправлення (конформність-драбина), а повертається найгірший код.
|
|
19
|
+
У режимі `--full` без `--read-only` після конформність-фази (`runFullConformancePhase`) викликається escalation-аналітика (`analyze-escalation.mjs`): фіксує зсув escalation-логу до фази, після — аналізує записи саме цього прогону. Аналіз не впливає на exit-код lint.
|
|
18
20
|
|
|
19
21
|
## Публічний API
|
|
20
22
|
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* викликає `rules/<id>/js/lint.mjs` → `lint(files, cwd, { readOnly })`:
|
|
10
10
|
* - default scope: `files` = змінені відносно origin (`collectChangedFilesSince`);
|
|
11
11
|
* - `--full`: `files = undefined` — весь проєкт.
|
|
12
|
-
* Порядок правил — алфавітний. Fail-fast
|
|
12
|
+
* Порядок правил — алфавітний. Fail-fast **лише в `--read-only`** (CI/детект): перший
|
|
13
|
+
* ненульовий код спиняє. У fix-режимі (default) ненульовий код НЕ спиняє — проганяємо всі
|
|
14
|
+
* правила й доходимо до кроку виправлення (конформність-драбина), повертаючи найгірший код.
|
|
13
15
|
*/
|
|
14
16
|
import { existsSync, readdirSync } from 'node:fs'
|
|
15
17
|
import { dirname, join } from 'node:path'
|
|
@@ -80,6 +82,53 @@ function readAllMeta(rulesDir) {
|
|
|
80
82
|
return out
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Per-file фаза: проганяє лінтер кожного правила. Fail-fast лише в read-only.
|
|
87
|
+
* @param {string[]} ids id правил (алфавітно)
|
|
88
|
+
* @param {{ rulesDir: string, changed: string[]|undefined, cwd: string, readOnly: boolean, metaById: Record<string, {llmFix?: boolean}>, log: (s: string) => void }} ctx контекст
|
|
89
|
+
* @returns {Promise<{ stop: boolean, code: number }>} `stop` — read-only fail-fast; `code` — найгірший код
|
|
90
|
+
*/
|
|
91
|
+
async function runPerFileRules(ids, ctx) {
|
|
92
|
+
const { rulesDir, changed, cwd, readOnly, metaById, log } = ctx
|
|
93
|
+
let worst = 0
|
|
94
|
+
for (const id of ids) {
|
|
95
|
+
const lintPath = join(rulesDir, id, 'js', 'lint.mjs')
|
|
96
|
+
if (!existsSync(lintPath)) {
|
|
97
|
+
log(`⚠️ lint: правило ${id} має lint-фазу, але немає js/lint.mjs — пропускаю.\n`)
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
// lintPath = join(rulesDir, id, …) — суто package-internal (rulesDir пакета + id зі
|
|
101
|
+
// selectLintRules за власним meta), не зовнішній вхід → ін'єкції немає.
|
|
102
|
+
// eslint-disable-next-line no-unsanitized/method
|
|
103
|
+
const mod = await import(lintPath)
|
|
104
|
+
// `llmFix` (opt-in opportunistic LLM-fix, спека 2026-06-15): лише правила з
|
|
105
|
+
// `meta.json: llmFix:true` отримують fix-сходинку; решта — detect-only.
|
|
106
|
+
const llmFix = metaById[id]?.llmFix === true
|
|
107
|
+
const code = await mod.lint(changed, cwd, { readOnly, llmFix })
|
|
108
|
+
if (code !== 0) {
|
|
109
|
+
if (readOnly) return { stop: true, code } // read-only — fail-fast (детект для CI)
|
|
110
|
+
worst = code // fix-режим — фіксуємо, але йдемо далі до кроку виправлення
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { stop: false, code: worst }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Конформність-фаза `--full` (поглинула `fix`): escalation-аналітику обрамляє зсувом логу
|
|
118
|
+
* (записи саме цього прогону), у fix-режимі по конформності викликає аналіз.
|
|
119
|
+
* @param {string} cwd корінь
|
|
120
|
+
* @param {boolean} readOnly лише детект
|
|
121
|
+
* @param {(s: string) => void} log логер
|
|
122
|
+
* @returns {Promise<number>} код конформності
|
|
123
|
+
*/
|
|
124
|
+
async function runFullConformancePhase(cwd, readOnly, log) {
|
|
125
|
+
const { escalationLogSize, maybeAnalyzeEscalation } = await import('../../../scripts/lib/fix/analyze-escalation.mjs')
|
|
126
|
+
const escOffset = readOnly ? 0 : escalationLogSize()
|
|
127
|
+
const conformanceCode = await runConformance(cwd, readOnly, log)
|
|
128
|
+
if (!readOnly) maybeAnalyzeEscalation(cwd, escOffset, log)
|
|
129
|
+
return conformanceCode
|
|
130
|
+
}
|
|
131
|
+
|
|
83
132
|
/**
|
|
84
133
|
* Запускає lint-оркестрацію.
|
|
85
134
|
* @param {{ full?: boolean, readOnly?: boolean, rules?: string[], cwd?: string, rulesDir?: string, log?: (s: string) => void }} [opts] параметри
|
|
@@ -110,29 +159,18 @@ export async function runLint(opts = {}) {
|
|
|
110
159
|
|
|
111
160
|
const metaById = readAllMeta(rulesDir)
|
|
112
161
|
const ids = selectLintRules(metaById, full)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
log(`⚠️ lint: правило ${id} має lint-фазу, але немає js/lint.mjs — пропускаю.\n`)
|
|
117
|
-
continue
|
|
118
|
-
}
|
|
119
|
-
// lintPath = join(rulesDir, id, …) — суто package-internal (rulesDir пакета + id зі
|
|
120
|
-
// selectLintRules за власним meta), не зовнішній вхід → ін'єкції немає.
|
|
121
|
-
// eslint-disable-next-line no-unsanitized/method
|
|
122
|
-
const mod = await import(lintPath)
|
|
123
|
-
// `llmFix` (opt-in opportunistic LLM-fix, спека 2026-06-15): лише правила з
|
|
124
|
-
// `meta.json: llmFix:true` отримують fix-сходинку; решта — detect-only. Це й
|
|
125
|
-
// забезпечує safety-тріаж (логічні лінтери не вмикають LLM-fix випадково).
|
|
126
|
-
const llmFix = metaById[id]?.llmFix === true
|
|
127
|
-
const code = await mod.lint(changed, cwd, { readOnly, llmFix })
|
|
128
|
-
if (code !== 0) return code
|
|
129
|
-
}
|
|
162
|
+
const perFile = await runPerFileRules(ids, { rulesDir, changed, cwd, readOnly, metaById, log })
|
|
163
|
+
if (perFile.stop) return perFile.code
|
|
164
|
+
let worst = perFile.code
|
|
130
165
|
|
|
131
|
-
//
|
|
132
|
-
//
|
|
166
|
+
// Конформність-фаза: whole-repo, лише у `--full`. Кастомний rulesDir (юніт-тести
|
|
167
|
+
// селектора) — реальний пакет недоступний, тож пропускаємо.
|
|
133
168
|
if (full && opts.rulesDir === undefined) {
|
|
134
|
-
const conformanceCode = await
|
|
135
|
-
if (conformanceCode !== 0)
|
|
169
|
+
const conformanceCode = await runFullConformancePhase(cwd, readOnly, log)
|
|
170
|
+
if (conformanceCode !== 0) {
|
|
171
|
+
if (readOnly) return conformanceCode
|
|
172
|
+
worst = conformanceCode
|
|
173
|
+
}
|
|
136
174
|
}
|
|
137
|
-
return
|
|
175
|
+
return worst
|
|
138
176
|
}
|
|
@@ -3,224 +3,26 @@ type: JS Module
|
|
|
3
3
|
title: release.mjs
|
|
4
4
|
resource: npm/rules/release/release.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 06d3556b
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
7
10
|
---
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## Огляд
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
2. новий розділ у `CHANGELOG.md` відповідного воркспейсу,
|
|
13
|
-
3. git-коміт зі стандартизованим subject `release: <name@version>, ...`,
|
|
14
|
-
4. git-теги у форматі `<name>@<version>` для кожного зрелізованого пакета,
|
|
15
|
-
5. фізичне видалення «спожитих» change-файлів,
|
|
16
|
-
6. `git push --follow-tags`.
|
|
14
|
+
Файл автоматизує процес випуску версій. Він агрегує зміна-файли для всіх робочих просторів у `version-bump` та `CHANGELOG`, комітить зміни, ставить тег `<name>@<version>` та видаляє використані зміна-файли. Цей процес виконується у CI на гілці `main` (n-cursor-release-design, варіант A). Публічні функції, що надає модуль, — `release` та `runReleaseCli`.
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
## Поведінка
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
release агрегує change-файли для всіх робочих просторів, оновлює версії в маніфестах, додає секції до `CHANGELOG.md`, видаляє використані change-файли, створює коміт та анотовані теги для всіх зібраних релізів, а потім намагається пушити їх у апстрім з повторними спробами.
|
|
19
|
+
runReleaseCli виконує процес релізу, викликаючи функцію release, та виводить відповідний статус у консоль.
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
- single-package репо (`workspaces === ['.']`) — релізиться сам root.
|
|
21
|
+
## Публічний API
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
release — агрегує change-файли для кожного робочого простору у version-bump та CHANGELOG, комітить зміни, ставить тег `<name>@<version>` та видаляє використані change-файли.
|
|
24
|
+
runReleaseCli — виконує команди командного інтерфейсу для запуску процесу випуску.
|
|
26
25
|
|
|
27
|
-
##
|
|
26
|
+
## Гарантії поведінки
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
| ----------------------------- | ---------------- | ------------------------------------------------------------------------------------------------- |
|
|
31
|
-
| `release(opts?)` | `async function` | Програмний API: виконує повний реліз-цикл і повертає масив зрелізованих пакетів. |
|
|
32
|
-
| `runReleaseCli(_args, opts?)` | `async function` | CLI-обгортка: запускає `release`, друкує підсумок у stdout/stderr, повертає exit-код (`0` / `1`). |
|
|
33
|
-
|
|
34
|
-
Внутрішні (не експортовані) функції-помічники: `writeManifestVersion`, `prependWorkspaceChangelog`, `collectChangeFiles`.
|
|
35
|
-
|
|
36
|
-
Внутрішні константи: `SEMVER_LINE_RE`, `PY_VERSION_LINE_RE` — regex для in-place заміни рядка `version` у відповідних типах маніфесту.
|
|
37
|
-
|
|
38
|
-
## Функції
|
|
39
|
-
|
|
40
|
-
### `writeManifestVersion(cwd, manifest, newVersion)` (внутрішня)
|
|
41
|
-
|
|
42
|
-
Записує нову version у маніфест пакета, зберігаючи форматування файлу.
|
|
43
|
-
|
|
44
|
-
- **Сигнатура:** `async (cwd: string, manifest: PackageManifest, newVersion: string) => Promise<void>`
|
|
45
|
-
- **Параметри:**
|
|
46
|
-
- `cwd` — абсолютний шлях кореня репо (root проекту);
|
|
47
|
-
- `manifest` — об'єкт маніфесту з типу `PackageManifest`, що містить поля `ws` (відносний шлях до воркспейсу, або `.` для root), `manifestRel` (відносний шлях до файлу маніфесту в межах воркспейсу) і `kind` (`'npm'` чи інший — трактується як Python);
|
|
48
|
-
- `newVersion` — новий рядок версії (SemVer для npm, PEP 440 / SemVer для Python — не валідується тут).
|
|
49
|
-
- **Повертає:** `Promise<void>` після успішного запису.
|
|
50
|
-
- **Side effects:**
|
|
51
|
-
- читає файл маніфесту з диска;
|
|
52
|
-
- перезаписує його зі зміненим рядком версії;
|
|
53
|
-
- **обирає regex за типом маніфесту**: `SEMVER_LINE_RE` для `'npm'`, `PY_VERSION_LINE_RE` інакше;
|
|
54
|
-
- **кидає `Error`**, якщо в файлі не знайдено патерн рядка `version` (тобто `text.replace(...)` не змінив текст).
|
|
55
|
-
|
|
56
|
-
### `prependWorkspaceChangelog(cwd, ws, sectionBlock)` (внутрішня)
|
|
57
|
-
|
|
58
|
-
Доклеює (prepend) новий блок CHANGELOG до початку `<ws>/CHANGELOG.md`; якщо файл не існує — створює.
|
|
59
|
-
|
|
60
|
-
- **Сигнатура:** `async (cwd: string, ws: string, sectionBlock: string) => Promise<void>`
|
|
61
|
-
- **Параметри:**
|
|
62
|
-
- `cwd` — корінь репо;
|
|
63
|
-
- `ws` — відносний шлях воркспейсу від `cwd` (наприклад, `'npm'`, `'.'`);
|
|
64
|
-
- `sectionBlock` — готовий markdown-блок нового розділу (формат сформований `aggregateWorkspace`).
|
|
65
|
-
- **Повертає:** `Promise<void>`.
|
|
66
|
-
- **Side effects:** читає (за наявності) і пише `<cwd>/<ws>/CHANGELOG.md`. Логіку конкатенації керує `prependChangelogSection` з `./lib/aggregate.mjs` — модуль `release.mjs` лише викликає її, не дублюючи правил вставки.
|
|
67
|
-
|
|
68
|
-
### `collectChangeFiles(cwd, manifest, runGit)` (внутрішня)
|
|
69
|
-
|
|
70
|
-
Збирає всі change-записи для воркспейсу: спочатку явні файли з `CHANGES_DIR`, інакше — fallback-синтез з історії комітів.
|
|
71
|
-
|
|
72
|
-
- **Сигнатура:** `async (cwd: string, manifest: PackageManifest, runGit: (args: string[]) => Promise<string | null>) => Promise<Array<{ file: string | null, entry: { bump: string, section: string, description: string } }>>`
|
|
73
|
-
- **Параметри:**
|
|
74
|
-
- `cwd` — корінь репо;
|
|
75
|
-
- `manifest` — маніфест воркспейсу;
|
|
76
|
-
- `runGit` — git-раннер; повертає stdout як рядок, або `null`, якщо команда зафейлилася.
|
|
77
|
-
- **Повертає:** масив об'єктів `{ file, entry }`:
|
|
78
|
-
- `file` — ім'я change-файлу у `CHANGES_DIR` (для подальшого видалення), або `null` для синтезованого запису;
|
|
79
|
-
- `entry` — нормалізований опис зміни: `{ bump, section, description }` (типи `bump`/`section` визначаються форматом change-файлу і `synthesizeChangeFromCommits`).
|
|
80
|
-
- **Поведінка:**
|
|
81
|
-
1. Викликає `readChangeFiles(manifest.ws, cwd)`; якщо результат непустий — повертає його (явні мають пріоритет).
|
|
82
|
-
2. Інакше: якщо у маніфесті немає `name` — повертає `[]` (без імені неможливо знайти попередній тег для fallback).
|
|
83
|
-
3. Інакше викликає `synthesizeChangeFromCommits(name, ws, { runGit })`; якщо нічого не синтезувалося — `[]`.
|
|
84
|
-
4. Якщо синтезовано — друкує в stderr попередження `⚠️ <ws>: немає change-файлів — синтезовано запис із комітів (fallback)` і повертає `[{ file: null, entry: synthesized }]`.
|
|
85
|
-
- **Side effects:** виклики git через `runGit` (всередині `synthesizeChangeFromCommits`); `console.warn` при fallback.
|
|
86
|
-
|
|
87
|
-
### `release(opts?)` (експорт)
|
|
88
|
-
|
|
89
|
-
Основний програмний вхід — виконує повний реліз-цикл для всіх релевантних воркспейсів.
|
|
90
|
-
|
|
91
|
-
- **Сигнатура:**
|
|
92
|
-
```
|
|
93
|
-
async (opts?: {
|
|
94
|
-
cwd?: string,
|
|
95
|
-
date?: string,
|
|
96
|
-
runGit?: (args: string[]) => Promise<string | null>,
|
|
97
|
-
}) => Promise<Array<{ ws: string, name: string | null, newVersion: string }>>
|
|
98
|
-
```
|
|
99
|
-
- **Параметри `opts` (усі необов'язкові):**
|
|
100
|
-
- `cwd` — корінь репо; за замовчуванням `process.cwd()`;
|
|
101
|
-
- `date` — рядок у форматі `YYYY-MM-DD` для дати релізу в CHANGELOG; за замовчуванням сьогоднішня UTC-дата (`new Date().toISOString().slice(0, 10)`);
|
|
102
|
-
- `runGit` — інжектований git-раннер; за замовчуванням `defaultRunGit(cwd)` (виконує справжні git-команди у `cwd`).
|
|
103
|
-
- **Повертає:** масив зрелізованих пакетів, кожен — `{ ws, name, newVersion }`. Якщо релізити нічого не було — повертає `[]` (без коміту/тегу/пушу).
|
|
104
|
-
- **Алгоритм:**
|
|
105
|
-
1. Отримує список воркспейсів через `getMonorepoProjectRootDirs(cwd)`.
|
|
106
|
-
2. Визначає, чи це monorepo: `subWorkspaces = workspaces.filter(w => w !== '.')`, `isMonorepoRoot = subWorkspaces.length > 0`.
|
|
107
|
-
3. Для кожного `ws`:
|
|
108
|
-
- якщо `ws === '.'` і `isMonorepoRoot` — пропустити (root у monorepo не релізиться сам по собі);
|
|
109
|
-
- читає маніфест через `readPackageManifest(ws, cwd)`; якщо манfest відсутній або без `version` — пропустити;
|
|
110
|
-
- збирає change-записи через `collectChangeFiles`;
|
|
111
|
-
- викликає `aggregateWorkspace({ currentVersion, changeFiles, date })` — якщо повертає `null` (нема чого релізити, всі записи відфільтровано) — пропустити;
|
|
112
|
-
- інакше: записує нову версію у маніфест (`writeManifestVersion`), prepend новий розділ у `CHANGELOG.md` (`prependWorkspaceChangelog`), видаляє кожен спожитий change-файл (`rm` у `<cwd>/<ws>/<CHANGES_DIR>/<file>`), додає запис у `released`, а якщо є `manifest.name` — формує тег `<name>@<newVersion>`.
|
|
113
|
-
4. Якщо `released.length > 0`:
|
|
114
|
-
- формує subject коміту: список тегів через `, `, або (якщо тегів нема — наприклад, у пакетів без `name`) — `<ws>@<newVersion>`-значення;
|
|
115
|
-
- `git add -A`;
|
|
116
|
-
- `git commit -m "release: <subject>"` — якщо `runGit` повертає `null` (коміт не вдався), кидає `Error('release: git commit не вдався — теги та push скасовано')`;
|
|
117
|
-
- для кожного тегу — `git tag <tag>`;
|
|
118
|
-
- `git push --follow-tags`.
|
|
119
|
-
- **Side effects:**
|
|
120
|
-
- читання/запис файлів маніфестів і `CHANGELOG.md`;
|
|
121
|
-
- видалення change-файлів з диска (`rm`);
|
|
122
|
-
- виконання git-команд через `runGit` (включно з push до remote);
|
|
123
|
-
- `console.warn` при fallback (через `collectChangeFiles`).
|
|
124
|
-
- **Помилки:** кидаються через `throw new Error(...)` у двох місцях:
|
|
125
|
-
- не знайдено патерн `version` у маніфесті (`writeManifestVersion`);
|
|
126
|
-
- не вдався `git commit` (`runGit` повернув `null`).
|
|
127
|
-
|
|
128
|
-
### `runReleaseCli(_args, opts?)` (експорт)
|
|
129
|
-
|
|
130
|
-
CLI-фасад: викликає `release(opts)`, друкує підсумок, мапить помилки на exit-код.
|
|
131
|
-
|
|
132
|
-
- **Сигнатура:** `async (_args: string[], opts?: { cwd?: string, date?: string, runGit?: ... }) => Promise<number>`
|
|
133
|
-
- **Параметри:**
|
|
134
|
-
- `_args` — позиційні CLI-аргументи (поточна імплементація опцій з CLI не приймає, тому ігнорується; параметр підкреслений `_` для лінту);
|
|
135
|
-
- `opts` — ті самі опції, що в `release` (використовується тестами для інжекції `cwd`/`date`/`runGit`).
|
|
136
|
-
- **Повертає:** `Promise<number>` — exit-код:
|
|
137
|
-
- `0` — успіх (включно з випадком «немає що релізити»);
|
|
138
|
-
- `1` — будь-яка помилка з `release`.
|
|
139
|
-
- **Поведінка:**
|
|
140
|
-
- якщо `released.length === 0` — друкує `release: немає змін для релізу`;
|
|
141
|
-
- інакше для кожного запису — `console.log` рядок `✅ <name або ws>@<newVersion>`;
|
|
142
|
-
- при exception — `console.error("❌ <message>")` і повертає `1`. Підтримує і `Error` (бере `.message`), і не-`Error`-значення (приводить через `String(...)`).
|
|
143
|
-
|
|
144
|
-
## Залежності
|
|
145
|
-
|
|
146
|
-
### Стандартна бібліотека Node.js
|
|
147
|
-
|
|
148
|
-
- `node:fs` — `existsSync` (для перевірки існування `CHANGELOG.md`);
|
|
149
|
-
- `node:fs/promises` — `readFile`, `writeFile`, `rm`;
|
|
150
|
-
- `node:path` — `join`.
|
|
151
|
-
|
|
152
|
-
### Внутрішні модулі проекту
|
|
153
|
-
|
|
154
|
-
- `../changelog/lib/package-manifest.mjs`:
|
|
155
|
-
- `getMonorepoProjectRootDirs(cwd)` — повертає список воркспейсів (включно з `.`);
|
|
156
|
-
- `readPackageManifest(ws, cwd)` — читає маніфест воркспейсу;
|
|
157
|
-
- тип `PackageManifest` (через JSDoc-імпорт).
|
|
158
|
-
- `./lib/aggregate.mjs`:
|
|
159
|
-
- `aggregateWorkspace({ currentVersion, changeFiles, date })` — обчислює `newVersion` і `sectionBlock` CHANGELOG за зібраними change-записами; повертає `null`, якщо немає змін до релізу;
|
|
160
|
-
- `prependChangelogSection(existing, sectionBlock)` — формує новий вміст `CHANGELOG.md` зі вставкою блоку на початок.
|
|
161
|
-
- `./lib/change-file.mjs`:
|
|
162
|
-
- константа `CHANGES_DIR` — назва теки з change-файлами всередині воркспейсу;
|
|
163
|
-
- `readChangeFiles(ws, cwd)` — читає всі change-файли воркспейсу.
|
|
164
|
-
- `./lib/fallback.mjs`:
|
|
165
|
-
- `defaultRunGit(cwd)` — фабрика git-раннера з прив'язкою до `cwd`;
|
|
166
|
-
- `synthesizeChangeFromCommits(name, ws, { runGit })` — синтезує change-запис із commit-історії з останнього тегу `<name>@*`.
|
|
167
|
-
|
|
168
|
-
### Зовнішні залежності
|
|
169
|
-
|
|
170
|
-
Жодних npm-пакетів — лише вбудовані модулі Node.js та внутрішні модулі проекту.
|
|
171
|
-
|
|
172
|
-
## Потік виконання / Використання
|
|
173
|
-
|
|
174
|
-
### CLI-використання (через диспатчер `n-cursor`)
|
|
175
|
-
|
|
176
|
-
`runReleaseCli` під'єднується до точки входу `n-cursor release`. Зазвичай викликається в CI на `main` після злиття PR:
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
n-cursor release
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
Exit-код `0` — навіть якщо нічого не зрелізовано (в стандартний випадок «нічого не змінилось»). Exit-код `1` — фейл (помилка запису маніфесту, фейл `git commit`, тощо).
|
|
183
|
-
|
|
184
|
-
### Програмне використання (у тестах / інших скриптах)
|
|
185
|
-
|
|
186
|
-
```js
|
|
187
|
-
import { release, runReleaseCli } from './release.mjs'
|
|
188
|
-
|
|
189
|
-
// 1) Прямий виклик
|
|
190
|
-
const released = await release({
|
|
191
|
-
cwd: '/abs/path/to/repo',
|
|
192
|
-
date: '2026-06-03',
|
|
193
|
-
runGit: async args => '...stdout...' // або null при помилці
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
// 2) Через CLI-фасад
|
|
197
|
-
const code = await runReleaseCli([], { cwd, date, runGit })
|
|
198
|
-
process.exit(code)
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Послідовність кроків у `release()`
|
|
202
|
-
|
|
203
|
-
1. **Дискавер воркспейсів** — `getMonorepoProjectRootDirs(cwd)`.
|
|
204
|
-
2. **Класифікація** — root пропускається у monorepo (де є суб-воркспейси).
|
|
205
|
-
3. **Для кожного воркспейсу:**
|
|
206
|
-
- читання маніфесту;
|
|
207
|
-
- збір change-файлів (явні → fallback-синтез з комітів);
|
|
208
|
-
- агрегація → `newVersion` + `sectionBlock`;
|
|
209
|
-
- запис маніфесту;
|
|
210
|
-
- prepend CHANGELOG;
|
|
211
|
-
- видалення «спожитих» change-файлів;
|
|
212
|
-
- реєстрація запису і (за наявності `name`) тегу.
|
|
213
|
-
4. **Якщо є зрелізоване хоча б одне:**
|
|
214
|
-
- формування subject коміту;
|
|
215
|
-
- `git add -A` → `git commit -m "release: <subject>"`;
|
|
216
|
-
- перевірка успішності коміту (інакше throw);
|
|
217
|
-
- проставляння всіх тегів;
|
|
218
|
-
- `git push --follow-tags`.
|
|
219
|
-
5. **Повернення масиву** зрелізованих пакетів.
|
|
220
|
-
|
|
221
|
-
### Інваріанти
|
|
222
|
-
|
|
223
|
-
- Жодних версій/тегів/коміту, якщо немає реальних змін у жодному воркспейсі — масив `released` залишається порожнім, і блок git взагалі не виконується.
|
|
224
|
-
- Якщо хоча б у одному воркспейсі не вдалось оновити маніфест — функція кидає виняток до `git add -A`; часткові зміни на диску можуть залишитися (виклик не транзакційний — це відповідальність CI/runner-а відкотити робоче дерево).
|
|
225
|
-
- Якщо `git commit` зафейлився — теги не проставляються і push не виконується; кидається явна помилка.
|
|
226
|
-
- Усі git-операції проходять через інжектований `runGit`, що дає змогу тестувати функцію без реальних git-викликів.
|
|
28
|
+
- (специфічних машинно-виведених гарантій немає)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: fix.mjs
|
|
4
|
+
resource: npm/rules/tool-surface/fix.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 38cf876b
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
issues: judge:inaccurate:0.93
|
|
10
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Огляд
|
|
14
|
+
|
|
15
|
+
Цей файл запускає логіку перевірки відповідності політики через функцію `run`, використовуючи контекст прогону, який містить дані, кешовані у межах цього прогону. При самостійному запуску скрипт ініціює повний цикл: завантажує конфігурацію, перевіряє білий список та формує підсумок. Результат виконання визначається кодом виходу, що сигналізує про успішне виконання або виявлені порушення.
|
|
16
|
+
|
|
17
|
+
## Поведінка
|
|
18
|
+
|
|
19
|
+
1. Викликається функція `run` для запуску правила.
|
|
20
|
+
2. Правило виконується, використовуючи контекст прогону, що може містити кеш.
|
|
21
|
+
3. Якщо скрипт виконується як окрема програма (standalone), запускається оркестрація правила.
|
|
22
|
+
4. Оркестрація виконує завантаження конфігурації, перевірку білого списку та підсумок.
|
|
23
|
+
5. Код завершує роботу з кодом виходу, який вказує на успіх або наявність порушень.
|
|
24
|
+
|
|
25
|
+
## Публічний API
|
|
26
|
+
|
|
27
|
+
run — виконує послідовність перевірок: застосовує правила, аналізує JS-занепокоєння, перевіряє політику та посилання MDC.
|
|
28
|
+
|
|
29
|
+
## Гарантії поведінки
|
|
30
|
+
|
|
31
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
32
|
+
- Кешує результати в межах одного прогону.
|
|
@@ -3,36 +3,45 @@ type: JS Module
|
|
|
3
3
|
title: normalize-pipeline.mjs
|
|
4
4
|
resource: npm/scripts/lib/adr/normalize-pipeline.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 9f2d42d3
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
|
+
issues: judge:inaccurate:0.99
|
|
10
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
9
11
|
---
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## Огляд
|
|
14
|
+
|
|
15
|
+
Файл реалізує локально-орієнтований конвеєр для нормалізації чернеток архітектурних рішень (ADR). Він працює за принципом інверсії керування: JavaScript оркеструє процес, а LLM відповідає лише на вузькі, верифіковані запитання, будучи заточеним під малу локальну модель (omlx/gemma-4b). LLM використовується лише для бінарного судження схожості між записами (наприклад, через `edge-judge`) та витягування змісту секцій у JSON (через `gen-MADR`). Конвеєр збирає, валідує та складає повний MADR-каркас, використовуючи кластеризацію (`cluster` та `union-find`) та проходить через `validation gate`, повертаючи результат у вигляді операцій для застосування.
|
|
12
16
|
|
|
13
17
|
## Поведінка
|
|
14
18
|
|
|
15
|
-
tokenize
|
|
16
|
-
jaccard
|
|
17
|
-
draftTitle
|
|
18
|
-
isNoDecision визначає, чи
|
|
19
|
-
buildEdges
|
|
20
|
-
validateMadr
|
|
21
|
-
|
|
19
|
+
tokenize: Токенізує назву або слаг, видаляючи стоп-слова та розширюючи його на значущі токени.
|
|
20
|
+
jaccard: Обчислює коефіцієнт Jaccard між двома множинами токенів.
|
|
21
|
+
draftTitle: Витягує заголовок з тіла чернетки, використовуючи ADR-шаблон або перший не-MADR заголовок.
|
|
22
|
+
isNoDecision: Детерміновано визначає, чи рішення в чернетці явно не прийняте.
|
|
23
|
+
buildEdges: Будує кандидати-ребра між чернетками та між чернетками і чистими ADR-файлами на основі лексичної схожості.
|
|
24
|
+
validateMadr: Перевіряє згенерований MADR-текст на відповідність вимогам OKF та структурним елементам.
|
|
25
|
+
madrDate: Детерміновано визначає ISO-дату для поля **Date:**, використовуючи дані з чернетки або імені файлу.
|
|
26
|
+
normalizeSections: Нормалізує сирий JSON-вивід LLM у строгу структуру секцій MADR, толерантно до дрібних відхилень моделі.
|
|
27
|
+
assembleMadr: Збирає канонічний MADR-markdown, використовуючи заголовок, дату та нормалізований контент секцій.
|
|
28
|
+
genMadr: Витягує зміст архітектурного рішення з чернетки у JSON, а потім збирає його у валідний MADR-текст.
|
|
29
|
+
normalizePipeline: Виконує повний конвеєр нормалізації, кластеризуючи чернетки та генеруючи операції для застосування.
|
|
22
30
|
|
|
23
31
|
## Публічний API
|
|
24
32
|
|
|
25
|
-
tokenize —
|
|
26
|
-
jaccard —
|
|
27
|
-
draftTitle —
|
|
28
|
-
isNoDecision —
|
|
29
|
-
buildEdges —
|
|
30
|
-
validateMadr —
|
|
31
|
-
|
|
33
|
+
tokenize — Розбиває назву чи слаг на значущі слова, ігноруючи стоп-слова.
|
|
34
|
+
jaccard — Вимірює схожість двох наборів слів.
|
|
35
|
+
draftTitle — Витягує заголовок з чернетки, надаючи пріоритет заголовку ADR.
|
|
36
|
+
isNoDecision — Визначає, чи не було прийнято рішення у чернетці, щоб уникнути створення зайвих ADR.
|
|
37
|
+
buildEdges — Створює потенційні зв'язки між елементами на основі схожості слів.
|
|
38
|
+
validateMadr — Перевіряє якість згенерованого документа ADR.
|
|
39
|
+
madrDate — Формує стандартизовану дату для документа, використовуючи метадані або ім'я файлу.
|
|
40
|
+
normalizeSections — Приводить неструктурований вивід генеративної моделі до чіткого формату секцій.
|
|
41
|
+
assembleMadr — Збирає повний, стандартизований документ ADR, використовуючи фіксовані шаблони.
|
|
42
|
+
genMadr — Створює чернетку документа ADR на основі вхідних даних.
|
|
43
|
+
normalizePipeline — Виконує повний потік обробки даних, повертаючи результати та статистику.
|
|
32
44
|
|
|
33
45
|
## Гарантії поведінки
|
|
34
46
|
|
|
35
|
-
-
|
|
36
|
-
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
37
|
-
- За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
|
|
38
|
-
- Не звертається до мережі.
|
|
47
|
+
- (специфічних машинно-виведених гарантій немає)
|
|
@@ -3,29 +3,28 @@ type: JS Module
|
|
|
3
3
|
title: worktree-notice.mjs
|
|
4
4
|
resource: npm/scripts/lib/worktree-notice.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 1f7d5e0d
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
7
10
|
---
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## Огляд
|
|
13
|
+
|
|
14
|
+
Цей файл вшиває worktree-інструкцію у синкнутий `SKILL.md` (рішення D2 зі spec). Коли `meta.json.worktree === true`, скіл вставляє/замінює ідемпотентний ре-синкнутий блок, що містить маркери WORKTREE_START та WORKTREE_END, забезпечуючи виконання скілу в окремому git-worktree та запобігаючи паралелізації. Функція `injectWorktreeNotice` керує наявністю або відсутністю цього блоку в `SKILL.md` на основі конфігурації.
|
|
10
15
|
|
|
11
16
|
## Поведінка
|
|
12
17
|
|
|
13
|
-
WORKTREE_START
|
|
14
|
-
WORKTREE_END
|
|
15
|
-
injectWorktreeNotice
|
|
18
|
+
WORKTREE_START — Маркер початку блоку інструкцій для роботи в окремому git-worktree.
|
|
19
|
+
WORKTREE_END — Маркер кінця блоку інструкцій для роботи в окремому git-worktree.
|
|
20
|
+
injectWorktreeNotice — Вставляє, оновлює або видаляє блок інструкцій для роботи в worktree у вмісті SKILL.md залежно від булевого значення.
|
|
16
21
|
|
|
17
22
|
## Публічний API
|
|
18
23
|
|
|
19
|
-
WORKTREE_START —
|
|
20
|
-
WORKTREE_END —
|
|
21
|
-
injectWorktreeNotice —
|
|
24
|
+
WORKTREE_START — Позначає початок секції, що описує робоче дерево.
|
|
25
|
+
WORKTREE_END — Позначає кінець секції, що описує робоче дерево.
|
|
26
|
+
injectWorktreeNotice — Вставляє, змінює або видаляє блок інформації про робоче дерево у файлі `SKILL.md`.
|
|
22
27
|
|
|
23
28
|
## Гарантії поведінки
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
Скіл не паралелізується.
|
|
27
|
-
Після створення worktree виконується `bun install` у цьому worktree.
|
|
28
|
-
Виконується shell-обгортка `n_cursor_npx` навколо `npx` для bootstrap-виклику.
|
|
29
|
-
Обгортка `n_cursor_npx` виконує retry на транзитні помилки реєстру/мережі (інтервал 30с, дефолт 5 хв, `N_CURSOR_NPX_RETRY_MAX_MIN`, ceiling 10 хв).
|
|
30
|
-
При виникненні nonzero CLI повертається одразу.
|
|
31
|
-
Команди, що вимагають command substitution, виконуються після створення worktree.
|
|
30
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|