@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.
- package/CHANGELOG.md +12 -0
- package/bin/n-cursor.js +3 -2
- package/package.json +1 -1
- package/rules/abie/js/env_dns.mdc +33 -0
- package/rules/abie/js/firebase_hosting.mdc +3 -0
- package/rules/abie/js/hc_pairing.mdc +23 -0
- package/rules/abie/js/ua_http_route.mdc +47 -0
- package/rules/abie/js/ua_node_selector.mdc +27 -0
- package/rules/abie/main.mdc +53 -0
- package/rules/doc-files/js/docs/index.md +15 -15
- package/rules/js/docs/index.md +3 -3
- package/rules/js/docs/main.md +7 -7
- package/rules/js/js/docs/check.md +12 -16
- package/rules/js/js/docs/index.md +4 -4
- package/rules/js/js/docs/tooling.md +8 -8
- package/rules/js/js/docs/utils_imports.md +22 -200
- package/rules/npm-module/js/docs/index.md +5 -5
- package/rules/npm-module/js/docs/rule_meta.md +13 -13
- package/rules/npm-module/js/docs/skill_meta.md +19 -9
- package/rules/npm-module/js/rule_meta.mjs +9 -9
- package/rules/npm-module/js/skill_meta.mjs +6 -6
- package/rules/test/js/docs/index.md +7 -7
- package/rules/test/js/docs/stryker_config.md +18 -35
- package/rules/test/js/docs/vitest-config-pool-forks.md +20 -12
- package/schemas/v8r-catalog.json +4 -4
- package/scripts/docs/index.md +16 -16
- package/scripts/docs/sync-setup-bun-deps-action.md +14 -14
- package/scripts/lib/check-mdc-template-refs.mjs +2 -2
- package/scripts/lib/docs/check-mdc-template-refs.md +12 -214
- package/scripts/lib/docs/index.md +36 -36
- package/scripts/lib/docs/inline-template-links.md +13 -293
- package/scripts/lib/docs/mirror-parity.md +18 -157
- package/scripts/lib/docs/rule-meta.md +19 -22
- package/scripts/lib/docs/run-rule.md +11 -11
- package/scripts/lib/docs/skill-meta.md +17 -19
- package/scripts/lib/docs/timing-summary.md +7 -7
- package/scripts/lib/inline-template-links.mjs +31 -0
- package/scripts/lib/mirror-parity.mjs +6 -4
- package/scripts/lib/rule-meta.mjs +1 -1
- package/scripts/lib/run-rule.mjs +4 -4
- package/scripts/lib/skill-meta.mjs +1 -1
- package/scripts/utils/docs/index.md +14 -14
- package/scripts/utils/docs/resolve-js-root.md +12 -13
- package/types/bin/n-cursor.d.ts +1 -1
- package/rules/abie/abie.mdc +0 -181
- /package/rules/abie/{meta.json → main.json} +0 -0
- /package/rules/adr/{meta.json → main.json} +0 -0
- /package/rules/adr/{adr.mdc → main.mdc} +0 -0
- /package/rules/bun/{meta.json → main.json} +0 -0
- /package/rules/bun/{bun.mdc → main.mdc} +0 -0
- /package/rules/capacitor/{meta.json → main.json} +0 -0
- /package/rules/capacitor/{capacitor.mdc → main.mdc} +0 -0
- /package/rules/changelog/{meta.json → main.json} +0 -0
- /package/rules/changelog/{changelog.mdc → main.mdc} +0 -0
- /package/rules/ci4/{meta.json → main.json} +0 -0
- /package/rules/ci4/{ci4.mdc → main.mdc} +0 -0
- /package/rules/doc-files/{meta.json → main.json} +0 -0
- /package/rules/doc-files/{doc-files.mdc → main.mdc} +0 -0
- /package/rules/docker/{meta.json → main.json} +0 -0
- /package/rules/docker/{docker.mdc → main.mdc} +0 -0
- /package/rules/efes/{meta.json → main.json} +0 -0
- /package/rules/efes/{efes.mdc → main.mdc} +0 -0
- /package/rules/feedback/{meta.json → main.json} +0 -0
- /package/rules/feedback/{feedback.mdc → main.mdc} +0 -0
- /package/rules/ga/{meta.json → main.json} +0 -0
- /package/rules/ga/{ga.mdc → main.mdc} +0 -0
- /package/rules/graphql/{meta.json → main.json} +0 -0
- /package/rules/graphql/{graphql.mdc → main.mdc} +0 -0
- /package/rules/hasura/{meta.json → main.json} +0 -0
- /package/rules/hasura/{hasura.mdc → main.mdc} +0 -0
- /package/rules/image-avif/{meta.json → main.json} +0 -0
- /package/rules/image-avif/{image-avif.mdc → main.mdc} +0 -0
- /package/rules/image-compress/{meta.json → main.json} +0 -0
- /package/rules/image-compress/{image-compress.mdc → main.mdc} +0 -0
- /package/rules/js/{meta.json → main.json} +0 -0
- /package/rules/js/{js.mdc → main.mdc} +0 -0
- /package/rules/js-bun-db/{meta.json → main.json} +0 -0
- /package/rules/js-bun-db/{js-bun-db.mdc → main.mdc} +0 -0
- /package/rules/js-bun-redis/{meta.json → main.json} +0 -0
- /package/rules/js-bun-redis/{js-bun-redis.mdc → main.mdc} +0 -0
- /package/rules/js-mssql/{meta.json → main.json} +0 -0
- /package/rules/js-mssql/{js-mssql.mdc → main.mdc} +0 -0
- /package/rules/js-run/{meta.json → main.json} +0 -0
- /package/rules/js-run/{js-run.mdc → main.mdc} +0 -0
- /package/rules/k8s/{meta.json → main.json} +0 -0
- /package/rules/k8s/{k8s.mdc → main.mdc} +0 -0
- /package/rules/nginx-default-tpl/{meta.json → main.json} +0 -0
- /package/rules/nginx-default-tpl/{nginx-default-tpl.mdc → main.mdc} +0 -0
- /package/rules/npm-module/{meta.json → main.json} +0 -0
- /package/rules/npm-module/{npm-module.mdc → main.mdc} +0 -0
- /package/rules/php/{meta.json → main.json} +0 -0
- /package/rules/php/{php.mdc → main.mdc} +0 -0
- /package/rules/python/{meta.json → main.json} +0 -0
- /package/rules/python/{python.mdc → main.mdc} +0 -0
- /package/rules/rego/{meta.json → main.json} +0 -0
- /package/rules/rego/{rego.mdc → main.mdc} +0 -0
- /package/rules/release/{meta.json → main.json} +0 -0
- /package/rules/release/{release.mdc → main.mdc} +0 -0
- /package/rules/rust/{meta.json → main.json} +0 -0
- /package/rules/rust/{rust.mdc → main.mdc} +0 -0
- /package/rules/security/{meta.json → main.json} +0 -0
- /package/rules/security/{security.mdc → main.mdc} +0 -0
- /package/rules/style/{meta.json → main.json} +0 -0
- /package/rules/style/{style.mdc → main.mdc} +0 -0
- /package/rules/tauri/{meta.json → main.json} +0 -0
- /package/rules/tauri/{tauri.mdc → main.mdc} +0 -0
- /package/rules/test/{meta.json → main.json} +0 -0
- /package/rules/test/{test.mdc → main.mdc} +0 -0
- /package/rules/text/{meta.json → main.json} +0 -0
- /package/rules/text/{text.mdc → main.mdc} +0 -0
- /package/rules/tool-surface/{meta.json → main.json} +0 -0
- /package/rules/tool-surface/{tool-surface.mdc → main.mdc} +0 -0
- /package/rules/vue/{meta.json → main.json} +0 -0
- /package/rules/vue/{vue.mdc → main.mdc} +0 -0
- /package/rules/worktree/{meta.json → main.json} +0 -0
- /package/rules/worktree/{worktree.mdc → main.mdc} +0 -0
- /package/skills/adr-normalize/{meta.json → main.json} +0 -0
- /package/skills/coverage-fix/{meta.json → main.json} +0 -0
- /package/skills/doc-aggregate/{meta.json → main.json} +0 -0
- /package/skills/doc-files/{meta.json → main.json} +0 -0
- /package/skills/lint/{meta.json → main.json} +0 -0
- /package/skills/llm-patch/{meta.json → main.json} +0 -0
- /package/skills/publish-telegram/{meta.json → main.json} +0 -0
- /package/skills/start-check/{meta.json → main.json} +0 -0
- /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:
|
|
6
|
+
crc: c0918db5
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
7
9
|
---
|
|
8
10
|
|
|
9
|
-
|
|
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
|
|
15
|
-
skillRequiresRoot
|
|
16
|
-
readSkillMetaRaw
|
|
17
|
+
SKILL_ALWAYS — надає літерал для безумовної автоактивації скіла.
|
|
18
|
+
parseSkillAutoSpec — перетворює значення поля `auto` з метаданих скіла у специфікацію автоактивації.
|
|
19
|
+
skillRequiresRoot — визначає, чи вимагає скіл запуску з кореня репозиторію, виходячи з метаданих.
|
|
20
|
+
readSkillMetaRaw — читає та парсить файл `main.json` у каталозі скіла, повертаючи його вміст або `null` у разі помилки.
|
|
17
21
|
|
|
18
22
|
## Публічний API
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
SKILL_ALWAYS — маркер, що вказує на безумовну активацію
|
|
25
|
+
parseSkillAutoSpec — витягує налаштування автоматичного запуску з `meta.json`
|
|
26
|
+
skillRequiresRoot — визначає, чи потрібен запуск скіла з кореня репозиторію
|
|
27
|
+
readSkillMetaRaw — зчитує та обробляє метадані окремого скіла з `meta.json`
|
|
24
28
|
|
|
25
29
|
## Гарантії поведінки
|
|
26
30
|
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
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:
|
|
6
|
+
crc: 47660e16
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 95
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Форматує тривалість у форматі `<ціла>.<десята>s`. Генерує таблицю-резюме часу виконання для оркестратора `fix` або `lint`, яка включає детальні записи та загальний час прогону. Таблиця містить маркер `❌` для позначення невдач. Функція повертає готовий рядок із фінальним `\n`; друк здійснюється на стороні виклику.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
formatDurationMs
|
|
18
|
-
formatTimingSummary генерує багаторядковий
|
|
17
|
+
formatDurationMs форматує тривалість у мілісекундах у рядок у форматі `<ціла>.<десята>s`.
|
|
18
|
+
formatTimingSummary генерує багаторядковий рядок із таблицею-резюме часу виконання, включаючи окремі записи та загальний час.
|
|
19
19
|
|
|
20
20
|
## Публічний API
|
|
21
21
|
|
|
22
|
-
⏱ formatDurationMs: Перетворює мілісекунди
|
|
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,
|
|
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
|
-
|
|
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, '
|
|
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'))
|
package/scripts/lib/run-rule.mjs
CHANGED
|
@@ -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:
|
|
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} не існує — створи згідно
|
|
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
|
|
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(
|
|
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, '
|
|
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)
|
|
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)
|
|
14
|
-
| [lock-cache-dir.mjs](lock-cache-dir.md)
|
|
15
|
-
| [pass.mjs](pass.md)
|
|
16
|
-
| [resolve-cargo-manifest.mjs](resolve-cargo-manifest.md)
|
|
17
|
-
| [resolve-cmd.mjs](resolve-cmd.md)
|
|
18
|
-
| [resolve-js-root.mjs](resolve-js-root.md)
|
|
19
|
-
| [test-helpers.mjs](test-helpers.md)
|
|
20
|
-
| [walk-cache.mjs](walk-cache.md)
|
|
21
|
-
| [walkDir.mjs](walkDir.md)
|
|
22
|
-
| [with-lock.mjs](with-lock.md)
|
|
23
|
-
| [worktree-fingerprint.mjs](worktree-fingerprint.md)
|
|
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:
|
|
6
|
+
crc: 99e5a8a4
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
7
9
|
---
|
|
8
10
|
|
|
9
|
-
|
|
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
|
|
14
|
-
resolveAllJsRoots
|
|
17
|
+
resolveJsRoot повертає абсолютний шлях до першого JS-кореня проєкту. Якщо кореневий `package.json` відсутній, повертає null.
|
|
18
|
+
resolveAllJsRoots повертає масив абсолютних шляхів до всіх JS-коренів проєкту. Ігнорує каталоги `.git` та `node_modules`.
|
|
15
19
|
|
|
16
20
|
## Публічний API
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
resolveJsRoot — знаходить кореневий каталог JavaScript-проєкту.
|
|
23
|
+
resolveAllJsRoots — повертає всі каталоги JavaScript-проєктів у робочому просторі.
|
|
20
24
|
|
|
21
25
|
## Гарантії поведінки
|
|
22
26
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
- Повертає `null`, якщо не вдалося визначити кореневий JS-файл.
|
|
26
|
-
- Не обробляє помилки, а повертає `false` або `null`.
|
|
27
|
-
- Ігнорує директорії `.git` та `node_modules`.
|
|
28
|
-
- Не використовує кешування.
|
|
29
|
-
- Не здійснює мережевих запитів.
|
|
27
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
28
|
+
- Свідомо пропускає шляхи: `.git`, `node_modules`.
|
package/types/bin/n-cursor.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export {}
|
|
2
|
+
export {};
|
package/rules/abie/abie.mdc
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|