@nitra/cursor 12.8.9 → 12.9.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/bin/n-cursor.js +18 -10
  3. package/package.json +5 -5
  4. package/rules/abie/docs/index.md +0 -1
  5. package/rules/abie/lib/docs/http-route.md +11 -12
  6. package/rules/abie/lib/http-route.mjs +3 -0
  7. package/rules/abie/policy/health_check_policy/health_check_policy.mdc +3 -1
  8. package/rules/abie/policy/health_check_policy/health_check_policy.rego +27 -0
  9. package/rules/adr/docs/index.md +0 -1
  10. package/rules/adr/js/madr_format.mdc +13 -1
  11. package/rules/bun/docs/index.md +0 -1
  12. package/rules/bun/policy/package_json/package_json.rego +12 -0
  13. package/rules/capacitor/docs/index.md +0 -1
  14. package/rules/changelog/docs/index.md +0 -1
  15. package/rules/ci4/docs/index.md +0 -1
  16. package/rules/doc-files/docs/index.md +0 -1
  17. package/rules/doc-files/docs/main.md +7 -9
  18. package/rules/doc-files/main.mjs +2 -3
  19. package/rules/docker/docs/index.md +0 -1
  20. package/rules/efes/docs/index.md +0 -1
  21. package/rules/feedback/docs/index.md +0 -1
  22. package/rules/ga/docs/index.md +0 -1
  23. package/rules/graphql/docs/index.md +0 -1
  24. package/rules/hasura/docs/index.md +0 -1
  25. package/rules/hasura/js/docs/index.md +3 -2
  26. package/rules/hasura/js/docs/migrations.md +30 -0
  27. package/rules/hasura/js/migrations.mjs +47 -0
  28. package/rules/image-avif/docs/index.md +0 -1
  29. package/rules/image-compress/docs/index.md +0 -1
  30. package/rules/js/docs/index.md +0 -1
  31. package/rules/js/js/dep-policy.mjs +87 -0
  32. package/rules/js/js/docs/dep-policy.md +36 -0
  33. package/rules/js/js/docs/index.md +1 -0
  34. package/rules/js/policy/package_json/package_json.rego +16 -0
  35. package/rules/js-bun-db/docs/index.md +0 -1
  36. package/rules/js-bun-redis/docs/index.md +0 -1
  37. package/rules/js-mssql/docs/index.md +0 -1
  38. package/rules/js-run/docs/index.md +0 -1
  39. package/rules/k8s/docs/index.md +0 -1
  40. package/rules/nginx-default-tpl/docs/index.md +0 -1
  41. package/rules/npm-module/docs/index.md +0 -1
  42. package/rules/php/docs/index.md +0 -1
  43. package/rules/python/docs/index.md +0 -1
  44. package/rules/rego/docs/index.md +0 -1
  45. package/rules/rego/js/docs/index.md +3 -3
  46. package/rules/rego/js/docs/tooling.md +28 -0
  47. package/rules/rego/js/tooling.mjs +24 -0
  48. package/rules/rego/policy/package_json/package_json.rego +21 -0
  49. package/rules/rego/policy/package_json/target.json +4 -0
  50. package/rules/release/docs/index.md +0 -1
  51. package/rules/rust/docs/index.md +0 -1
  52. package/rules/rust/policy/lint_rust_yml/lint_rust_yml.rego +24 -0
  53. package/rules/rust/policy/package_json/package_json.rego +20 -0
  54. package/rules/rust/policy/package_json/target.json +4 -0
  55. package/rules/security/docs/index.md +0 -1
  56. package/rules/style/docs/index.md +0 -1
  57. package/rules/style/js/docs/index.md +2 -3
  58. package/rules/style/js/docs/tooling.md +14 -10
  59. package/rules/style/js/tooling.mjs +8 -2
  60. package/rules/style/policy/lint_style_yml/lint_style_yml.rego +5 -0
  61. package/rules/tauri/docs/index.md +0 -1
  62. package/rules/test/docs/index.md +0 -1
  63. package/rules/test/js/docs/index.md +2 -0
  64. package/rules/test/js/docs/no-console-store-restore.md +32 -0
  65. package/rules/test/js/docs/sandbox-aware-test.md +32 -0
  66. package/rules/test/js/no-console-store-restore.mjs +88 -0
  67. package/rules/test/js/sandbox-aware-test.mjs +89 -0
  68. package/rules/text/docs/index.md +0 -1
  69. package/rules/tool-surface/docs/index.md +0 -1
  70. package/rules/vue/docs/index.md +0 -1
  71. package/rules/worktree/docs/index.md +0 -1
  72. package/scripts/docs/hook.md +29 -0
  73. package/scripts/docs/index.md +1 -2
  74. package/scripts/hook.mjs +72 -0
  75. package/scripts/lib/docs/index.md +0 -1
  76. package/scripts/lib/docs/run-lint.md +9 -8
  77. package/scripts/lib/docs/run-rule.md +7 -7
  78. package/scripts/lib/fix/docs/index.md +0 -1
  79. package/scripts/lib/run-lint.mjs +15 -2
  80. package/scripts/lib/run-rule.mjs +1 -2
  81. package/skills/adr-normalize/SKILL.md +1 -0
  82. package/skills/coverage-fix/SKILL.md +1 -0
  83. package/skills/doc-aggregate/SKILL.md +1 -0
  84. package/skills/doc-files/SKILL.md +1 -0
  85. package/skills/lint/SKILL.md +24 -19
  86. package/skills/llm-patch/SKILL.md +1 -0
  87. package/skills/publish-telegram/SKILL.md +1 -0
  88. package/skills/start-check/SKILL.md +1 -0
  89. package/skills/taze/SKILL.md +3 -2
  90. package/types/bin/n-cursor.d.ts +1 -1
  91. package/rules/abie/docs/fix.md +0 -37
  92. package/rules/adr/docs/fix.md +0 -37
  93. package/rules/bun/docs/fix.md +0 -30
  94. package/rules/capacitor/docs/fix.md +0 -36
  95. package/rules/changelog/docs/fix.md +0 -37
  96. package/rules/ci4/docs/fix.md +0 -32
  97. package/rules/doc-files/docs/fix.md +0 -29
  98. package/rules/docker/docs/fix.md +0 -35
  99. package/rules/efes/docs/fix.md +0 -37
  100. package/rules/feedback/docs/fix.md +0 -30
  101. package/rules/ga/docs/fix.md +0 -30
  102. package/rules/graphql/docs/fix.md +0 -37
  103. package/rules/hasura/docs/fix.md +0 -39
  104. package/rules/image-avif/docs/fix.md +0 -28
  105. package/rules/image-compress/docs/fix.md +0 -27
  106. package/rules/js/docs/fix.md +0 -37
  107. package/rules/js-bun-db/docs/fix.md +0 -30
  108. package/rules/js-bun-redis/docs/fix.md +0 -32
  109. package/rules/js-mssql/docs/fix.md +0 -30
  110. package/rules/js-run/docs/fix.md +0 -36
  111. package/rules/k8s/docs/fix.md +0 -31
  112. package/rules/nginx-default-tpl/docs/fix.md +0 -35
  113. package/rules/npm-module/docs/fix.md +0 -34
  114. package/rules/php/docs/fix.md +0 -35
  115. package/rules/python/docs/fix.md +0 -38
  116. package/rules/rego/docs/fix.md +0 -31
  117. package/rules/release/docs/fix.md +0 -28
  118. package/rules/rust/docs/fix.md +0 -32
  119. package/rules/security/docs/fix.md +0 -33
  120. package/rules/style/docs/fix.md +0 -28
  121. package/rules/tauri/docs/fix.md +0 -39
  122. package/rules/test/docs/fix.md +0 -31
  123. package/rules/text/docs/fix.md +0 -37
  124. package/rules/tool-surface/docs/fix.md +0 -32
  125. package/rules/vue/docs/fix.md +0 -32
  126. package/rules/worktree/docs/fix.md +0 -40
  127. package/scripts/docs/post-tool-use-fix.md +0 -32
  128. package/scripts/docs/worktree-cli.md +0 -27
  129. package/scripts/lib/docs/worktree.md +0 -42
  130. package/scripts/lib/fix/docs/run-fix-check.md +0 -33
@@ -0,0 +1,89 @@
1
+ /** @see ./docs/sandbox-aware-test.md */
2
+ import { readFile } from 'node:fs/promises'
3
+ import { basename, relative } from 'node:path'
4
+
5
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
6
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
7
+ import { walkDir } from '../../../scripts/utils/walkDir.mjs'
8
+
9
+ /**
10
+ * Чи файл — JS-тест (`*.test.mjs` / `*.test.js`).
11
+ * @param {string} absPath абсолютний шлях
12
+ * @returns {boolean} `true` для `.test.{mjs,js}` файлів
13
+ */
14
+ function isTestFile(absPath) {
15
+ const name = basename(absPath)
16
+ return name.endsWith('.test.mjs') || name.endsWith('.test.js')
17
+ }
18
+
19
+ /**
20
+ * Чи файл містить `import.meta.dirname`/`import.meta.url`-навігацію з ≥4 `..`-рівнів.
21
+ * Для кожного вживання `import.meta.dirname|url` рахує `'..'`/`".."` у вікні 400 символів.
22
+ * @param {string} body вміст файлу
23
+ * @returns {boolean} `true` якщо знайдено глибоку навігацію
24
+ */
25
+ function hasDeepMetaNavigation(body) {
26
+ const RE = /import\.meta\.(?:dirname|url)\b/gu
27
+ let match
28
+ while ((match = RE.exec(body)) !== null) {
29
+ const chunk = body.slice(match.index, match.index + 400)
30
+ const dots = (chunk.match(/'\.\.'|"\.\."/gu) ?? []).length
31
+ if (dots >= 4) return true
32
+ }
33
+ return false
34
+ }
35
+
36
+ /** Захист через тимчасову пісочницю — `withTmpDir`. */
37
+ const WITH_TMP_DIR_RE = /\bwithTmpDir\b/u
38
+
39
+ /** Захист через явний skip у Stryker-sandbox (`test.skipIf`). */
40
+ const SKIP_IF_STRYKER_RE = /\btest\.skipIf\s*\(\s*(?:env|process\.env)\.STRYKER_MUTATOR_WORKER\b/u
41
+
42
+ /**
43
+ * Перевіряє, що `*.test.{mjs,js}` з глибокою `import.meta`-навігацією (≥4 `..`-рівнів)
44
+ * захищені `withTmpDir` або `test.skipIf(env.STRYKER_MUTATOR_WORKER)`.
45
+ * Без ізоляції Stryker-sandbox (`reports/stryker/.tmp/sandbox-XXX/`) не має `.git/`,
46
+ * тому git-операції у таких тестах падають і мутаційний прогін не стартує.
47
+ * @param {string} [cwdParam] корінь репозиторію
48
+ * @returns {Promise<number>} 0 — чисто, 1 — є порушення
49
+ */
50
+ export async function check(cwdParam = process.cwd()) {
51
+ const reporter = createCheckReporter()
52
+ const { pass, fail } = reporter
53
+
54
+ const cwd = cwdParam
55
+ const ignorePaths = await loadCursorIgnorePaths(cwd)
56
+
57
+ /** @type {string[]} */
58
+ const testFiles = []
59
+ await walkDir(
60
+ cwd,
61
+ absPath => {
62
+ if (isTestFile(absPath)) testFiles.push(absPath)
63
+ },
64
+ ignorePaths
65
+ )
66
+
67
+ /** @type {string[]} */
68
+ const offenders = []
69
+ for (const absPath of testFiles) {
70
+ const body = await readFile(absPath, 'utf8')
71
+ if (!hasDeepMetaNavigation(body)) continue
72
+ if (WITH_TMP_DIR_RE.test(body) || SKIP_IF_STRYKER_RE.test(body)) continue
73
+ offenders.push(relative(cwd, absPath))
74
+ }
75
+
76
+ if (offenders.length === 0) {
77
+ pass(`Усі ${testFiles.length} тестові файли sandbox-aware (test.mdc)`)
78
+ return reporter.getExitCode()
79
+ }
80
+
81
+ for (const file of offenders) {
82
+ fail(
83
+ `${file}: import.meta deep navigation (≥4 рівні ..) без ізоляції — ` +
84
+ `оберни у withTmpDir() або захисти test.skipIf(env.STRYKER_MUTATOR_WORKER) (test.mdc, sandbox-aware-test)`
85
+ )
86
+ }
87
+
88
+ return reporter.getExitCode()
89
+ }
@@ -8,5 +8,4 @@ resource: npm/rules/text/
8
8
 
9
9
  | Файл | Тип |
10
10
  | ------------------- | --------- |
11
- | [fix.mjs](fix.md) | JS Module |
12
11
  | [main.mjs](main.md) | JS Module |
@@ -8,5 +8,4 @@ resource: npm/rules/tool-surface/
8
8
 
9
9
  | Файл | Тип |
10
10
  | ------------------- | --------- |
11
- | [fix.mjs](fix.md) | JS Module |
12
11
  | [main.mjs](main.md) | JS Module |
@@ -8,5 +8,4 @@ resource: npm/rules/vue/
8
8
 
9
9
  | Файл | Тип |
10
10
  | ------------------- | --------- |
11
- | [fix.mjs](fix.md) | JS Module |
12
11
  | [main.mjs](main.md) | JS Module |
@@ -8,5 +8,4 @@ resource: npm/rules/worktree/
8
8
 
9
9
  | Файл | Тип |
10
10
  | ------------------- | --------- |
11
- | [fix.mjs](fix.md) | JS Module |
12
11
  | [main.mjs](main.md) | JS Module |
@@ -0,0 +1,29 @@
1
+ ---
2
+ type: JS Module
3
+ title: hook.mjs
4
+ resource: npm/scripts/hook.mjs
5
+ docgen:
6
+ crc: 8a19fee4
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 90
9
+ ---
10
+
11
+ ## Огляд
12
+
13
+ Модуль є точкою входу для хуків Claude Code. Він зчитує контекст, отримуючи шлях до файлу з вхідного потоку або визначаючи його через Git. У режимі `--post-tool-use` шлях до файлу зчитується з JSON, що надходить у stdin. У режимі `--stop` визначається робоче дерево проти HEAD (`git diff HEAD` + untracked). Після зчитування контексту, він делегує виконання перевірки за допомогою `runLint`. Код виходу, отриманий від перевірки, перекодовується у відповідний протокол хука (1 $\rightarrow$ 2).
14
+
15
+ ## Поведінка
16
+
17
+ extractFilePath: Витягує шлях до файлу з JSON, отриманого з вхідного потоку.
18
+ runHookCli: Виконує логіку хука, визначаючи файли для перевірки на основі аргументів командного рядка, запускає перевірку та повертає відповідний код виходу.
19
+
20
+ ## Публічний API
21
+
22
+ extractFilePath — витягує шлях до файлу з вхідних даних JSON хука Claude Code PostToolUse.
23
+ runHookCli — виконує команду для хука n-cursor.
24
+
25
+ ## Гарантії поведінки
26
+
27
+ - Read-only: не виконує операцій запису (ФС/БД).
28
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
29
+ - За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
@@ -15,11 +15,10 @@ resource: npm/scripts/
15
15
  | [coverage-fix-extract.mjs](coverage-fix-extract.md) | JS Module |
16
16
  | [coverage-fix.mjs](coverage-fix.md) | JS Module |
17
17
  | [ensure-nitra-cursor-dev-dependencies.mjs](ensure-nitra-cursor-dev-dependencies.md) | JS Module |
18
+ | [hook.mjs](hook.md) | JS Module |
18
19
  | [post-tool-use-check.mjs](post-tool-use-check.md) | JS Module |
19
- | [post-tool-use-fix.mjs](post-tool-use-fix.md) | JS Module |
20
20
  | [rename-yaml-extensions.mjs](rename-yaml-extensions.md) | JS Module |
21
21
  | [skills-cli.mjs](skills-cli.md) | JS Module |
22
22
  | [sync-claude-config.mjs](sync-claude-config.md) | JS Module |
23
23
  | [sync-setup-bun-deps-action.mjs](sync-setup-bun-deps-action.md) | JS Module |
24
24
  | [upgrade-nitra-cursor-and-install.mjs](upgrade-nitra-cursor-and-install.md) | JS Module |
25
- | [worktree-cli.mjs](worktree-cli.md) | JS Module |
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Thin hook entrypoint для Claude Code hooks: зчитує контекст (stdin / git),
3
+ * делегує в `runLint({ readOnly: true })`, перекодовує exit-код у hook-протокол (1 → 2).
4
+ *
5
+ * Режими:
6
+ * --post-tool-use PostToolUse: file_path зі stdin JSON Claude Code.
7
+ * --stop Stop: робоче дерево vs HEAD (`git diff HEAD` + untracked).
8
+ */
9
+ import { once } from 'node:events'
10
+ import { cwd as processCwd } from 'node:process'
11
+
12
+ import { runLint } from './lib/run-lint.mjs'
13
+ import { collectChangedFiles } from './lib/changed-files.mjs'
14
+
15
+ /**
16
+ * @returns {Promise<string>} вміст stdin або '' на TTY
17
+ */
18
+ async function readStdin() {
19
+ if (process.stdin.isTTY) return ''
20
+ process.stdin.setEncoding('utf8')
21
+ const chunks = []
22
+ process.stdin.on('data', c => chunks.push(c))
23
+ try {
24
+ await once(process.stdin, 'end')
25
+ } catch {
26
+ // error на stdin — повертаємо що встигли
27
+ }
28
+ return chunks.join('')
29
+ }
30
+
31
+ /**
32
+ * Дістає `tool_input.file_path` зі stdin JSON Claude Code PostToolUse hook.
33
+ * @param {string} json сирий stdin
34
+ * @returns {string|null}
35
+ */
36
+ export function extractFilePath(json) {
37
+ if (!json) return null
38
+ try {
39
+ const fp = JSON.parse(json)?.tool_input?.file_path
40
+ return typeof fp === 'string' && fp !== '' ? fp : null
41
+ } catch {
42
+ return null
43
+ }
44
+ }
45
+
46
+ /**
47
+ * CLI для `n-cursor hook`.
48
+ * @param {string[]} argv аргументи після 'hook'
49
+ * @returns {Promise<number>} exit-код (0 — чисто; 2 — є порушення hook-протокол)
50
+ */
51
+ export async function runHookCli(argv) {
52
+ const cwd = processCwd()
53
+ const postToolUse = argv.includes('--post-tool-use')
54
+ const stop = argv.includes('--stop')
55
+
56
+ if (!postToolUse && !stop) {
57
+ process.stderr.write('hook: потрібен --post-tool-use або --stop\n')
58
+ return 1
59
+ }
60
+
61
+ let files
62
+ if (postToolUse) {
63
+ const fp = extractFilePath(await readStdin())
64
+ if (!fp) return 0
65
+ files = [fp]
66
+ } else {
67
+ files = collectChangedFiles(cwd)
68
+ }
69
+
70
+ const code = await runLint({ files, readOnly: true, cwd })
71
+ return code !== 0 ? 2 : 0
72
+ }
@@ -42,4 +42,3 @@ resource: npm/scripts/lib/
42
42
  | [timing-summary.mjs](timing-summary.md) | JS Module |
43
43
  | [workspaces.mjs](workspaces.md) | JS Module |
44
44
  | [worktree-notice.mjs](worktree-notice.md) | JS Module |
45
- | [worktree.mjs](worktree.md) | JS Module |
@@ -3,27 +3,28 @@ type: JS Module
3
3
  title: run-lint.mjs
4
4
  resource: npm/scripts/lib/run-lint.mjs
5
5
  docgen:
6
- crc: 7d8f3637
6
+ crc: f574f097
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
11
  ## Огляд
12
12
 
13
- Модуль ініціалізує та керує процесом лінтування коду. Він визначає набір правил лінтування, спираючись на конфігурацію, описану в `meta.json`, та прапорець `full`, використовуючи активні правила з `.n-cursor.json`. Після визначення правил, він запускає лінтер-оркестрацію для виконання лінтування по всьому репозиторію або в режимі дельти. Модуль підтримує автоматичне форматування коду за допомогою `oxfmt`.
13
+ Модуль керує процесом лінтування коду. Він визначає набір активних правил лінтування, використовуючи конфігурації з `meta.json` та `.n-cursor.json`. Модуль надає можливість вибрати ці правила за допомогою `selectLintRules` та ініціювати запуск перевірки коду за допомогою `runLint`.
14
14
 
15
15
  ## Поведінка
16
16
 
17
- selectLintRules вибирає і відсортовує ідентифікатори правил для лінтування на основі їхньої конфігурації лінту та прапорця `full`, враховуючи активні правила з `.n-cursor.json`.
18
- runLint запускає лінтер-оркестрацію, виконуючи лінтування в режимі дельти або по всьому репозиторію, залежно від прапорця `full`, і може виконувати форматування за допомогою `oxfmt` у режимі фіксації.
17
+ selectLintRules визначає, які правила лінтуються на основі їхньої конфігурації та активності в `.n-cursor.json`, повертаючи відсортований список ID.
18
+ runLint запускає лінтер-оркестрацію залежно від наданих опцій: виконує прогін для конкретних правил, перевіряє лише змінені файли, виконує повний прогін репозиторію або форматування.
19
19
 
20
20
  ## Публічний API
21
21
 
22
- selectLintRules — вибирає ідентифікатори правил для контексту, розташовуючи їх в алфавітному порядку.
22
+ selectLintRules — вибирає ідентифікатори правил для контексту в алфавітному порядку.
23
23
  runLint — ініціює процес лінтування.
24
- full — сканує весь репозиторій, порівнюючи його з базовою версією.
25
- readOnly — лише виявляє проблеми без внесення змін, порівнюючи з базовою версією.
26
- rules — виконує повне сканування лише для заданого набору правил.
24
+ full — аналізує весь репозиторій, порівнюючи поточний стан із початковим.
25
+ readOnly — лише виявляє проблеми, не вносячи змін.
26
+ rules — виконує повний прогін лише для заданого набору правил у вказаному контексті.
27
+ files — виконує перевірку лише для перелічених файлів у режимі хука.
27
28
 
28
29
  ## Гарантії поведінки
29
30
 
@@ -3,25 +3,25 @@ type: JS Module
3
3
  title: run-rule.mjs
4
4
  resource: npm/scripts/lib/run-rule.mjs
5
5
  docgen:
6
- crc: 7d0585e1
6
+ crc: c9b164c7
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
- score: 90
8
+ score: 100
9
9
  ---
10
10
 
11
11
  ## Огляд
12
12
 
13
- Файл оркеструє виконання одного правила під CLI `fix`. Він послідовно застосовує гейт `applies` з `js/applies.mjs` для визначення придатності правила. Якщо гейт повертає `false`, правило не застосовується. Далі виконуються JS-концерни та Policy-концерни в алфавітному порядку. Резолвер `resolveTargetFiles` ділить кеш між концернами. Кожен concern створює власний репортер, а їхні exit-коди OR-уються в єдиний контракт, що забезпечує 0/1 результат для правила. Оркестратор спирається на конфігурації `target.json` та `.n-cursor.json`.
13
+ Файл оркеструє виконання одного правила під CLI `fix`. Він послідовно застосовує `applies`-гейт, а потім виконує JS-концерни та Policy-концерни. Резолвер ділить кеш між концернами, а кожен concern має власний механізм звітності, що об'єднується в єдиний exit-код правила. Процес спирається на конфігураційні файли, зокрема `target.json` та `.n-cursor.json`.
14
14
 
15
15
  ## Поведінка
16
16
 
17
- runTemplateSubsetConcern виконує перевірку концерну, де канон визначається сніпетом у `target.json`, звіряючи його з актуальними файлами-таргетами.
17
+ runTemplateSubsetConcern виконує перевірку концерну, де канон визначено у `target.json` як `template`, звіряючи вміст файлів-таргетів з шаблоном, визначеним у відповідному каталозі.
18
18
 
19
- runRule оркеструє виконання одного правила, послідовно застосовуючи applies-гейт, виконуючи JS-концерни та запускаючи policy-концерни, а також перевіряючи відсутність markdown-посилань.
19
+ runRule оркеструє виконання одного правила, послідовно застосовуючи `applies`-гейт, виконуючи JS-концерни, запускаючи policy-концерни та перевіряючи відсутність markdown-посилань у `main.mdc`.
20
20
 
21
21
  ## Публічний API
22
22
 
23
- runTemplateSubsetConcern — Порівнює фактичний файл із канонічним шаблоном (з `target.json`) для перевірки, чи всі обов'язкові елементи присутні, дозволяючи додаткові.
24
- runRule — Виконує окреме правило, яке проходить через перевірки застосовності, JavaScript-концерни та політики.
23
+ runTemplateSubsetConcern — Порівнює фактичний файл з канонічним шаблоном (`target.json:"check":"template"`), визначаючи, чи всі обов'язкові елементи з шаблону присутні у файлі.
24
+ runRule — Виконує окреме правило, перевіряючи його відповідність через гейт, JavaScript-концерни та політики.
25
25
 
26
26
  ## Гарантії поведінки
27
27
 
@@ -15,5 +15,4 @@ resource: npm/scripts/lib/fix/
15
15
  | [llm-worker.mjs](llm-worker.md) | JS Module |
16
16
  | [orchestrator.mjs](orchestrator.md) | JS Module |
17
17
  | [run-conformance-check.mjs](run-conformance-check.md) | JS Module |
18
- | [run-fix-check.mjs](run-fix-check.md) | JS Module |
19
18
  | [t0.mjs](t0.md) | JS Module |
@@ -237,16 +237,18 @@ async function runScopedRules(rules, ctx) {
237
237
 
238
238
  /**
239
239
  * Запускає lint-оркестрацію.
240
- * @param {{ full?: boolean, readOnly?: boolean, rules?: string[], cwd?: string, rulesDir?: string, log?: (s: string) => void }} [opts] параметри
240
+ * @param {{ full?: boolean, readOnly?: boolean, rules?: string[], files?: string[], cwd?: string, rulesDir?: string, log?: (s: string) => void }} [opts] параметри
241
241
  * - `full` — весь репо (`true`) проти дельти vs origin (`false`, default);
242
242
  * - `readOnly` — лише детект без мутацій (`true`) проти fix (`false`, default);
243
- * - `rules` — непорожній scope → повний прогін лише цих правил (лінтер + конформність, whole-repo).
243
+ * - `rules` — непорожній scope → повний прогін лише цих правил (лінтер + конформність, whole-repo);
244
+ * - `files` — явний список файлів (hook-режим): per-file правила без conformance/format/delta-full.
244
245
  * @returns {Promise<number>} exit code
245
246
  */
246
247
  export async function runLint(opts = {}) {
247
248
  const full = opts.full === true
248
249
  const readOnly = opts.readOnly === true
249
250
  const rules = Array.isArray(opts.rules) ? opts.rules : []
251
+ const explicitFiles = Array.isArray(opts.files) ? opts.files : null
250
252
  const cwd = opts.cwd ?? processCwd()
251
253
  const rulesDir = opts.rulesDir ?? RULES_DIR
252
254
  const log = opts.log ?? (s => process.stdout.write(s))
@@ -256,6 +258,17 @@ export async function runLint(opts = {}) {
256
258
  return runScopedRules(rules, { cwd, readOnly, rulesDir, conformance: opts.rulesDir === undefined, log })
257
259
  }
258
260
 
261
+ // Hook-режим (явний список файлів): per-file правила, без conformance/format/delta-full.
262
+ // Правила отримують точний список файлів; пусті files (Stop без змін) — правила однаково
263
+ // викликаються (orphan-детект у doc-files не залежить від списку джерел).
264
+ if (explicitFiles !== null) {
265
+ const metaById = readAllMeta(rulesDir)
266
+ const enabledRuleIds = await readEnabledLintRuleIds(metaById, cwd)
267
+ const ids = selectLintRules(metaById, false, enabledRuleIds)
268
+ const perFile = await runPerFileRules(ids, { rulesDir, changed: explicitFiles, cwd, readOnly, metaById, log })
269
+ return perFile.stop ? perFile.code : perFile.code
270
+ }
271
+
259
272
  // Default scope — дельта vs origin (merge-base main/origin/main); `--full` — весь репо.
260
273
  const changed = full ? undefined : collectChangedFilesSince(resolveChangedBase(cwd), cwd)
261
274
  if (!full && changed.length === 0) {
@@ -117,8 +117,7 @@ async function runPolicyConcern(bundledRulesDir, ruleId, concernName, walkCache)
117
117
  if (files.length === 0) {
118
118
  if (target.files.required && target.files.single) {
119
119
  const msg =
120
- target.missingMessage ??
121
- `${target.files.single} не існує — створи згідно main.mdc (${ruleId}.${concernName})`
120
+ target.missingMessage ?? `${target.files.single} не існує — створи згідно main.mdc (${ruleId}.${concernName})`
122
121
  reporter.fail(msg)
123
122
  }
124
123
  return reporter.getExitCode()
@@ -3,6 +3,7 @@ name: n-adr-normalize
3
3
  description: >-
4
4
  Ручний запуск ADR-нормалізації — обхід порогу й min-interval, прогон одного
5
5
  батчу чернеток через LLM, перегляд результату через git diff
6
+ version: '1.0'
6
7
  ---
7
8
 
8
9
  # n-adr-normalize — ручна нормалізація ADR-чернеток
@@ -2,6 +2,7 @@
2
2
  name: n-coverage-fix
3
3
  description: >-
4
4
  Автономна команда: запускає n-cursor coverage → читає вцілілих мутантів → ітеративно пише тести до конвергенції (max 3 ітерації)
5
+ version: '1.0'
5
6
  ---
6
7
 
7
8
  # n-coverage-fix — підвищення mutation score
@@ -2,6 +2,7 @@
2
2
  name: doc-aggregate
3
3
  description: >-
4
4
  Агрегуюча документація за запитом: module-summary на кожен логічний модуль (docs/ARCHITECTURE.md) і доменні доки бізнес-процесів у кореневій docs/ — синтез поверх готових файлових док (doc-files), батч-диспатч субагентів у worktree
5
+ version: '1.0'
5
6
  ---
6
7
 
7
8
  # doc-aggregate — агрегуюча документація (за запитом)
@@ -2,6 +2,7 @@
2
2
  name: doc-files
3
3
  description: >-
4
4
  Обовʼязковий крок задачі (як lint): для кожного зміненого/нового кодового файлу (js/mjs/ts/vue/py) JS-оркестрована генерація лаконічної поведінкової української md-документації у теку docs/ поряд із кодом, зі звіркою застарілості за CRC у frontmatter
5
+ version: '1.0'
5
6
  ---
6
7
 
7
8
  # doc-files — файлова документація (обовʼязковий крок)
@@ -1,39 +1,42 @@
1
1
  ---
2
2
  name: n-lint
3
3
  description: >-
4
- Запустити кореневий bun run lint, виправити порушення й підтвердити чистий вихід
4
+ Запустити дельта-лінт (npx @nitra/cursor lint) по змінених файлах vs origin, виправити порушення й підтвердити чистий вихід
5
+ version: '1.0'
5
6
  ---
6
7
 
7
- # n-lint — лінт проєкту через кореневий скрипт
8
+ # n-lint — лінт проєкту по змінених файлах
8
9
 
9
10
  ## Мета
10
11
 
11
- Один раз узгоджено прогнати весь ланцюжок **`lint`** з кореневого **`package.json`**, усунути помилки (авто- та вручну) і переконатися, що **`bun run lint`** завершується з кодом **`0`**.
12
+ Прогнати **`npx @nitra/cursor lint`** (**дельта-режим**: лише файли, змінені vs `origin`), усунути порушення (авто- та вручну) і переконатися, що команда завершується з кодом **`0`**.
13
+
14
+ > **Чому дельта, не `--full`?** `--full` — CI-режим: сканує весь репо незалежно від змін. Під час задачі це зайво — перевіряємо лише те, що змінили. `lint --full` запускати **не треба**.
12
15
 
13
16
  ## Передумови
14
17
 
15
- - Поточна робоча директорія — **корінь репозиторію**, де є **`package.json`** зі скриптом **`lint`**.
18
+ - Поточна робоча директорія — **корінь репозиторію**.
16
19
  - Залежності встановлені (**`bun i`**) — якщо після правок змінювався **`package.json`** / lockfile, знову виконай **`bun i`** перед наступним запуском лінту.
17
20
 
18
21
  ## Workflow
19
22
 
20
- 1. **Запуск** — виконай повний лінт:
23
+ 1. **Запуск** — дельта-лінт по змінених файлах:
21
24
 
22
25
  ```bash
23
- bun run lint
26
+ npx @nitra/cursor lint
24
27
  ```
25
28
 
26
- 2. **Якщо exit code не 0** — проаналізуй вивід (останній упавший крок у ланцюжку **`lint`** часто видно з stderr / логів):
27
- - Де скрипт уже робить **auto-fix** (**`--fix`**, **`markdownlint-cli2 --fix`**, **`oxfmt`** тощо) — перезапусти **`bun run lint`** після змін файлів.
28
- - Де auto-fix **немає** (наприклад, **jscpd**, **cspell**, **zizmor**, перевірки без прапорця fix) — **за замовчуванням рефактори код проєкту**, щоб усунути порушення: перейменуй ідентифікатори, перепиши логіку, видали дублікати тощо. **Не** розширюй конфіги з винятками «мовчки» — див. блок **«Винятки в конфігурації»** нижче.
29
+ 2. **Якщо exit code не 0** — проаналізуй вивід:
30
+ - Де лінт уже робить **auto-fix** (**`--fix`**, **`oxfmt`** тощо) — перезапусти **`npx @nitra/cursor lint`** після змін файлів.
31
+ - Де auto-fix **немає** (наприклад, **jscpd**, **cspell**, **zizmor**) — **за замовчуванням рефактори код проєкту**, щоб усунути порушення. **Не** розширюй конфіги з винятками «мовчки» — див. блок **«Винятки в конфігурації»** нижче.
29
32
  - Якщо спрацьовує **`sonarjs/cognitive-complexity`** — див. окремий блок нижче.
30
33
 
31
- 3. **Цикл** — повторюй кроки 1–2, доки **`bun run lint`** не завершиться успішно. Після суттєвих правок за потреби ще раз **`bun run lint`**, щоб переконатися, що не зламав наступний крок у скрипті **`lint`**.
34
+ 3. **Цикл** — повторюй кроки 1–2, доки **`npx @nitra/cursor lint`** не завершиться успішно.
32
35
 
33
- 4. **Верифікація** — фінальна перевірка (обов’язково з кодом **0**):
36
+ 4. **Верифікація** — фінальна перевірка (обов'язково з кодом **0**):
34
37
 
35
38
  ```bash
36
- bun run lint
39
+ npx @nitra/cursor lint
37
40
  ```
38
41
 
39
42
  5. **Результат** — коротко опиши, що саме виправлено; якщо щось блокує нульовий exit code — залиш чітке пояснення й наступні кроки для людини.
@@ -88,20 +91,20 @@ bun run lint
88
91
  - **Перед будь-яким рефакторингом** перевір, чи є **тести**, які покривають змінювану поведінку:
89
92
  - **unit** — **`bun test`** (або скрипт тестів у відповідному пакеті репозиторію);
90
93
  - **e2e** — **Playwright**, якщо в проєкті він використовується для UI/потоків.
91
- - Якщо тестів **немає** або вони **не покривають** блок, який змінюєш — **спочатку** додай/розшир тести, переконайся, що вони стабільно проходять, **потім** роби рефакторинг, **потім** знову прогони тести й **`bun run lint`**, щоб підтвердити, що функціональність коректна й лінт чистий.
94
+ - Якщо тестів **немає** або вони **не покривають** блок, який змінюєш — **спочатку** додай/розшир тести, переконайся, що вони стабільно проходять, **потім** роби рефакторинг, **потім** знову прогони тести й **`npx @nitra/cursor lint`**, щоб підтвердити, що функціональність коректна й лінт чистий.
92
95
  - Якщо після рефакторингу тести або лінт падають — **не** залишай «половинчастий» рефакторинг: відкотись або доведи зміни до зеленого стану.
93
96
 
94
97
  ## Паралелізм і навантаження на macOS
95
98
 
96
- **Паралельно по різних файлах — дозволено.** Диз'юнктні набори (per-file `n-cursor lint` на змінених vs origin) не конфліктують: кожен процес обробляє свій підмножину файлів, тож гонки за тим самим корпусом немає.
99
+ **Паралельно по різних файлах — дозволено.** Дельта-прогони на диз'юнктних наборах файлів не конфліктують.
97
100
 
98
- Проблема не паралелізм як такий, а **кілька одночасних whole-tree прогонів того самого корпусу** (**`bun run lint`**, **`n-cursor lint --full`**) у різних Bash-задачах/shells: кожен повторно сканує **весь** репо (eslint + oxlint + jscpd + knip), і диск/CPU перевантажуються дублюванням важкого full-scan.
101
+ Серіалізувати треба лише **`n-cursor lint --full`** (whole-tree CI-прогін) запускати не більше одного за раз. Але цей скіл `--full` **не використовує**: `npx @nitra/cursor lint` (дельта) сам по собі не перевантажує репо.
99
102
 
100
103
  ### Що робити агенту під час виконання цього скілу
101
104
 
102
- 1. **Whole-tree** прогін (**`bun run lint`** / **`lint --full`**) — **один** за раз, у одному foreground shell; **не** запускати другий full-прогін того самого корпусу паралельно.
103
- 2. **Per-file** лінт по диз'юнктних файлах (різні субагенти на різні файли) **дозволено** й не потребує серіалізації.
104
- 3. Якщо сесія/користувач уже запускає **whole-tree** лінт — не дублювати його; зачекати завершення.
105
+ 1. **Дельта-лінт** (`npx @nitra/cursor lint`) — можна запускати повторно; паралелізм із субагентами по різних файлах OK.
106
+ 2. **`lint --full`** у цьому скілі **не запускати**це CI-команда, не задачна.
107
+ 3. Якщо сесія/користувач уже запускає `lint --full` — не дублювати; зачекати завершення.
105
108
 
106
109
  **Що можна змінити у проєкті (локально або в `package.json`)**
107
110
 
@@ -113,4 +116,6 @@ bun run lint
113
116
 
114
117
  ## Примітка
115
118
 
116
- Цей скіл **не** замінює **`npx @nitra/cursor fix`**: **`lint`** перевіряє лінтери/формат у **`package.json`**, а **`check`** — програмні правила пакета **`@nitra/cursor`**. За потреби запускай обидва.
119
+ Цей скіл **не** замінює **`npx @nitra/cursor fix`**: **`lint`** перевіряє лінтери/формат (eslint, oxlint, rego тощо), а **`check`** / **`fix`** — програмні правила пакета **`@nitra/cursor`**. За потреби запускай обидва.
120
+
121
+ Для CI або явного повного сканування всього репо незалежно від змін — `npx @nitra/cursor lint --full`. Але в рамках задачі це **зайво**.
@@ -3,6 +3,7 @@ name: n-llm-patch
3
3
  description: >-
4
4
  Підготовка самодостатнього текстового промпта для іншого Claude/Cursor-агента —
5
5
  read-only аналіз CWD без жодних змін у поточному репо
6
+ version: '1.0'
6
7
  ---
7
8
 
8
9
  <!-- markdownlint-disable-file MD024 MD025 -->
@@ -2,6 +2,7 @@
2
2
  name: n-publish-telegram
3
3
  description: >-
4
4
  Підготовка матеріалу з поточного контексту для публікації в Telegram-каналі команди
5
+ version: '1.0'
5
6
  ---
6
7
 
7
8
  # Публікація в Telegram
@@ -3,6 +3,7 @@ name: n-start-check
3
3
  description: >-
4
4
  Smoke-перевірка bun-монорепо: зайти в кожен воркспейс зі `start`-скриптом, прогнати
5
5
  `start` і зафіксувати, чи проєкт взагалі запускається без негайного краху
6
+ version: '1.0'
6
7
  ---
7
8
 
8
9
  # n-start-check — чи запускається кожен воркспейс
@@ -3,6 +3,7 @@ name: n-taze
3
3
  description: >-
4
4
  Оновлення версій модулів проекту з аналізом major-змін і автоматичним
5
5
  рефакторингом несумісного коду
6
+ version: '1.0'
6
7
  ---
7
8
 
8
9
  # n-taze — Оновлення версій проекту
@@ -81,7 +82,7 @@ rg -n "<імпорт|функція|опція>" --type ts --type js --type vue
81
82
  Для кожного несумісного місця — застосувати міграцію згідно з changelog модуля (перейменувати імпорт, оновити сигнатуру виклику, замінити видалену опцію еквівалентом тощо). Після правок:
82
83
 
83
84
  ```bash
84
- bun run lint
85
+ npx @nitra/cursor lint
85
86
  bun run typecheck # якщо є
86
87
  bun test # якщо є
87
88
  ```
@@ -108,6 +109,6 @@ rm package.json.taze-bak bun.lock.taze-bak
108
109
 
109
110
  ## Примітка
110
111
 
111
- - Не запускати `bun run lint` паралельно з іншими ESLint-задачами — діє правило з кореневого `CLAUDE.md`.
112
+ - Не запускати `npx @nitra/cursor lint` паралельно з іншими ESLint-задачами — діє правило з кореневого `CLAUDE.md`.
112
113
  - Якщо проект — `npm/` пакет цього репо, після змін у `package.json` / коді треба підняти `version` і додати запис у `CHANGELOG.md` згідно з `npm/CLAUDE.md`.
113
114
  - При великій кількості major-оновлень розбити PR по одному модулю на коміт — щоб `git bisect` залишався корисним.
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ export {}
@@ -1,37 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix.mjs
4
- resource: npm/rules/abie/fix.mjs
5
- docgen:
6
- crc: 38cf876b
7
- score: 100
8
- ---
9
-
10
- Запуск правила приймає контекст прогону, застосовує JS-занепокоєні та політику, генерує посилання MDC та повертає результат прогону.
11
-
12
- Виконання у режимі CLI виконує повний еквівалент команди `npx @nitra/cursor fix <id>` та повертає код виходу.
13
-
14
- ## Поведінка
15
-
16
- 1. Запуск правила.
17
- - Приймає контекст прогону.
18
- - Виконує застосування JS-занепокоєних.
19
- - Застосовує політику.
20
- - Генерує посилання MDC.
21
- - Повертає результат прогону.
22
-
23
- 2. Виконання у режимі CLI.
24
- - Виконується при запуску через CLI.
25
- - Виконує повний еквівалент команди `npx @nitra/cursor fix <id>`.
26
- - Повертає код виходу.
27
-
28
- ## Публічний API
29
-
30
- run — запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
31
- Library mode — викликається CLI orchestration через `import + run`.
32
-
33
- ## Гарантії поведінки
34
-
35
- - Read-only: файл не виконує операцій запису у файлову систему.
36
- - Кешує результати в межах одного прогону.
37
- - Не звертається до мережі.