@nitra/cursor 12.11.3 → 12.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/bin/n-cursor.js +5 -22
- package/package.json +2 -1
- package/rules/bun/docs/index.md +2 -2
- package/rules/bun/docs/main.md +7 -8
- package/rules/bun/js/docs/index.md +3 -3
- package/rules/bun/main.json +1 -1
- package/rules/bun/main.mdc +3 -1
- package/rules/bun/main.mjs +24 -12
- package/rules/changelog/js/docs/index.md +3 -3
- package/rules/ci4/main.mdc +1 -0
- package/rules/ga/main.mdc +9 -1
- package/rules/js/js/docs/index.md +5 -5
- package/rules/js/main.mdc +4 -0
- package/rules/js-run/js/docs/index.md +3 -3
- package/rules/js-run/main.mdc +3 -0
- package/rules/npm-module/main.mdc +4 -0
- package/rules/python/docs/index.md +2 -2
- package/rules/python/docs/main.md +11 -10
- package/rules/python/main.mjs +49 -1
- package/rules/rego/main.mdc +2 -0
- package/rules/rust/docs/index.md +2 -2
- package/rules/rust/docs/main.md +8 -7
- package/rules/rust/main.json +1 -1
- package/rules/rust/main.mjs +16 -3
- package/rules/security/main.mdc +2 -0
- package/rules/style/js/docs/index.md +3 -3
- package/rules/style/main.mdc +5 -1
- package/rules/test/main.mdc +2 -1
- package/rules/text/main.mdc +9 -0
- package/scripts/docs/index.md +1 -0
- package/scripts/docs/update-blue-oak.md +28 -0
- package/scripts/lib/blue-oak.mjs +45 -0
- package/scripts/lib/discover-checkable-rules.mjs +7 -14
- package/scripts/lib/docs/blue-oak.md +29 -0
- package/scripts/lib/docs/index.md +35 -35
- package/scripts/lib/docs/run-rule.md +8 -6
- package/scripts/lib/fix/discover-t0-patterns.mjs +15 -48
- package/scripts/lib/fix/docs/index.md +12 -12
- package/scripts/lib/fix/docs/llm-worker.md +13 -8
- package/scripts/lib/fix/docs/t0.md +7 -7
- package/scripts/lib/fix/llm-worker.mjs +83 -3
- package/scripts/lib/fix/t0.mjs +1 -2
- package/scripts/lib/inline-template-links.mjs +9 -17
- package/scripts/lib/list-project-rules-mdc.mjs +3 -3
- package/scripts/lib/run-rule.mjs +0 -11
- package/scripts/update-blue-oak.mjs +51 -0
- package/scripts/utils/walkDir.mjs +32 -72
- package/skills/doc-aggregate/SKILL.md +1 -0
- package/skills/publish-telegram/SKILL.md +26 -28
- package/scripts/lib/check-mdc-template-refs.mjs +0 -57
- package/scripts/lib/docs/check-mdc-template-refs.md +0 -22
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
|
-
import { readFile
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
3
|
import { basename, extname, join } from 'node:path'
|
|
4
|
+
import { globby } from 'globby'
|
|
4
5
|
|
|
5
6
|
const MD_LINK_RE = /\[([^\]]{1,200})\]\((\.\/[^)]{1,500})\)/g
|
|
6
7
|
const TEMPLATE_SEGMENT_RE = /\/templates?\//
|
|
@@ -80,27 +81,18 @@ export async function appendDiscoveredMdcFiles(text, ruleDir) {
|
|
|
80
81
|
|
|
81
82
|
const jsDir = join(ruleDir, 'js')
|
|
82
83
|
if (existsSync(jsDir)) {
|
|
83
|
-
const
|
|
84
|
-
for (const
|
|
85
|
-
|
|
86
|
-
sections.push((await readFile(join(jsDir, e.name), 'utf8')).trim())
|
|
87
|
-
}
|
|
84
|
+
const files = await globby('*.mdc', { cwd: jsDir, onlyFiles: true, gitignore: false })
|
|
85
|
+
for (const f of files.toSorted()) {
|
|
86
|
+
sections.push((await readFile(join(jsDir, f), 'utf8')).trim())
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
|
|
91
90
|
const policyDir = join(ruleDir, 'policy')
|
|
92
91
|
if (existsSync(policyDir)) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const concernDir = join(policyDir, concern.name)
|
|
98
|
-
const files = (await readdir(concernDir, { withFileTypes: true }))
|
|
99
|
-
.filter(e => e.isFile() && e.name.endsWith('.mdc'))
|
|
100
|
-
.sort((a, b) => a.name.localeCompare(b.name))
|
|
101
|
-
for (const f of files) {
|
|
102
|
-
sections.push((await readFile(join(concernDir, f.name), 'utf8')).trim())
|
|
103
|
-
}
|
|
92
|
+
// '*/*.mdc' → 'concern/file.mdc'; рядкове сортування дає concern-first, потім file
|
|
93
|
+
const files = await globby('*/*.mdc', { cwd: policyDir, onlyFiles: true, gitignore: false })
|
|
94
|
+
for (const rel of files.toSorted()) {
|
|
95
|
+
sections.push((await readFile(join(policyDir, rel), 'utf8')).trim())
|
|
104
96
|
}
|
|
105
97
|
}
|
|
106
98
|
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Винесено зі `bin/n-cursor.js`, щоб ділити між CLI-dispatch і `run-conformance-check` (конформність-детект).
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync } from 'node:fs'
|
|
6
|
-
import { readdir } from 'node:fs/promises'
|
|
7
6
|
import { join } from 'node:path'
|
|
8
7
|
import { cwd as processCwd } from 'node:process'
|
|
8
|
+
import { globby } from 'globby'
|
|
9
9
|
|
|
10
10
|
/** Каталог правил у проєкті-споживачі (відносно кореня). */
|
|
11
11
|
export const CURSOR_RULES_DIR = '.cursor/rules'
|
|
@@ -17,6 +17,6 @@ export const CURSOR_RULES_DIR = '.cursor/rules'
|
|
|
17
17
|
export async function listProjectRulesMdcFiles(cwd = processCwd()) {
|
|
18
18
|
const dir = join(cwd, CURSOR_RULES_DIR)
|
|
19
19
|
if (!existsSync(dir)) return []
|
|
20
|
-
const names = await
|
|
21
|
-
return names.
|
|
20
|
+
const names = await globby('*.mdc', { cwd: dir, onlyFiles: true, gitignore: false })
|
|
21
|
+
return names.toSorted((a, b) => a.localeCompare(b))
|
|
22
22
|
}
|
package/scripts/lib/run-rule.mjs
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
import { readFile } from 'node:fs/promises'
|
|
16
16
|
import { join, relative } from 'node:path'
|
|
17
17
|
|
|
18
|
-
import { findMissingMdcRefs } from './check-mdc-template-refs.mjs'
|
|
19
18
|
import { createCheckReporter } from './check-reporter.mjs'
|
|
20
19
|
import { resolveTargetFiles } from './resolve-target-files.mjs'
|
|
21
20
|
import { runConftestBatch } from './run-conftest-batch.mjs'
|
|
@@ -180,15 +179,5 @@ export async function runRule(rule, bundledRulesDir, walkCache) {
|
|
|
180
179
|
if (code !== 0) totalCode = 1
|
|
181
180
|
}
|
|
182
181
|
|
|
183
|
-
const ruleDir = join(bundledRulesDir, rule.id)
|
|
184
|
-
const missing = await findMissingMdcRefs(ruleDir)
|
|
185
|
-
if (missing.length > 0) {
|
|
186
|
-
const reporter = createCheckReporter()
|
|
187
|
-
for (const rel of missing) {
|
|
188
|
-
reporter.fail(`main.mdc: відсутнє markdown-посилання на template-файл ${rel}`)
|
|
189
|
-
}
|
|
190
|
-
if (reporter.getExitCode() !== 0) totalCode = 1
|
|
191
|
-
}
|
|
192
|
-
|
|
193
182
|
return totalCode
|
|
194
183
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Оновлює вбудований snapshot Blue Oak Council license list.
|
|
3
|
+
*
|
|
4
|
+
* Fetching: https://blueoakcouncil.org/list.json
|
|
5
|
+
* Виводить: npm/data/blue-oak.json — SPDX-ідентифікатори рівнів Model+Gold+Silver+Bronze.
|
|
6
|
+
*
|
|
7
|
+
* Запуск (вручну, у @nitra/cursor):
|
|
8
|
+
* bun npm/scripts/update-blue-oak.mjs
|
|
9
|
+
*
|
|
10
|
+
* Коли запускати:
|
|
11
|
+
* - При апгрейді @nitra/cursor (n-taze або вручну) — Blue Oak список змінюється рідко,
|
|
12
|
+
* але нові permissive ліцензії зʼявляються раз на кілька місяців;
|
|
13
|
+
* - Якщо проєкт падає на license-check через ліцензію якої нема в списку,
|
|
14
|
+
* а вона точно permissive — спочатку перевір, чи вона в Bronze+ на blueoakcouncil.org.
|
|
15
|
+
*
|
|
16
|
+
* Lead-рівень (найгірший, GPL-compatible) — навмисно виключений.
|
|
17
|
+
*/
|
|
18
|
+
import { writeFileSync } from 'node:fs'
|
|
19
|
+
import { dirname, join } from 'node:path'
|
|
20
|
+
import { fileURLToPath } from 'node:url'
|
|
21
|
+
|
|
22
|
+
const BLUE_OAK_URL = 'https://blueoakcouncil.org/list.json'
|
|
23
|
+
const OUT_PATH = join(dirname(dirname(fileURLToPath(import.meta.url))), 'data', 'blue-oak.json')
|
|
24
|
+
const KEEP_RATINGS = new Set(['Model', 'Gold', 'Silver', 'Bronze'])
|
|
25
|
+
|
|
26
|
+
console.log(`⬇ Fetching ${BLUE_OAK_URL} …`)
|
|
27
|
+
const res = await fetch(BLUE_OAK_URL)
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
console.error(`✗ HTTP ${res.status}`)
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** @type {{ version: string, ratings: Array<{ name: string, licenses: Array<{ id: string, name: string, url: string }> }> }} */
|
|
34
|
+
const data = await res.json()
|
|
35
|
+
|
|
36
|
+
const bronzeAndAbove = []
|
|
37
|
+
for (const rating of data.ratings) {
|
|
38
|
+
if (KEEP_RATINGS.has(rating.name)) {
|
|
39
|
+
bronzeAndAbove.push(...rating.licenses.map(l => l.id))
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const out = {
|
|
44
|
+
version: data.version,
|
|
45
|
+
source: BLUE_OAK_URL,
|
|
46
|
+
bronzeAndAbove
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
writeFileSync(OUT_PATH, JSON.stringify(out, null, 2) + '\n', 'utf8')
|
|
50
|
+
console.log(`✓ ${OUT_PATH}`)
|
|
51
|
+
console.log(` version=${out.version} bronzeAndAbove=${bronzeAndAbove.length} licenses`)
|
|
@@ -1,84 +1,44 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Обходить дерево від заданого кореня; для кожного звичайного файлу викликає переданий callback.
|
|
5
|
-
* Каталоги node_modules, .git, dist, coverage, .turbo, .next не заходяться.
|
|
6
|
-
* Додатково можна передати `ignorePaths` — повні шляхи каталогів (абсолютні posix), які слід
|
|
7
|
-
* пропускати разом з усім вмістом (поле `ignore` у `.n-cursor.json`). Якщо readdir для каталогу
|
|
8
|
-
* не вдається — тихо виходить без throw.
|
|
9
|
-
*/
|
|
10
|
-
import { readdir } from 'node:fs/promises'
|
|
11
|
-
import { isAbsolute, join, resolve, sep } from 'node:path'
|
|
1
|
+
/** @see ./docs/walkDir.md */
|
|
2
|
+
import { join, relative, resolve, sep } from 'node:path'
|
|
3
|
+
import { globby } from 'globby'
|
|
12
4
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* @returns {string} абсолютний posix-шлях
|
|
17
|
-
*/
|
|
18
|
-
function toAbsPosix(p) {
|
|
19
|
-
const abs = isAbsolute(p) ? p : resolve(p)
|
|
20
|
-
let posix = abs.split(sep).join('/')
|
|
21
|
-
while (posix.endsWith('/')) posix = posix.slice(0, -1)
|
|
22
|
-
return posix
|
|
23
|
-
}
|
|
5
|
+
// .git ніколи не потрапляє в .gitignore — пропускаємо завжди.
|
|
6
|
+
// node_modules — safety net: проєкт може не мати .gitignore або запускатись поза git-репо.
|
|
7
|
+
const ALWAYS_IGNORE = ['.git/**', 'node_modules/**']
|
|
24
8
|
|
|
25
9
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* @param {string}
|
|
29
|
-
* @param {string[]}
|
|
30
|
-
* @returns {
|
|
31
|
-
*/
|
|
32
|
-
function isIgnoredDir(dirAbsPosix, ignorePosix) {
|
|
33
|
-
for (const ig of ignorePosix) {
|
|
34
|
-
if (dirAbsPosix === ig) return true
|
|
35
|
-
if (dirAbsPosix.startsWith(`${ig}/`)) return true
|
|
36
|
-
}
|
|
37
|
-
return false
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Рекурсивно обходить каталог, пропускає типові артефакти збірки/залежностей та `ignorePaths`.
|
|
42
|
-
* @param {string} dir абсолютний шлях
|
|
43
|
-
* @param {(filePath: string) => void} onFile виклик для кожного файлу
|
|
44
|
-
* @param {string[]} [ignorePaths] шляхи каталогів (відносні від cwd або абсолютні), що повністю виключаються з обходу
|
|
45
|
-
* @returns {Promise<void>} визначається по завершенню обходу
|
|
10
|
+
* Рекурсивно обходить каталог, поважаючи .gitignore (включно з вкладеними).
|
|
11
|
+
* @param {string} dir абсолютний або відносний шлях до кореня обходу
|
|
12
|
+
* @param {(filePath: string) => void} onFile колбек для кожного файлу (абсолютний шлях)
|
|
13
|
+
* @param {string[]} [ignorePaths] додаткові шляхи для пропуску (абсолютні або відносні від cwd)
|
|
14
|
+
* @returns {Promise<void>}
|
|
46
15
|
*/
|
|
47
16
|
export async function walkDir(dir, onFile, ignorePaths = []) {
|
|
48
|
-
const
|
|
49
|
-
await walkDirInner(dir, onFile, ignorePosix)
|
|
50
|
-
}
|
|
17
|
+
const absDir = resolve(dir)
|
|
51
18
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
let
|
|
19
|
+
const extraIgnore = ignorePaths
|
|
20
|
+
.map(p => {
|
|
21
|
+
const abs = resolve(p.replace(/\/+$/, ''))
|
|
22
|
+
const rel = relative(absDir, abs).split(sep).join('/')
|
|
23
|
+
if (rel.startsWith('..') || rel === '') return null
|
|
24
|
+
return `${rel}/**`
|
|
25
|
+
})
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
|
|
28
|
+
let files
|
|
62
29
|
try {
|
|
63
|
-
|
|
30
|
+
files = await globby('**/*', {
|
|
31
|
+
cwd: absDir,
|
|
32
|
+
gitignore: true,
|
|
33
|
+
dot: true,
|
|
34
|
+
onlyFiles: true,
|
|
35
|
+
ignore: [...ALWAYS_IGNORE, ...extraIgnore]
|
|
36
|
+
})
|
|
64
37
|
} catch {
|
|
65
38
|
return
|
|
66
39
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const skipDir =
|
|
71
|
-
e.name === 'node_modules' ||
|
|
72
|
-
e.name === '.git' ||
|
|
73
|
-
e.name === 'dist' ||
|
|
74
|
-
e.name === 'coverage' ||
|
|
75
|
-
e.name === '.turbo' ||
|
|
76
|
-
e.name === '.next'
|
|
77
|
-
if (skipDir) continue
|
|
78
|
-
if (ignorePosix.length > 0 && isIgnoredDir(toAbsPosix(p), ignorePosix)) continue
|
|
79
|
-
await walkDirInner(p, onFile, ignorePosix)
|
|
80
|
-
} else if (e.isFile()) {
|
|
81
|
-
onFile(p)
|
|
82
|
-
}
|
|
40
|
+
|
|
41
|
+
for (const rel of files) {
|
|
42
|
+
onFile(join(absDir, rel))
|
|
83
43
|
}
|
|
84
44
|
}
|
|
@@ -44,6 +44,7 @@ node -e "const p=JSON.parse(require('fs').readFileSync('package.json','utf8'));
|
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Для кожного воркспейсу `<ws>`:
|
|
47
|
+
|
|
47
48
|
- `relRoot` = `<ws>` (напр. `npm`, `demo`)
|
|
48
49
|
- `docPath` = `<ws>/docs/ARCHITECTURE.md`
|
|
49
50
|
- `members` — кодові файли (`.mjs .ts .vue .py`, крім тестів) у `<ws>/`
|
|
@@ -11,12 +11,15 @@ version: '1.0'
|
|
|
11
11
|
|
|
12
12
|
## Формат Telegram
|
|
13
13
|
|
|
14
|
-
Telegram підтримує
|
|
14
|
+
Telegram підтримує Markdown-форматування безпосередньо в клієнті:
|
|
15
15
|
|
|
16
|
-
-
|
|
17
|
-
- `
|
|
16
|
+
- `**bold**` — жирний (заголовки, мітки секцій)
|
|
17
|
+
- `_italic_` — курсив (акценти)
|
|
18
|
+
- `` `inline code` `` — inline-код, команди, назви файлів/пакетів
|
|
19
|
+
- ` ```block``` ` (потрійні backticks) — блок коду, коли є реальний код
|
|
20
|
+
- `~~strikethrough~~` — закреслення (опціонально)
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
Секції (Проблема:, Рішення: тощо) виділяй `**жирним**`. Технічні терміни (назви файлів, пакетів, команди) — в `` `inline code` ``.
|
|
20
23
|
|
|
21
24
|
## Workflow
|
|
22
25
|
|
|
@@ -28,23 +31,23 @@ Telegram підтримує моноширний шрифт через markdown-
|
|
|
28
31
|
|
|
29
32
|
## Шаблон посту
|
|
30
33
|
|
|
31
|
-
```
|
|
34
|
+
```
|
|
32
35
|
#тег
|
|
33
36
|
|
|
34
|
-
📌
|
|
37
|
+
📌 **<Короткий заголовок>**
|
|
35
38
|
|
|
36
|
-
|
|
39
|
+
**Проблема:**
|
|
37
40
|
<1-3 речення — що було не так / яке завдання>
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
**Рішення:**
|
|
40
43
|
<2-5 речень — що зробили, який підхід>
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
**Деталі:**
|
|
43
46
|
• <ключовий момент 1>
|
|
44
47
|
• <ключовий момент 2>
|
|
45
48
|
• <ключовий момент 3>
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
**Результат:**
|
|
48
51
|
<1-2 речення — що отримали, яка вигода>
|
|
49
52
|
```
|
|
50
53
|
|
|
@@ -64,32 +67,27 @@ Telegram підтримує моноширний шрифт через markdown-
|
|
|
64
67
|
- Тег: рівно 1 хештег першим рядком поста (#devops, #frontend, #bugfix, #refactoring, #CI, #performance тощо)
|
|
65
68
|
- Emoji: лише структурні (📌 для заголовка, • для списку), не перевантажувати
|
|
66
69
|
- Цільова аудиторія: розробники та менеджери — тому пояснюй простою мовою, без надмірного жаргону
|
|
67
|
-
-
|
|
70
|
+
- Назви файлів, пакетів, команди — у `` `inline code` ``
|
|
71
|
+
- Блок коду (потрійні backticks) — лише якщо є реальний код/конфіг, не для всього тексту
|
|
68
72
|
|
|
69
73
|
## Приклад
|
|
70
74
|
|
|
71
|
-
````
|
|
72
75
|
```
|
|
73
76
|
#dev
|
|
74
77
|
|
|
75
|
-
📌
|
|
78
|
+
📌 **Міграція з Prettier на oxfmt**
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
Prettier повільно форматував великі файли і
|
|
79
|
-
конфліктував з ESLint при роботі з Vue SFC.
|
|
80
|
+
**Проблема:**
|
|
81
|
+
Prettier повільно форматував великі файли і конфліктував з ESLint при роботі з Vue SFC.
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
Замінили Prettier на oxfmt — нативний
|
|
83
|
-
форматер від OXC. Оновили VS Code settings,
|
|
84
|
-
видалили prettier-конфіги, додали .oxfmtrc.json.
|
|
83
|
+
**Рішення:**
|
|
84
|
+
Замінили Prettier на `oxfmt` — нативний форматер від OXC. Оновили VS Code settings, видалили prettier-конфіги, додали `.oxfmtrc.json`.
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
• oxfmt працює в 10-50x швидше за Prettier
|
|
88
|
-
• Єдиний конфіг
|
|
89
|
-
• CI перевіряє форматування через lint-js
|
|
86
|
+
**Деталі:**
|
|
87
|
+
• `oxfmt` працює в 10-50x швидше за Prettier
|
|
88
|
+
• Єдиний конфіг `.oxfmtrc.json` у корені
|
|
89
|
+
• CI перевіряє форматування через `lint-js`
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
Форматування працює миттєво при збереженні,
|
|
93
|
-
зникли конфлікти між форматером і лінтером.
|
|
91
|
+
**Результат:**
|
|
92
|
+
Форматування працює миттєво при збереженні, зникли конфлікти між форматером і лінтером.
|
|
94
93
|
```
|
|
95
|
-
````
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns list of template/ files that are NOT referenced in <id>.mdc as
|
|
3
|
-
* markdown link targets. Paths returned are relative to ruleDir.
|
|
4
|
-
*/
|
|
5
|
-
import { existsSync } from 'node:fs'
|
|
6
|
-
import { readdir, readFile, stat } from 'node:fs/promises'
|
|
7
|
-
import { join, relative } from 'node:path'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {string} ruleDir абсолютний шлях до каталогу правила
|
|
11
|
-
* @returns {Promise<string[]>} абсолютні шляхи всіх файлів у template/
|
|
12
|
-
*/
|
|
13
|
-
async function walkTemplateDirs(ruleDir) {
|
|
14
|
-
const out = []
|
|
15
|
-
for (const kind of ['fix', 'policy']) {
|
|
16
|
-
const kindDir = join(ruleDir, kind)
|
|
17
|
-
if (!existsSync(kindDir)) continue
|
|
18
|
-
for (const concern of await readdir(kindDir)) {
|
|
19
|
-
const tpl = join(kindDir, concern, 'template')
|
|
20
|
-
if (!existsSync(tpl)) continue
|
|
21
|
-
const tplStat = await stat(tpl)
|
|
22
|
-
if (!tplStat.isDirectory()) continue
|
|
23
|
-
out.push(...(await collectFiles(tpl)))
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return out.map(p => relative(ruleDir, p))
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @param {string} dir каталог для обходу
|
|
31
|
-
* @returns {Promise<string[]>} абсолютні шляхи знайдених файлів
|
|
32
|
-
*/
|
|
33
|
-
async function collectFiles(dir) {
|
|
34
|
-
const out = []
|
|
35
|
-
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
36
|
-
const full = join(dir, entry.name)
|
|
37
|
-
if (entry.isDirectory()) out.push(...(await collectFiles(full)))
|
|
38
|
-
else out.push(full)
|
|
39
|
-
}
|
|
40
|
-
return out
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* @param {string} ruleDir абсолютний шлях до npm/rules/<id>/
|
|
45
|
-
* @param {string} ruleId basename правила (напр. "security")
|
|
46
|
-
* @returns {Promise<string[]>} відносні шляхи template-файлів без посилань у .mdc
|
|
47
|
-
*/
|
|
48
|
-
export async function findMissingMdcRefs(ruleDir) {
|
|
49
|
-
const mdcPath = join(ruleDir, 'main.mdc')
|
|
50
|
-
if (!existsSync(mdcPath)) return []
|
|
51
|
-
const mdc = await readFile(mdcPath, 'utf8')
|
|
52
|
-
const allFiles = await walkTemplateDirs(ruleDir)
|
|
53
|
-
return allFiles.filter(rel => {
|
|
54
|
-
// Match markdown link to ./<rel> or (<rel>) anywhere in the .mdc
|
|
55
|
-
return !mdc.includes(`./${rel}`) && !mdc.includes(`(${rel})`)
|
|
56
|
-
})
|
|
57
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: check-mdc-template-refs.mjs
|
|
4
|
-
resource: npm/scripts/lib/check-mdc-template-refs.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: c116210e
|
|
7
|
-
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score: 100
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
Визначає список файлів шаблонів, що знаходяться у каталогах `fix` та `policy`, які не є цільовими посиланнями у файлі `<id>.mdc` як markdown link targets. Це дозволяє ідентифікувати ресурси шаблонів, які не були згадані в контексті правила.
|
|
12
|
-
|
|
13
|
-
## Поведінка
|
|
14
|
-
|
|
15
|
-
1. Збирає абсолютні шляхи всіх файлів, що знаходяться у каталогах `template` всередині підкаталогів `fix` та `policy` у вказаному каталозі правила.
|
|
16
|
-
2. Зчитує вміст файлу `main.mdc` у каталозі правила.
|
|
17
|
-
3. Фільтрує зібрані шляхи, залишаючи лише ті, які не з'являються у вмісті `main.mdc` у вигляді посилання Markdown (`./<шлях>` або ``).
|
|
18
|
-
4. Повертає відносні шляхи цих незгаданих шаблонних файлів відносно каталогу правила.
|
|
19
|
-
|
|
20
|
-
## Гарантії поведінки
|
|
21
|
-
|
|
22
|
-
- Read-only: не виконує операцій запису (ФС/БД).
|