@nitra/cursor 3.21.1 → 3.23.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/.pi-template/extensions/n-cursor-adr/docs/index.md +181 -0
- package/CHANGELOG.md +37 -3
- package/bin/docs/n-cursor.md +636 -0
- package/bin/docs/rename-yaml-extensions.md +207 -0
- package/bin/n-cursor.js +30 -3
- package/package.json +1 -1
- package/rules/abie/docs/fix.md +18 -0
- package/rules/abie/js/docs/applies.md +26 -0
- package/rules/abie/js/docs/env_dns.md +32 -0
- package/rules/abie/js/docs/firebase_hosting.md +23 -0
- package/rules/abie/js/docs/hc_pairing.md +35 -0
- package/rules/abie/js/docs/ua_http_route.md +28 -0
- package/rules/abie/js/docs/ua_node_selector.md +28 -0
- package/rules/abie/lib/docs/enabled.md +29 -0
- package/rules/abie/lib/docs/env-dns.md +35 -0
- package/rules/abie/lib/docs/hc-yaml.md +33 -0
- package/rules/abie/lib/docs/http-route.md +44 -0
- package/rules/abie/lib/docs/k8s-tree.md +40 -0
- package/rules/abie/lib/docs/kustomization-patches.md +47 -0
- package/rules/abie/lib/docs/overlay-paths.md +38 -0
- package/rules/abie/lib/docs/yaml.md +29 -0
- package/rules/adr/docs/fix.md +148 -0
- package/rules/adr/js/docs/hooks.md +259 -0
- package/rules/bun/docs/fix.md +156 -0
- package/rules/bun/js/docs/layout.md +393 -0
- package/rules/capacitor/docs/fix.md +121 -0
- package/rules/capacitor/js/docs/platforms.md +295 -0
- package/rules/changelog/changelog.mdc +2 -2
- package/rules/changelog/docs/fix.md +174 -0
- package/rules/changelog/js/consistency.mjs +114 -13
- package/rules/changelog/js/docs/consistency.md +387 -0
- package/rules/changelog/lib/docs/package-manifest.md +210 -0
- package/rules/ci4/docs/fix.md +179 -0
- package/rules/ci4/js/docs/marksman_config.md +128 -0
- package/rules/docker/docker.mdc +8 -3
- package/rules/docker/docs/fix.md +171 -0
- package/rules/docker/js/docs/lint.md +258 -0
- package/rules/docker/lib/docs/docker-hadolint.md +184 -0
- package/rules/docker/lib/docs/docker-mirror.md +247 -0
- package/rules/docker/lib/docs/docker-native-addon.md +170 -0
- package/rules/docker/lib/docs/docker-nginx-user.md +219 -0
- package/rules/docker/lint/docs/lint.md +193 -0
- package/rules/efes/docs/fix.md +203 -0
- package/rules/feedback/docs/fix.md +140 -0
- package/rules/flow/docs/fix.md +152 -0
- package/rules/ga/docs/fix.md +158 -0
- package/rules/ga/js/docs/lint.md +100 -0
- package/rules/ga/js/docs/workflows.md +217 -0
- package/rules/ga/lint/docs/lint.md +209 -0
- package/rules/ga/policy/clean_merged_branch/clean_merged_branch.rego +11 -2
- package/rules/ga/policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +3 -4
- package/rules/graphql/docs/fix.md +126 -0
- package/rules/graphql/js/docs/tooling.md +264 -0
- package/rules/graphql/lib/docs/graphql-gql-scan.md +219 -0
- package/rules/hasura/docs/fix.md +120 -0
- package/rules/hasura/hasura.mdc +14 -0
- package/rules/hasura/js/docs/internal_urls.md +326 -0
- package/rules/image-avif/docs/fix.md +132 -0
- package/rules/image-avif/js/docs/avif_generation.md +241 -0
- package/rules/image-compress/docs/fix.md +150 -0
- package/rules/image-compress/js/docs/package_setup.md +191 -0
- package/rules/js-bun-db/docs/fix.md +148 -0
- package/rules/js-bun-db/js/docs/safety.md +231 -0
- package/rules/js-bun-db/js-bun-db.mdc +42 -13
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +347 -0
- package/rules/js-bun-redis/docs/fix.md +123 -0
- package/rules/js-bun-redis/js/docs/imports.md +176 -0
- package/rules/js-bun-redis/lib/docs/redis-imports.md +223 -0
- package/rules/js-lint/docs/fix.md +117 -0
- package/rules/js-lint/js/docs/lint.md +250 -0
- package/rules/js-lint/js/docs/tooling.md +348 -0
- package/rules/js-lint/js/docs/utils_imports.md +207 -0
- package/rules/js-lint/js/lint-findings.mjs +110 -0
- package/rules/js-lint/js/lint.mjs +86 -15
- package/rules/js-lint-ci/docs/fix.md +154 -0
- package/rules/js-lint-ci/js/docs/lint.md +144 -0
- package/rules/js-mssql/docs/fix.md +128 -0
- package/rules/js-mssql/js/docs/deps.md +263 -0
- package/rules/js-mssql/lib/docs/mssql-pool-scan.md +367 -0
- package/rules/js-run/docs/fix.md +144 -0
- package/rules/js-run/js/docs/runtime.md +388 -0
- package/rules/js-run/lib/docs/bunyan-imports.md +117 -0
- package/rules/js-run/lib/docs/check-env-scan.md +433 -0
- package/rules/js-run/lib/docs/conn-file-rules.md +300 -0
- package/rules/js-run/lib/docs/conn-imports-scan.md +204 -0
- package/rules/js-run/lib/docs/promise-settimeout-scan.md +326 -0
- package/rules/k8s/docs/fix.md +129 -0
- package/rules/k8s/js/docs/manifests.md +344 -0
- package/rules/k8s/js/manifests.mjs +6 -2
- package/rules/k8s/k8s.mdc +4 -2
- package/rules/k8s/lint/docs/lint.md +411 -0
- package/rules/k8s/policy/network_policy/template/deployment.snippet.yaml +2 -0
- package/rules/k8s/policy/network_policy/template/stateful-set.snippet.yaml +2 -0
- package/rules/nginx-default-tpl/docs/fix.md +124 -0
- package/rules/nginx-default-tpl/js/docs/template.md +378 -0
- package/rules/npm-module/docs/fix.md +98 -0
- package/rules/npm-module/js/docs/package_structure.md +274 -0
- package/rules/npm-module/js/docs/rule_meta.md +137 -0
- package/rules/npm-module/js/docs/skill_meta.md +190 -0
- package/rules/php/docs/fix.md +107 -0
- package/rules/php/js/docs/tooling.md +152 -0
- package/rules/php/lint/docs/lint.md +215 -0
- package/rules/python/docs/fix.md +163 -0
- package/rules/python/js/docs/applies.md +108 -0
- package/rules/python/js/docs/tooling.md +153 -0
- package/rules/python/lint/docs/lint.md +322 -0
- package/rules/rego/docs/fix.md +121 -0
- package/rules/rego/js/docs/applies.md +174 -0
- package/rules/rego/js/docs/lint.md +118 -0
- package/rules/rego/lint/docs/lint.md +204 -0
- package/rules/release/docs/change.md +185 -0
- package/rules/release/docs/fix.md +119 -0
- package/rules/release/docs/release.md +222 -0
- package/rules/release/lib/docs/aggregate.md +246 -0
- package/rules/release/lib/docs/change-file.md +200 -0
- package/rules/release/lib/docs/fallback.md +203 -0
- package/rules/rust/docs/fix.md +129 -0
- package/rules/rust/js/docs/applies.md +140 -0
- package/rules/rust/lib/docs/has-cargo-toml.md +130 -0
- package/rules/security/docs/fix.md +86 -0
- package/rules/security/js/docs/lint.md +171 -0
- package/rules/security/js/docs/sample_secret.md +190 -0
- package/rules/security/js/docs/trufflehog.md +137 -0
- package/rules/security/js/lint.mjs +9 -1
- package/rules/style-lint/docs/fix.md +155 -0
- package/rules/style-lint/js/docs/lint.md +184 -0
- package/rules/style-lint/js/docs/tooling.md +194 -0
- package/rules/tauri/docs/fix.md +158 -0
- package/rules/tauri/js/docs/cargo_mutants_config.md +168 -0
- package/rules/tauri/js/docs/tooling.md +228 -0
- package/rules/test/coverage/coverage.mjs +15 -3
- package/rules/test/docs/fix.md +132 -0
- package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +138 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +134 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +160 -0
- package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +195 -0
- package/rules/test/js/docs/cargo_mutants_config.md +173 -0
- package/rules/test/js/docs/location.md +136 -0
- package/rules/test/js/docs/no-process-chdir.md +160 -0
- package/rules/test/js/docs/no-relative-fs-path.md +271 -0
- package/rules/test/js/docs/stryker_config.md +152 -0
- package/rules/test/js/docs/vitest-config-pool-forks.md +174 -0
- package/rules/text/docs/fix.md +118 -0
- package/rules/text/js/docs/forbidden-prettier.md +143 -0
- package/rules/text/js/docs/formatting.md +256 -0
- package/rules/text/js/docs/lint.md +122 -0
- package/rules/text/lint/docs/lint.md +220 -0
- package/rules/text/lint/docs/run-dotenv-linter.md +157 -0
- package/rules/text/lint/docs/run-shellcheck.md +212 -0
- package/rules/text/lint/docs/run-v8r.md +197 -0
- package/rules/vue/docs/fix.md +127 -0
- package/rules/vue/js/docs/packages.md +335 -0
- package/rules/vue/lib/docs/vue-forbidden-imports.md +261 -0
- package/rules/worktree/docs/fix.md +161 -0
- package/schemas/rule-meta.json +5 -1
- package/scripts/auto-rules.mjs +7 -4
- package/scripts/coverage-classify/docs/apply.md +202 -0
- package/scripts/coverage-classify/docs/cache.md +203 -0
- package/scripts/coverage-classify/docs/index.md +218 -0
- package/scripts/coverage-classify/docs/prompt.md +132 -0
- package/scripts/coverage-classify/docs/verdict-schema.md +169 -0
- package/scripts/coverage-fix-extract.mjs +122 -0
- package/scripts/coverage-fix.mjs +1 -1
- package/scripts/dispatcher/docs/graph.md +346 -0
- package/scripts/dispatcher/docs/index.md +236 -0
- package/scripts/dispatcher/docs/trace.md +296 -0
- package/scripts/dispatcher/index.mjs +1 -1
- package/scripts/dispatcher/lib/active.mjs +4 -8
- package/scripts/dispatcher/lib/commands.mjs +7 -11
- package/scripts/dispatcher/lib/docs/active.md +348 -0
- package/scripts/dispatcher/lib/docs/artifact.md +232 -0
- package/scripts/dispatcher/lib/docs/budget.md +167 -0
- package/scripts/dispatcher/lib/docs/capability.md +196 -0
- package/scripts/dispatcher/lib/docs/commands.md +210 -0
- package/scripts/dispatcher/lib/docs/events.md +182 -0
- package/scripts/dispatcher/lib/docs/executor.md +190 -0
- package/scripts/dispatcher/lib/docs/flow-lock.md +161 -0
- package/scripts/dispatcher/lib/docs/flow-resolve.md +267 -0
- package/scripts/dispatcher/lib/docs/gate.md +231 -0
- package/scripts/dispatcher/lib/docs/level.md +335 -0
- package/scripts/dispatcher/lib/docs/plan-panel.md +181 -0
- package/scripts/dispatcher/lib/docs/plan.md +200 -0
- package/scripts/dispatcher/lib/docs/planner.md +269 -0
- package/scripts/dispatcher/lib/docs/review.md +255 -0
- package/scripts/dispatcher/lib/docs/reviewer.md +240 -0
- package/scripts/dispatcher/lib/docs/snapshot.md +247 -0
- package/scripts/dispatcher/lib/docs/spec.md +203 -0
- package/scripts/dispatcher/lib/docs/state-store.md +303 -0
- package/scripts/dispatcher/lib/docs/subagent-runner.md +173 -0
- package/scripts/dispatcher/lib/executor.mjs +6 -1
- package/scripts/dispatcher/lib/flow-resolve.mjs +3 -1
- package/scripts/dispatcher/lib/level.mjs +29 -3
- package/scripts/dispatcher/lib/review.mjs +1 -1
- package/scripts/dispatcher/lib/subagent-runner.mjs +5 -3
- package/scripts/docs/auto-rules.md +376 -0
- package/scripts/docs/auto-skills.md +173 -0
- package/scripts/docs/build-agents-commands.md +183 -0
- package/scripts/docs/cli-entry.md +153 -0
- package/scripts/docs/coverage-fix.md +177 -0
- package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +189 -0
- package/scripts/lib/changed-files.mjs +4 -1
- package/scripts/lib/diff-added-lines.mjs +85 -0
- package/scripts/lib/docs/changed-files.md +149 -0
- package/scripts/lib/docs/check-mdc-template-refs.md +222 -0
- package/scripts/lib/docs/check-reporter.md +175 -0
- package/scripts/lib/docs/discover-check-rules-from-cursor.md +157 -0
- package/scripts/lib/docs/discover-checkable-rules.md +165 -0
- package/scripts/lib/docs/ensure-tool.md +254 -0
- package/scripts/lib/docs/generated-markdown.md +275 -0
- package/scripts/lib/docs/gha-workflow.md +326 -0
- package/scripts/lib/docs/inline-template-links.md +303 -0
- package/scripts/lib/docs/list-rule-ids.md +156 -0
- package/scripts/lib/docs/load-cursor-config.md +147 -0
- package/scripts/lib/docs/mirror-parity.md +167 -0
- package/scripts/lib/worktree.mjs +26 -0
- package/scripts/worktree-cli.mjs +12 -2
- package/skills/coverage-fix/SKILL.md +34 -45
- package/skills/docgen/SKILL.md +44 -23
- package/skills/docgen/bench/etalon/firebase_hosting.md +19 -0
- package/skills/docgen/bench/etalon/k8s-tree.md +24 -0
- package/skills/docgen/bench/etalon/overlay-paths.md +24 -0
- package/skills/docgen/js/docgen-ignore.mjs +54 -0
- package/skills/docgen/js/docgen-scan.mjs +37 -21
- package/skills/llm-patch/SKILL.md +23 -2
- package/skills/start-check/SKILL.md +26 -53
- package/skills/start-check/js/check.mjs +211 -0
- package/skills/taze/SKILL.md +9 -3
- package/skills/taze/js/diff.mjs +154 -0
- package/types/bin/n-cursor.d.ts +1 -1
- package/skills/fix-tests/SKILL.md +0 -119
- package/skills/fix-tests/meta.json +0 -1
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
## Огляд
|
|
2
|
+
|
|
3
|
+
Модуль `mirror-parity.mjs` забезпечує перевірку парності (parity) між «дзеркальними» файлами правил Cursor у `.cursor/rules/n-<id>.mdc` та їх канонічними джерелами у `npm/rules/<id>/<id>.mdc`. Ідея така: репозиторій містить два представлення одного й того ж правила:
|
|
4
|
+
|
|
5
|
+
- канонічне джерело правила лежить у `npm/rules/<id>/<id>.mdc` (з можливими посиланнями на шаблони у тій самій теці);
|
|
6
|
+
- дзеркало лежить у `.cursor/rules/n-<id>.mdc` і повинно бути результатом застосування трансформу `inlineTemplateLinks` до канону (тобто посилання на шаблони мають бути вставлені «інлайном»).
|
|
7
|
+
|
|
8
|
+
Коли канонічний `.mdc` змінюють і забувають регенерувати дзеркало, виникає «дрейф» (drift). Цей модуль:
|
|
9
|
+
|
|
10
|
+
- перелічує керовані дзеркала (ті, для яких знайдено канон);
|
|
11
|
+
- обчислює очікуваний вміст дзеркала через ту саму трансформацію, що і синхронізатор;
|
|
12
|
+
- виявляє ідентифікатори дрейфних правил (де `actual ≠ expected`).
|
|
13
|
+
|
|
14
|
+
Модуль використовується одночасно і як гард у тестах (очікувано `drift === []`), і для разової регенерації дзеркал. Контекст беклогу — адаптація flow #10.
|
|
15
|
+
|
|
16
|
+
## Експорти / API
|
|
17
|
+
|
|
18
|
+
Модуль експортує три іменовані функції:
|
|
19
|
+
|
|
20
|
+
- `listManagedMirrors(repoRoot)` — синхронна; повертає опис керованих дзеркал.
|
|
21
|
+
- `expectedMirrorContent(canonicalPath)` — асинхронна (повертає `Promise<string>`); обчислює очікуваний вміст дзеркала.
|
|
22
|
+
- `findMirrorDrift(repoRoot)` — асинхронна; повертає масив id правил із дрейфом, відсортований за зростанням.
|
|
23
|
+
|
|
24
|
+
Жодних дефолтних експортів, побічних ефектів на рівні модуля немає (лише імпорти стандартних модулів і одного локального).
|
|
25
|
+
|
|
26
|
+
## Функції
|
|
27
|
+
|
|
28
|
+
### `listManagedMirrors(repoRoot)`
|
|
29
|
+
|
|
30
|
+
Сигнатура: `function listManagedMirrors(repoRoot: string): { id: string, mirrorPath: string, canonicalPath: string }[]`
|
|
31
|
+
|
|
32
|
+
Параметри:
|
|
33
|
+
|
|
34
|
+
- `repoRoot` — абсолютний шлях до кореня репозиторію (директорія, у якій лежать `.cursor/` та `npm/`).
|
|
35
|
+
|
|
36
|
+
Повертає масив об'єктів-описів дзеркал, де:
|
|
37
|
+
|
|
38
|
+
- `id` — ідентифікатор правила без префіксу `n-` і без розширення `.mdc` (наприклад, для файлу `n-bun.mdc` → `bun`);
|
|
39
|
+
- `mirrorPath` — абсолютний шлях до файлу дзеркала всередині `.cursor/rules/`;
|
|
40
|
+
- `canonicalPath` — абсолютний шлях до канонічного `.mdc` всередині `npm/rules/<id>/`.
|
|
41
|
+
|
|
42
|
+
Алгоритм:
|
|
43
|
+
|
|
44
|
+
1. Будує шлях до `.cursor/rules`.
|
|
45
|
+
2. Якщо такої директорії не існує (`existsSync` false) — повертає порожній масив (раннє повернення, безпечне для свіжих репо).
|
|
46
|
+
3. Зчитує перелік файлів директорії синхронно (`readdirSync`).
|
|
47
|
+
4. Фільтрує файли: повинні починатися з префіксу `'n-'` (константа `MIRROR_PREFIX`) і завершуватися розширенням `'.mdc'` (константа `MDC_EXT`).
|
|
48
|
+
5. Мапить кожен файл у об'єкт із обчисленими шляхами: `id` отримується вирізанням префіксу і розширення (`f.slice(MIRROR_PREFIX.length, -MDC_EXT.length)`).
|
|
49
|
+
6. Залишає лише ті записи, для яких реально існує канонічне джерело `canonicalPath` (друга фільтрація через `existsSync`). Це дозволяє пропускати «зовнішні» дзеркала, які не мають канону в монорепо.
|
|
50
|
+
|
|
51
|
+
Side effects: лише читання директорії з диску (синхронні `existsSync`, `readdirSync`). Жодних записів, мережевих викликів, мутацій глобального стану.
|
|
52
|
+
|
|
53
|
+
Помилки: якщо файлова система кине помилку під час `readdirSync` (наприклад, права доступу) — вона прокинеться вгору; для типового сценарію відсутності директорії `.cursor/rules` функція не падає (гард через `existsSync`).
|
|
54
|
+
|
|
55
|
+
### `expectedMirrorContent(canonicalPath)`
|
|
56
|
+
|
|
57
|
+
Сигнатура: `function expectedMirrorContent(canonicalPath: string): Promise<string>`
|
|
58
|
+
|
|
59
|
+
Параметри:
|
|
60
|
+
|
|
61
|
+
- `canonicalPath` — абсолютний шлях до канонічного `.mdc`-файлу (`npm/rules/<id>/<id>.mdc`).
|
|
62
|
+
|
|
63
|
+
Повертає `Promise<string>` — очікуваний (нормалізований) текст дзеркала після інлайнінгу шаблонів. Хоча сама функція не використовує `async`/`await`, її результат типізовано як `Promise`, оскільки делегує виклик до `inlineTemplateLinks`, яка є асинхронною (її результат може бути thenable). Це означає, що споживач має `await`-нути повернене значення.
|
|
64
|
+
|
|
65
|
+
Алгоритм:
|
|
66
|
+
|
|
67
|
+
1. Синхронно зчитує вміст канонічного файлу як UTF-8 (`readFileSync(canonicalPath, 'utf8')`).
|
|
68
|
+
2. Викликає `inlineTemplateLinks(content, dirname(canonicalPath))`, передаючи прочитаний текст та теку канону як base для розв'язання шляхів до шаблонів.
|
|
69
|
+
3. Повертає результат трансформації.
|
|
70
|
+
|
|
71
|
+
Side effects: одне читання з диску. Подальша поведінка щодо шаблонів інкапсульована в `inlineTemplateLinks` (див. її документацію). Контракт: цей же самий трансформ застосовує синк, тому очікуваний вміст детермінований по канону.
|
|
72
|
+
|
|
73
|
+
Помилки: якщо файл за `canonicalPath` не існує або недоступний — `readFileSync` кине помилку. Викличні сторони мають гарантувати існування (`listManagedMirrors` уже фільтрує неіснуючі канони).
|
|
74
|
+
|
|
75
|
+
### `findMirrorDrift(repoRoot)`
|
|
76
|
+
|
|
77
|
+
Сигнатура: `async function findMirrorDrift(repoRoot: string): Promise<string[]>`
|
|
78
|
+
|
|
79
|
+
Параметри:
|
|
80
|
+
|
|
81
|
+
- `repoRoot` — абсолютний шлях до кореня репозиторію.
|
|
82
|
+
|
|
83
|
+
Повертає `Promise<string[]>` — відсортований за зростанням масив `id` правил, де дзеркало розійшлося з очікуваним вмістом.
|
|
84
|
+
|
|
85
|
+
Алгоритм:
|
|
86
|
+
|
|
87
|
+
1. Заводить порожній масив `drift`.
|
|
88
|
+
2. Послідовно (через `for...of`) обходить усі керовані дзеркала, отримані з `listManagedMirrors(repoRoot)`.
|
|
89
|
+
3. Для кожного дзеркала `m`:
|
|
90
|
+
- `await`-ить очікуваний вміст через `expectedMirrorContent(m.canonicalPath)`;
|
|
91
|
+
- синхронно зчитує фактичний вміст файлу дзеркала `readFileSync(m.mirrorPath, 'utf8')`;
|
|
92
|
+
- якщо стрічки не дорівнюють — додає `m.id` до `drift`.
|
|
93
|
+
4. Повертає `drift.sort()` (лексикографічне сортування).
|
|
94
|
+
|
|
95
|
+
Side effects: тільки читання з диску. Не записує жодних файлів, не пише в лог.
|
|
96
|
+
|
|
97
|
+
Зауваження щодо порівняння: рівність стрічок є точною (строге `!==` по UTF-8 тексту). Будь-яка різниця в пробілах, символах кінця рядка чи порядку інлайнених шаблонів буде розцінена як дрейф.
|
|
98
|
+
|
|
99
|
+
## Залежності
|
|
100
|
+
|
|
101
|
+
Стандартна бібліотека Node.js:
|
|
102
|
+
|
|
103
|
+
- `node:fs` — `existsSync`, `readdirSync`, `readFileSync`. Усі виклики синхронні. Імпорт зроблено через `node:`-префікс для явної прив'язки до built-in модуля.
|
|
104
|
+
- `node:path` — `dirname`, `join`. Використовуються для безпечного складання абсолютних шляхів і отримання батьківської теки канону.
|
|
105
|
+
|
|
106
|
+
Локальні модулі:
|
|
107
|
+
|
|
108
|
+
- `./inline-template-links.mjs` — імпорт іменованої функції `inlineTemplateLinks(content, baseDir)`. Це той самий трансформ, що його використовує синк дзеркал; саме завдяки спільному використанню досягається консистентність між «гардом дрейфу» і «генератором дзеркал».
|
|
109
|
+
|
|
110
|
+
Зовнішніх npm-залежностей немає.
|
|
111
|
+
|
|
112
|
+
Константи на рівні модуля:
|
|
113
|
+
|
|
114
|
+
- `MIRROR_PREFIX = 'n-'` — префікс імені файлів дзеркал.
|
|
115
|
+
- `MDC_EXT = '.mdc'` — розширення файлів правил Cursor.
|
|
116
|
+
|
|
117
|
+
## Потік виконання / Використання
|
|
118
|
+
|
|
119
|
+
Типові сценарії використання модуля:
|
|
120
|
+
|
|
121
|
+
1. Тест-гард парності (CI). Споживач передає корінь репо й перевіряє, що дрейф порожній:
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
import { findMirrorDrift } from './mirror-parity.mjs'
|
|
125
|
+
|
|
126
|
+
const drift = await findMirrorDrift(repoRoot)
|
|
127
|
+
if (drift.length) {
|
|
128
|
+
throw new Error(`Mirror drift detected for: ${drift.join(', ')}`)
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
2. Разова регенерація дзеркал. Споживач отримує список керованих дзеркал і для кожного перезаписує файл дзеркала очікуваним вмістом:
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
import { writeFileSync } from 'node:fs'
|
|
136
|
+
import { listManagedMirrors, expectedMirrorContent } from './mirror-parity.mjs'
|
|
137
|
+
|
|
138
|
+
for (const m of listManagedMirrors(repoRoot)) {
|
|
139
|
+
const expected = await expectedMirrorContent(m.canonicalPath)
|
|
140
|
+
writeFileSync(m.mirrorPath, expected)
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
(Сам модуль `mirror-parity.mjs` записів не виконує — це задача викличного скрипта.)
|
|
145
|
+
|
|
146
|
+
Внутрішній потік виконання `findMirrorDrift`:
|
|
147
|
+
|
|
148
|
+
1. Виклик `listManagedMirrors(repoRoot)`:
|
|
149
|
+
- перевірка існування `.cursor/rules`;
|
|
150
|
+
- читання списку файлів;
|
|
151
|
+
- фільтрація за префіксом `n-` і розширенням `.mdc`;
|
|
152
|
+
- обчислення `id`, `mirrorPath`, `canonicalPath`;
|
|
153
|
+
- відкидання дзеркал без канону.
|
|
154
|
+
2. Послідовний обхід отриманих описів:
|
|
155
|
+
- читання канону → `inlineTemplateLinks` → очікуваний текст;
|
|
156
|
+
- читання дзеркала → порівняння;
|
|
157
|
+
- накопичення id з дрейфом.
|
|
158
|
+
3. Сортування результату та повернення.
|
|
159
|
+
|
|
160
|
+
Контрактна гарантія: якщо синк використовує ту ж саму функцію `inlineTemplateLinks` для генерації дзеркал, то регенерація закриває будь-який дрейф, виявлений `findMirrorDrift`. Якщо дрейф виявлено — він завжди вказує на людську помилку (зміна канону без регенерації) або на розбіжність версій трансформу.
|
|
161
|
+
|
|
162
|
+
Обмеження:
|
|
163
|
+
|
|
164
|
+
- Обхід послідовний (без `Promise.all`) — простіший контракт по помилках, але повільніший на великих наборах правил. Зважаючи на типову кількість правил у `.cursor/rules`, це не критично.
|
|
165
|
+
- Усі читання файлів синхронні, попри асинхронну сигнатуру `findMirrorDrift` — асинхронність зумовлена тільки інтерфейсом `inlineTemplateLinks`.
|
|
166
|
+
- Префікс `n-` зашитий константою — додавання інших префіксів дзеркал вимагатиме зміни модуля.
|
|
167
|
+
- Зовнішні дзеркала (без канону в `npm/rules/<id>/`) автоматично пропускаються; модуль не повідомляє про них окремо.
|
package/scripts/lib/worktree.mjs
CHANGED
|
@@ -87,6 +87,32 @@ export function buildDescription({ branch, task, baseCommit, date }) {
|
|
|
87
87
|
].join('\n')
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/** Поріг переліку файлів у нагадуванні: понад нього показуємо лише кількість. */
|
|
91
|
+
const DIRTY_LIST_LIMIT = 10
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Нагадування про незакомічені зміни основного дерева, які **не** потрапляють
|
|
95
|
+
* у новий worktree (він створюється від HEAD, без брудного стану). До
|
|
96
|
+
* `limit` файлів — перелік шляхів; більше — лише підсумкова кількість, щоб не
|
|
97
|
+
* залити екран. Призначене для виводу одразу після `worktree add`.
|
|
98
|
+
* @param {string} porcelain вивід `git status --porcelain` основного дерева
|
|
99
|
+
* @param {number} [limit] поріг переліку (понад нього — лише кількість)
|
|
100
|
+
* @returns {string | null} текст нагадування або `null`, якщо дерево чисте
|
|
101
|
+
*/
|
|
102
|
+
export function buildDirtyNotice(porcelain, limit = DIRTY_LIST_LIMIT) {
|
|
103
|
+
// Порядок: XY + пробіл (3 символи) + шлях; для перейменування — `orig -> dest`.
|
|
104
|
+
const files = String(porcelain ?? '')
|
|
105
|
+
.split('\n')
|
|
106
|
+
.map(line => line.slice(3).trim())
|
|
107
|
+
.filter(Boolean)
|
|
108
|
+
if (files.length === 0) return null
|
|
109
|
+
const head = `⚠️ Основне дерево має ${files.length} незакомічених змін — вони НЕ потрапили в цей worktree (створено від HEAD).`
|
|
110
|
+
const tail = ' Закоміть потрібні файли, якщо worktree-скіл має їх бачити.'
|
|
111
|
+
if (files.length > limit) return `${head}\n${tail}`
|
|
112
|
+
const list = files.map(f => ` - ${f}`).join('\n')
|
|
113
|
+
return `${head}\n${list}\n${tail}`
|
|
114
|
+
}
|
|
115
|
+
|
|
90
116
|
/**
|
|
91
117
|
* `.md`-описи без відповідного зареєстрованого worktree-checkout.
|
|
92
118
|
* @param {string[]} descFiles абсолютні шляхи `.worktrees/*.md`
|
package/scripts/worktree-cli.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* CLI-оркестратор worktree-tool `n-cursor worktree` (виконавець конвенції `.worktrees/`).
|
|
3
3
|
*
|
|
4
4
|
* Підкоманди:
|
|
5
|
-
* add <branch> "<опис>" — git worktree add .worktrees/<sanit> -b <branch> (від HEAD) + .md-опис
|
|
5
|
+
* add <branch> "<опис>" — git worktree add .worktrees/<sanit> -b <branch> (від HEAD) + .md-опис + нагадування про незакомічені зміни
|
|
6
6
|
* remove <branch> [--force] — прибрати checkout + .md (гілку лишає)
|
|
7
7
|
* list — git worktree list + вміст .md-описів
|
|
8
8
|
* prune — git worktree prune + видалити осиротілі .md
|
|
@@ -16,7 +16,13 @@ import { join } from 'node:path'
|
|
|
16
16
|
import { cwd as processCwd } from 'node:process'
|
|
17
17
|
|
|
18
18
|
import { cleanupFlowSiblings } from './dispatcher/lib/state-store.mjs'
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
buildDescription,
|
|
21
|
+
buildDirtyNotice,
|
|
22
|
+
findOrphanDescFiles,
|
|
23
|
+
firstFreeBranch,
|
|
24
|
+
worktreePaths
|
|
25
|
+
} from './lib/worktree.mjs'
|
|
20
26
|
|
|
21
27
|
const USAGE = [
|
|
22
28
|
'Usage:',
|
|
@@ -110,6 +116,9 @@ function cmdAdd(rest, ctx) {
|
|
|
110
116
|
if (chosen !== branch) {
|
|
111
117
|
ctx.log(`ℹ️ гілка/worktree "${branch}" уже існує — обрано вільну назву "${chosen}"`)
|
|
112
118
|
}
|
|
119
|
+
// Знімаємо статус ДО створення worktree: інакше щойно створений checkout/опис у `.worktrees/`
|
|
120
|
+
// потрапили б у `git status` (коли `.worktrees/` не в .gitignore) і дали б хибне нагадування.
|
|
121
|
+
const dirty = buildDirtyNotice(git(['status', '--porcelain'], ctx.cwd).stdout)
|
|
113
122
|
const added = git(['worktree', 'add', paths.checkout, '-b', chosen], ctx.cwd)
|
|
114
123
|
if (added.status !== 0) {
|
|
115
124
|
ctx.logError(`worktree add не вдався: ${added.stderr.trim()}`)
|
|
@@ -120,6 +129,7 @@ function cmdAdd(rest, ctx) {
|
|
|
120
129
|
writeFileSync(paths.descFile, md, 'utf8')
|
|
121
130
|
ctx.log(`✅ worktree: ${paths.checkout}`)
|
|
122
131
|
ctx.log(` опис: ${paths.descFile}`)
|
|
132
|
+
if (dirty) ctx.log(dirty) // нагадування про незакомічені зміни основного дерева (зняте ДО add)
|
|
123
133
|
return 0
|
|
124
134
|
}
|
|
125
135
|
|
|
@@ -24,35 +24,46 @@ description: >-
|
|
|
24
24
|
|
|
25
25
|
## Workflow
|
|
26
26
|
|
|
27
|
-
### Крок
|
|
27
|
+
### Крок 0: Визнач команди (з `package.json#scripts`)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
Прочитай кореневий `package.json` і зафіксуй дві команди (перша що існує):
|
|
30
|
+
|
|
31
|
+
- **coverage-команда**: `scripts["coverage"]` → виклик `bun run coverage`; fallback `n-cursor coverage`
|
|
32
|
+
- **test-команда**: `scripts["test"]` → виклик `bun run test` (або те, що в скрипті); fallback `bun test`
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
Далі по тексту «coverage-команда» / «test-команда» = знайдені тут значення.
|
|
35
|
+
|
|
36
|
+
### Крок 1: Згенеруй або переви́користай `COVERAGE.md`
|
|
37
|
+
|
|
38
|
+
**Early-skip.** Якщо `COVERAGE.md` уже існує, свіжий (новіший за останню зміну source/тестів) і має секцію `## Вцілілі мутанти` — можеш одразу перейти до Кроку 2 без повторної генерації. Інакше — згенеруй звіт:
|
|
34
39
|
|
|
35
40
|
```bash
|
|
36
|
-
|
|
41
|
+
n-cursor coverage # або coverage-команда з Кроку 0
|
|
37
42
|
```
|
|
38
43
|
|
|
39
44
|
Ця команда генерує `COVERAGE.md`. Якщо є survived mutants — COVERAGE.md матиме секцію `## Вцілілі мутанти` з JSON-блоком.
|
|
40
45
|
|
|
41
|
-
### Крок 2:
|
|
46
|
+
### Крок 2: Прочитай компактний index вцілілих
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
> **Не читай `COVERAGE.md` сам.** Файл може важити мегабайти (секція `## Вцілілі
|
|
49
|
+
мутанти` на сотні файлів) — його читання спалило б сотні тисяч токенів. Важкий
|
|
50
|
+
> парсинг несе CLI; ти отримуєш лише крихітний index.
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
```bash
|
|
53
|
+
n-cursor coverage-fix index
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Друкує компактний JSON-масив `[{ "file": "<path>", "mutants": <N> }]` (кілобайти, не мегабайти). Якщо `[]` — зупинись:
|
|
46
57
|
|
|
47
58
|
```
|
|
48
59
|
✓ Жодних вцілілих мутантів — mutation score повний. Coverage завершено.
|
|
49
60
|
```
|
|
50
61
|
|
|
51
|
-
Запам'ятай `prevCount =
|
|
62
|
+
Запам'ятай `prevCount = сума всіх mutants` (загальна кількість вцілілих мутантів).
|
|
52
63
|
|
|
53
|
-
### Крок 3: Для кожного файлу —
|
|
64
|
+
### Крок 3: Для кожного файлу — slice + Agent
|
|
54
65
|
|
|
55
|
-
|
|
66
|
+
Для кожного запису `{ file, mutants }` з index:
|
|
56
67
|
|
|
57
68
|
**3a. Визнач test файл (завжди у `tests/` директорії):**
|
|
58
69
|
|
|
@@ -65,42 +76,20 @@ bun run coverage
|
|
|
65
76
|
- Оновити відносні imports (тепер `../` рівень вгору до source)
|
|
66
77
|
3. Жоден не знайдено → буде створено `<dir>/tests/<basename>.test.mjs`
|
|
67
78
|
|
|
68
|
-
**3b.
|
|
79
|
+
**3b. Отримай готовий зріз-промпт лише для цього файлу:**
|
|
69
80
|
|
|
81
|
+
```bash
|
|
82
|
+
n-cursor coverage-fix slice --file <file>
|
|
70
83
|
```
|
|
71
|
-
Тобі дані вцілілі мутанти зі Stryker для файлу `<file>`.
|
|
72
|
-
Ці мутанти вціліли, бо наявні тести НЕ вловили конкретні зміни коду.
|
|
73
|
-
|
|
74
|
-
**Вихідний код** (`<file>`):
|
|
75
|
-
\`\`\`
|
|
76
|
-
<зміст source-файлу>
|
|
77
|
-
\`\`\`
|
|
78
|
-
|
|
79
|
-
**Наявні тести** (`<test-file>`):
|
|
80
|
-
\`\`\`
|
|
81
|
-
<зміст test-файлу або "файл ще не існує">
|
|
82
|
-
\`\`\`
|
|
83
|
-
|
|
84
|
-
**Вцілілі мутанти** (кожен — зміна коду що НЕ вловлена):
|
|
85
|
-
<для кожного мутанта:>
|
|
86
|
-
- Рядок <line>, колонка <col>: `<original>` → `<replacement>` (тип: <mutantType>)
|
|
87
|
-
|
|
88
|
-
**Завдання:**
|
|
89
|
-
Допиши мінімальні test-cases у файл `<test-file>` які вловлять кожен мутант.
|
|
90
|
-
Правила:
|
|
91
|
-
- НЕ видаляй і НЕ змінюй наявні тести
|
|
92
|
-
- Стиль тестів — відповідно до наявного файлу (той самий фреймворк, describe/test)
|
|
93
|
-
- Якщо файл ще не існує — створи `<dir>/tests/<basename>.test.mjs` з правильними імпортами.
|
|
94
|
-
Приклад: source `src/services/auth-store.js` → import `import { ... } from '../auth-store.js'`
|
|
95
|
-
- Після написання запусти: `bun test <test-file>` і переконайся що тести проходять (виправ якщо падають, 1-2 спроби)
|
|
96
|
-
```
|
|
97
84
|
|
|
98
|
-
|
|
85
|
+
CLI друкує самодостатній промпт **рівно для одного файлу**: список вцілілих мутантів цього файлу (рядок/колонка/`original → replacement`/тип) з контекстом ±3 рядки навколо кожного та фіксовані правила. Це і є «порція під когнітивне навантаження» одного субагента — нічого зайвого.
|
|
86
|
+
|
|
87
|
+
**3c. Запусти Agent** з цим зрізом як промптом, дописавши один рядок про цільовий test-файл із 3a (`<dir>/tests/<basename>.test.mjs`, з правильними відносними imports). Дочекайся завершення.
|
|
99
88
|
|
|
100
89
|
### Крок 4: Перевір що всі тести проходять
|
|
101
90
|
|
|
102
91
|
```bash
|
|
103
|
-
bun test
|
|
92
|
+
bun test # або test-команда з Кроку 0
|
|
104
93
|
```
|
|
105
94
|
|
|
106
95
|
Якщо падають — поверни відповідний Agent з помилкою і попроси виправити.
|
|
@@ -108,15 +97,15 @@ bun test
|
|
|
108
97
|
### Крок 5: Запусти coverage і порівняй
|
|
109
98
|
|
|
110
99
|
```bash
|
|
111
|
-
n-cursor coverage
|
|
100
|
+
n-cursor coverage # або coverage-команда з Кроку 0
|
|
101
|
+
n-cursor coverage-fix index
|
|
112
102
|
```
|
|
113
103
|
|
|
114
|
-
|
|
115
|
-
`newCount = новий масив.length`
|
|
104
|
+
`newCount = сума mutants` зі свіжого index (знову — без читання `COVERAGE.md` вручну).
|
|
116
105
|
|
|
117
106
|
**Рішення:**
|
|
118
107
|
|
|
119
|
-
- `newCount < prevCount` AND iterations < 3 → повтор з Кроку 2 з оновленим
|
|
108
|
+
- `newCount < prevCount` AND iterations < 3 → повтор з Кроку 2 з оновленим index
|
|
120
109
|
- `newCount >= prevCount` → конвергенція:
|
|
121
110
|
|
|
122
111
|
```
|
package/skills/docgen/SKILL.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: docgen
|
|
3
3
|
description: >-
|
|
4
|
-
Обходить проєкт і для кожного кодового файлу (js/mjs/ts/vue/py) пише
|
|
4
|
+
Обходить проєкт і для кожного кодового файлу (js/mjs/ts/vue/py) пише лаконічну поведінкову українську md-документацію у теку docs/ поряд із кодом — диспатчить окремого субагента на кожен файл, за правилами adr/ci4
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# docgen — генерація документації по файлах
|
|
8
8
|
|
|
9
9
|
## Мета
|
|
10
10
|
|
|
11
|
-
Для кожного кодового файлу проєкту створити
|
|
11
|
+
Для кожного кодового файлу проєкту створити лаконічну поведінкову `.md`-документацію у теці `docs/`
|
|
12
12
|
**поряд із самим файлом** (`<dir>/docs/<stem>.md`). Документацію пише **окремий субагент**
|
|
13
13
|
на кожен файл — не один прохід, а батч-диспатч. Джерело правди стилю — правила `adr` і
|
|
14
14
|
`ci4` (`docs/explanation`/`docs/adr`-каталоги з тих правил **не застосовуємо** — доку
|
|
@@ -46,13 +46,10 @@ Tier 3 — лише після завершення всього Tier 2.
|
|
|
46
46
|
npx @nitra/cursor docgen scan
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Команда друкує JSON-масив об'
|
|
49
|
+
Команда друкує JSON-масив об'єктів. Усі шляхи в ньому — відносні до кореня проєкту:
|
|
50
50
|
|
|
51
51
|
```json
|
|
52
|
-
[
|
|
53
|
-
{ "sourcePath": "/abs/src/lib/foo.js", "relSource": "src/lib/foo.js",
|
|
54
|
-
"docPath": "/abs/src/lib/docs/foo.md", "exists": false }
|
|
55
|
-
]
|
|
52
|
+
[{ "sourcePath": "src/lib/foo.js", "docPath": "src/lib/docs/foo.md", "exists": false }]
|
|
56
53
|
```
|
|
57
54
|
|
|
58
55
|
Розпарси JSON.
|
|
@@ -80,7 +77,7 @@ npx @nitra/cursor docgen scan
|
|
|
80
77
|
Промпт кожного субагента (підстав `sourcePath` і `docPath`):
|
|
81
78
|
|
|
82
79
|
```
|
|
83
|
-
Напиши
|
|
80
|
+
Напиши лаконічну технічну документацію для одного файлу коду — орієнтовану на поведінку, не реалізацію.
|
|
84
81
|
|
|
85
82
|
ФАЙЛ-ДЖЕРЕЛО: <sourcePath>
|
|
86
83
|
ЗАПИСАТИ В: <docPath>
|
|
@@ -94,18 +91,32 @@ npx @nitra/cursor docgen scan
|
|
|
94
91
|
- Мова — УКРАЇНСЬКА для всього тексту (заголовки, абзаци, таблиці). Code identifiers,
|
|
95
92
|
шляхи, імена API, команди — лишай як у коді (зазвичай ASCII).
|
|
96
93
|
- ЧИСТИЙ Markdown. Жодних HTML-обгорток (<div>/<span>/класів) — токен-ефективність.
|
|
97
|
-
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
-
|
|
94
|
+
- ФОКУС НА ПОВЕДІНЦІ, не реалізації. Пиши ЩО і НАВІЩО, а не як саме це зроблено.
|
|
95
|
+
- НЕ перелічуй модулі стандартної бібліотеки (node:fs, node:path, node:crypto, python stdlib
|
|
96
|
+
тощо) — вони не несуть бізнес-значення. Зовнішні залежності (npm-пакети, внутрішні модулі)
|
|
97
|
+
згадуй лише якщо їхня роль не очевидна з контексту.
|
|
98
|
+
- НЕ перелічуй внутрішні назви допоміжних функцій/змінних — описуй їхню роль і поведінку.
|
|
99
|
+
Імена публічних exports згадуй лише коли export — справжня точка інтеграції, яку кличуть
|
|
100
|
+
ззовні. Для дрібних/листкових модулів з однією відповідальністю опиши роль поведінково,
|
|
101
|
+
БЕЗ сигнатур, таблиць типів і переліку параметрів — це деталі реалізації.
|
|
102
|
+
- Контекстна незалежність: кожна секція самодостатня. Уникай «як вище», «ця функція», «той сервіс».
|
|
103
|
+
- Секції (включай лише доречні — порожніх не вигадуй):
|
|
104
|
+
## Огляд — 1-3 речення: що файл робить і навіщо він існує (роль у системі). Згадай
|
|
105
|
+
ключову семантику, якщо вона визначає сенс файлу (opt-in/gate, кеш, ідемпотентність тощо).
|
|
106
|
+
## Поведінка — покроковий алгоритм у бізнес-термінах (не деталі реалізації).
|
|
107
|
+
Нумерований список: що відбувається, умови, гілки логіки. Якщо файл керується
|
|
108
|
+
конфігом чи форматом даних — наведи короткий приклад (тільки реальний з коду).
|
|
109
|
+
## Публічний API — ЛИШЕ якщо модуль має нетривіальну зовнішню поверхню, яку називають
|
|
110
|
+
споживачі. Для кожного export: назва + що робить. Без сигнатур і таблиць типів.
|
|
111
|
+
Не дублюй ## Поведінка. Для модуля з однією функцією-предикатом цю секцію пропусти.
|
|
112
|
+
## Де використовується — де в системі цей файл вживається (якщо відомо з коду).
|
|
113
|
+
## Гарантії поведінки — інваріанти й крайові випадки: що гарантовано (read-only,
|
|
114
|
+
не кидає винятків, fail-safe-значення за замовчуванням, безпечна обробка поганих даних)
|
|
115
|
+
і що стається при відсутніх ресурсах чи некоректному вводі. Пропусти, якщо таких гарантій немає.
|
|
116
|
+
- Для .vue додай ## Інтерфейс компонента — props (типи, defaults), emits, slots, реактивний стан.
|
|
107
117
|
- НЕ вигадуй деталей, яких немає в коді.
|
|
108
|
-
- Мета —
|
|
118
|
+
- Мета — Behavior Test: читач розуміє, що робить файл і як він вписується в систему,
|
|
119
|
+
без потреби читати реалізацію.
|
|
109
120
|
|
|
110
121
|
Поверни лише підтвердження, що файл <docPath> записано.
|
|
111
122
|
```
|
|
@@ -122,9 +133,14 @@ npx @nitra/cursor docgen modules
|
|
|
122
133
|
|
|
123
134
|
```json
|
|
124
135
|
[
|
|
125
|
-
{
|
|
126
|
-
"
|
|
127
|
-
"
|
|
136
|
+
{
|
|
137
|
+
"moduleRoot": "/abs/npm/rules/adr",
|
|
138
|
+
"relRoot": "npm/rules/adr",
|
|
139
|
+
"slug": "npm-rules-adr",
|
|
140
|
+
"docPath": "/abs/npm/rules/adr/docs/ARCHITECTURE.md",
|
|
141
|
+
"members": ["npm/rules/adr/index.mjs"],
|
|
142
|
+
"exists": false
|
|
143
|
+
}
|
|
128
144
|
]
|
|
129
145
|
```
|
|
130
146
|
|
|
@@ -138,7 +154,7 @@ module-summary **завжди регенерується** (це агрегат
|
|
|
138
154
|
ФАЙЛИ МОДУЛЯ (members): <members>
|
|
139
155
|
|
|
140
156
|
Кроки:
|
|
141
|
-
1. Прочитай файлові доки членів модуля. <member> —
|
|
157
|
+
1. Прочитай файлові доки членів модуля. <member> — sourcePath відносно кореня проєкту
|
|
142
158
|
(= поточний CWD); його файлова дока — <CWD>/<dir>/docs/<stem>.md. За потреби зазирни
|
|
143
159
|
в самі файли.
|
|
144
160
|
2. Створи теку для <docPath>, якщо її немає.
|
|
@@ -201,3 +217,8 @@ Tier 3 (домени): <D> доменних доків у docs/.
|
|
|
201
217
|
- Не комітити автоматично — користувач вирішує, коли комітити згенеровану доку.
|
|
202
218
|
- Scanner ігнорує `node_modules`, `dist`, `.git`, `__pycache__`, `coverage`, `.cursor`,
|
|
203
219
|
`.claude`, усі теки `docs/`, а також `*.test.*` / `*.spec.*` / `*.d.ts`.
|
|
220
|
+
Кореневий repo `docs/` — system-wide only: file-level docs туди не пишуться, і Tier 1
|
|
221
|
+
має трактувати цей корінь як повністю нецільовий.
|
|
222
|
+
- Список glob-ів для ignore живе в окремому snippet-модулі
|
|
223
|
+
`npm/skills/docgen/js/docgen-ignore.mjs` (`DOCGEN_IGNORE_GLOBS`).
|
|
224
|
+
Scanner лише читає цей список.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# firebase_hosting.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Перевірка-концерн правила abie: у підкаталогах першого рівня репозиторію не повинно бути артефактів Firebase Hosting (`.firebaserc`, `firebase.json`, `.firebase/`), бо `abie.mdc` забороняє Firebase Hosting. Сам корінь репозиторію не перевіряється — там ці імена можуть належати суміжним проєктам.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
1. Прочитати список елементів кореня репозиторію. Якщо каталог не читається — зафіксувати помилку (fail) і завершитися.
|
|
10
|
+
2. Відібрати підкаталоги першого рівня, пропустивши `.git` і `node_modules`.
|
|
11
|
+
3. У кожному такому підкаталозі перевірити наявність заборонених імен: файлів `.firebaserc`, `firebase.json` і каталогу `.firebase/`. Кожна знахідка — окремий fail.
|
|
12
|
+
4. Якщо жодного порушення не знайдено — зафіксувати pass.
|
|
13
|
+
5. Повернути підсумковий exit-код.
|
|
14
|
+
|
|
15
|
+
## Гарантії поведінки
|
|
16
|
+
|
|
17
|
+
- Read-only: лише перелічує й перевіряє існування шляхів.
|
|
18
|
+
- Помилка читання кореня не валить процес винятком, а стає fail.
|
|
19
|
+
- Перевіряється лише перший рівень; корінь і глибші рівні поза охопленням.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# k8s-tree.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Обхід Kubernetes-дерева для перевірок abie з кешуванням на час одного прогону. Знаходить YAML під сегментом `k8s/` і визначає каталоги з `Deployment`. Перший виклик платить за обхід; наступні концерни прогону беруть із кешу.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
1. Пошук маніфестів: рекурсивно обійти дерево, відібравши `.yaml`/`.yml` під сегментом `k8s/`; `.github/` свідомо пропускається. Результат відсортований.
|
|
10
|
+
2. Каталоги з Deployment: розпарсити передані YAML, відібрати `kind: Deployment`, зібрати унікальні каталоги.
|
|
11
|
+
3. Кешування: обидві операції кешуються module-level singleton-ом за ключем із входів; повтор без I/O.
|
|
12
|
+
4. Пошкоджені YAML за замовчуванням мовчки пропускаються; репортер передає викликач.
|
|
13
|
+
|
|
14
|
+
## Публічний API
|
|
15
|
+
|
|
16
|
+
- `findK8sYamlFiles` — відсортований список YAML під `k8s/` (з кешем, пропуск `.github/`).
|
|
17
|
+
- `collectDeploymentDirs` — множина каталогів із Deployment (кеш, опц. репортер помилок).
|
|
18
|
+
|
|
19
|
+
## Гарантії поведінки
|
|
20
|
+
|
|
21
|
+
- Read-only щодо проєкту.
|
|
22
|
+
- Стійкість до пошкоджених YAML: помилкові документи пропускаються, обхід не переривається.
|
|
23
|
+
- Детермінований вивід (стабільне сортування).
|
|
24
|
+
- Кеш у межах прогону; повторні виклики безкоштовні.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# overlay-paths.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Набір чистих path-хелперів для overlay-перевірок правила abie: класифікація шляхів (ua-overlay проти base-шару), виведення каталогу пакета з overlay-шляху, умовні питання правила (чи потрібен HTTPRoute, чи є Deployment). Уся логіка — над рядками/шляхами та перевіркою існування файлів; YAML не парситься.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
- ua-overlay: шлях закінчується на `ua/kustomization.yaml`; base-шар — за сегментом `base/`.
|
|
10
|
+
- Каталог пакета: з `…/k8s/ua/kustomization.yaml` виділяється батько `k8s/`; без збігу — немає результату.
|
|
11
|
+
- HTTPRoute-gate: вимога лише для Vite-пакетів (є `vite.config.{js,mjs,ts}`).
|
|
12
|
+
- Deployment: чи хоч один каталог із Deployment лежить у `k8s/` цього пакета.
|
|
13
|
+
- base-шар: yaml під `<пакет>/k8s/` і не в `ua/`.
|
|
14
|
+
- Шляхи нормалізуються до posix (`\`→`/`).
|
|
15
|
+
|
|
16
|
+
## Публічний API
|
|
17
|
+
|
|
18
|
+
- `isUaKustomizationPath`, `abiePackageDirFromK8sOverlay`, `abieOverlayRequiresHttpRouteByVite`, `abieOverlayK8sTreeHasDeployment`, `isAbieK8sBaseYamlPath`, `isK8sYamlInAbiePackageExcludingUaOverlay`.
|
|
19
|
+
|
|
20
|
+
## Гарантії поведінки
|
|
21
|
+
|
|
22
|
+
- Read-only, без побічних ефектів.
|
|
23
|
+
- Невідповідність шаблону → негативний/порожній результат, не виняток.
|
|
24
|
+
- Незалежність від ОС (розділювачі зводяться до `/`).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Глоби, які `docgen` завжди ігнорує.
|
|
3
|
+
*
|
|
4
|
+
* Це окремий snippet-модуль: список правиться тут, scanner лише читає його
|
|
5
|
+
* через predicate. Патерни пишуться в posix-формі відносно кореня проєкту.
|
|
6
|
+
*/
|
|
7
|
+
import picomatch from 'picomatch'
|
|
8
|
+
|
|
9
|
+
/** Базовий список glob-ів для `docgen` ignore. */
|
|
10
|
+
export const DOCGEN_IGNORE_GLOBS = Object.freeze([
|
|
11
|
+
'**/node_modules/**',
|
|
12
|
+
'**/dist/**',
|
|
13
|
+
'.git/**',
|
|
14
|
+
'**/__pycache__/**',
|
|
15
|
+
'**/coverage/**',
|
|
16
|
+
'.cursor/**',
|
|
17
|
+
'.claude/**',
|
|
18
|
+
'.pi/**',
|
|
19
|
+
'.pi-template/**',
|
|
20
|
+
'.worktrees/**',
|
|
21
|
+
'**/benchmarks/**',
|
|
22
|
+
'**/demo/**',
|
|
23
|
+
'**/docs/**'
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
const IGNORE_MATCHERS = DOCGEN_IGNORE_GLOBS.map(glob => picomatch(glob, { dot: true }))
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Нормалізує відносний шлях до posix-формату для glob-matching.
|
|
30
|
+
* @param {string} relPath відносний шлях із path.relative(...)
|
|
31
|
+
* @returns {string} posix-вигляд шляху
|
|
32
|
+
*/
|
|
33
|
+
function toPosixRelPath(relPath) {
|
|
34
|
+
return relPath.split('\\').join('/')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Перевіряє, чи шлях має бути пропущений `docgen`.
|
|
39
|
+
* Для `kind = 'dir'` це працює і на піддерево каталогу, тож glob на кшталт
|
|
40
|
+
* `**\\/demo/**` спрацьовує на `demo/x` під час рекурсивного обходу.
|
|
41
|
+
* @param {string} relPath відносний шлях від кореня проєкту
|
|
42
|
+
* @param {'path'|'dir'} [kind='path'] тип перевірки
|
|
43
|
+
* @returns {boolean} `true`, якщо шлях ігнорується
|
|
44
|
+
*/
|
|
45
|
+
export function isDocgenIgnored(relPath, kind = 'path') {
|
|
46
|
+
if (typeof relPath !== 'string' || relPath.length === 0) {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
const posixRelPath = toPosixRelPath(relPath)
|
|
50
|
+
if (kind === 'dir') {
|
|
51
|
+
return IGNORE_MATCHERS.some(match => match(posixRelPath) || match(`${posixRelPath}/__docgen__`))
|
|
52
|
+
}
|
|
53
|
+
return IGNORE_MATCHERS.some(match => match(posixRelPath))
|
|
54
|
+
}
|