@nitra/cursor 12.11.2 → 12.12.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 (59) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bin/n-cursor.js +5 -37
  3. package/package.json +1 -1
  4. package/rules/bun/docs/index.md +2 -2
  5. package/rules/bun/docs/main.md +8 -9
  6. package/rules/bun/js/docs/index.md +3 -2
  7. package/rules/bun/main.json +1 -1
  8. package/rules/bun/main.mdc +3 -1
  9. package/rules/bun/main.mjs +63 -3
  10. package/rules/changelog/js/docs/index.md +3 -2
  11. package/rules/ci4/main.mdc +1 -0
  12. package/rules/ci4/policy/vscode_extensions/docs/index.md +11 -0
  13. package/rules/ga/main.mdc +9 -1
  14. package/rules/ga/policy/vscode_extensions/docs/index.md +11 -0
  15. package/rules/graphql/policy/vscode_extensions/docs/index.md +11 -0
  16. package/rules/js/js/dep-policy.mdc +19 -0
  17. package/rules/js/js/dep-policy.mjs +14 -6
  18. package/rules/js/js/docs/index.md +5 -5
  19. package/rules/js/main.mdc +4 -0
  20. package/rules/js/policy/vscode_extensions/docs/index.md +11 -0
  21. package/rules/js-run/js/docs/index.md +3 -2
  22. package/rules/js-run/main.mdc +3 -0
  23. package/rules/nginx-default-tpl/policy/vscode_extensions/docs/index.md +11 -0
  24. package/rules/npm-module/main.mdc +4 -0
  25. package/rules/python/docs/index.md +2 -2
  26. package/rules/python/docs/main.md +13 -10
  27. package/rules/python/main.mjs +43 -0
  28. package/rules/rego/main.mdc +2 -0
  29. package/rules/rego/policy/vscode_extensions/docs/index.md +11 -0
  30. package/rules/rust/docs/index.md +2 -2
  31. package/rules/rust/docs/main.md +10 -7
  32. package/rules/rust/main.json +1 -1
  33. package/rules/rust/main.mjs +23 -1
  34. package/rules/rust/policy/vscode_extensions/docs/index.md +11 -0
  35. package/rules/security/main.mdc +2 -0
  36. package/rules/style/js/docs/index.md +3 -2
  37. package/rules/style/main.mdc +5 -1
  38. package/rules/style/policy/vscode_extensions/docs/index.md +11 -0
  39. package/rules/tauri/policy/vscode_extensions/docs/index.md +11 -0
  40. package/rules/test/main.mdc +2 -1
  41. package/rules/text/main.mdc +9 -0
  42. package/rules/text/policy/vscode_extensions/docs/index.md +11 -0
  43. package/scripts/docs/index.md +14 -13
  44. package/scripts/docs/update-blue-oak.md +28 -0
  45. package/scripts/lib/blue-oak.mjs +45 -0
  46. package/scripts/lib/docs/blue-oak.md +29 -0
  47. package/scripts/lib/docs/index.md +35 -35
  48. package/scripts/lib/docs/run-rule.md +8 -6
  49. package/scripts/lib/fix/docs/index.md +12 -10
  50. package/scripts/lib/fix/docs/t0.md +7 -7
  51. package/scripts/lib/fix/t0.mjs +1 -2
  52. package/scripts/lib/run-rule.mjs +0 -11
  53. package/scripts/update-blue-oak.mjs +51 -0
  54. package/skills/doc-aggregate/SKILL.md +8 -18
  55. package/skills/doc-aggregate/js/docs/index.md +0 -1
  56. package/scripts/lib/check-mdc-template-refs.mjs +0 -57
  57. package/scripts/lib/docs/check-mdc-template-refs.md +0 -22
  58. package/skills/doc-aggregate/js/docgen-scan.mjs +0 -195
  59. package/skills/doc-aggregate/js/docs/docgen-scan.md +0 -76
@@ -37,29 +37,19 @@ npx @nitra/cursor lint-doc-files --git
37
37
 
38
38
  ## Крок 1: Tier 2 — module-summary
39
39
 
40
- Зібрати список модулів:
40
+ Зібрати список воркспейсів з кореневого `package.json`:
41
41
 
42
42
  ```bash
43
- npx @nitra/cursor doc-aggregate modules
43
+ node -e "const p=JSON.parse(require('fs').readFileSync('package.json','utf8')); console.log(JSON.stringify(p.workspaces))"
44
44
  ```
45
45
 
46
- Команда друкує JSON-масив (усі шляхи абсолютні):
47
-
48
- ```json
49
- [
50
- {
51
- "moduleRoot": "/abs/npm/rules/adr",
52
- "relRoot": "npm/rules/adr",
53
- "slug": "npm-rules-adr",
54
- "docPath": "/abs/npm/rules/adr/docs/ARCHITECTURE.md",
55
- "members": ["npm/rules/adr/index.mjs"],
56
- "exists": false
57
- }
58
- ]
59
- ```
46
+ Для кожного воркспейсу `<ws>`:
47
+ - `relRoot` = `<ws>` (напр. `npm`, `demo`)
48
+ - `docPath` = `<ws>/docs/ARCHITECTURE.md`
49
+ - `members` — кодові файли (`.mjs .ts .vue .py`, крім тестів) у `<ws>/`
60
50
 
61
- module-summary **завжди регенерується** (це агрегат поле `exists` ігноруй). Розбий модулі
62
- на батчі по 5 і диспатч субагентів. Промпт кожного (підстав `relRoot`, `docPath`, `members`):
51
+ module-summary **завжди регенерується**. Розбий воркспейси на батчі по 5 і диспатч субагентів.
52
+ Промпт кожного (підстав `relRoot`, `docPath`, `members`):
63
53
 
64
54
  ```
65
55
  Напиши module-summary для одного логічного модуля.
@@ -9,4 +9,3 @@ resource: npm/skills/doc-aggregate/js/
9
9
  | Файл | Тип |
10
10
  | ------------------------------------- | --------- |
11
11
  | [docgen-ignore.mjs](docgen-ignore.md) | JS Module |
12
- | [docgen-scan.mjs](docgen-scan.md) | JS Module |
@@ -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: не виконує операцій запису (ФС/БД).
@@ -1,195 +0,0 @@
1
- /** @see ./docs/docgen-scan.md */
2
- import { join, relative, dirname, extname, sep, isAbsolute, resolve } from 'node:path'
3
- import { existsSync, readdirSync, statSync } from 'node:fs'
4
-
5
- import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
6
- import { isDocgenIgnored } from './docgen-ignore.mjs'
7
-
8
- /** Кодові розширення, для яких генеруємо документацію. */
9
- const SOURCE_EXTENSIONS = new Set(['.js', '.mjs', '.ts', '.vue', '.py'])
10
-
11
- /** `*.test.*`, `*.spec.*` — тести, документувати не треба. */
12
- const TEST_FILE_RE = /\.(?:test|spec)\.[^.]+$/u
13
-
14
- /**
15
- * Чи корінь має system-wide docs layout (зарезервований під repo docs/adr тощо).
16
- * @param {string} root абсолютний корінь обходу
17
- * @returns {boolean} true — корінь system-wide docs
18
- */
19
- function isSystemWideDocsRoot(root) {
20
- return existsSync(join(root, 'docs', 'adr')) || existsSync(join(root, 'docs', 'explanation'))
21
- }
22
-
23
- /**
24
- * Чи є файл кодовим джерелом для документування.
25
- * @param {string} fileName базове ім'я файлу
26
- * @returns {boolean} true — документуємо
27
- */
28
- export function isSourceFile(fileName) {
29
- if (fileName.endsWith('.d.ts')) return false
30
- if (TEST_FILE_RE.test(fileName)) return false
31
- return SOURCE_EXTENSIONS.has(extname(fileName))
32
- }
33
-
34
- /**
35
- * Рекурсивно збирає кодові файли проєкту (posix-шляхи від кореня).
36
- * @param {string} root абсолютний корінь обходу
37
- * @returns {string[]} sourcePath-и
38
- */
39
- export function scanSourceFiles(root) {
40
- const results = []
41
-
42
- /** @param {string} dir поточний каталог обходу */
43
- function walk(dir) {
44
- let entries
45
- try {
46
- entries = readdirSync(dir, { withFileTypes: true })
47
- } catch {
48
- return
49
- }
50
- for (const entry of entries) {
51
- const fullPath = join(dir, entry.name)
52
- const relPath = relative(root, fullPath)
53
- if (entry.isDirectory()) {
54
- if (isDocgenIgnored(relPath, 'dir')) continue
55
- walk(fullPath)
56
- } else if (entry.isFile() && isSourceFile(entry.name)) {
57
- if (isSystemWideDocsRoot(root) && dirname(relPath) === '.') continue
58
- const sourcePath = relPath.split(sep).join('/')
59
- if (isDocgenIgnored(sourcePath)) continue
60
- results.push(sourcePath)
61
- }
62
- }
63
- }
64
-
65
- walk(root)
66
- return results
67
- }
68
-
69
- /**
70
- * Стабільний slug модуля з його відносного шляху (для лейблів/логів).
71
- * @param {string} root абсолютний корінь обходу
72
- * @param {string} moduleRoot абсолютний корінь модуля
73
- * @returns {string} slug: `npm/rules/adr` → `npm-rules-adr`, корінь → `root`
74
- */
75
- export function slugForModule(root, moduleRoot) {
76
- const rel = relative(root, moduleRoot)
77
- if (rel === '') return 'root'
78
- return rel
79
- .split(sep)
80
- .join('-')
81
- .replaceAll(/[^\w-]+/gu, '-')
82
- }
83
-
84
- /**
85
- * Знаходить корені модулів — теки з `package.json` (корінь завжди модуль).
86
- * @param {string} root абсолютний корінь обходу
87
- * @returns {string[]} абсолютні шляхи коренів модулів
88
- */
89
- export function findModuleRoots(root) {
90
- const roots = [root]
91
-
92
- /** @param {string} dir поточний каталог обходу */
93
- function walk(dir) {
94
- let entries
95
- try {
96
- entries = readdirSync(dir, { withFileTypes: true })
97
- } catch {
98
- return
99
- }
100
- for (const entry of entries) {
101
- const fullPath = join(dir, entry.name)
102
- const relPath = relative(root, fullPath)
103
- if (entry.isDirectory()) {
104
- if (isDocgenIgnored(relPath, 'dir')) continue
105
- walk(fullPath)
106
- } else if (entry.isFile() && entry.name === 'package.json' && dir !== root) {
107
- roots.push(dir)
108
- }
109
- }
110
- }
111
-
112
- walk(root)
113
- return roots
114
- }
115
-
116
- /**
117
- * Найближчий модуль-предок для файлу (найдовший збіг шляху).
118
- * @param {string} filePath абсолютний шлях до файлу
119
- * @param {string[]} moduleRoots абсолютні корені модулів
120
- * @returns {string|null} абсолютний корінь модуля або null
121
- */
122
- export function nearestModuleRoot(filePath, moduleRoots) {
123
- let best = null
124
- for (const moduleRoot of moduleRoots) {
125
- const rel = relative(moduleRoot, filePath)
126
- if (rel.startsWith('..') || isAbsolute(rel)) continue
127
- if (best === null || moduleRoot.length > best.length) best = moduleRoot
128
- }
129
- return best
130
- }
131
-
132
- /**
133
- * Лістить логічні модулі проєкту з членами-файлами і docPath module-summary.
134
- * Модулі без кодових файлів пропускаються.
135
- * @param {string} root абсолютний корінь обходу
136
- * @returns {Array<{moduleRoot:string, relRoot:string, slug:string, docPath:string, members:string[], exists:boolean}>} модулі (members — sourcePath-и від root)
137
- */
138
- export function scanForModules(root) {
139
- const files = scanSourceFiles(root)
140
- const moduleRoots = findModuleRoots(root)
141
- const byRoot = new Map()
142
- for (const sourcePath of files) {
143
- const moduleRoot = nearestModuleRoot(join(root, sourcePath), moduleRoots)
144
- if (moduleRoot === null) continue
145
- if (!byRoot.has(moduleRoot)) byRoot.set(moduleRoot, [])
146
- byRoot.get(moduleRoot).push(sourcePath)
147
- }
148
-
149
- const results = []
150
- for (const moduleRoot of moduleRoots) {
151
- const members = byRoot.get(moduleRoot)
152
- if (!members || members.length === 0) continue
153
- const docPath = join(moduleRoot, 'docs', 'ARCHITECTURE.md')
154
- results.push({
155
- moduleRoot,
156
- relRoot: relative(root, moduleRoot) || '.',
157
- slug: slugForModule(root, moduleRoot),
158
- docPath,
159
- members: members.toSorted(),
160
- exists: existsSync(docPath)
161
- })
162
- }
163
- return results
164
- }
165
-
166
- /**
167
- * Парсить `--root <dir>`; default — cwd.
168
- * @param {string[]} argv аргументи після підкоманди
169
- * @returns {string} абсолютний корінь
170
- */
171
- export function resolveRoot(argv) {
172
- const i = argv.indexOf('--root')
173
- return i !== -1 && argv[i + 1] ? resolve(argv[i + 1]) : process.cwd()
174
- }
175
-
176
- /**
177
- * `doc-aggregate modules` — сканує модулі і друкує JSON-масив у stdout.
178
- * @param {string[]} argv аргументи після назви субкоманди
179
- * @returns {number} exit-код: 0 — успіх, 1 — корінь не існує
180
- */
181
- export function runDocAggregateModulesCli(argv) {
182
- const root = resolveRoot(argv)
183
- if (!existsSync(root) || !statSync(root).isDirectory()) {
184
- console.error(`doc-aggregate modules: корінь не існує або не є директорією: ${root}`)
185
- return 1
186
- }
187
- console.log(JSON.stringify(scanForModules(root), null, 2))
188
- return 0
189
- }
190
-
191
- if (isRunAsCli(import.meta.url)) {
192
- // Прямий запуск: `node skills/doc-aggregate/js/docgen-scan.mjs modules --root <dir>`
193
- const [sub, ...rest] = process.argv.slice(2)
194
- process.exitCode = runDocAggregateModulesCli(sub === 'modules' ? rest : process.argv.slice(2))
195
- }
@@ -1,76 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: docgen-scan.mjs
4
- resource: npm/skills/doc-aggregate/js/docgen-scan.mjs
5
- docgen:
6
- crc: 193dd362
7
- score: 100
8
- ---
9
-
10
- isSourceFile
11
- Перевіряє, чи є файл коду, який слугує джерелом для документування.
12
-
13
- scanSourceFiles
14
- Рекурсивно збирає кодові файли проєкту за позикс-шляхами від кореня.
15
-
16
- slugForModule
17
- Генерує стабільний slug модуля на основі його відносного шляху.
18
-
19
- findModuleRoots
20
- Знаходить абсолютні шляхи коренів модулів, використовуючи дані з package.json.
21
-
22
- nearestModuleRoot
23
- Визначає найближчий модуль-предок для заданого файлу серед усіх доступних коренів модулів.
24
-
25
- scanForModules
26
- Лістить логічні модулі, збираючи члени-файли та інформацію про наявність документації.
27
-
28
- resolveRoot
29
- Парсить аргументи для визначення абсолютної кореневої директорії.
30
-
31
- runDocAggregateModulesCli
32
- Сканує модулі та виводить масив JSON у stdout.
33
-
34
- ## Поведінка
35
-
36
- isSourceFile
37
- Перевіряє, чи є файл кодовим джерелом для документування
38
-
39
- scanSourceFiles
40
- Рекурсивно збирає кодові файли проєкту по позикс-шляхах від кореня
41
-
42
- slugForModule
43
- Генерує стабільний slug модуля з його відносним шляхом
44
-
45
- findModuleRoots
46
- Знаходить абсолютні шляхи коренів модулів з файлами package.json
47
-
48
- nearestModuleRoot
49
- Знаходить найближчий модуль-предок для файлу серед наявних коренів модулів
50
-
51
- scanForModules
52
- Лістить логічні модулі, збираючи члени-файли та інформацію про наявність документації
53
-
54
- resolveRoot
55
- Парсить аргументи для визначення абсолютного кореня
56
-
57
- runDocAggregateModulesCli
58
- Сканує модулі та виводить JSON-масив у stdout
59
-
60
- ## Публічний API
61
-
62
- - isSourceFile — визначає, чи є файл коду для документування.
63
- - scanSourceFiles — рекурсивно збирає файли коду проєкту (від кореня за POSIX-шляхом).
64
- - slugForModule — генерує стабільний slug модуля з його відносним шляхом (для лейблів/логів).
65
- - findModuleRoots — знаходить кореневі директорії модулів у `package.json` (корінь — це модуль).
66
- - nearestModuleRoot — визначає найближчий модуль-предок для файлу (найдовший збіг шляху).
67
- - scanForModules — лістить логічні модулі проєкту з членами-файлами та `docPath module-summary`. Пропускає модулі без кодових файлів.
68
- - resolveRoot — парсить аргумент `--root <dir>`; за замовчуванням використовує поточну директорію.
69
- - runDocAggregateModulesCli — виконує команду `doc-aggregate modules`, скануючи модулі та виводячи JSON-масив у stdout.
70
-
71
- ## Гарантії поведінки
72
-
73
- - Read-only: файл не виконує операцій запису у файлову систему.
74
- - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
75
- - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
76
- - Не звертається до мережі.