@nitra/cursor 12.8.5 → 12.8.7

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 (202) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/bin/n-cursor.js +5 -5
  3. package/package.json +1 -1
  4. package/rules/abie/js/http_route_base.mdc +25 -0
  5. package/rules/abie/js/ua_http_route.mdc +1 -1
  6. package/rules/abie/main.mdc +12 -0
  7. package/rules/adr/js/hooks.mdc +32 -0
  8. package/rules/adr/js/madr_format.mdc +96 -0
  9. package/rules/adr/js/settings_policy.mdc +34 -0
  10. package/rules/adr/main.mdc +13 -95
  11. package/rules/bun/js/bunfig.mdc +12 -0
  12. package/rules/bun/js/layout.mdc +60 -0
  13. package/rules/bun/js/lint.mdc +9 -0
  14. package/rules/bun/js/package_json.mdc +19 -0
  15. package/rules/bun/main.mdc +9 -61
  16. package/rules/capacitor/js/ios_spm.mdc +69 -0
  17. package/rules/capacitor/js/version.mdc +29 -0
  18. package/rules/capacitor/main.mdc +8 -22
  19. package/rules/changelog/js/agent-workflow.mdc +15 -0
  20. package/rules/changelog/js/changelog-format.mdc +33 -0
  21. package/rules/changelog/js/comparison-models.mdc +40 -0
  22. package/rules/changelog/main.mdc +4 -98
  23. package/rules/ci4/js/marksman_config.mdc +31 -0
  24. package/rules/ci4/js/vscode_extensions.mdc +33 -0
  25. package/rules/ci4/main.mdc +14 -14
  26. package/rules/docker/js/compile.mdc +44 -0
  27. package/rules/docker/js/hadolint.mdc +50 -0
  28. package/rules/docker/js/mirror.mdc +13 -0
  29. package/rules/docker/js/multistage.mdc +13 -0
  30. package/rules/docker/js/native-addon.mdc +43 -0
  31. package/rules/docker/js/nginx-tag.mdc +7 -0
  32. package/rules/docker/js/nginx-user.mdc +37 -0
  33. package/rules/docker/js/non-root.mdc +39 -0
  34. package/rules/docker/main.mdc +15 -196
  35. package/rules/ga/js/lint_toolchain.mdc +15 -0
  36. package/rules/ga/js/required_workflows.mdc +35 -0
  37. package/rules/ga/js/vscode.mdc +17 -0
  38. package/rules/ga/js/workflow_common.mdc +108 -0
  39. package/rules/ga/js/workflows.mdc +32 -0
  40. package/rules/ga/js/zizmor.mdc +7 -0
  41. package/rules/ga/main.mdc +17 -125
  42. package/rules/graphql/js/tooling.mdc +13 -0
  43. package/rules/graphql/js/vscode_extensions.mdc +13 -0
  44. package/rules/graphql/main.mdc +3 -22
  45. package/rules/hasura/js/internal_urls.mdc +27 -0
  46. package/rules/hasura/js/migrations.mdc +13 -0
  47. package/rules/hasura/js/svc_hl.mdc +17 -0
  48. package/rules/hasura/main.mdc +8 -30
  49. package/rules/image-avif/js/avif_generation.mdc +26 -0
  50. package/rules/image-avif/js/package_json_optout.mdc +21 -0
  51. package/rules/image-avif/main.mdc +7 -34
  52. package/rules/image-compress/js/package_json.mdc +7 -0
  53. package/rules/image-compress/js/package_setup.mdc +13 -0
  54. package/rules/image-compress/main.mdc +4 -12
  55. package/rules/js/docs/index.md +3 -3
  56. package/rules/js/js/dep-policy.mdc +17 -0
  57. package/rules/js/js/eslint-config.mdc +28 -0
  58. package/rules/js/js/extensions.mdc +8 -0
  59. package/rules/js/js/file-extensions.mdc +12 -0
  60. package/rules/js/js/for-in.mdc +26 -0
  61. package/rules/js/js/jscpd.mdc +42 -0
  62. package/rules/js/js/knip.mdc +15 -0
  63. package/rules/js/js/lint-js-workflow.mdc +58 -0
  64. package/rules/js/js/oxlintrc.mdc +20 -0
  65. package/rules/js/js/package-json.mdc +31 -0
  66. package/rules/js/js/tests.mdc +9 -0
  67. package/rules/js/js/utils-lib-structure.mdc +15 -0
  68. package/rules/js/main.mdc +21 -214
  69. package/rules/js-bun-db/js/bun-sql-migration.mdc +15 -0
  70. package/rules/js-bun-db/js/connection.mdc +42 -0
  71. package/rules/js-bun-db/js/pg-format-identifiers.mdc +102 -0
  72. package/rules/js-bun-db/js/pg-format-shim.mdc +99 -0
  73. package/rules/js-bun-db/js/pg-leftover.mdc +27 -0
  74. package/rules/js-bun-db/js/pg-listen-notify.mdc +51 -0
  75. package/rules/js-bun-db/js/query-safety.mdc +117 -0
  76. package/rules/js-bun-db/js/sql-array.mdc +88 -0
  77. package/rules/js-bun-db/js/unsafe.mdc +65 -0
  78. package/rules/js-bun-db/main.mdc +15 -605
  79. package/rules/js-bun-redis/js/imports.mdc +47 -0
  80. package/rules/js-bun-redis/js/package_json.mdc +44 -0
  81. package/rules/js-bun-redis/main.mdc +3 -11
  82. package/rules/js-mssql/js/mssql-in-list.mdc +38 -0
  83. package/rules/js-mssql/js/mssql-pool.mdc +56 -0
  84. package/rules/js-mssql/js/mssql-query-template.mdc +33 -0
  85. package/rules/js-mssql/js/mssql-tvp.mdc +75 -0
  86. package/rules/js-mssql/js/mssql-version.mdc +7 -0
  87. package/rules/js-mssql/main.mdc +10 -198
  88. package/rules/js-run/js/check-env.mdc +35 -0
  89. package/rules/js-run/js/conn-aliases.mdc +109 -0
  90. package/rules/js-run/js/jsconfig.mdc +20 -0
  91. package/rules/js-run/js/otel-configmap.mdc +6 -0
  92. package/rules/js-run/js/pino.mdc +6 -0
  93. package/rules/js-run/js/project-structure.mdc +11 -0
  94. package/rules/js-run/js/runtime.mdc +14 -0
  95. package/rules/js-run/js/scope.mdc +11 -0
  96. package/rules/js-run/js/settimeout.mdc +11 -0
  97. package/rules/js-run/js/temporal.mdc +5 -0
  98. package/rules/js-run/main.mdc +16 -218
  99. package/rules/k8s/js/configmap.mdc +41 -0
  100. package/rules/k8s/js/deployment_resources.mdc +49 -0
  101. package/rules/k8s/js/hasura_httproute.mdc +91 -0
  102. package/rules/k8s/js/hpa_apiversion.mdc +27 -0
  103. package/rules/k8s/js/ingress_gateway.mdc +16 -0
  104. package/rules/k8s/js/kustomize_structure.mdc +144 -0
  105. package/rules/k8s/js/lint_k8s.mdc +72 -0
  106. package/rules/k8s/js/multidoc_yaml.mdc +5 -0
  107. package/rules/k8s/js/network_policy.mdc +136 -0
  108. package/rules/k8s/js/schema_modeline.mdc +57 -0
  109. package/rules/k8s/js/service.mdc +44 -0
  110. package/rules/k8s/js/topology_hpa_pdb.mdc +181 -0
  111. package/rules/k8s/main.mdc +30 -843
  112. package/rules/nginx-default-tpl/js/dockerfile.mdc +36 -0
  113. package/rules/nginx-default-tpl/js/http-route.mdc +41 -0
  114. package/rules/nginx-default-tpl/js/ini-keys.mdc +21 -0
  115. package/rules/nginx-default-tpl/js/template-structure.mdc +86 -0
  116. package/rules/nginx-default-tpl/js/vscode.mdc +37 -0
  117. package/rules/nginx-default-tpl/main.mdc +6 -112
  118. package/rules/npm-module/js/docs/index.md +5 -5
  119. package/rules/npm-module/js/docs/rule_meta.md +6 -6
  120. package/rules/npm-module/js/docs/skill_meta.md +8 -8
  121. package/rules/npm-module/js/header_doc_pointer.mdc +18 -0
  122. package/rules/npm-module/js/package_structure.mdc +62 -0
  123. package/rules/npm-module/js/rule_meta.mdc +11 -0
  124. package/rules/npm-module/js/skill_meta.mdc +11 -0
  125. package/rules/npm-module/main.mdc +10 -55
  126. package/rules/php/js/lint_php_yml.mdc +12 -0
  127. package/rules/php/js/tooling.mdc +66 -0
  128. package/rules/php/main.mdc +7 -66
  129. package/rules/python/js/lint_python_yml.mdc +23 -0
  130. package/rules/python/js/pyproject_toml.mdc +32 -0
  131. package/rules/python/js/tooling.mdc +23 -0
  132. package/rules/python/main.mdc +9 -33
  133. package/rules/rego/js/rego-lint.mdc +31 -0
  134. package/rules/rego/js/vscode_extensions.mdc +11 -0
  135. package/rules/rego/js/vscode_settings.mdc +13 -0
  136. package/rules/rego/main.mdc +8 -24
  137. package/rules/rust/js/coverage.mdc +28 -0
  138. package/rules/rust/js/lint.mdc +22 -0
  139. package/rules/rust/js/tauri_composition.mdc +8 -0
  140. package/rules/rust/js/vscode_extensions.mdc +12 -0
  141. package/rules/rust/main.mdc +8 -38
  142. package/rules/security/js/rego_policies.mdc +15 -0
  143. package/rules/security/js/sample_secret.mdc +19 -0
  144. package/rules/security/js/trufflehog.mdc +21 -0
  145. package/rules/security/main.mdc +7 -35
  146. package/rules/style/js/admin-table.mdc +88 -0
  147. package/rules/style/js/colors.mdc +21 -0
  148. package/rules/style/js/gap.mdc +22 -0
  149. package/rules/style/js/quasar-fixes.mdc +32 -0
  150. package/rules/style/js/quasar.mdc +7 -0
  151. package/rules/style/js/tooling.mdc +85 -0
  152. package/rules/style/main.mdc +13 -253
  153. package/rules/tauri/js/cargo_mutants_config.mdc +39 -0
  154. package/rules/tauri/js/tool_surface.mdc +21 -0
  155. package/rules/tauri/js/tooling.mdc +25 -0
  156. package/rules/tauri/main.mdc +8 -78
  157. package/rules/test/js/cargo_mutants_config.mdc +18 -0
  158. package/rules/test/js/docs/index.md +7 -7
  159. package/rules/test/js/location.mdc +52 -0
  160. package/rules/test/js/no-console-store-restore.mdc +11 -0
  161. package/rules/test/js/no-process-chdir.mdc +15 -0
  162. package/rules/test/js/no-relative-fs-path.mdc +22 -0
  163. package/rules/test/js/sandbox-aware-test.mdc +28 -0
  164. package/rules/test/js/stryker_config.mdc +26 -0
  165. package/rules/test/js/vitest-config-pool-forks.mdc +33 -0
  166. package/rules/test/main.mdc +18 -184
  167. package/rules/text/js/ci-lint-text.mdc +15 -0
  168. package/rules/text/js/cspell.mdc +81 -0
  169. package/rules/text/js/dotenv-linter.mdc +16 -0
  170. package/rules/text/js/forbidden-prettier.mdc +13 -0
  171. package/rules/text/js/markdownlint.mdc +25 -0
  172. package/rules/text/js/oxfmt.mdc +35 -0
  173. package/rules/text/js/package-json.mdc +26 -0
  174. package/rules/text/js/shellcheck.mdc +18 -0
  175. package/rules/text/js/v8r.mdc +23 -0
  176. package/rules/text/js/vscode.mdc +86 -0
  177. package/rules/text/main.mdc +20 -237
  178. package/rules/vue/js/composition-api.mdc +82 -0
  179. package/rules/vue/js/nheader-layout.mdc +171 -0
  180. package/rules/vue/js/node-imports.mdc +25 -0
  181. package/rules/vue/js/quasar-ui.mdc +32 -0
  182. package/rules/vue/js/structure.mdc +101 -0
  183. package/rules/vue/js/testing.mdc +32 -0
  184. package/rules/vue/js/tfm-translations.mdc +26 -0
  185. package/rules/vue/js/vite-config.mdc +126 -0
  186. package/rules/vue/js/vite-env.mdc +55 -0
  187. package/rules/vue/js/vue-imports.mdc +25 -0
  188. package/rules/vue/main.mdc +16 -640
  189. package/scripts/auto-rules.mjs +6 -6
  190. package/scripts/auto-skills.mjs +3 -3
  191. package/scripts/docs/auto-rules.md +17 -31
  192. package/scripts/docs/auto-skills.md +18 -163
  193. package/scripts/docs/index.md +16 -16
  194. package/scripts/lib/docs/index.md +36 -36
  195. package/scripts/lib/docs/mirror-parity.md +7 -7
  196. package/scripts/lib/docs/rule-meta.md +12 -12
  197. package/scripts/lib/docs/skill-meta.md +9 -9
  198. package/scripts/lib/docs/worktree-notice.md +10 -8
  199. package/scripts/lib/rule-meta.mjs +6 -6
  200. package/scripts/lib/skill-meta.mjs +6 -6
  201. package/scripts/lib/worktree-notice.mjs +2 -2
  202. package/scripts/utils/docs/index.md +14 -14
@@ -0,0 +1,69 @@
1
+ ## iOS: лише SPM, виняток через Podfile
2
+
3
+ ### Правило за замовчуванням
4
+
5
+ Не залишай `Podfile` (поза `Pods/`) у вихідному iOS-шарі, **якщо** уся потрібна
6
+ iOS-функціональність (нативні плагіни/модулі) може працювати **лише** через **SPM** (Swift Package Manager).
7
+
8
+ Перевірка рекурсивно шукає `Podfile` у `ios/`, пропускаючи `Pods/`, `build/`, `DerivedData/`.
9
+
10
+ ### Плагіни @nitra/
11
+
12
+ Плагіни зі скоупу `@nitra/` за політикою **підтримують SPM** — перевіряти їх на SPM **не потрібно**
13
+ (check не обходить `package.json` на предмет `@nitra/`).
14
+
15
+ ### Коли Podfile дозволений
16
+
17
+ Якщо не вся потрібна iOS-функціональність поза `@nitra/` (сторонні Capacitor-плагіни, інша
18
+ нативна залежність) доступна через SPM — `Podfile` дозволяється, але це **обов'язково** треба
19
+ явно задати в кореневому **`package.json`** або в **`capacitor.config.json` / `capacitor.config.ts` / `capacitor.config.mjs`**:
20
+
21
+ - **`"iosCocoaPodsBecausePluginsLackSpm": true`** — семантика: не вся потрібна нативна частина
22
+ поза `@nitra/` на SPM; `@nitra/` у це не входить;
23
+ - або **`"iosCocoaPodsAllowed": true`** — короткий alias для того самого винятку.
24
+
25
+ Без одного з цих прапорів `true` наявний `Podfile` поза `Pods/` вважається порушенням правила «лише SPM».
26
+
27
+ **Де задати виняток у `package.json`:**
28
+
29
+ ```json
30
+ {
31
+ "nitra": {
32
+ "iosCocoaPodsBecausePluginsLackSpm": true
33
+ }
34
+ }
35
+ ```
36
+
37
+ або коротший alias:
38
+
39
+ ```json
40
+ {
41
+ "nitra": {
42
+ "iosCocoaPodsAllowed": true
43
+ }
44
+ }
45
+ ```
46
+
47
+ **Де задати виняток у `capacitor.config.json`:**
48
+
49
+ ```json
50
+ {
51
+ "nitra": {
52
+ "iosCocoaPodsAllowed": true
53
+ }
54
+ }
55
+ ```
56
+
57
+ **Де задати виняток у `capacitor.config.ts` / `capacitor.config.mjs`:**
58
+
59
+ ```ts
60
+ const config = {
61
+ // ...
62
+ nitra: {
63
+ iosCocoaPodsAllowed: true,
64
+ },
65
+ }
66
+ ```
67
+
68
+ Перевірка читає **лише** кореневі файли: `package.json`, потім capacitor-конфіги у корені.
69
+ У `.ts` / `.mjs`: шукається блок `nitra { ... }` і на його тілі перевіряються ці boolean-поля.
@@ -0,0 +1,29 @@
1
+ ## Версія Capacitor
2
+
3
+ У `package.json` (у **корені** репозиторію чи **workspace**-пакеті) оголошення **`@capacitor/core`**
4
+ має вказувати діапазон, **сумісний лише з мажорною версією 8 і вище** (наприклад `^8.0.0`).
5
+ **`*`**, `latest` і діапазони, де можлива 7-мажор, — неприйнятні.
6
+
7
+ Перевірка обходить усі `package.json` у дереві (крім `node_modules`, `.git`, `dist`, `coverage`,
8
+ `Pods`, `.turbo`, `.next`, `build`) і перевіряє нижню межу діапазону через `capacitorVersionRangeMinMajor`.
9
+ Підтримуються `||`-частини, hyphen-range (`7 - 9`), `^`, `~`, `>=`, `>`, `=`, bare-version;
10
+ `workspace:*` / `*` / `x` / `latest` — пропускаються (не блокують).
11
+
12
+ **Приклади допустимих діапазонів:**
13
+
14
+ ```
15
+ "@capacitor/core": "^8.0.0"
16
+ "@capacitor/core": ">=8"
17
+ "@capacitor/core": "8.x"
18
+ "@capacitor/core": "workspace:*"
19
+ ```
20
+
21
+ **Приклади неприйнятних діапазонів:**
22
+
23
+ ```
24
+ "@capacitor/core": "^7.0.0" // мажор 7 — занадто старий
25
+ "@capacitor/core": "*" // будь-яка — неприйнятна
26
+ "@capacitor/core": "latest" // неприйнятна
27
+ "@capacitor/core": ">=6" // нижня межа 6 — занадто старий
28
+ "@capacitor/core": "6 - 9" // hyphen-range: нижня межа 6 — неприйнятна
29
+ ```
@@ -5,30 +5,16 @@ alwaysApply: false
5
5
  version: '1.1'
6
6
  ---
7
7
 
8
- ## Версія Capacitor
8
+ Правило перевіряє версію `@capacitor/core` у `package.json` та коректність використання CocoaPods/SPM у iOS-шарі Capacitor-проєкту.
9
9
 
10
- У `package.json` (у **корені** репозиторію чи **workspace**-пакеті) оголошення **`@capacitor/core`**
11
- має вказувати діапазон, **сумісний лише з мажорною версією 8 і вище** (наприклад `^8.0.0`).
12
- **`*`**, `latest` і діапазони, де можлива 7-мажор, — неприйнятні.
10
+ [capacitor-version](./js/version.mdc)
13
11
 
14
- ## iOS: зазвичай лише SPM, виняток Podfile
12
+ [capacitor-ios-spm](./js/ios_spm.mdc)
15
13
 
16
- - **Правило за замовчуванням:** не залишай `Podfile` (поза `Pods/`) у вихідному iOS-шарі, **якщо** уся потрібна
17
- iOS-функціональність (нативні плагіни/модулі) може працювати **лише** через **SPM** (Swift Package Manager).
14
+ ## Швидкий gate через conftest
18
15
 
19
- - **Плагіни зі скоупу @nitra/:** за політикою вони **підтримуюють SPM**; **перевіряти** їх на **SPM** **не
20
- потрібно** (і **check** цього **не** робить — **немає** обходу `package.json` на предмет **@nitra/**).
16
+ | namespace | що перевіряє |
17
+ |---|---|
18
+ | `capacitor.package_json` | версія `@capacitor/core` у `dependencies` — мінімум major 8 (спрощений JS-порт для одиничного `package.json`) |
21
19
 
22
- - **Коли `Podfile` дозволений:** якщо **не** вся потрібна iOS-функціональність **поза** **@nitra/** (сторонні
23
- **Capacitor**-плагіни, інша нативна залежність) **доступна** через **SPM** — `Podfile` **дозволяється**,
24
- але це **обов’язково** треба явно задати в кореневому **`package.json`** або в
25
- **`capacitor.config.json` / `capacitor.config.ts` / `capacitor.config.mjs`**
26
-
27
- - **`"iosCocoaPodsBecausePluginsLackSpm": true`** (семантика: **не** **вся** потрібна нативна частина
28
- **поза** **@nitra/** **на** **SPM**; **@nitra/** у це **не** входить);
29
- - або **`"iosCocoaPodsAllowed": true`** (короткий **alias** для того самого **винятку**);
30
-
31
- Без **одного** з цих прапорів `true` наявний **Podfile** поза **`Pods/`** вважається **порушенням** правила **«лише** **SPM**».
32
-
33
- - Перевірка читає **лише** кореневі файли: **`package.json`**, потім **capacitor-конфіги** у **корені** (див. вище).
34
- У **`.ts` / `.mjs`**: шукається блок **nitra** `{ ... }` і **на його тілі** перевіряються ці **boolean**-поля.
20
+ JS-перевірка (`platforms.mjs`) authoritative: підтримує `||`-діапазони, hyphen-range, workspace-monorepo та iOS-специфічну логіку (Podfile).
@@ -0,0 +1,15 @@
1
+ ## STOP — перед завершенням відповіді агента
2
+
3
+ > **Якщо в цій сесії ти змінив(ла) файли в пакетному workspace** (код, rego, правила, скіли, скрипти, конфіги, тести — **не** лише `docs/` / `doc/`) — **не завершуй задачу**, поки не виконаєш **усі три** кроки нижче в **тому ж** наборі змін. Це не «опційно після синку» — це частина PR.
4
+
5
+ 1. **Поклади change-файл** `<ws>/.changes/YYMMDD-HHMM.md` з frontmatter `bump:` (`major|minor|patch`) + `section:` (`Added|Changed|Fixed|Removed`) і текстом опису. Команда: `npx @nitra/cursor change --bump <…> --section <…> --message "<…>" [--ws <шлях>]`. Якщо файл за ту саму хвилину вже існує, CLI атомарно створює `YYMMDD-HHMM-2.md`, потім `-3` тощо.
6
+ 2. **Ніколи** не редагуй `version` і `CHANGELOG.md` вручну — навіть для hotfix. Єдиний артефакт зміни — change-файл; `version`/CHANGELOG формує `n-cursor release` у CI на `main` (агрегує change-файли, ставить git-тег `<name>@<version>`). Будь-яка зміна `version` поза CI (drift від бази чи опублікованої) завалює `check changelog` — навіть якщо поряд є change-файл.
7
+ 3. **`npx @nitra/cursor fix changelog`** → exit **`0`** (достатньо наявності change-файлу; `version` лишається незмінним).
8
+
9
+ **Це правило релевантне завжди після будь-якої зміни** коду/конфігу/тестів/воркспейсу — **не** класифікуй його як «лише для релізу». Уже запущені тести чи білд **не** знімають вимогу changelog. Операційно перед фінальною відповіддю: `git status --short` → зіставити змінені шляхи з workspace-ами → change-файл для кожного релевантного workspace → `npx @nitra/cursor fix changelog` (exit `0`). Якщо у фінальній відповіді не можеш заповнити рядок `Changelog: …`, отже ти пропустив(ла) цей крок.
10
+
11
+ **Тригер шляхів (приклади):** `npm/**`, `packages/foo/**`, будь-який каталог з власним `package.json` / `pyproject.toml`, куди потрапили правки.
12
+
13
+ **Інверсія (change-файл не потрібен):** лише `docs/` / `doc/`; синхронізований із `@nitra/cursor` інструментарій (`.cursor/`, `.claude/`); лише `.gitignore`. **Корінь монорепо** (воркспейс `.` за наявності підпакетів) не перевіряється взагалі — отже й кореневі `AGENTS.md` / `CLAUDE.md` та bump `@nitra/cursor` у `devDependencies`. Окремого «релізного кроку» у feature-флоу немає — `version`/`CHANGELOG.md` змінює лише CI.
14
+
15
+ **Pre-commit (людина):** `hk` у цьому репо запускає `fix changelog` при змінах під `npm/**` в **autofix-режимі** (env `N_CURSOR_CHANGELOG_AUTOFIX=1`): за відсутності change-файлу хук **сам** його створює (дефолти `patch`/`Changed`, опис = subject останнього коміту) і ставить у git-індекс, тож коміт не падає. Це лише підстраховка — агент не покладайся на неї: виконуй кроки 1–3 **до** фінальної відповіді, бо автоген ставить осмислений опис лише випадково (subject попереднього коміту), і `bump`/`section`/текст майже завжди треба відредагувати. Autofix-режим також **не робить мережевих викликів** (`npm view` / PyPI fetch пропускаються) — реєстрова drift-перевірка `version` у хуці не виконується (її ловить CI / ручний `fix`). Поза хуком (CI, ручний `fix`/`check`) autofix вимкнено — поведінка лишається fail-on-missing з повною drift-перевіркою.
@@ -0,0 +1,33 @@
1
+ ## Формат CHANGELOG.md
2
+
3
+ [Keep a Changelog 1.1.0](https://keepachangelog.com/uk/1.1.0/), мова — українська, новіші версії зверху.
4
+
5
+ ```md title="<ws>/CHANGELOG.md"
6
+ # Changelog
7
+
8
+ Усі помітні зміни цього пакета документуються тут.
9
+
10
+ Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
11
+
12
+ ## [1.2.3] - 2026-05-05
13
+
14
+ ### Added
15
+
16
+ - ...
17
+
18
+ ### Changed
19
+
20
+ - ...
21
+
22
+ ### Fixed
23
+
24
+ - ...
25
+ ```
26
+
27
+ Секції — підмножина `### Added`, `### Changed`, `### Fixed`, `### Removed` (одна або кілька).
28
+
29
+ ## Post-release інваріант (гарантує CI)
30
+
31
+ Перша (верхня) секція `## [version]` у `CHANGELOG.md` дорівнює полю `version` у маніфесті — але це **post-release** твердження, яке забезпечує `n-cursor release` у CI, агрегуючи change-файли (bump `version` + генерація секції + git-тег `<name>@<version>`). **Локально цю рівність руками не підтримують**: у feature-флоу `version`/`CHANGELOG.md` не чіпають, тож верхня секція може відставати від майбутньої версії — це нормально. Drift `version` поза CI (vs реєстр / vs git-база) ловить `check changelog` як заборонений ручний bump.
32
+
33
+ Інструкції щодо bump `version` і редагування `CHANGELOG.md` живуть **лише** в правилі `changelog` — джерелі істини. Інші правила (зокрема `n-npm-module.mdc`) їй підпорядковані й власних інструкцій bump/CHANGELOG не дублюють.
@@ -0,0 +1,40 @@
1
+ ## Дві моделі бази порівняння
2
+
3
+ Режим визначається автоматично з маніфесту.
4
+
5
+ ### registry-published (npm / PyPI)
6
+
7
+ **npm:** непорожнє `name`, не `private: true`, масив `files`.
8
+
9
+ **Python:** статичні `project.name` і `project.version` у `pyproject.toml` (або Poetry-секція).
10
+
11
+ 1. **Локальна `version` ≠ опублікованій** (npm / PyPI): drift поза CI → **fail** (ручний bump заборонено; навіть із change-файлом). Відкоти `version`.
12
+ 2. **Версії збігаються**, але в git є **релевантні** зміни без change-файлу → fail. Для npm `"CHANGELOG.md"` має бути в `files` (публікується разом із пакетом).
13
+ 3. **Реєстр недосяжний** — fail-safe pass.
14
+ 4. **Немає релевантних змін** — pass.
15
+
16
+ ### local-only
17
+
18
+ **npm:** `private: true` або без `files`. **Python:** без пари name+version для реєстру. База залежить від гілки:
19
+
20
+ 1. На **`dev`** local-only не активний (крім незакомічених registry-published).
21
+ 2. На **`main`** — diff від **`origin/main`** (попередній опублікований `main`); без remote — від `HEAD~1`. **`dev` не використовується** як база на `main`.
22
+ 3. На **feature-гілці** — `merge-base` з **`dev`**, якщо є; інакше з **`main`** (репо без `dev`).
23
+ 4. Drift `version` від бази → **fail** (ручний bump заборонено). Зміни фіксуй change-файлом; bump зробить CI.
24
+
25
+ Якщо немає git або немає `dev`/`main`/`origin/main` — local-only пропускається.
26
+
27
+ ## Чеклист агента (деталі)
28
+
29
+ Повний алгоритм — у блоці **STOP** (changelog-agent-workflow); тут лише уточнення.
30
+
31
+ **Інверсія (за замовчуванням не вимагають change-файлу):**
32
+
33
+ - зміни **лише** під `docs/` або `doc/`;
34
+ - синхронізований із `@nitra/cursor` інструментарій під `.cursor/` (канонічні правила й скіли) і `.claude/` (ADR-хуки) — це дзеркало tooling-пакета, а не логіка воркспейсу;
35
+ - будь-які зміни в **корені монорепо** (воркспейс `.` за наявності підпакетів) — корінь веде glue/конфіг/tooling, власного CHANGELOG не має; помітні зміни документують підпакети. Сюди потрапляють і кореневі `AGENTS.md` / `CLAUDE.md`, і bump `@nitra/cursor` у `devDependencies`;
36
+ - файли під **`.gitignore`**.
37
+
38
+ **Вимагають change-файл** — усі інші зміни в каталозі workspace (код, rego, правила, скіли, конфіги, тести тощо). Виняток `.cursor/` / `.claude/` **не** поширюється на джерело правил у репо `@nitra/cursor` — воно лежить під `npm/`, тож зміни в ньому далі вимагають change-файлу.
39
+
40
+ Перевірка програмна (`changelog/js/consistency.mjs`).
@@ -4,104 +4,10 @@ version: '3.4'
4
4
  alwaysApply: true
5
5
  ---
6
6
 
7
- ## STOPперед завершенням відповіді агента
7
+ У кожному **пакетному** workspace (каталог із `package.json` або `pyproject.toml`) має бути власний **`CHANGELOG.md`**. Спільного на репозиторій змісту змін **не існує** кожен пакет веде свій. Маніфест версії: **JS/Bun/npm** — `package.json` (`version`); **Python** — `pyproject.toml` (`[project].version` або `[tool.poetry].version`).
8
8
 
9
- > **Якщо в цій сесії ти змінив(ла) файли в пакетному workspace** (код, rego, правила, скіли, скрипти, конфіги, тести — **не** лише `docs/` / `doc/`) — **не завершуй задачу**, поки не виконаєш **усі три** кроки нижче в **тому ж** наборі змін. Це не «опційно після синку» — це частина PR.
9
+ [changelog-agent-workflow](./js/agent-workflow.mdc)
10
10
 
11
- 1. **Поклади change-файл** `<ws>/.changes/YYMMDD-HHMM.md` з frontmatter `bump:` (`major|minor|patch`) + `section:` (`Added|Changed|Fixed|Removed`) і текстом опису. Команда: `npx @nitra/cursor change --bump <…> --section <…> --message "<…>" [--ws <шлях>]`. Якщо файл за ту саму хвилину вже існує, CLI атомарно створює `YYMMDD-HHMM-2.md`, потім `-3` тощо.
12
- 2. **Ніколи** не редагуй `version` і `CHANGELOG.md` вручну — навіть для hotfix. Єдиний артефакт зміни — change-файл; `version`/CHANGELOG формує `n-cursor release` у CI на `main` (агрегує change-файли, ставить git-тег `<name>@<version>`). Будь-яка зміна `version` поза CI (drift від бази чи опублікованої) завалює `check changelog` — навіть якщо поряд є change-файл.
13
- 3. **`npx @nitra/cursor fix changelog`** → exit **`0`** (достатньо наявності change-файлу; `version` лишається незмінним).
11
+ [changelog-comparison-models](./js/comparison-models.mdc)
14
12
 
15
- **Це правило релевантне завжди після будь-якої зміни** коду/конфігу/тестів/воркспейсу — **не** класифікуй його як «лише для релізу». Уже запущені тести чи білд **не** знімають вимогу changelog. Операційно перед фінальною відповіддю: `git status --short` → зіставити змінені шляхи з workspace-ами → change-файл для кожного релевантного workspace → `npx @nitra/cursor fix changelog` (exit `0`). Якщо у фінальній відповіді не можеш заповнити рядок `Changelog: …`, отже ти пропустив(ла) цей крок.
16
-
17
- **Тригер шляхів (приклади):** `npm/**`, `packages/foo/**`, будь-який каталог з власним `package.json` / `pyproject.toml`, куди потрапили правки.
18
-
19
- **Інверсія (change-файл не потрібен):** лише `docs/` / `doc/`; синхронізований із `@nitra/cursor` інструментарій (`.cursor/`, `.claude/`); лише `.gitignore`. **Корінь монорепо** (воркспейс `.` за наявності підпакетів) не перевіряється взагалі — отже й кореневі `AGENTS.md` / `CLAUDE.md` та bump `@nitra/cursor` у `devDependencies`. Окремого «релізного кроку» у feature-флоу немає — `version`/`CHANGELOG.md` змінює лише CI.
20
-
21
- **Pre-commit (людина):** `hk` у цьому репо запускає `fix changelog` при змінах під `npm/**` в **autofix-режимі** (env `N_CURSOR_CHANGELOG_AUTOFIX=1`): за відсутності change-файлу хук **сам** його створює (дефолти `patch`/`Changed`, опис = subject останнього коміту) і ставить у git-індекс, тож коміт не падає. Це лише підстраховка — агент не покладайся на неї: виконуй кроки 1–3 **до** фінальної відповіді, бо автоген ставить осмислений опис лише випадково (subject попереднього коміту), і `bump`/`section`/текст майже завжди треба відредагувати. Autofix-режим також **не робить мережевих викликів** (`npm view` / PyPI fetch пропускаються) — реєстрова drift-перевірка `version` у хуці не виконується (її ловить CI / ручний `fix`). Поза хуком (CI, ручний `fix`/`check`) autofix вимкнено — поведінка лишається fail-on-missing з повною drift-перевіркою.
22
-
23
- ---
24
-
25
- У кожному **пакетному** workspace (каталог із `package.json` або `pyproject.toml`) має бути власний **`CHANGELOG.md`**. Спільного на репозиторій змісту змін **не існує** — кожен пакет веде свій.
26
-
27
- **Маніфест версії:**
28
-
29
- - **JS / Bun / npm** — `<ws>/package.json` (`version`, для монорепо ще `workspaces` у кореневому `package.json`).
30
- - **Python** — `<ws>/pyproject.toml`: `[project].name` / `[project].version` (PEP 621) або `[tool.poetry].name` / `[tool.poetry].version`.
31
-
32
- Каталоги лише з `pyproject.toml` (без `package.json`) теж враховуються; `node_modules/`, `.venv/`, `venv/` при пошуку ігноруються.
33
-
34
- ## Чеклист агента (деталі)
35
-
36
- Повний алгоритм — у блоці **STOP** вище; тут лише уточнення.
37
-
38
- **Інверсія (за замовчуванням не вимагають change-файлу):**
39
-
40
- - зміни **лише** під `docs/` або `doc/`;
41
- - синхронізований із `@nitra/cursor` інструментарій під `.cursor/` (канонічні правила та скіли) і `.claude/` (ADR-хуки) — це дзеркало tooling-пакета, а не логіка воркспейсу;
42
- - будь-які зміни в **корені монорепо** (воркспейс `.` за наявності підпакетів) — корінь веде glue/конфіг/tooling, власного CHANGELOG не має; помітні зміни документують підпакети. Сюди потрапляють і кореневі `AGENTS.md` / `CLAUDE.md`, і bump `@nitra/cursor` у `devDependencies`;
43
- - файли під **`.gitignore`**.
44
-
45
- **Вимагають change-файл** — усі інші зміни в каталозі workspace (код, rego, правила, скіли, конфіги, тести тощо). Виняток `.cursor/` / `.claude/` **не** поширюється на джерело правил у репо `@nitra/cursor` — воно лежить під `npm/`, тож зміни в ньому далі вимагають change-файлу.
46
-
47
- Перевірка програмна (`changelog/js/consistency.mjs`).
48
-
49
- ## Дві моделі бази порівняння
50
-
51
- Режим визначається автоматично з маніфесту.
52
-
53
- ### registry-published (npm / PyPI)
54
-
55
- **npm:** непорожнє `name`, не `private: true`, масив `files`.
56
-
57
- **Python:** статичні `project.name` і `project.version` у `pyproject.toml` (або Poetry-секція).
58
-
59
- 1. **Локальна `version` ≠ опублікованій** (npm / PyPI): drift поза CI → **fail** (ручний bump заборонено; навіть із change-файлом). Відкоти `version`.
60
- 2. **Версії збігаються**, але в git є **релевантні** зміни без change-файлу → fail. Для npm `"CHANGELOG.md"` має бути в `files` (публікується разом із пакетом).
61
- 3. **Реєстр недосяжний** — fail-safe pass.
62
- 4. **Немає релевантних змін** — pass.
63
-
64
- ### local-only
65
-
66
- **npm:** `private: true` або без `files`. **Python:** без пари name+version для реєстру. База залежить від гілки:
67
-
68
- 1. На **`dev`** local-only не активний (крім незакомічених registry-published).
69
- 2. На **`main`** — diff від **`origin/main`** (попередній опублікований `main`); без remote — від `HEAD~1`. **`dev` не використовується** як база на `main`.
70
- 3. На **feature-гілці** — `merge-base` з **`dev`**, якщо є; інакше з **`main`** (репо без `dev`).
71
- 4. Drift `version` від бази → **fail** (ручний bump заборонено). Зміни фіксуй change-файлом; bump зробить CI.
72
-
73
- Якщо немає git або немає `dev`/`main`/`origin/main` — local-only пропускається.
74
-
75
- ## Формат CHANGELOG.md
76
-
77
- [Keep a Changelog 1.1.0](https://keepachangelog.com/uk/1.1.0/), мова — українська, новіші версії зверху.
78
-
79
- ```md title="<ws>/CHANGELOG.md"
80
- # Changelog
81
-
82
- Усі помітні зміни цього пакета документуються тут.
83
-
84
- Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
85
-
86
- ## [1.2.3] - 2026-05-05
87
-
88
- ### Added
89
-
90
- - ...
91
-
92
- ### Changed
93
-
94
- - ...
95
-
96
- ### Fixed
97
-
98
- - ...
99
- ```
100
-
101
- Секції — підмножина `### Added`, `### Changed`, `### Fixed`, `### Removed` (одна або кілька).
102
-
103
- ## Post-release інваріант (гарантує CI)
104
-
105
- Перша (верхня) секція `## [version]` у `CHANGELOG.md` дорівнює полю `version` у маніфесті — але це **post-release** твердження, яке забезпечує `n-cursor release` у CI, агрегуючи change-файли (bump `version` + генерація секції + git-тег `<name>@<version>`). **Локально цю рівність руками не підтримують**: у feature-флоу `version`/`CHANGELOG.md` не чіпають, тож верхня секція може відставати від майбутньої версії — це нормально. Drift `version` поза CI (vs реєстр / vs git-база) ловить `check changelog` як заборонений ручний bump.
106
-
107
- Інструкції щодо bump `version` і редагування `CHANGELOG.md` живуть **лише** в цьому правилі — джерелі істини. Інші правила (зокрема `n-npm-module.mdc`) їй підпорядковані й власних інструкцій bump/CHANGELOG не дублюють.
13
+ [changelog-format](./js/changelog-format.mdc)
@@ -0,0 +1,31 @@
1
+ ## Конфіг marksman LSP (`.marksman.toml`)
2
+
3
+ Правило перевіряє наявність `.marksman.toml` у корені проєкту. Якщо файл відсутній — автоматично створює його з canonical baseline, що постачається разом із пакетом (`js/data/marksman_config/marksman.baseline.toml`). Повторні прогони ідемпотентні: існуючий файл не перетирається, ручні правки зберігаються.
4
+
5
+ ### Baseline-конфіг
6
+
7
+ Canonical baseline ([data/marksman_config/marksman.baseline.toml](./data/marksman_config/marksman.baseline.toml)) містить такі налаштування:
8
+
9
+ ```toml
10
+ [core]
11
+ markdown.file_extensions = ["md", "markdown"]
12
+ markdown.glfm = true # GitHub-Flavored Markdown (таблиці, todo, alerts)
13
+
14
+ [completion]
15
+ wiki.style = "file-stem" # [[oidc-pkce-flow]] → docs/adr/oidc-pkce-flow.md
16
+
17
+ [code_action]
18
+ toc.enable = true # «Insert/Update TOC» для довгих arc42-сторінок
19
+ ```
20
+
21
+ **Чому `file-stem`?** ADR-slug збігається з іменем файлу — стабільний ідентифікатор у AUTOGEN `sources`, `manifest.json` і валідаторі. Якщо заголовок ADR змінюється, посилання `[[wiki-link]]` не ламається. Без явного конфіга marksman використовує `title-slug-ref` і вимкнений GLFM — частина задокументованої навігації працювала б інакше.
22
+
23
+ ### Поведінка перевірки
24
+
25
+ | Стан | Результат |
26
+ | ----------------------------- | -------------------------------------------------- |
27
+ | baseline-файл пакета відсутній | `fail` — перевстанови `@nitra/cursor` |
28
+ | `.marksman.toml` вже існує | `pass` — файл не чіпається |
29
+ | `.marksman.toml` відсутній | створює файл з baseline, `pass` |
30
+
31
+ Перевірка запускається як `check`-поверхня; `lint`-поверхні правило не має.
@@ -0,0 +1,33 @@
1
+ ## Рекомендовані розширення VSCode (`.vscode/extensions.json`)
2
+
3
+ Правило перевіряє, що `.vscode/extensions.json` містить усі обовʼязкові записи з канонічного шаблону. Перевірка реалізована через Rego (пакет `ci4.vscode_extensions`) і запускається `conftest` у фазі `check`.
4
+
5
+ ### Канонічний шаблон
6
+
7
+ Канон надходить через `--data` як `{ "template": { "snippet": ... } }`. Поточний snippet ([policy/vscode_extensions/template/extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)):
8
+
9
+ ```json
10
+ {
11
+ "recommendations": ["arr.marksman"]
12
+ }
13
+ ```
14
+
15
+ `arr.marksman` — офіційне VSCode-розширення marksman LSP. Дає ту саму навігацію `cmd+click` / `[[wiki-link]]` / find-references / refactor-перейменування, що й Zed built-in marksman, для контрибʼюторів поза Zed.
16
+
17
+ ### Rego-логіка
18
+
19
+ Для кожного запису з `data.template.snippet.recommendations` перевіряє, що він присутній у `input.recommendations`. Якщо запис відсутній — deny з повідомленням:
20
+
21
+ ```
22
+ .vscode/extensions.json: recommendations має містити "arr.marksman" (ci4.mdc)
23
+ ```
24
+
25
+ ### Приклад коректного файлу
26
+
27
+ ```json title=".vscode/extensions.json"
28
+ {
29
+ "recommendations": ["arr.marksman"]
30
+ }
31
+ ```
32
+
33
+ Інші розширення в масиві `recommendations` дозволені — перевірка лише на наявність обовʼязкових, без виключення додаткових.
@@ -4,9 +4,7 @@ alwaysApply: true
4
4
  version: '3.2'
5
5
  ---
6
6
 
7
- Архітектурна документація проєкту живе у Markdown поряд із кодом. Це не довідник «для людей із порталу архітектора» — це **джерело істини**, з якого LLM-агент і людина читають намір системи перед будь-якою зміною коду. Тому правила нижче — не оформлення, а робочий процес: який стек використовуємо, як зберігаємо рішення, як автоматично перегенеровуємо проекції з ADR і як рендеримо для змішаної аудиторії (менеджери + інженери + ops).
8
-
9
- Формат самих ADR-файлів — MADR v4.0.0 minimal — описаний у правилі **`adr`** (`npm/rules/adr/adr.mdc`). Тут описано, як ADR використовуються як вхід для архітектурних проекцій, а не як вони створюються.
7
+ Архітектурна документація проєкту живе у Markdown поряд із кодом — це **джерело істини**, з якого LLM-агент і людина читають намір системи перед будь-якою зміною коду. Правила нижче — не оформлення, а робочий процес: який стек використовуємо, як зберігаємо рішення, як автоматично перегенеровуємо проекції з ADR і як рендеримо для змішаної аудиторії (менеджери + інженери + ops). Формат самих ADR-файлів — MADR v4.0.0 minimal — описаний у правилі **`adr`** (`npm/rules/adr/adr.mdc`).
10
8
 
11
9
  ## Markdown як джерело істини
12
10
 
@@ -208,17 +206,7 @@ User Service відповідає за автентифікацію та про
208
206
  }
209
207
  ```
210
208
 
211
- **`.marksman.toml`** у корені проєкту авто-створюється правилом ci4 при першому `npx @nitra/cursor fix ci4` із canonical baseline ([data/marksman_config/marksman.baseline.toml](./js/data/marksman_config/marksman.baseline.toml)). Ключові опції`markdown.glfm = true` (потрібен для GFM-alerts/таблиць/todo з portable subset), `[completion] wiki.style = "file-stem"` (ADR slug == ім'я файла, стабільний ідентифікатор у AUTOGEN `sources`/manifest/валідаторі — заголовок змінюється, посилання не ламається), `[code_action] toc.enable = true` (TOC code action для довгих arc42-сторінок). Без явного конфіга marksman використовує `title-slug-ref` і вимкнений GLFM — частина задокументованої навігації працювала б інакше. Ручні правки конфіга не перетираються — `ensureBaselineFile` ідемпотентний.
212
-
213
- **VSCode-альтернатива.** Контрибʼютори, що працюють у VSCode/Cursor замість Zed, отримують той самий шар навігації через офіційне розширення marksman LSP. Канонічний запис у `.vscode/extensions.json`:
214
-
215
- ```json title=".vscode/extensions.json"
216
- {
217
- "recommendations": ["arr.marksman"]
218
- }
219
- ```
220
-
221
- Канон `recommendations` (substring requirement): [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json). Інші marksman-сумісні редактори (Neovim, Helix, Emacs) налаштовують `marksman` як LSP-сервер за документацією свого редактора — поведінка ідентична (cmd+click по `[link](file.md)`/`[[wiki-link]]`, find-references, refactor-перейменування).
209
+ Деталі конфігурації `.marksman.toml` та VSCode-альтернативи — у відповідних секціях нижче.
222
210
 
223
211
  **Portable-only синтаксис.** Усе, що пишемо в `docs/`, обмежене **CommonMark + GFM + Mermaid у fenced code (` ```mermaid `) + KaTeX (`$...$`) + нативний HTML5 `<details>`**. Заборонено:
224
212
 
@@ -375,3 +363,15 @@ ADR (`docs/adr/<slug>.md`) — джерело правди для autogen-про
375
363
  ## Query mode як бонус
376
364
 
377
365
  Поверх проекцій — інтерактивний RAG над clean ADR. Інженер питає: «чому ми обрали Percona замість MariaDB?». LLM шукає по semantic index `Context and Problem Statement + Decision Outcome`, повертає `docs/adr/<slug>.md` з прямою цитатою. Корпус малий (десятки-сотні clean ADR після normalize) і структурований (фіксовані MADR-заголовки) — дешево й точно, без галюцинацій.
366
+
367
+ ## Швидкий gate через conftest
368
+
369
+ Rego-перевірки, що запускаються через `conftest` у фазі `check`:
370
+
371
+ | Пакет | Що перевіряє |
372
+ | --------------------------- | -------------------------------------------------------------------- |
373
+ | `ci4.vscode_extensions` | `.vscode/extensions.json` містить `arr.marksman` у `recommendations` |
374
+
375
+ [ci4-marksman_config](./js/marksman_config.mdc)
376
+
377
+ [ci4-vscode_extensions](./js/vscode_extensions.mdc)
@@ -0,0 +1,44 @@
1
+ ## Компіляція bun-проєкту в бінарник
2
+
3
+ Якщо проект має `bun install` крок, та не є фронтенд проектом (тобто не має `bun build` крок без `--compile`), **і не має нативного `.node`-аддона з динамічним завантаженням** (див. `native-addon.mdc`), то потрібно щоб була компіляція коду, і далі у фінальному образі був тільки бінарник. Цей образ не містить компілятора, npm, Bun — тільки runtime libs.
4
+
5
+ Тригер перевірки:
6
+ - у Dockerfile є крок `bun install` (або `bun i`);
7
+ - фінальний FROM — `mirror.gcr.io/library/alpine:*` (тобто не nginx/openresty frontend).
8
+
9
+ Очікування:
10
+ - у build stage є `bun build --compile`;
11
+ - у фінальному stage немає викликів `bun` (залишків build tooling).
12
+
13
+ ### Канон — компільований бінарник на alpine
14
+
15
+ ```dockerfile
16
+ FROM mirror.gcr.io/oven/bun:alpine AS build-env
17
+
18
+ WORKDIR /app
19
+
20
+ ENV NODE_ENV=production
21
+
22
+ COPY package.json .
23
+ COPY bunfig.toml .
24
+
25
+ RUN bun install --production
26
+
27
+ COPY ./src ./src
28
+
29
+ # Компілюємо в бінарник
30
+ RUN bun build --compile --outfile app ./src/index.js
31
+
32
+ FROM mirror.gcr.io/library/alpine:latest
33
+
34
+ # (libstdc++ libgcc) для Bun runtime, (tzdata) для часового поясу
35
+ RUN apk add --no-cache libstdc++ libgcc tzdata
36
+
37
+ WORKDIR /app
38
+
39
+ COPY --from=build-env /app/app ./app
40
+
41
+ CMD ["./app"]
42
+ ```
43
+
44
+ Перевіряє `getBunCompileHint` у **`npm/rules/docker/js/lint.mjs`**.
@@ -0,0 +1,50 @@
1
+ ## lint-docker: hadolint і CI-workflow
2
+
3
+ ### Область lint-docker
4
+
5
+ CLI **`hadolint`** приймає лише **явні шляхи** (`[DOCKERFILE...]` у **`hadolint --help`**); обхід репозиторію робить правило **`n-cursor lint docker`** (реалізація — **`npm/rules/docker/js/lint.mjs`**).
6
+
7
+ **Область lint-docker (вужча, ніж `check docker`):** лише файли з іменем **`Dockerfile`** та **`*.Dockerfile`** (суфікс **`.dockerfile`** без урахування регістру, наприклад **`api.Dockerfile`**). Файли **`Dockerfile.prod`**, **`Containerfile`** тощо **не** входять у **`lint-docker`**; їх ловить **`check docker`** (`rules/docker/fix.mjs`).
8
+
9
+ Обхід: **`walkDir`** з тими самими пропусками каталогів, що й **`rules/docker/fix.mjs`**. Виклик **`hadolint`** як **нативного бінарника** через **`ensureTool`** (PATH → кеш → авто-install brew/scoop/GitHub Release; **без** `docker run`) — спільна логіка **`npm/rules/docker/lib/docker-hadolint.mjs`**.
10
+
11
+ ### GitHub Actions workflow
12
+
13
+ Додай workflow **`.github/workflows/lint-docker.yml`** (гілки **`dev`** і **`main`**, лише **`.yml`**, узгоджено з **`ga.mdc`**):
14
+
15
+ - Канон: [lint-docker.yml.snippet.yml](./policy/lint_docker_yml/template/lint-docker.yml.snippet.yml)
16
+
17
+ Локально hadolint авто-встановлюється через **`ensureTool`** (latest, без піну версії). У CI встанови його кроком із workflow-сніпета (curl-download бінарника — без `docker run`).
18
+
19
+ ### Конфігурація hadolint
20
+
21
+ Кореневий **`.hadolint.yaml`**: вимкнення правил, trusted registries — [документація](https://github.com/hadolint/hadolint#configure). Щоб не додавати **`# hadolint ignore=DL3007`** у кожному **`FROM`** з **`:latest`**, у корені репозиторію задати глобально:
22
+
23
+ ```yaml title=".hadolint.yaml"
24
+ ignored:
25
+ - DL3007
26
+ - DL3008
27
+ - DL3018
28
+ ```
29
+
30
+ Де DL3007 — «Не використовуй тег latest у FROM»
31
+ Де DL3018 — «Піни версії пакетів у apk add»
32
+
33
+ ### Запуск
34
+
35
+ 1. **`n-cursor lint docker`** — **`js/lint.mjs`**: **`Dockerfile`** та **`*.Dockerfile`** (lint-docker); у CI використовуй **`n-cursor lint docker --read-only`** і встанови hadolint (приклад у workflow).
36
+ 2. **`npx @nitra/cursor fix docker`** — **`rules/docker/fix.mjs`**, виклик hadolint як у **`docker-hadolint.mjs`** (нативний бінарник через **`ensureTool`**; **без** `docker run`).
37
+
38
+ Окремий `package.json`-скрипт `lint-docker` не потрібен і не перевіряється — єдина точка входу для правила: **`n-cursor lint docker`**.
39
+
40
+ Якщо немає файлів у межах відповідного набору (**`lint-docker`** або **`check docker`**) — перевірка пропускається (exit 0).
41
+
42
+ Винятки: **`# hadolint ignore=DL3008`** (або інший код) у Dockerfile або **`ignored`** у **`.hadolint.yaml`** (наприклад **DL3007** для **`:latest`** — div. вище).
43
+
44
+ ### Rego-перевірка (conftest)
45
+
46
+ Пакет `docker.lint_docker_yml` перевіряє `.github/workflows/lint-docker.yml` на відповідність канонічному сніпету:
47
+
48
+ - `on.push.paths` містить обов'язкові шляхи з template;
49
+ - присутні всі `uses:` кроки з template;
50
+ - всі `run:` підрядки з template є у workflow.
@@ -0,0 +1,13 @@
1
+ ## GCR-дзеркало замість Docker Hub
2
+
3
+ Для образів з Docker Hub — **`oven/bun`**, **`alpine`**, **`nginx`**, **`nginxinc/nginx-unprivileged`**, **`node`** — у **`FROM`** треба вказувати дзеркало GCR, а не pull напряму з Hub:
4
+
5
+ | Docker Hub | GCR-дзеркало |
6
+ |---|---|
7
+ | `oven/bun` | `mirror.gcr.io/oven/bun` |
8
+ | `alpine` | `mirror.gcr.io/library/alpine` |
9
+ | `nginx` | `mirror.gcr.io/library/nginx` |
10
+ | `node` | `mirror.gcr.io/library/node` |
11
+ | `nginxinc/nginx-unprivileged` | `mirror.gcr.io/nginxinc/nginx-unprivileged` |
12
+
13
+ Перевіряє **`npm/rules/docker/lib/docker-mirror.mjs`** (через `getMirrorGcrHint`); точка входу обходу — **`npm/rules/docker/fix.mjs`** та **`npm/rules/docker/js/lint.mjs`**.
@@ -0,0 +1,13 @@
1
+ ## Multistage build і дозволені runtime-образи
2
+
3
+ Dockerfile/Containerfile **має бути multistage build**: окремий build stage (залежності/компіляція) і окремий runtime stage. У фінальному stage дозволені лише мінімальні базові образи:
4
+
5
+ - **backend (типово)**: `mirror.gcr.io/library/alpine:*`
6
+ - **ультра-легкі (glibc / одна статична збірка)**: `scratch` — тільки як `FROM scratch` (офіційний порожній базовий шар), коли весь **runtime** уже в `COPY --from=…`
7
+ - **glibc, Debian (slim)**: `mirror.gcr.io/library/debian:*` **лише** з тегом, у якому є `slim` (наприклад `bookworm-slim`, `trixie-slim`), а не `bookworm` без `slim`. Debian-slim виправданий **лише** коли потрібен саме glibc-рантайм (нативні glibc-залежності, prebuilds без musl-варіанта). **Non-root сам по собі не є підставою** переходити сюди: `mirror.gcr.io/oven/bun:alpine` уже має користувача `bun` (uid/gid 1000), тож `USER bun` дає non-root **без зміни бази**. Перехід Alpine→Debian заради лише non-root — **антипатерн** (більший образ + зайва musl→glibc міграція bun-бінарника)
8
+ - **виняток (інтерпретовані стеки)**: `mirror.gcr.io/library/php:*` або `mirror.gcr.io/library/python:*` — якщо сервіс має крутитися в офіційному runtime PHP чи Python, а не як один бінарник на Alpine; інакше лишай **alpine** у фінальному stage
9
+ - **frontend**: `mirror.gcr.io/nginxinc/nginx-unprivileged:*` або `mirror.gcr.io/openresty/openresty:*`
10
+
11
+ Це стримує зайвий build tooling (Bun, **node_modules** зі збірки) у фінальному образі; для **alpine** / **nginx** / **openresty** у **runtime** лишаються лише відповідні вимоги, для **php** / **python** (виняток) — цільовий інтерпретований **stack**; **scratch** і **debian** з тегом **`*slim*`** — коли glibc і мінімальне оточення Debian важливіші за musl в **alpine**.
12
+
13
+ Перевіряє `getMultistageAndRuntimeHint` у **`npm/rules/docker/js/lint.mjs`**.