@nitra/cursor 4.1.2 → 5.0.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 +14 -0
- package/bin/docs/n-cursor.md +1 -9
- package/bin/n-cursor.js +3 -25
- package/package.json +1 -1
- package/rules/docker/lib/docs/docker-mirror.md +1 -1
- package/rules/docker/lib/docs/docker-native-addon.md +1 -1
- package/rules/test/coverage/coverage.mjs +9 -19
- package/rules/test/test.mdc +1 -1
- package/scripts/dispatcher/trace.mjs +4 -16
- package/scripts/docs/build-agents-commands.md +1 -1
- package/scripts/docs/worktree-cli.md +1 -1
- package/scripts/lib/changed-files.mjs +19 -3
- package/scripts/lib/sync-gitignore-worktree.mjs +4 -5
- package/scripts/worktree-cli.mjs +1 -2
- package/skills/docgen/js/docgen-gen.mjs +7 -7
- package/docs/flow.MD +0 -1364
- package/scripts/dispatcher/docs/graph.md +0 -346
- package/scripts/dispatcher/docs/index.md +0 -236
- package/scripts/dispatcher/docs/trace.md +0 -296
- package/scripts/dispatcher/graph/lib/cmd-init.mjs +0 -112
- package/scripts/dispatcher/graph/lib/cmd-invalidate.mjs +0 -96
- package/scripts/dispatcher/graph/lib/cmd-kill.mjs +0 -141
- package/scripts/dispatcher/graph/lib/cmd-plan.mjs +0 -142
- package/scripts/dispatcher/graph/lib/cmd-run.mjs +0 -328
- package/scripts/dispatcher/graph/lib/cmd-scan.mjs +0 -115
- package/scripts/dispatcher/graph/lib/cmd-setup.mjs +0 -111
- package/scripts/dispatcher/graph/lib/cmd-signals.mjs +0 -328
- package/scripts/dispatcher/graph/lib/cmd-status.mjs +0 -131
- package/scripts/dispatcher/graph/lib/cmd-verify.mjs +0 -100
- package/scripts/dispatcher/graph/lib/cmd-watch.mjs +0 -128
- package/scripts/dispatcher/graph/lib/config.mjs +0 -103
- package/scripts/dispatcher/graph/lib/frontmatter.mjs +0 -224
- package/scripts/dispatcher/graph/lib/nnn.mjs +0 -127
- package/scripts/dispatcher/graph/lib/node-state.mjs +0 -157
- package/scripts/dispatcher/graph/lib/scanner.mjs +0 -235
- package/scripts/dispatcher/graph/lib/worktree-ops.mjs +0 -193
- package/scripts/dispatcher/graph-tasks.mjs +0 -92
- package/scripts/dispatcher/graph.mjs +0 -212
- package/scripts/dispatcher/index.mjs +0 -45
- package/scripts/dispatcher/lib/docs/active.md +0 -348
- package/scripts/dispatcher/lib/docs/artifact.md +0 -232
- package/scripts/dispatcher/lib/docs/budget.md +0 -167
- package/scripts/dispatcher/lib/docs/capability.md +0 -196
- package/scripts/dispatcher/lib/docs/commands.md +0 -210
- package/scripts/dispatcher/lib/docs/events.md +0 -183
- package/scripts/dispatcher/lib/docs/executor.md +0 -190
- package/scripts/dispatcher/lib/docs/gate.md +0 -231
- package/scripts/dispatcher/lib/docs/level.md +0 -335
- package/scripts/dispatcher/lib/docs/plan-panel.md +0 -181
- package/scripts/dispatcher/lib/docs/plan.md +0 -200
- package/scripts/dispatcher/lib/docs/planner.md +0 -269
- package/scripts/dispatcher/lib/docs/review.md +0 -255
- package/scripts/dispatcher/lib/docs/reviewer.md +0 -240
- package/scripts/dispatcher/lib/docs/snapshot.md +0 -247
- package/scripts/dispatcher/lib/docs/spec.md +0 -203
- package/scripts/dispatcher/lib/docs/state-store.md +0 -303
- package/scripts/dispatcher/lib/docs/subagent-runner.md +0 -173
- package/scripts/dispatcher/lib/events.mjs +0 -67
- package/scripts/dispatcher/lib/executor.mjs +0 -107
- package/scripts/dispatcher/lib/plan-panel.mjs +0 -76
- package/scripts/dispatcher/lib/state-store.mjs +0 -173
- package/scripts/dispatcher/lib/subagent-runner.mjs +0 -53
- package/scripts/graph/index.mjs +0 -115
- package/scripts/graph/lib/config.mjs +0 -62
- package/scripts/graph/lib/dag.mjs +0 -161
- package/scripts/graph/lib/frontmatter.mjs +0 -70
- package/scripts/graph/lib/nnn.mjs +0 -77
- package/scripts/graph/lib/state.mjs +0 -110
- package/scripts/graph/scan.mjs +0 -64
- package/scripts/graph/status.mjs +0 -86
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.0.0] - 2026-06-08
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- docs: додати план extraction Meta-task
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- coverage `--changed` більше не залежить від runtime-стану `.flow.json`; scope визначається через git merge-base
|
|
12
|
+
|
|
13
|
+
### Removed
|
|
14
|
+
|
|
15
|
+
- видалено вбудовані flow і graph; Meta-task винесено в окремий пакет @7n/mt
|
|
16
|
+
|
|
3
17
|
## [4.1.2] - 2026-06-08
|
|
4
18
|
|
|
5
19
|
### Added
|
package/bin/docs/n-cursor.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Файл `npm/bin/n-cursor.js` — це виконуваний скрипт (shebang `#!/usr/bin/env node`), що слугує єдиною точкою входу CLI пакета `@nitra/cursor`. Скрипт виконує дві ролі:
|
|
6
6
|
|
|
7
7
|
1. **Синхронізатор пакетних артефактів у проєкті-споживачі** — без аргументів копіює `.mdc`-правила, скіли, slash-команди, генерує `AGENTS.md`, `CLAUDE.md`, синхронізує `.claude/settings.json`, `.cursor/hooks.json`, composite GitHub Action `setup-bun-deps`, `.pi/skills`, а також `.gitignore` для `.worktrees/`.
|
|
8
|
-
2. **Маршрутизатор підкоманд** — диспатчить `fix`, `check`, `rename-yaml-extensions`, `post-tool-use-fix`, `lint`, `lint-ci`, `lint-ga`, `lint-rego`, `lint-k8s`, `lint-docker`, `lint-text`, `coverage`, `change`, `release`, `skill`, `worktree`, `
|
|
8
|
+
2. **Маршрутизатор підкоманд** — диспатчить `fix`, `check`, `rename-yaml-extensions`, `post-tool-use-fix`, `lint`, `lint-ci`, `lint-ga`, `lint-rego`, `lint-k8s`, `lint-docker`, `lint-text`, `coverage`, `change`, `release`, `skill`, `worktree`, `trace`, `docgen` у відповідні внутрішні модулі пакета.
|
|
9
9
|
|
|
10
10
|
Скрипт — ES-модуль (`import` синтаксис). Виконує реальні файлові операції в `cwd()` і у каталогах пакету (`BUNDLED_PACKAGE_ROOT`). Усі шляхи відносно поточної робочої директорії проєкту-споживача.
|
|
11
11
|
|
|
@@ -29,9 +29,7 @@
|
|
|
29
29
|
- `npx @nitra/cursor release` — реліз-команда.
|
|
30
30
|
- `npx @nitra/cursor skill list|taze|cursor|claude …` — керування скілами (промпт на stdout, виклик Cursor/Claude CLI).
|
|
31
31
|
- `npx @nitra/cursor worktree …` — керування git-worktree.
|
|
32
|
-
- `npx @nitra/cursor flow` — Dual-Mode Dispatcher (Пасивний Турнікет + Активний Раннер).
|
|
33
32
|
- `npx @nitra/cursor trace` — наскрізна простежуваність ADR↔spec↔plan↔change.
|
|
34
|
-
- `npx @nitra/cursor graph` — read-only DAG-статус.
|
|
35
33
|
- `npx @nitra/cursor docgen scan|modules` — детермінований JSON-лістинг для скілу docgen; `scan` друкує відносний `sourcePath`, ignore-glob snippet живе в `npm/skills/docgen/js/docgen-ignore.mjs`.
|
|
36
34
|
|
|
37
35
|
### Конвенції та ключові артефакти
|
|
@@ -77,9 +75,7 @@
|
|
|
77
75
|
- `runCoverageCli` з `../rules/test/coverage/coverage.mjs`
|
|
78
76
|
- `runChangeCli` з `../rules/release/change.mjs`
|
|
79
77
|
- `runReleaseCli` з `../rules/release/release.mjs`
|
|
80
|
-
- `runFlowCli` зі `../scripts/dispatcher/index.mjs`
|
|
81
78
|
- `runTraceCli` зі `../scripts/dispatcher/trace.mjs`
|
|
82
|
-
- `runGraphCli` зі `../scripts/dispatcher/graph.mjs`
|
|
83
79
|
- `runDocgenScanCli`, `runDocgenModulesCli` зі `../skills/docgen/js/docgen-scan.mjs`
|
|
84
80
|
|
|
85
81
|
## Константи модуля
|
|
@@ -481,9 +477,7 @@ try {
|
|
|
481
477
|
- `'release'` → динамічний import `../rules/release/release.mjs` → `runReleaseCli(args)`.
|
|
482
478
|
- `'skill'` → `runSkillsCli(args)` (синхронний).
|
|
483
479
|
- `'worktree'` → `runWorktreeCli(args)`.
|
|
484
|
-
- `'flow'` → динамічний import `../scripts/dispatcher/index.mjs` → `runFlowCli(args)`.
|
|
485
480
|
- `'trace'` → динамічний import `../scripts/dispatcher/trace.mjs` → `runTraceCli(args)`.
|
|
486
|
-
- `'graph'` → динамічний import `../scripts/dispatcher/graph.mjs` → `runGraphCli(args)`.
|
|
487
481
|
- `'docgen'` → динамічний import `../skills/docgen/js/docgen-scan.mjs`. Якщо `args[0] === 'scan'` → `runDocgenScanCli(args.slice(1))`; якщо `'modules'` → `runDocgenModulesCli(args.slice(1))`; інакше друкує `Usage: …` і `process.exitCode = 1`.
|
|
488
482
|
- `undefined` або `''` (нема команди) → `runSync()`.
|
|
489
483
|
- `default` — невідома команда: stderr, перелік очікуваних, `process.exitCode = 1`.
|
|
@@ -543,9 +537,7 @@ try {
|
|
|
543
537
|
- `../rules/test/coverage/coverage.mjs` — `runCoverageCli`.
|
|
544
538
|
- `../rules/release/change.mjs` — `runChangeCli`.
|
|
545
539
|
- `../rules/release/release.mjs` — `runReleaseCli`.
|
|
546
|
-
- `../scripts/dispatcher/index.mjs` — `runFlowCli`.
|
|
547
540
|
- `../scripts/dispatcher/trace.mjs` — `runTraceCli`.
|
|
548
|
-
- `../scripts/dispatcher/graph.mjs` — `runGraphCli`.
|
|
549
541
|
- `../skills/docgen/js/docgen-scan.mjs` — `runDocgenScanCli`, `runDocgenModulesCli`.
|
|
550
542
|
|
|
551
543
|
### Зовнішні інструменти (виконуються через `spawnSync` / в `fix.mjs`)
|
package/bin/n-cursor.js
CHANGED
|
@@ -1549,8 +1549,8 @@ async function runSync() {
|
|
|
1549
1549
|
|
|
1550
1550
|
/**
|
|
1551
1551
|
* Команди, що мутують проєкт у CWD і вимагають кореня репо. `undefined`/`''` —
|
|
1552
|
-
* дефолтний sync; `check` — deprecated-alias `fix`. Решта (read-only `trace
|
|
1553
|
-
*
|
|
1552
|
+
* дефолтний sync; `check` — deprecated-alias `fix`. Решта (read-only `trace`,
|
|
1553
|
+
* `--root`-команди `docgen`/`rename-yaml-extensions`, `worktree`,
|
|
1554
1554
|
* sub-лінтери) гард не зачіпає.
|
|
1555
1555
|
*/
|
|
1556
1556
|
const ROOT_GUARDED_COMMANDS = new Set([undefined, '', 'fix', 'check', 'lint', 'coverage', 'change', 'release'])
|
|
@@ -1757,14 +1757,6 @@ try {
|
|
|
1757
1757
|
|
|
1758
1758
|
break
|
|
1759
1759
|
}
|
|
1760
|
-
case 'flow': {
|
|
1761
|
-
// n-cursor flow — Dual-Mode Dispatcher (spec §8): Пасивний Турнікет
|
|
1762
|
-
// (init/verify/release) + Активний Раннер (run) навколо .flow.json.
|
|
1763
|
-
const { runFlowCli } = await import('../scripts/dispatcher/index.mjs')
|
|
1764
|
-
process.exitCode = await runFlowCli(args)
|
|
1765
|
-
|
|
1766
|
-
break
|
|
1767
|
-
}
|
|
1768
1760
|
case 'trace': {
|
|
1769
1761
|
// n-cursor trace — наскрізна простежуваність (spec §5.4/§7): граф
|
|
1770
1762
|
// ADR↔spec↔plan↔change за front-matter + флаг розривів. exit 1 на розрив.
|
|
@@ -1773,20 +1765,6 @@ try {
|
|
|
1773
1765
|
|
|
1774
1766
|
break
|
|
1775
1767
|
}
|
|
1776
|
-
case 'graph': {
|
|
1777
|
-
// n-cursor graph — task DAG orchestration system (думка.MD, file-presence protocol)
|
|
1778
|
-
const { runGraphTasksCli } = await import('../scripts/dispatcher/graph-tasks.mjs')
|
|
1779
|
-
process.exitCode = await runGraphTasksCli(args)
|
|
1780
|
-
|
|
1781
|
-
break
|
|
1782
|
-
}
|
|
1783
|
-
case 'watch': {
|
|
1784
|
-
// n-cursor watch — one-shot DAG scan: audit queue + stale worktrees + needs-plan
|
|
1785
|
-
const { runGraphTasksCli } = await import('../scripts/dispatcher/graph-tasks.mjs')
|
|
1786
|
-
process.exitCode = await runGraphTasksCli(['watch', ...args])
|
|
1787
|
-
|
|
1788
|
-
break
|
|
1789
|
-
}
|
|
1790
1768
|
case 'docgen': {
|
|
1791
1769
|
// n-cursor docgen scan|modules — детермінований лістинг для скілу docgen.
|
|
1792
1770
|
// scan — кодові файли; modules — логічні модулі (межі за package.json).
|
|
@@ -1812,7 +1790,7 @@ try {
|
|
|
1812
1790
|
default: {
|
|
1813
1791
|
console.error(`❌ Невідома команда: ${command}`)
|
|
1814
1792
|
console.error(
|
|
1815
|
-
` Очікується: (без аргументів) синхронізація правил, fix, check, rename-yaml-extensions, post-tool-use-fix, lint, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text, coverage, coverage-fix, taze, start-check, fix-t0, change, release, skill, worktree, lint-ci,
|
|
1793
|
+
` Очікується: (без аргументів) синхронізація правил, fix, check, rename-yaml-extensions, post-tool-use-fix, lint, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text, coverage, coverage-fix, taze, start-check, fix-t0, change, release, skill, worktree, lint-ci, trace, docgen`
|
|
1816
1794
|
)
|
|
1817
1795
|
process.exitCode = 1
|
|
1818
1796
|
}
|
package/package.json
CHANGED
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
- **Внутрішні модулі:** немає; модуль самодостатній.
|
|
167
167
|
- **Глобальні API:** виключно стандартний JavaScript — `RegExp`, `String.prototype` (`split`, `trim`, `match`, `slice`, `toLowerCase`, `replace`, `includes`, `startsWith`, `lastIndexOf`), `Set`, `Array.prototype.entries`.
|
|
168
168
|
- **Runtime:** ESM, працює в Node.js та Bun. Розширення `.mjs` обов'язкове за правилом `n-bun`/`n-js-run`.
|
|
169
|
-
- **JSDoc типи:** використовуються `@type`, `@param`, `@returns` із касти `/** @type {const} */` для
|
|
169
|
+
- **JSDoc типи:** використовуються `@type`, `@param`, `@returns` із касти `/** @type {const} */` для імутабельних літералів — допомагають TypeScript/JSDoc-перевірці й ESLint.
|
|
170
170
|
|
|
171
171
|
## Потік виконання / Використання
|
|
172
172
|
|
|
@@ -79,7 +79,7 @@ getNativeAddonDeps(dependencies: unknown): string[]
|
|
|
79
79
|
1. Якщо `!dependencies` (null/undefined/інше falsy), або `typeof dependencies !== 'object'`, або `Array.isArray(dependencies)` — повернути `[]`.
|
|
80
80
|
2. Зібрати ключі через `Object.keys(dependencies)`.
|
|
81
81
|
3. Відфільтрувати через `isNativeAddonPackage`.
|
|
82
|
-
4. Повернути копію, відсортовану через `.toSorted((a, b) => a.localeCompare(b))` (
|
|
82
|
+
4. Повернути копію, відсортовану через `.toSorted((a, b) => a.localeCompare(b))` (імутабельне сортування, не мутує вихідний масив ключів).
|
|
83
83
|
|
|
84
84
|
Side effects: відсутні. Чиста функція; виклик `Object.keys` не мутує вхід.
|
|
85
85
|
|
|
@@ -19,8 +19,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
|
19
19
|
|
|
20
20
|
import { applyVerdicts } from '../../../scripts/coverage-classify/apply.mjs'
|
|
21
21
|
import { classify } from '../../../scripts/coverage-classify/index.mjs'
|
|
22
|
-
import {
|
|
23
|
-
import { collectChangedFilesSince } from '../../../scripts/lib/changed-files.mjs'
|
|
22
|
+
import { collectChangedFilesSince, resolveChangedBase } from '../../../scripts/lib/changed-files.mjs'
|
|
24
23
|
import { readNCursorConfigLite } from '../../../scripts/lib/read-n-cursor-config-lite.mjs'
|
|
25
24
|
import { withLock } from '../../../scripts/utils/with-lock.mjs'
|
|
26
25
|
|
|
@@ -211,23 +210,14 @@ async function readClassifyThreshold(cwd) {
|
|
|
211
210
|
/**
|
|
212
211
|
* Резолвить scope змінених файлів для `--changed`-режиму.
|
|
213
212
|
*
|
|
214
|
-
* Base —
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* @param {string} cwd корінь проєкту (= worktree-checkout у межах flow)
|
|
213
|
+
* Base — git merge-base поточної гілки з `main` або `origin/main`.
|
|
214
|
+
* `git diff <base>` проти робочого дерева ловить committed і uncommitted однаково,
|
|
215
|
+
* тож scope не залежить від того, чи крок уже закомічено.
|
|
216
|
+
* @param {string} cwd корінь проєкту
|
|
219
217
|
* @returns {{base: string|null, files: string[]}} base-ref і relative-posix список змінених файлів
|
|
220
218
|
*/
|
|
221
|
-
function resolveChangedScope(cwd) {
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
const state = readState(flowStatePath(cwd))
|
|
225
|
-
base = state?.metadata?.base_commit ?? null
|
|
226
|
-
} catch {
|
|
227
|
-
// пошкоджений/несумісний стан — попереджаємо й падаємо на HEAD (working-tree scope).
|
|
228
|
-
console.error('coverage --changed: стан flow нечитабельний — scope визначається від HEAD робочого дерева')
|
|
229
|
-
base = null
|
|
230
|
-
}
|
|
219
|
+
export function resolveChangedScope(cwd) {
|
|
220
|
+
const base = resolveChangedBase(cwd)
|
|
231
221
|
return { base, files: collectChangedFilesSince(base, cwd) }
|
|
232
222
|
}
|
|
233
223
|
|
|
@@ -237,7 +227,7 @@ function resolveChangedScope(cwd) {
|
|
|
237
227
|
* При `opts.fix === true` після запису COVERAGE.md запускає агента (coverage-fix.mjs)
|
|
238
228
|
* для написання тестів по вцілілих мутантах.
|
|
239
229
|
* При `opts.changed === true` провайдери звужують scope до змінених від base файлів
|
|
240
|
-
*
|
|
230
|
+
* для швидкого gate. Порожній scope (нема релевантних змін) — це pass (exit 0)
|
|
241
231
|
* без перезапису наявного COVERAGE.md, а НЕ помилка «жодного провайдера».
|
|
242
232
|
* @param {{cwd?:string, rulesDir?:string, fix?:boolean, changed?:boolean}} [opts] ін'єкція cwd/rulesDir для тестів; fix — --fix режим; changed — scope лише змінених
|
|
243
233
|
* @returns {Promise<number>} exit code (0 OK, 1 коли жоден провайдер не дав даних у full-режимі)
|
|
@@ -314,7 +304,7 @@ export async function runCoverageSteps(opts = {}) {
|
|
|
314
304
|
* CLI entrypoint для `n-cursor coverage [--fix] [--changed]`.
|
|
315
305
|
* Із `--fix`: збирає метрики → запускає агента → повторно збирає метрики.
|
|
316
306
|
* Без `--fix`: лише збирає метрики.
|
|
317
|
-
* Із `--changed`: звужує scope до змінених від base
|
|
307
|
+
* Із `--changed`: звужує scope до змінених від git merge-base файлів.
|
|
318
308
|
* Лок охоплює кожен coverage-прогін окремо.
|
|
319
309
|
* @param {{fix?:boolean, changed?:boolean}} [opts] прапори --fix / --changed
|
|
320
310
|
* @returns {Promise<number>} exit code
|
package/rules/test/test.mdc
CHANGED
|
@@ -130,7 +130,7 @@ test.skipIf(env.STRYKER_MUTATOR_WORKER)('узгоджені з поточним
|
|
|
130
130
|
|
|
131
131
|
Канонічна команда — `n-cursor coverage`: збирає метрики покриття (`vitest run --coverage`, `cargo llvm-cov` тощо) і мутаційного тестування (Stryker з vitest-runner + `coverageAnalysis: 'perTest'`, `cargo-mutants`) з усіх активних провайдерів у `.n-cursor.json#rules` і пише `COVERAGE.md` у корінь проєкту. Лок і дедуп — `withLock('coverage', ...)`.
|
|
132
132
|
|
|
133
|
-
**Scoped-режим `--changed
|
|
133
|
+
**Scoped-режим `--changed`:** `n-cursor coverage --changed` звужує scope до файлів, змінених від git merge-base поточної гілки з `main` або `origin/main`; якщо обидва refs відсутні — до робочого дерева vs HEAD. `git diff <base>` проти робочого дерева ловить committed і uncommitted однаково, тож результат не залежить від того, чи крок уже закомічено. Недосяжний `base` (rebase/force-update) — fail-closed (помилка, не тихий pass). JS-провайдер ганяє `vitest --changed <base>` (лише зачеплені тести) і Stryker `--mutate` по змінених production-файлах (тест-файли відкидаються); roots без змінених JS пропускаються. Rust-провайдер пропускається, якщо не змінено `.rs`/`Cargo.*` (інакше — повний crate-прогін; per-file scoping cargo-mutants — окремий крок). Порожній scope (нема релевантних змін) — pass. У changed-режимі `COVERAGE.md` **не** перезаписується (рішення гейту — лише exit-код) і LLM-класифікація не запускається. Швидкі gate-и викликають саме `coverage --changed`; повний coverage (увесь проєкт, запис `COVERAGE.md`) лишається для `bun run coverage` / `/n-coverage-fix`.
|
|
134
134
|
|
|
135
135
|
Провайдери живуть у `npm/rules/<rule>/coverage/coverage.mjs` (постачаються правилами мови/рантайму: `js-lint`, `rust`, у майбутньому `python` тощо). Оркестратор — у `npm/rules/test/coverage/coverage.mjs`.
|
|
136
136
|
|
|
@@ -11,15 +11,7 @@ import { dirname, join } from 'node:path'
|
|
|
11
11
|
import { cwd as processCwd } from 'node:process'
|
|
12
12
|
|
|
13
13
|
/** Поля-лінки у front-matter (порядок відображення). */
|
|
14
|
-
const LINK_FIELDS = ['adr', 'spec', 'plan', '
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Інформаційні лінк-поля: показуються, але їх відсутність НЕ є розривом ланцюга.
|
|
18
|
-
* `flow` вказує на runtime-стан `.worktrees/<branch>.flow.json` — gitignored, поза
|
|
19
|
-
* `docs/`, існує лише під час задачі; у чистому checkout/CI його нема ніколи, тож
|
|
20
|
-
* рахувати його розривом — хибний сигнал. Решта полів — ланки ланцюга (breaking).
|
|
21
|
-
*/
|
|
22
|
-
const INFO_LINK_FIELDS = new Set(['flow'])
|
|
14
|
+
const LINK_FIELDS = ['adr', 'spec', 'plan', 'change', 'task']
|
|
23
15
|
|
|
24
16
|
/** Каталоги з traceable-артефактами. */
|
|
25
17
|
const DIRS = ['docs/tasks', 'docs/specs', 'docs/plans', 'docs/adr']
|
|
@@ -64,8 +56,7 @@ function isSimpleKey(key) {
|
|
|
64
56
|
|
|
65
57
|
/**
|
|
66
58
|
* Будує аналіз: для кожного артефакту — його лінки зі статусом ok/розрив.
|
|
67
|
-
* `breaking` — чи відсутність цього лінка рве
|
|
68
|
-
* інформаційна (`flow` → runtime-стан).
|
|
59
|
+
* `breaking` — чи відсутність цього лінка рве ланцюг.
|
|
69
60
|
* @param {{ file: string, fm: Record<string, string | null> }[]} artifacts артефакти з front-matter
|
|
70
61
|
* @param {(target: string, artifactFile: string) => boolean} resolve чи резолвиться лінк (відносно артефакту/кореня)
|
|
71
62
|
* @returns {{ file: string, kind: string | null, id: string | null, status: string | null, links: { field: string, target: string, ok: boolean, breaking: boolean }[] }[]} аналіз
|
|
@@ -80,7 +71,7 @@ export function analyze(artifacts, resolve) {
|
|
|
80
71
|
field: f,
|
|
81
72
|
target: fm[f],
|
|
82
73
|
ok: resolve(fm[f], file),
|
|
83
|
-
breaking:
|
|
74
|
+
breaking: true
|
|
84
75
|
}))
|
|
85
76
|
}))
|
|
86
77
|
}
|
|
@@ -117,14 +108,12 @@ export function render(analysis) {
|
|
|
117
108
|
|
|
118
109
|
/**
|
|
119
110
|
* Рядок одного лінка: `→` ok; `✗ … (РОЗРИВ)` — нерезолвлене chain-поле;
|
|
120
|
-
* `~ … (не рве ланцюг)` — нерезолвлене інформаційне поле (`flow`, runtime-стан).
|
|
121
111
|
* @param {{ field: string, target: string, ok: boolean, breaking: boolean }} l лінк
|
|
122
112
|
* @returns {string} рядок
|
|
123
113
|
*/
|
|
124
114
|
function renderLink(l) {
|
|
125
115
|
if (l.ok) return `→ ${l.field}: ${l.target}`
|
|
126
|
-
|
|
127
|
-
return `~ ${l.field}: ${l.target} (runtime-стан — не рве ланцюг)`
|
|
116
|
+
return `✗ ${l.field}: ${l.target} (РОЗРИВ — файл відсутній)`
|
|
128
117
|
}
|
|
129
118
|
|
|
130
119
|
/**
|
|
@@ -152,6 +141,5 @@ export function runTraceCli(args, deps = {}) {
|
|
|
152
141
|
|
|
153
142
|
const analysis = analyze(artifacts, (target, file) => resolveLink(root, file, target, exists))
|
|
154
143
|
log(args.includes('--json') ? JSON.stringify(analysis, null, 2) : render(analysis))
|
|
155
|
-
// Розрив ланцюга — лише нерезолвлене chain-поле; нерезолвлений `flow` (runtime-стан) не рахуємо.
|
|
156
144
|
return analysis.some(a => a.links.some(l => l.breaking && !l.ok)) ? 1 : 0
|
|
157
145
|
}
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
`- **Залежності**: \`bun i\``.
|
|
55
55
|
3. Створює `Set<string>` під назвою `added` для відстеження уже доданих ключів скриптів (запобігає дублюванню при подальшому fallback-проході).
|
|
56
56
|
4. Ітерує `SCRIPT_KEYS_ORDER` у заданому порядку: `test`, `lint`, `lint-js`, `lint-text`, `lint-ga`, `lint-k8s`, `lint-docker`, `start`, `dev`, `build`. Для кожного ключа, для якого `scripts[key]` — непорожній рядок, додає у `items` об'єкт `{ name: '- **<key>**: \`bun run <key>\`' }`і фіксує ключ у`added`.
|
|
57
|
-
5. Збирає масив `lintExtraKeys`: усі ключі `scripts`, які починаються з `lint-`, не входять у `added` і мають значення типу `string`. Сортує лексикографічно через `toSorted((a, b) => a.localeCompare(b))` (
|
|
57
|
+
5. Збирає масив `lintExtraKeys`: усі ключі `scripts`, які починаються з `lint-`, не входять у `added` і мають значення типу `string`. Сортує лексикографічно через `toSorted((a, b) => a.localeCompare(b))` (імутабельне сортування — оригінальний масив `Object.keys(scripts)` не змінюється).
|
|
58
58
|
6. Дописує у `items` пункти для кожного `lintExtraKey` за тим самим шаблоном.
|
|
59
59
|
7. Додає три фіксовані хвостові пункти:
|
|
60
60
|
- `- **Оновити правила та AGENTS.md** (після змін у правилах/шаблоні CLI): \`npx @nitra/cursor\``
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- Перевіряє наявність необхідного параметра: назви гілки.
|
|
18
18
|
- Видаляє checkout та файл `.md` для вказаної гілки.
|
|
19
19
|
- Видаляє всі осиротілі файли опису, які залишилися після видалення гілки.
|
|
20
|
-
-
|
|
20
|
+
- Не створює і не обслуговує окремі sibling runtime-файли.
|
|
21
21
|
5. **`list` (Перелік worktree):**
|
|
22
22
|
- Виводить список всіх створених worktree, отриманий за допомогою команди `git worktree list`.
|
|
23
23
|
- Для кожного worktree виводить його повний шлях та вміст файлу опису `.md`.
|
|
@@ -32,15 +32,31 @@ export function collectChangedFiles(cwd = process.cwd()) {
|
|
|
32
32
|
return [...new Set([...modified, ...untracked])]
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Визначає git base для scoped-перевірок без зовнішнього runtime-стану.
|
|
37
|
+
* Пріоритет: локальна `main`, потім `origin/main`; якщо обидві відсутні,
|
|
38
|
+
* повертає null і caller порівнює лише робоче дерево з HEAD.
|
|
39
|
+
* @param {string} [cwd] корінь репо
|
|
40
|
+
* @returns {string|null} merge-base commit або null
|
|
41
|
+
*/
|
|
42
|
+
export function resolveChangedBase(cwd = process.cwd()) {
|
|
43
|
+
for (const ref of ['main', 'origin/main']) {
|
|
44
|
+
const result = spawnSync('git', ['merge-base', 'HEAD', ref], { cwd, encoding: 'utf8' })
|
|
45
|
+
const base = result.status === 0 && !result.error ? result.stdout.trim() : ''
|
|
46
|
+
if (base) return base
|
|
47
|
+
}
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
35
51
|
/**
|
|
36
52
|
* Список змінених + untracked файлів **відносно базового комміту**.
|
|
37
53
|
*
|
|
38
54
|
* `git diff <base>` (без `..`/`...`, без `HEAD`) порівнює base-комміт із поточним
|
|
39
55
|
* **робочим деревом** — тобто однаково ловить і закомічене від base, і staged, і
|
|
40
56
|
* незакомічені модифікації. Це гарантує однакову поведінку незалежно від того, чи
|
|
41
|
-
* зміни вже закомічені у worktree
|
|
42
|
-
*
|
|
43
|
-
* @param {string|null} [base] базовий комміт
|
|
57
|
+
* зміни вже закомічені у worktree. Без `base` — fallback на `collectChangedFiles`
|
|
58
|
+
* (робоче дерево vs HEAD).
|
|
59
|
+
* @param {string|null} [base] базовий комміт
|
|
44
60
|
* @param {string} [cwd] корінь репо
|
|
45
61
|
* @returns {string[]} унікальні шляхи (без видалених)
|
|
46
62
|
*/
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
* Гарантує, що кореневий `.gitignore` проєкту ігнорує локальні git-worktree
|
|
3
3
|
* (`.worktrees/`). Викликається з дефолтного sync (`npx \@nitra/cursor`) окремим
|
|
4
4
|
* top-level кроком — поза `syncClaudeConfig`, бо `.worktrees/` — артефакт
|
|
5
|
-
* завжди-активного
|
|
5
|
+
* завжди-активного worktree-tooling, а не Claude/Cursor-конфігу.
|
|
6
6
|
*
|
|
7
|
-
* Один запис `.worktrees/` покриває
|
|
8
|
-
* (
|
|
9
|
-
*
|
|
10
|
-
* завжди-активний flow, тож гейт міг би розсинхронитися з ним.
|
|
7
|
+
* Один запис `.worktrees/` покриває checkout-и та локальні описи worktree.
|
|
8
|
+
* Запис безумовний (без гейта за `.n-cursor.json`-правилами), щоб config не міг
|
|
9
|
+
* розсинхронитися з реальною поведінкою worktree-команд.
|
|
11
10
|
*
|
|
12
11
|
* Делегує наявній idempotent+append-only утиліті `ensureGitignoreEntries` (header-
|
|
13
12
|
* секція, не перезаписує/не видаляє наявні рядки; створює `.gitignore`, якщо нема).
|
package/scripts/worktree-cli.mjs
CHANGED
|
@@ -160,8 +160,7 @@ function cmdRemove(rest, ctx) {
|
|
|
160
160
|
return 1
|
|
161
161
|
}
|
|
162
162
|
if (existsSync(paths.descFile)) rmSync(paths.descFile, { force: true })
|
|
163
|
-
//
|
|
164
|
-
// а не у .flow.json/.events.jsonl/lock sibling-ах. Cleanup sibling-ів більше не потрібен.
|
|
163
|
+
// Окремих sibling runtime-файлів більше немає, тож cleanup обмежений checkout і описом.
|
|
165
164
|
ctx.log(`✅ прибрано: ${paths.checkout} (гілку ${branch} лишено)`)
|
|
166
165
|
return 0
|
|
167
166
|
}
|
|
@@ -29,7 +29,7 @@ function stripSection(text) {
|
|
|
29
29
|
*/
|
|
30
30
|
function stripSignatures(text) {
|
|
31
31
|
let t = text
|
|
32
|
-
for (let i = 0; i < 2; i++) t = t.
|
|
32
|
+
for (let i = 0; i < 2; i++) t = t.replaceAll(/([`\w$.]+)\([^()]*\)/g, '$1')
|
|
33
33
|
return t
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -40,7 +40,7 @@ function parseSections(md) {
|
|
|
40
40
|
for (const line of md.split('\n')) {
|
|
41
41
|
const m = line.match(/^##\s+(.+)/)
|
|
42
42
|
if (m) {
|
|
43
|
-
cur = m[1].toLowerCase().
|
|
43
|
+
cur = m[1].toLowerCase().replaceAll(/[^а-яіїєґa-z0-9]/gi, '')
|
|
44
44
|
result[cur] = ''
|
|
45
45
|
} else if (cur) result[cur] += line + '\n'
|
|
46
46
|
}
|
|
@@ -185,12 +185,12 @@ export async function generateDoc(
|
|
|
185
185
|
r = facts.unsupported
|
|
186
186
|
? piOneShot(facts, src, model, LOCAL_TIMEOUT_MS)
|
|
187
187
|
: piOrchestrated(facts, src, model, LOCAL_TIMEOUT_MS)
|
|
188
|
-
} catch (
|
|
188
|
+
} catch (error) {
|
|
189
189
|
if (cloudModel) {
|
|
190
190
|
const r2 = piOneShot(facts, src, cloudModel)
|
|
191
|
-
return { ...r2, ms: Date.now() - t0, score: null, issues: [`tier1-error: ${
|
|
191
|
+
return { ...r2, ms: Date.now() - t0, score: null, issues: [`tier1-error: ${error.message}`], tier: 2, model: cloudModel }
|
|
192
192
|
}
|
|
193
|
-
throw
|
|
193
|
+
throw error
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
// Stage 2.5: детермінований скоринг (0 токенів) — gate перед Tier 2
|
|
@@ -211,9 +211,9 @@ if (isRunAsCli(import.meta.url)) {
|
|
|
211
211
|
const file = args.find(a => !a.startsWith('--'))
|
|
212
212
|
const tierOnly = args.includes('--tier-only')
|
|
213
213
|
const mi = args.indexOf('--model')
|
|
214
|
-
const model = mi
|
|
214
|
+
const model = mi !== -1 ? args[mi + 1] : DEFAULT_LOCAL_MODEL
|
|
215
215
|
const si = args.indexOf('--sym-threshold')
|
|
216
|
-
const symThreshold = si
|
|
216
|
+
const symThreshold = si !== -1 ? Number(args[si + 1]) : DEFAULT_SYM_THRESHOLD
|
|
217
217
|
if (!file) {
|
|
218
218
|
console.error('Usage: node docgen-gen.mjs <file> [--model <m>] [--sym-threshold N] [--tier-only]')
|
|
219
219
|
process.exit(1)
|