@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bin/n-cursor.js +5 -22
  3. package/package.json +2 -1
  4. package/rules/bun/docs/index.md +2 -2
  5. package/rules/bun/docs/main.md +7 -8
  6. package/rules/bun/js/docs/index.md +3 -3
  7. package/rules/bun/main.json +1 -1
  8. package/rules/bun/main.mdc +3 -1
  9. package/rules/bun/main.mjs +24 -12
  10. package/rules/changelog/js/docs/index.md +3 -3
  11. package/rules/ci4/main.mdc +1 -0
  12. package/rules/ga/main.mdc +9 -1
  13. package/rules/js/js/docs/index.md +5 -5
  14. package/rules/js/main.mdc +4 -0
  15. package/rules/js-run/js/docs/index.md +3 -3
  16. package/rules/js-run/main.mdc +3 -0
  17. package/rules/npm-module/main.mdc +4 -0
  18. package/rules/python/docs/index.md +2 -2
  19. package/rules/python/docs/main.md +11 -10
  20. package/rules/python/main.mjs +49 -1
  21. package/rules/rego/main.mdc +2 -0
  22. package/rules/rust/docs/index.md +2 -2
  23. package/rules/rust/docs/main.md +8 -7
  24. package/rules/rust/main.json +1 -1
  25. package/rules/rust/main.mjs +16 -3
  26. package/rules/security/main.mdc +2 -0
  27. package/rules/style/js/docs/index.md +3 -3
  28. package/rules/style/main.mdc +5 -1
  29. package/rules/test/main.mdc +2 -1
  30. package/rules/text/main.mdc +9 -0
  31. package/scripts/docs/index.md +1 -0
  32. package/scripts/docs/update-blue-oak.md +28 -0
  33. package/scripts/lib/blue-oak.mjs +45 -0
  34. package/scripts/lib/discover-checkable-rules.mjs +7 -14
  35. package/scripts/lib/docs/blue-oak.md +29 -0
  36. package/scripts/lib/docs/index.md +35 -35
  37. package/scripts/lib/docs/run-rule.md +8 -6
  38. package/scripts/lib/fix/discover-t0-patterns.mjs +15 -48
  39. package/scripts/lib/fix/docs/index.md +12 -12
  40. package/scripts/lib/fix/docs/llm-worker.md +13 -8
  41. package/scripts/lib/fix/docs/t0.md +7 -7
  42. package/scripts/lib/fix/llm-worker.mjs +83 -3
  43. package/scripts/lib/fix/t0.mjs +1 -2
  44. package/scripts/lib/inline-template-links.mjs +9 -17
  45. package/scripts/lib/list-project-rules-mdc.mjs +3 -3
  46. package/scripts/lib/run-rule.mjs +0 -11
  47. package/scripts/update-blue-oak.mjs +51 -0
  48. package/scripts/utils/walkDir.mjs +32 -72
  49. package/skills/doc-aggregate/SKILL.md +1 -0
  50. package/skills/publish-telegram/SKILL.md +26 -28
  51. package/scripts/lib/check-mdc-template-refs.mjs +0 -57
  52. 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, readdir } from 'node:fs/promises'
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 entries = await readdir(jsDir, { withFileTypes: true })
84
- for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
85
- if (e.isFile() && e.name.endsWith('.mdc')) {
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
- const concerns = (await readdir(policyDir, { withFileTypes: true }))
94
- .filter(e => e.isDirectory())
95
- .sort((a, b) => a.name.localeCompare(b.name))
96
- for (const concern of concerns) {
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 readdir(dir)
21
- return names.filter(n => n.endsWith('.mdc')).toSorted((a, b) => a.localeCompare(b))
20
+ const names = await globby('*.mdc', { cwd: dir, onlyFiles: true, gitignore: false })
21
+ return names.toSorted((a, b) => a.localeCompare(b))
22
22
  }
@@ -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
- * Рекурсивний обхід каталогів для скриптів перевірки (Dockerfile, k8s YAML тощо).
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
- * Перетворює довільний шлях у абсолютний posix-формат без trailing-slash.
15
- * @param {string} p шлях
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
- * Чи каталог `dirAbsPosix` входить у список ignore (точний збіг або префікс з '/').
27
- * Часткові збіги басенейму не враховуються (postgres-master-test postgres-master).
28
- * @param {string} dirAbsPosix абсолютний posix-шлях каталогу
29
- * @param {string[]} ignorePosix вже нормалізовані ignore-шляхи
30
- * @returns {boolean} `true`, якщо шлях слід пропустити (точний збіг або префікс з `/`)
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 ignorePosix = ignorePaths.map(p => toAbsPosix(p))
49
- await walkDirInner(dir, onFile, ignorePosix)
50
- }
17
+ const absDir = resolve(dir)
51
18
 
52
- /**
53
- * Внутрішній рекурсор. ignorePosix вже нормалізовано — не нормалізуємо повторно на кожному рівні.
54
- * @param {string} dir абсолютний шлях каталогу для обходу
55
- * @param {(filePath: string) => void} onFile колбек, що викликається для кожного звичайного файлу
56
- * @param {string[]} ignorePosix вже нормалізовані абсолютні posix-шляхи ігнорованих каталогів
57
- * @returns {Promise<void>} визначається по завершенню рекурсії
58
- */
59
- async function walkDirInner(dir, onFile, ignorePosix) {
60
- if (ignorePosix.length > 0 && isIgnoredDir(toAbsPosix(dir), ignorePosix)) return
61
- let entries
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
- entries = await readdir(dir, { withFileTypes: true })
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
- for (const e of entries) {
68
- const p = join(dir, e.name)
69
- if (e.isDirectory()) {
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 підтримує моноширний шрифт через markdown-розмітку:
14
+ Telegram підтримує Markdown-форматування безпосередньо в клієнті:
15
15
 
16
- - `` `inline code` `` для inline
17
- - ` ```pre``` ` (потрійні backticks) — для блоку коду / моноширного тексту
16
+ - `**bold**` жирний (заголовки, мітки секцій)
17
+ - `_italic_` курсив (акценти)
18
+ - `` `inline code` `` — inline-код, команди, назви файлів/пакетів
19
+ - ` ```block``` ` (потрійні backticks) — блок коду, коли є реальний код
20
+ - `~~strikethrough~~` — закреслення (опціонально)
18
21
 
19
- Весь пост оформлюй як **один блок** потрійних backticks, щоб текст був моноширним.
22
+ Секції (Проблема:, Рішення: тощо) виділяй `**жирним**`. Технічні терміни (назви файлів, пакетів, команди) — в `` `inline code` ``.
20
23
 
21
24
  ## Workflow
22
25
 
@@ -28,23 +31,23 @@ Telegram підтримує моноширний шрифт через markdown-
28
31
 
29
32
  ## Шаблон посту
30
33
 
31
- ```markdown
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
- - Весь текст повинен бути всередині потрійних backticks для моноширного відображення в Telegram
70
+ - Назви файлів, пакетів, команди у `` `inline code` ``
71
+ - Блок коду (потрійні backticks) — лише якщо є реальний код/конфіг, не для всього тексту
68
72
 
69
73
  ## Приклад
70
74
 
71
- ````
72
75
  ```
73
76
  #dev
74
77
 
75
- 📌 Міграція з Prettier на oxfmt
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
- • Єдиний конфіг .oxfmtrc.json у корені
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: не виконує операцій запису (ФС/БД).