@nitra/cursor 12.8.3 → 12.8.5

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 (125) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/bin/n-cursor.js +3 -2
  3. package/package.json +1 -1
  4. package/rules/abie/js/env_dns.mdc +33 -0
  5. package/rules/abie/js/firebase_hosting.mdc +3 -0
  6. package/rules/abie/js/hc_pairing.mdc +23 -0
  7. package/rules/abie/js/ua_http_route.mdc +47 -0
  8. package/rules/abie/js/ua_node_selector.mdc +27 -0
  9. package/rules/abie/main.mdc +53 -0
  10. package/rules/doc-files/js/docs/index.md +15 -15
  11. package/rules/js/docs/index.md +3 -3
  12. package/rules/js/docs/main.md +7 -7
  13. package/rules/js/js/docs/check.md +12 -16
  14. package/rules/js/js/docs/index.md +4 -4
  15. package/rules/js/js/docs/tooling.md +8 -8
  16. package/rules/js/js/docs/utils_imports.md +22 -200
  17. package/rules/npm-module/js/docs/index.md +5 -5
  18. package/rules/npm-module/js/docs/rule_meta.md +13 -13
  19. package/rules/npm-module/js/docs/skill_meta.md +19 -9
  20. package/rules/npm-module/js/rule_meta.mjs +9 -9
  21. package/rules/npm-module/js/skill_meta.mjs +6 -6
  22. package/rules/test/js/docs/index.md +7 -7
  23. package/rules/test/js/docs/stryker_config.md +18 -35
  24. package/rules/test/js/docs/vitest-config-pool-forks.md +20 -12
  25. package/schemas/v8r-catalog.json +4 -4
  26. package/scripts/docs/index.md +16 -16
  27. package/scripts/docs/sync-setup-bun-deps-action.md +14 -14
  28. package/scripts/lib/check-mdc-template-refs.mjs +2 -2
  29. package/scripts/lib/docs/check-mdc-template-refs.md +12 -214
  30. package/scripts/lib/docs/index.md +36 -36
  31. package/scripts/lib/docs/inline-template-links.md +13 -293
  32. package/scripts/lib/docs/mirror-parity.md +18 -157
  33. package/scripts/lib/docs/rule-meta.md +19 -22
  34. package/scripts/lib/docs/run-rule.md +11 -11
  35. package/scripts/lib/docs/skill-meta.md +17 -19
  36. package/scripts/lib/docs/timing-summary.md +7 -7
  37. package/scripts/lib/inline-template-links.mjs +31 -0
  38. package/scripts/lib/mirror-parity.mjs +6 -4
  39. package/scripts/lib/rule-meta.mjs +1 -1
  40. package/scripts/lib/run-rule.mjs +4 -4
  41. package/scripts/lib/skill-meta.mjs +1 -1
  42. package/scripts/utils/docs/index.md +14 -14
  43. package/scripts/utils/docs/resolve-js-root.md +12 -13
  44. package/types/bin/n-cursor.d.ts +1 -1
  45. package/rules/abie/abie.mdc +0 -181
  46. /package/rules/abie/{meta.json → main.json} +0 -0
  47. /package/rules/adr/{meta.json → main.json} +0 -0
  48. /package/rules/adr/{adr.mdc → main.mdc} +0 -0
  49. /package/rules/bun/{meta.json → main.json} +0 -0
  50. /package/rules/bun/{bun.mdc → main.mdc} +0 -0
  51. /package/rules/capacitor/{meta.json → main.json} +0 -0
  52. /package/rules/capacitor/{capacitor.mdc → main.mdc} +0 -0
  53. /package/rules/changelog/{meta.json → main.json} +0 -0
  54. /package/rules/changelog/{changelog.mdc → main.mdc} +0 -0
  55. /package/rules/ci4/{meta.json → main.json} +0 -0
  56. /package/rules/ci4/{ci4.mdc → main.mdc} +0 -0
  57. /package/rules/doc-files/{meta.json → main.json} +0 -0
  58. /package/rules/doc-files/{doc-files.mdc → main.mdc} +0 -0
  59. /package/rules/docker/{meta.json → main.json} +0 -0
  60. /package/rules/docker/{docker.mdc → main.mdc} +0 -0
  61. /package/rules/efes/{meta.json → main.json} +0 -0
  62. /package/rules/efes/{efes.mdc → main.mdc} +0 -0
  63. /package/rules/feedback/{meta.json → main.json} +0 -0
  64. /package/rules/feedback/{feedback.mdc → main.mdc} +0 -0
  65. /package/rules/ga/{meta.json → main.json} +0 -0
  66. /package/rules/ga/{ga.mdc → main.mdc} +0 -0
  67. /package/rules/graphql/{meta.json → main.json} +0 -0
  68. /package/rules/graphql/{graphql.mdc → main.mdc} +0 -0
  69. /package/rules/hasura/{meta.json → main.json} +0 -0
  70. /package/rules/hasura/{hasura.mdc → main.mdc} +0 -0
  71. /package/rules/image-avif/{meta.json → main.json} +0 -0
  72. /package/rules/image-avif/{image-avif.mdc → main.mdc} +0 -0
  73. /package/rules/image-compress/{meta.json → main.json} +0 -0
  74. /package/rules/image-compress/{image-compress.mdc → main.mdc} +0 -0
  75. /package/rules/js/{meta.json → main.json} +0 -0
  76. /package/rules/js/{js.mdc → main.mdc} +0 -0
  77. /package/rules/js-bun-db/{meta.json → main.json} +0 -0
  78. /package/rules/js-bun-db/{js-bun-db.mdc → main.mdc} +0 -0
  79. /package/rules/js-bun-redis/{meta.json → main.json} +0 -0
  80. /package/rules/js-bun-redis/{js-bun-redis.mdc → main.mdc} +0 -0
  81. /package/rules/js-mssql/{meta.json → main.json} +0 -0
  82. /package/rules/js-mssql/{js-mssql.mdc → main.mdc} +0 -0
  83. /package/rules/js-run/{meta.json → main.json} +0 -0
  84. /package/rules/js-run/{js-run.mdc → main.mdc} +0 -0
  85. /package/rules/k8s/{meta.json → main.json} +0 -0
  86. /package/rules/k8s/{k8s.mdc → main.mdc} +0 -0
  87. /package/rules/nginx-default-tpl/{meta.json → main.json} +0 -0
  88. /package/rules/nginx-default-tpl/{nginx-default-tpl.mdc → main.mdc} +0 -0
  89. /package/rules/npm-module/{meta.json → main.json} +0 -0
  90. /package/rules/npm-module/{npm-module.mdc → main.mdc} +0 -0
  91. /package/rules/php/{meta.json → main.json} +0 -0
  92. /package/rules/php/{php.mdc → main.mdc} +0 -0
  93. /package/rules/python/{meta.json → main.json} +0 -0
  94. /package/rules/python/{python.mdc → main.mdc} +0 -0
  95. /package/rules/rego/{meta.json → main.json} +0 -0
  96. /package/rules/rego/{rego.mdc → main.mdc} +0 -0
  97. /package/rules/release/{meta.json → main.json} +0 -0
  98. /package/rules/release/{release.mdc → main.mdc} +0 -0
  99. /package/rules/rust/{meta.json → main.json} +0 -0
  100. /package/rules/rust/{rust.mdc → main.mdc} +0 -0
  101. /package/rules/security/{meta.json → main.json} +0 -0
  102. /package/rules/security/{security.mdc → main.mdc} +0 -0
  103. /package/rules/style/{meta.json → main.json} +0 -0
  104. /package/rules/style/{style.mdc → main.mdc} +0 -0
  105. /package/rules/tauri/{meta.json → main.json} +0 -0
  106. /package/rules/tauri/{tauri.mdc → main.mdc} +0 -0
  107. /package/rules/test/{meta.json → main.json} +0 -0
  108. /package/rules/test/{test.mdc → main.mdc} +0 -0
  109. /package/rules/text/{meta.json → main.json} +0 -0
  110. /package/rules/text/{text.mdc → main.mdc} +0 -0
  111. /package/rules/tool-surface/{meta.json → main.json} +0 -0
  112. /package/rules/tool-surface/{tool-surface.mdc → main.mdc} +0 -0
  113. /package/rules/vue/{meta.json → main.json} +0 -0
  114. /package/rules/vue/{vue.mdc → main.mdc} +0 -0
  115. /package/rules/worktree/{meta.json → main.json} +0 -0
  116. /package/rules/worktree/{worktree.mdc → main.mdc} +0 -0
  117. /package/skills/adr-normalize/{meta.json → main.json} +0 -0
  118. /package/skills/coverage-fix/{meta.json → main.json} +0 -0
  119. /package/skills/doc-aggregate/{meta.json → main.json} +0 -0
  120. /package/skills/doc-files/{meta.json → main.json} +0 -0
  121. /package/skills/lint/{meta.json → main.json} +0 -0
  122. /package/skills/llm-patch/{meta.json → main.json} +0 -0
  123. /package/skills/publish-telegram/{meta.json → main.json} +0 -0
  124. /package/skills/start-check/{meta.json → main.json} +0 -0
  125. /package/skills/taze/{meta.json → main.json} +0 -0
@@ -3,33 +3,31 @@ type: JS Module
3
3
  title: skill-meta.mjs
4
4
  resource: npm/scripts/lib/skill-meta.mjs
5
5
  docgen:
6
- crc: 0c638d18
6
+ crc: c0918db5
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
7
9
  ---
8
10
 
9
- Цей файл парсить метадані скілу з файлу `meta.json` та надає інформацію про його конфігурацію. Він служить єдиним джерелом правди про скіл, замінюючи старий `auto.md`, і використовується для визначення, чи потрібно запускати скіл в окремому worktree, чи з кореня репозиторію. Це забезпечує узгодженість даних про скіли та полегшує їх використання в інших частинах системи.
11
+ ## Огляд
12
+
13
+ Спільний парсер метаданих скіла, що зчитує дані з `npm/skills/<id>/meta.json`. Він є єдиним джерелом правди для конфігурації скілів, визначаючи умови автоактивації (через поле `auto`, де `SKILL_ALWAYS` означає "завжди"), чи виконувати скіл в окремому git-worktree (`worktree`), та чи вимагає скіл запуску з кореня репозиторію (`requireRoot`). Модуль надає механізми для парсингу цих метаданих, перевірки вимог до кореня та читання сирих даних. Він працює в режимі fail-safe, перехоплюючи помилки та повертаючи порожнє значення замість винятків.
10
14
 
11
15
  ## Поведінка
12
16
 
13
- SKILL_ALWAYS: визначає літерал для безумовної автоактивації.
14
- parseSkillAutoSpec: перетворює значення `auto` з `meta.json` у об’єкт `SkillAutoSpec`.
15
- skillRequiresRoot: визначає, чи вимагає скіл запуску з кореня репо.
16
- readSkillMetaRaw: читає та парсить `meta.json` одного скіла, повертаючи розпарсений об’єкт або `null`.
17
+ SKILL_ALWAYS надає літерал для безумовної автоактивації скіла.
18
+ parseSkillAutoSpec перетворює значення поля `auto` з метаданих скіла у специфікацію автоактивації.
19
+ skillRequiresRoot визначає, чи вимагає скіл запуску з кореня репозиторію, виходячи з метаданих.
20
+ readSkillMetaRaw читає та парсить файл `main.json` у каталозі скіла, повертаючи його вміст або `null` у разі помилки.
17
21
 
18
22
  ## Публічний API
19
23
 
20
- - SKILL_ALWAYS — Активує скіл завжди.
21
- - parseSkillAutoSpec — Перетворює специфікацію авто-скілу з JSON.
22
- - skillRequiresRoot — Перевіряє, чи потрібен скілу доступ до кореневої директорії.
23
- - readSkillMetaRaw — Зчитує та аналізує метадані скілу з файлу JSON.
24
+ SKILL_ALWAYS — маркер, що вказує на безумовну активацію
25
+ parseSkillAutoSpec — витягує налаштування автоматичного запуску з `meta.json`
26
+ skillRequiresRoot — визначає, чи потрібен запуск скіла з кореня репозиторію
27
+ readSkillMetaRaw — зчитує та обробляє метадані окремого скіла з `meta.json`
24
28
 
25
29
  ## Гарантії поведінки
26
30
 
27
- - Повертає `false` якщо не вдається розібрати `meta.json`.
28
- - Повертає `null` якщо не вдається визначити `auto.spec`.
29
- - `auto.spec` завжди є масивом id правил, якщо `auto.spec` визначено.
30
- - `worktree` має значення `true` лише для скілів, які потребують окремого git-worktree.
31
- - `requireRoot` має значення `true` лише для скілів, які мутують в CWD без worktree-ізоляції.
32
- - Якщо `worktree` має значення `true`, поле `requireRoot` не використовується.
33
- - Не використовує кеш.
34
- - Не кидає винятків.
35
- - Гарантує, що `meta.json` є єдиним джерелом правди для скілу.
31
+ - Read-only: не виконує операцій запису (ФС/БД).
32
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
33
+ - За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
@@ -3,24 +3,24 @@ type: JS Module
3
3
  title: timing-summary.mjs
4
4
  resource: npm/scripts/lib/timing-summary.mjs
5
5
  docgen:
6
- crc: ee74c1cd
6
+ crc: 47660e16
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
- score: 100
8
+ score: 95
9
9
  ---
10
10
 
11
11
  ## Огляд
12
12
 
13
- Формує таблицю-резюме часу виконання для оркестратора `fix` або `lint`. Таблиця використовується після прогону всіх правил перевірки (`runFixCommand` у `bin/n-cursor.js`) або після прогону скриптів лінтингу (`runLintCli` у `scripts/lib/run-lint-cli.mjs`), які визначаються у `package.json`. Звіт містить деталі часу виконання для кожного елемента у форматі `<ціла>.<десята>s`, а маркер `❌` на рядку вказує на невдачу (`ok === false`).
13
+ Форматує тривалість у форматі `<ціла>.<десята>s`. Генерує таблицю-резюме часу виконання для оркестратора `fix` або `lint`, яка включає детальні записи та загальний час прогону. Таблиця містить маркер `❌` для позначення невдач. Функція повертає готовий рядок із фінальним `\n`; друк здійснюється на стороні виклику.
14
14
 
15
15
  ## Поведінка
16
16
 
17
- formatDurationMs перетворює тривалість у мілісекундах у рядок формату `<ціла>.<десята>s`.
18
- formatTimingSummary генерує багаторядковий текст таблиці-резюме часу виконання на основі наданого заголовка та списку записів про час.
17
+ formatDurationMs форматує тривалість у мілісекундах у рядок у форматі `<ціла>.<десята>s`.
18
+ formatTimingSummary генерує багаторядковий рядок із таблицею-резюме часу виконання, включаючи окремі записи та загальний час.
19
19
 
20
20
  ## Публічний API
21
21
 
22
- ⏱ formatDurationMs: Перетворює мілісекунди на формат `<sec>.<десята>s`, використовуючи округлення вниз.
23
- ⏱ formatTimingSummary: Генерує багаторядковий вивід у форматі таблиці-резюме для стандартного виводу.
22
+ ⏱ formatDurationMs: Перетворює мілісекунди у формат `<sec>.<десята>s`, використовуючи нижнє округлення для забезпечення консистентності виводу.
23
+ ⏱ formatTimingSummary: Генерує багаторядковий текстовий звіт про час виконання, структурований як таблиця з сумарними даними.
24
24
 
25
25
  ## Гарантії поведінки
26
26
 
@@ -4,6 +4,7 @@ import { basename, extname, join } from 'node:path'
4
4
 
5
5
  const MD_LINK_RE = /\[([^\]]{1,200})\]\((\.\/[^)]{1,500})\)/g
6
6
  const TEMPLATE_SEGMENT_RE = /\/templates?\//
7
+ const MDC_EXT_RE = /\.mdc$/
7
8
  /** Статичні regexp-літерали `^(.+)\.<slot>\.<ext>$` — без `RegExp(variable)`. */
8
9
  const SLOT_SUFFIX_RES = [/^(.+)\.snippet\.[^.]+$/, /^(.+)\.deny\.[^.]+$/, /^(.+)\.contains\.[^.]+$/]
9
10
 
@@ -66,3 +67,33 @@ export async function inlineTemplateLinks(text, ruleDir) {
66
67
 
67
68
  return result
68
69
  }
70
+
71
+ /**
72
+ * Finds markdown links whose href ends with `.mdc` (and is not a /template/ path) and
73
+ * replaces them with the raw markdown content of the linked file (no fencing).
74
+ * Intended for per-concern section files living alongside their .mjs implementations.
75
+ * Throws Error if a matched link target doesn't exist (fail loud).
76
+ * @param {string} text .mdc file contents (after inlineTemplateLinks)
77
+ * @param {string} ruleDir absolute path to the rule directory
78
+ * @returns {Promise<string>} transformed text
79
+ */
80
+ export async function inlineMarkdownIncludes(text, ruleDir) {
81
+ const matches = [...text.matchAll(MD_LINK_RE)].filter(m => MDC_EXT_RE.test(m[2]) && !TEMPLATE_SEGMENT_RE.test(m[2]))
82
+ if (matches.length === 0) return text
83
+
84
+ let result = text
85
+ for (const match of matches) {
86
+ const [fullMatch, , href] = match
87
+ const relPath = href.slice(2) // strip leading ./
88
+ const absPath = join(ruleDir, relPath)
89
+
90
+ if (!existsSync(absPath)) {
91
+ throw new Error(`inlineMarkdownIncludes: file not found: ${absPath} (referenced from .mdc)`)
92
+ }
93
+
94
+ const raw = await readFile(absPath, 'utf8')
95
+ result = result.replace(fullMatch, () => raw.trim())
96
+ }
97
+
98
+ return result
99
+ }
@@ -9,7 +9,7 @@
9
9
  import { existsSync, readdirSync, readFileSync } from 'node:fs'
10
10
  import { dirname, join } from 'node:path'
11
11
 
12
- import { inlineTemplateLinks } from './inline-template-links.mjs'
12
+ import { inlineMarkdownIncludes, inlineTemplateLinks } from './inline-template-links.mjs'
13
13
 
14
14
  const MIRROR_PREFIX = 'n-'
15
15
  const MDC_EXT = '.mdc'
@@ -30,7 +30,7 @@ export function listManagedMirrors(repoRoot) {
30
30
  return {
31
31
  id,
32
32
  mirrorPath: join(rulesDir, f),
33
- canonicalPath: join(repoRoot, 'npm/rules', id, `${id}${MDC_EXT}`)
33
+ canonicalPath: join(repoRoot, 'npm/rules', id, `main${MDC_EXT}`)
34
34
  }
35
35
  })
36
36
  .filter(m => existsSync(m.canonicalPath))
@@ -41,8 +41,10 @@ export function listManagedMirrors(repoRoot) {
41
41
  * @param {string} canonicalPath абсолютний шлях `npm/rules/<id>/<id>.mdc`
42
42
  * @returns {Promise<string>} очікуваний текст дзеркала
43
43
  */
44
- export function expectedMirrorContent(canonicalPath) {
45
- return inlineTemplateLinks(readFileSync(canonicalPath, 'utf8'), dirname(canonicalPath))
44
+ export async function expectedMirrorContent(canonicalPath) {
45
+ const dir = dirname(canonicalPath)
46
+ const withTemplates = await inlineTemplateLinks(readFileSync(canonicalPath, 'utf8'), dir)
47
+ return inlineMarkdownIncludes(withTemplates, dir)
46
48
  }
47
49
 
48
50
  /**
@@ -70,7 +70,7 @@ export function parseRuleLintSpec(value) {
70
70
  * @returns {Record<string, unknown> | null} обʼєкт або `null` (немає файлу / невалідний JSON / не-обʼєкт)
71
71
  */
72
72
  export function readRuleMetaRaw(ruleDir) {
73
- const metaPath = join(ruleDir, 'meta.json')
73
+ const metaPath = join(ruleDir, 'main.json')
74
74
  if (!existsSync(metaPath)) return null
75
75
  try {
76
76
  const parsed = JSON.parse(readFileSync(metaPath, 'utf8'))
@@ -81,7 +81,7 @@ export async function runTemplateSubsetConcern(concernAbsDir, target, files, rul
81
81
  for (const file of files) {
82
82
  const rel = relative(process.cwd(), file) || file
83
83
  const actual = await parseByExt(file)
84
- const opts = { targetPath: rel, source: `${ruleId}.mdc` }
84
+ const opts = { targetPath: rel, source: 'main.mdc' }
85
85
  const violations = [
86
86
  ...(typeof data.snippet === 'string'
87
87
  ? checkTextSubset(actual, data.snippet, opts)
@@ -118,7 +118,7 @@ async function runPolicyConcern(bundledRulesDir, ruleId, concernName, walkCache)
118
118
  if (target.files.required && target.files.single) {
119
119
  const msg =
120
120
  target.missingMessage ??
121
- `${target.files.single} не існує — створи згідно ${ruleId}.mdc (${ruleId}.${concernName})`
121
+ `${target.files.single} не існує — створи згідно main.mdc (${ruleId}.${concernName})`
122
122
  reporter.fail(msg)
123
123
  }
124
124
  return reporter.getExitCode()
@@ -182,11 +182,11 @@ export async function runRule(rule, bundledRulesDir, walkCache) {
182
182
  }
183
183
 
184
184
  const ruleDir = join(bundledRulesDir, rule.id)
185
- const missing = await findMissingMdcRefs(ruleDir, rule.id)
185
+ const missing = await findMissingMdcRefs(ruleDir)
186
186
  if (missing.length > 0) {
187
187
  const reporter = createCheckReporter()
188
188
  for (const rel of missing) {
189
- reporter.fail(`${rule.id}.mdc: відсутнє markdown-посилання на template-файл ${rel}`)
189
+ reporter.fail(`main.mdc: відсутнє markdown-посилання на template-файл ${rel}`)
190
190
  }
191
191
  if (reporter.getExitCode() !== 0) totalCode = 1
192
192
  }
@@ -56,7 +56,7 @@ export function skillRequiresRoot(meta) {
56
56
  * @returns {Record<string, unknown> | null} розпарсений обʼєкт або `null` (немає файлу / невалідний JSON / не-обʼєкт)
57
57
  */
58
58
  export function readSkillMetaRaw(skillDir) {
59
- const metaPath = join(skillDir, 'meta.json')
59
+ const metaPath = join(skillDir, 'main.json')
60
60
  if (!existsSync(metaPath)) return null
61
61
  try {
62
62
  const parsed = JSON.parse(readFileSync(metaPath, 'utf8'))
@@ -6,18 +6,18 @@ resource: npm/scripts/utils/
6
6
 
7
7
  # npm/scripts/utils
8
8
 
9
- | Файл | Тип |
10
- | ----------------------------------------------------------- | --------- |
11
- | [ast-scan-utils.mjs](ast-scan-utils.md) | JS Module |
9
+ | Файл | Тип |
10
+ |---|---|
11
+ | [ast-scan-utils.mjs](ast-scan-utils.md) | JS Module |
12
12
  | [ensure-gitignore-entries.mjs](ensure-gitignore-entries.md) | JS Module |
13
- | [find-package-json-paths.mjs](find-package-json-paths.md) | JS Module |
14
- | [lock-cache-dir.mjs](lock-cache-dir.md) | JS Module |
15
- | [pass.mjs](pass.md) | JS Module |
16
- | [resolve-cargo-manifest.mjs](resolve-cargo-manifest.md) | JS Module |
17
- | [resolve-cmd.mjs](resolve-cmd.md) | JS Module |
18
- | [resolve-js-root.mjs](resolve-js-root.md) | JS Module |
19
- | [test-helpers.mjs](test-helpers.md) | JS Module |
20
- | [walk-cache.mjs](walk-cache.md) | JS Module |
21
- | [walkDir.mjs](walkDir.md) | JS Module |
22
- | [with-lock.mjs](with-lock.md) | JS Module |
23
- | [worktree-fingerprint.mjs](worktree-fingerprint.md) | JS Module |
13
+ | [find-package-json-paths.mjs](find-package-json-paths.md) | JS Module |
14
+ | [lock-cache-dir.mjs](lock-cache-dir.md) | JS Module |
15
+ | [pass.mjs](pass.md) | JS Module |
16
+ | [resolve-cargo-manifest.mjs](resolve-cargo-manifest.md) | JS Module |
17
+ | [resolve-cmd.mjs](resolve-cmd.md) | JS Module |
18
+ | [resolve-js-root.mjs](resolve-js-root.md) | JS Module |
19
+ | [test-helpers.mjs](test-helpers.md) | JS Module |
20
+ | [walk-cache.mjs](walk-cache.md) | JS Module |
21
+ | [walkDir.mjs](walkDir.md) | JS Module |
22
+ | [with-lock.mjs](with-lock.md) | JS Module |
23
+ | [worktree-fingerprint.mjs](worktree-fingerprint.md) | JS Module |
@@ -3,27 +3,26 @@ type: JS Module
3
3
  title: resolve-js-root.mjs
4
4
  resource: npm/scripts/utils/resolve-js-root.mjs
5
5
  docgen:
6
- crc: 2c2b9f11
6
+ crc: 99e5a8a4
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
7
9
  ---
8
10
 
9
- Цей файл визначає кореневий JavaScript-файл проєкту, необхідний для запуску інструментів аналізу коду. Він використовується як у великих проєктах з декількома workspace-ами, так і в окремих пакетах, забезпечуючи єдине місце для визначення кореневого розташування. Це спрощує інтеграцію з інструментами coverage та тестування.
11
+ ## Огляд
12
+
13
+ Визначає корінь JS-коду в проєкті, відповідно до логіки для workspace-projects (перший workspace з підтримкою glob-патернів `cf/*`) або single-package (корінь cwd). Ця утиліта є спільною для coverage-провайдера JS та test-концерну `stryker_config` (DRY). Публічні функції дозволяють отримати один або повний список шляхів до всіх JS-коренів, при цьому свідомо виключаються каталоги `.git` та `node_modules`. Код спирається на конфігураційні файли `package.json` та `.n-cursor.json`.
10
14
 
11
15
  ## Поведінка
12
16
 
13
- resolveJsRoot: повертає абсолютний шлях до першого JS-кореня проєкту.
14
- resolveAllJsRoots: повертає масив абсолютних шляхів до всіх JS-коренів проєкту, враховуючи glob-патерни з `workspaces` у кореневому `package.json`. Якщо JS-коренів немає, повертає абсолютний шлях до кореневого каталогу.
17
+ resolveJsRoot повертає абсолютний шлях до першого JS-кореня проєкту. Якщо кореневий `package.json` відсутній, повертає null.
18
+ resolveAllJsRoots повертає масив абсолютних шляхів до всіх JS-коренів проєкту. Ігнорує каталоги `.git` та `node_modules`.
15
19
 
16
20
  ## Публічний API
17
21
 
18
- - resolveJsRoot — Знаходить кореневий JS-файл проєкту.
19
- - resolveAllJsRoots — Знаходить всі кореневі JS-файли проєкту. Включає всі workspace-файли та їхні `package.json`, використовуючи glob-патерни. Для single-package повертає поточну директорію.
22
+ resolveJsRoot — знаходить кореневий каталог JavaScript-проєкту.
23
+ resolveAllJsRoots — повертає всі каталоги JavaScript-проєктів у робочому просторі.
20
24
 
21
25
  ## Гарантії поведінки
22
26
 
23
- - Повертає `true`, якщо кореневий JS-файл знайдено.
24
- - Повертає `false`, якщо кореневий JS-файл не знайдено.
25
- - Повертає `null`, якщо не вдалося визначити кореневий JS-файл.
26
- - Не обробляє помилки, а повертає `false` або `null`.
27
- - Ігнорує директорії `.git` та `node_modules`.
28
- - Не використовує кешування.
29
- - Не здійснює мережевих запитів.
27
+ - Read-only: не виконує операцій запису (ФС/БД).
28
+ - Свідомо пропускає шляхи: `.git`, `node_modules`.
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export {}
2
+ export {};
@@ -1,181 +0,0 @@
1
- ---
2
- description: Правила для проєктів AbInBev Efes
3
- alwaysApply: true
4
- version: '1.22'
5
- ---
6
-
7
- Правило **abie** для споживачів **@nitra/cursor**: **k8s** (Deployment + **HealthCheckPolicy** у **`hc.yaml`**, overlay **ua** — **nodeSelector**, **HTTPRoute** (будь-який непорожній **`target.name`**, для спільних сервісів **`auth-run-hl`** / **`file-link-hl`** — **`namespace: dev`** у base та patch **`…/backendRefs/…/namespace`** у **ua**)), гілки **dev**, **ua** у **clean-merged-branch**, а також заборона тримати артефакти **Firebase Hosting** у **підкаталогах першого рівня** (безпосередні діти кореня репозиторію; у самому корені ці імена не вимагаються до видалення).
8
-
9
- ## k8s: `hc.yaml` поруч із Deployment
10
-
11
- Якщо під **`k8s`** є **Deployment**, у **тій самій директорії** має бути **`hc.yaml`** з **HealthCheckPolicy** (**`networking.gke.io/v1`**): коректний modeline **`$schema`**, **`httpHealthCheck.requestPath`** — непорожній шлях від кореня (рядок, що починається з **`/`**: канонічно **`/healthz`**, але також допустимі **`/IsAlive`**, **`/api/live`** тощо — узгоджується з реальним endpoint сервісу), порт **8080**, **`targetRef.name`** — **headless** **Service** з суфіксом **`-hl`** (узгоджено з парою **`svc.yaml`** / **`svc-hl.yaml`** у **k8s.mdc**): або **`${metadata.name}-hl`**, або те саме ім’я, якщо **`metadata.name`** уже з **`-hl`**.
12
-
13
- ```yaml title="hc.yaml"
14
- # yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/networking.gke.io/healthcheckpolicy_v1.json
15
- apiVersion: networking.gke.io/v1
16
- kind: HealthCheckPolicy
17
- metadata:
18
- name: СЕРВІС
19
- namespace: dev # kustomize overlay
20
- spec:
21
- default:
22
- config:
23
- type: HTTP
24
- httpHealthCheck:
25
- requestPath: /healthz
26
- port: 8080
27
- targetRef:
28
- group: ''
29
- kind: Service
30
- name: СЕРВІС-hl
31
- ```
32
-
33
- ## k8s: overlay **HTTPRoute** (**ua**)
34
-
35
- За наявності **Deployment** під **k8s** і наявності **Vite** (**`vite.config.js`**, **`vite.config.mjs`** або **`vite.config.ts`** у каталозі пакета) у **`ua/kustomization.yaml`** цього пакета потрібні **inline JSON6902** у **`patches`**: **target** **`kind: HTTPRoute`**, **непорожній `name`** (як у маніфесті маршруту). Мають бути зміни **`/spec/hostnames`** (домени abie — у скрипті) та **`/spec/parentRefs/0/namespace`** (**`ua`**, також дозволені префікси **`ua-*`**, наприклад **`ua-b2b`**). Як обирати **`op`** (**add** / **replace** тощо) у patch — **k8s.mdc** (розділ про JSON patch у kustomization).
36
-
37
- ### HTTPRoute: спільні сервіси **`auth-run-hl`**, **`file-link-hl`**
38
-
39
- У **HTTPRoute** у шляху з **`…/k8s/base/…`** у **`spec.hostnames`** дозволені лише **`aiml.live`**, **`*.aiml.live`** та інші піддомени **aiml.live** (перевірка — Rego-пакет **`abie.http_route_base`**, див. розділ нижче).
40
-
41
- Ці **Service** (headless **`-hl`**) живуть у **базовому** неймспейсі **`dev`**. У маніфесті **HTTPRoute** під **`k8s`** (шар без **`ua/`** — наприклад **`…/k8s/base/hr.yaml`**) для кожного **`backendRefs`** до такого сервісу явно вкажи **`namespace: dev`** і порт **8080**:
42
-
43
- ```yaml title="…/k8s/base/hr.yaml (фрагмент)"
44
- spec:
45
- rules:
46
- - matches:
47
- - path:
48
- type: PathPrefix
49
- value: /
50
- backendRefs:
51
- - name: auth-run-hl
52
- namespace: dev
53
- port: 8080
54
- - name: file-link-hl
55
- namespace: dev
56
- port: 8080
57
- ```
58
-
59
- У **`ua/kustomization.yaml`** додай до того самого **inline** patch на **`HTTPRoute`** (той самий **`target.name`**) операції **JSON6902** з **`path`**: **`/spec/rules/<i>/backendRefs/<j>/namespace`**, де **`<i>`** / **`<j>`** — індекси відповідно до порядку **`spec.rules`** та **`backendRefs`** у base-файлі; **`value`**: **`ua`** (також дозволені **`ua-*`**). Якщо кілька таких **`backendRefs`**, потрібна окрема операція для кожного.
60
-
61
- ```yaml title="…/ua/kustomization.yaml (фрагмент)"
62
- - target:
63
- kind: HTTPRoute
64
- name: my-httproute
65
- patch: |-
66
- - op: replace
67
- path: /spec/hostnames
68
- value:
69
- - "abie.app" # зокрема vybeerai.com.ua, *.vybeerai.com.ua, *.abie.app
70
- - op: replace
71
- path: /spec/parentRefs/0/namespace
72
- value: ua
73
- - op: replace
74
- path: /spec/rules/0/backendRefs/0/namespace
75
- value: ua
76
- - op: replace
77
- path: /spec/rules/0/backendRefs/1/namespace
78
- value: ua
79
- ```
80
-
81
- ## k8s: overlay **ua** і nodeSelector
82
-
83
- У **`…/ua/kustomization.yaml`** того пакета, у дереві **`k8s`** якого є **Deployment**, потрібен patch на **`kind: Deployment`**: **`spec.template.spec.nodeSelector`** з **`preem: false`**. Форму **JSON6902** (шлях **`/spec/template/spec/nodeSelector`**, **`op`**) див. **k8s.mdc**.
84
-
85
- ```yaml title="…/ua/kustomization.yaml (фрагмент)"
86
- patches:
87
- - target:
88
- kind: Deployment
89
- name: my-app
90
- patch: |-
91
- - op: add
92
- path: /spec/template/spec/nodeSelector
93
- value:
94
- preem: 'false'
95
- ```
96
-
97
- ### Базовий Deployment (`…/base/`)
98
-
99
- Якщо **Deployment** у YAML під **`k8s`** лежить у шляху з сегментом **`base`**, у **`spec.template.spec.nodeSelector`** має бути **`preem`** зі значенням **істинно** (**`true`** або рядок **`'true'`**); overlay **ua** підміняє селектор.
100
-
101
- ```yaml title="…/base/deploy.yaml (фрагмент)"
102
- spec:
103
- template:
104
- spec:
105
- nodeSelector:
106
- preem: 'true' # буде замінено через kustomize
107
- ```
108
-
109
- ## Внутрішньокластерні URL у env-файлах (dev / ua)
110
-
111
- Правило стосується **будь-якого** внутрішньокластерного URL у env-файлах abie-проєкту, а не лише `HASURA_GRAPHQL_ENDPOINT`. Це може бути URL до Hasura, KVCMS, `auth-run-hl`, `file-link-hl` чи будь-якого іншого Service у кластері — у всіх випадках DNS-суфікс і namespace-префікс мають відповідати **середовищу** з імені env-файлу.
112
-
113
- abie-проєкти живуть у **двох GKE-кластерах** (dev / ua), тож DNS-суфікс і namespace у URL відрізняються між `*.env`-файлами:
114
-
115
- | env-файл (basename) | namespace-префікс у URL | DNS-суфікс кластера | примітка |
116
- | --- | --- | --- | --- |
117
- | `dev.env`, `.dev.env` | `dev-…` | `abie-dev.internal` | GKE-кластер dev |
118
- | `ua.env`, `.ua.env` | `ua-…` | `abie-ua.internal` | GKE-кластер ua |
119
-
120
- Канонічна форма URL — `http://<service>.<namespace>.svc.<cluster-dns-suffix>:<port>`. Суфікс — `<cluster>.internal`.
121
-
122
- Приклади для одного env-файлу з двома сервісами (Hasura + KVCMS):
123
-
124
- ```env title="hasura/.dev.env"
125
- HASURA_GRAPHQL_ENDPOINT=http://apruv-h-hl.dev-apruv.svc.abie-dev.internal:8080
126
- KVCMS_URL=http://kvcms-hl.dev-apruv.svc.abie-dev.internal:8080
127
- ```
128
-
129
- ```env title="hasura/.ua.env"
130
- HASURA_GRAPHQL_ENDPOINT=http://apruv-h-hl.ua-apruv.svc.abie-ua.internal:8080
131
- KVCMS_URL=http://kvcms-hl.ua-apruv.svc.abie-ua.internal:8080
132
- ```
133
-
134
- `<namespace>` (наприклад `dev-apruv` / `ua-apruv`) — `metadata.name` цільового namespace після kustomize-overlay для відповідного середовища; `<service>` — `metadata.name` headless Service (`-hl`) того сервісу, до якого йде URL.
135
-
136
- **Перевірка `js/env_dns.mjs`** сканує всі `*.env` файли, basename яких збігається з `dev.env` / `ua.env` (з провідною крапкою чи без), знаходить **усі** internal URL (`http://<svc>.<ns>.svc.<dns>` — як для Hasura-ендпоінта, так і для KVCMS чи будь-якого іншого) і вимагає, щоб для кожного:
137
-
138
- - DNS-суфікс відповідав env: `abie-dev.internal` / `abie-ua.internal`;
139
- - namespace починався з `dev-` / `ua-` відповідно.
140
-
141
- Загальне правило про **внутрішній** URL (не публічний домен) для `HASURA_GRAPHQL_ENDPOINT` лишається у **`hasura.mdc`** (для nitra і abie) — `rules/hasura/fix.mjs` приймає кластерний DNS-формат `<cluster>.internal`.
142
-
143
- ## `@nitra/abie-shared` у `devDependencies`
144
-
145
- У кореневому **`package.json`** abie-проєкту в **`devDependencies`** має бути **`@nitra/abie-shared`** — пакет зі спільними abie-ресурсами: канонічні GraphQL-схеми, скіли, типи (наприклад, шляхи `node_modules/@nitra/abie-shared/schema/...` для імпорту схем). Версію правило не фіксує — лише presence. Додати:
146
-
147
- ```bash
148
- bun add -d @nitra/abie-shared
149
- ```
150
-
151
- ## Firebase Hosting
152
-
153
- У **кожному** підкаталозі, що лежить **безпосередньо** в корені репозиторію, не тримати конфіг і кеш **Firebase Hosting**: у таких каталогах не повинно бути **`.firebaserc`**, **`firebase.json`** та каталогу **`.firebase/`** (у **самому** корені репозиторію ці імена перевіркою abie **не** розглядаються; `node_modules` / `.git` зі скану вилучаються).
154
-
155
- ## Git branches
156
-
157
- У **`.github/workflows/clean-merged-branch.yml`** у кроці **`phpdocker-io/github-actions-delete-abandoned-branches`** значення **`with.ignore_branches`** має містити **dev** та **ua** (разом з іншими гілками, якщо потрібно):
158
-
159
- - abie-specific канон (action marker + required ignore_branches tokens): [clean-merged-branch.yml.snippet.yml](./policy/clean_merged_ignore_branches/template/clean-merged-branch.yml.snippet.yml)
160
-
161
- ## Швидкий gate через conftest (Rego)
162
-
163
- Підмножину пер-документних правил продубльовано як rego-полісі у **`npm/rules/abie/policy/`** (запускається через **`bun run lint-rego`** для `*_test.rego` юніт-тестів і через **`npx @nitra/cursor fix abie`** для прогону по реальних YAML — деталі в **conftest.mdc** / **n-rego.mdc**). JS у **`js/<concern>.mjs`** authoritative — rego тільки швидкий gate для одиничного маніфеста (зокрема через IDE-розширення `tsandall.opa`).
164
-
165
- Пакети (директорія в **`npm/policy/abie/`** → namespace → що перевіряє):
166
-
167
- - **`http_route_base/`** → `abie.http_route_base` — у HTTPRoute під `…/k8s/.../base/...` усі `spec.hostnames` мають бути в домені `aiml.live` (включно з `*.aiml.live` та піддоменами). **Цільові файли:** `…/k8s/.../base/.../hr.yaml`.
168
- - **`health_check_policy/`** → `abie.health_check_policy` — структура HealthCheckPolicy: `apiVersion: networking.gke.io/v1`, `metadata.name`, `spec.default.config.type: HTTP`, `httpHealthCheck.requestPath` починається з `/`, `port: 8080`, `targetRef.kind: Service`, `targetRef.name` має суфікс `-hl`. **Цільові файли:** `…/k8s/.../hc.yaml`.
169
- - **`base_deployment_preem/`** → `abie.base_deployment_preem` — Deployment у base/ має `spec.template.spec.nodeSelector.preem` зі значенням `true` (boolean або рядок). **Цільові файли:** ресурсні YAML під `…/k8s/.../base/...`.
170
- - **`clean_merged_ignore_branches/`** → `abie.clean_merged_ignore_branches` — у workflow `.github/workflows/clean-merged-branch.yml` крок з `uses: phpdocker-io/github-actions-delete-abandoned-branches` має `with.ignore_branches`, що містить токени `dev,ua` (case-insensitive). **Цільові файли:** `.github/workflows/clean-merged-branch.yml`.
171
- - **`package_json_shared/`** → `abie.package_json_shared` — у кореневому `package.json` `devDependencies` має містити `@nitra/abie-shared` (presence-only, версію не фіксуємо). **Цільові файли:** `package.json`.
172
-
173
- Cross-file / FS-логіка лишається у JS-частинах (`js/<concern>.mjs`) — Rego не читає файлову систему й не робить cross-document резолюцію:
174
-
175
- - парність HCP↔Deployment у каталозі та modeline `hc.yaml` — `js/hc_pairing.mjs`;
176
- - валідація ua-overlay JSON6902 patches на HTTPRoute + аналіз cross-namespace `backendRefs` у пакетах — `js/ua_http_route.mjs`;
177
- - ua-overlay JSON6902 patch на `Deployment.nodeSelector` (`preem: false`) — `js/ua_node_selector.mjs`;
178
- - env→cluster DNS (`*.dev.env` / `*.ua.env`) — `js/env_dns.mjs`;
179
- - скан артефактів Firebase Hosting у підкаталогах першого рівня — `js/firebase_hosting.mjs`.
180
-
181
- Точна звірка `targetRef.name` HealthCheckPolicy з суфіксом `-hl` обчислюється з `hcp.metadata.name` і живе у Rego (`abie.health_check_policy`); за конвенцією `hcp.metadata.name` дорівнює `<deployment.name>`, тому окремий cross-file lookup до маніфесту Deployment не потрібен.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes