@7n/rules 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/rules/abie/http_route_base/http_route_base.mdc +23 -1
- package/rules/adr/madr_format/concern.json +1 -0
- package/rules/adr/madr_format/madr_format.mdc +119 -0
- package/rules/bun/bunfig/bunfig.mdc +5 -0
- package/rules/bun/lint-surface/concern.json +3 -0
- package/rules/bun/lint-surface/lint-surface.mdc +13 -0
- package/rules/capacitor/platforms/docs/main.md +1 -1
- package/rules/capacitor/platforms/main.mjs +5 -1
- package/rules/capacitor/platforms/platforms.mdc +108 -0
- package/rules/changelog/consistency/comparison-models.mdc +46 -0
- package/rules/changelog/consistency/consistency.mdc +35 -0
- package/rules/docker/main.mdc +235 -2
- package/rules/image-avif/avif_generation/avif_generation.mdc +14 -0
- package/rules/js/check/check.mdc +26 -0
- package/rules/js/file-extensions/concern.json +3 -0
- package/rules/js/file-extensions/file-extensions.mdc +12 -0
- package/rules/js/jscpd_config/jscpd_config.mdc +28 -0
- package/rules/js/knip/knip.mdc +15 -0
- package/rules/js/utils_imports/utils_imports.mdc +15 -0
- package/rules/js-bun-db/connection/concern.json +3 -0
- package/rules/js-bun-db/connection/connection.mdc +42 -0
- package/rules/js-bun-db/package_json/package_json.mdc +15 -1
- package/rules/js-bun-db/pg_format_identifiers/concern.json +3 -0
- package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +104 -0
- package/rules/js-bun-db/safety/safety.mdc +458 -0
- package/rules/js-mssql/main.mdc +130 -0
- package/rules/js-mssql/mssql-tvp/concern.json +3 -0
- package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +77 -0
- package/rules/js-run/configmap/configmap.mdc +6 -0
- package/rules/js-run/jsconfig/jsconfig.mdc +23 -0
- package/rules/js-run/package_json/package_json.mdc +6 -0
- package/rules/js-run/project-structure/concern.json +3 -0
- package/rules/js-run/project-structure/project-structure.mdc +11 -0
- package/rules/js-run/runtime/runtime.mdc +170 -0
- package/rules/js-run/scope/concern.json +3 -0
- package/rules/js-run/scope/scope.mdc +11 -0
- package/rules/k8s/hasura_configmap/hasura_configmap.mdc +6 -0
- package/rules/k8s/hpa_pdb/hpa_pdb.mdc +134 -0
- package/rules/k8s/kubeconform/kubeconform.mdc +38 -0
- package/rules/k8s/kustomization/kustomization.mdc +73 -0
- package/rules/k8s/main.mdc +68 -0
- package/rules/k8s/manifest/manifest.mdc +37 -0
- package/rules/k8s/manifests/docs/fix-manifests.md +3 -1
- package/rules/k8s/manifests/fix-manifests.mjs +11 -0
- package/rules/k8s/manifests/main.mjs +28 -0
- package/rules/k8s/network_policy/network_policy.mdc +33 -0
- package/rules/nginx-default-tpl/http-route/concern.json +1 -0
- package/rules/nginx-default-tpl/http-route/http-route.mdc +54 -0
- package/rules/nginx-default-tpl/template/template.mdc +152 -0
- package/rules/php/tooling/tooling.mdc +7 -6
- package/rules/python/pyproject_toml/pyproject_toml.mdc +17 -1
- package/rules/python/tooling/tooling.mdc +9 -10
- package/rules/rego/main.mdc +14 -0
- package/rules/rust/check/check.mdc +16 -0
- package/rules/style/admin_table/admin_table.mdc +88 -0
- package/rules/style/admin_table/concern.json +7 -0
- package/rules/style/admin_table/docs/index.md +9 -0
- package/rules/style/admin_table/docs/main.md +14 -0
- package/rules/style/admin_table/main.mjs +46 -0
- package/rules/style/colors/colors.mdc +21 -0
- package/rules/style/colors/concern.json +3 -0
- package/rules/style/gap/concern.json +7 -0
- package/rules/style/gap/docs/index.md +9 -0
- package/rules/style/gap/docs/main.md +15 -0
- package/rules/style/gap/gap.mdc +22 -0
- package/rules/style/gap/main.mjs +51 -0
- package/rules/style/quasar/concern.json +3 -0
- package/rules/style/quasar/quasar.mdc +7 -0
- package/rules/style/quasar_fixes/concern.json +7 -0
- package/rules/style/quasar_fixes/docs/index.md +9 -0
- package/rules/style/quasar_fixes/docs/main.md +16 -0
- package/rules/style/quasar_fixes/main.mjs +57 -0
- package/rules/style/quasar_fixes/quasar_fixes.mdc +32 -0
- package/rules/tauri/tool_surface/concern.json +16 -0
- package/rules/tauri/tool_surface/docs/index.md +9 -0
- package/rules/tauri/tool_surface/docs/main.md +24 -0
- package/rules/tauri/tool_surface/main.mjs +145 -0
- package/rules/tauri/tool_surface/tool_surface.mdc +29 -0
- package/rules/test/vitest-api-conventions/concern.json +7 -0
- package/rules/test/vitest-api-conventions/docs/index.md +9 -0
- package/rules/test/vitest-api-conventions/docs/main.md +40 -0
- package/rules/test/vitest-api-conventions/main.mjs +186 -0
- package/rules/test/vitest-api-conventions/vitest-api-conventions.mdc +129 -0
- package/rules/text/cspell/cspell.mdc +18 -0
- package/rules/text/markdownlint/markdownlint.mdc +4 -0
- package/rules/text/run-dotenv-linter/run-dotenv-linter.mdc +17 -0
- package/rules/text/run-shellcheck/run-shellcheck.mdc +17 -0
- package/rules/text/run-v8r/run-v8r.mdc +23 -0
- package/rules/vue/composition-api/composition-api.mdc +82 -0
- package/rules/vue/composition-api/concern.json +3 -0
- package/rules/vue/main.mdc +1 -1
- package/rules/vue/nheader-layout/concern.json +3 -0
- package/rules/vue/nheader-layout/nheader-layout.mdc +171 -0
- package/rules/vue/packages/packages.mdc +56 -0
- package/rules/vue/quasar-ui/concern.json +3 -0
- package/rules/vue/quasar-ui/quasar-ui.mdc +32 -0
- package/rules/vue/structure/concern.json +3 -0
- package/rules/vue/structure/structure.mdc +101 -0
- package/rules/vue/testing/concern.json +3 -0
- package/rules/vue/testing/testing.mdc +40 -0
- package/rules/vue/tfm-translations/concern.json +7 -0
- package/rules/vue/tfm-translations/docs/main.md +29 -0
- package/rules/vue/tfm-translations/main.mjs +55 -0
- package/rules/vue/tfm-translations/tfm-translations.mdc +32 -0
- package/rules/vue/vite-config/concern.json +3 -0
- package/rules/vue/vite-config/vite-config.mdc +153 -0
- package/rules/vue/vite-env/concern.json +3 -0
- package/rules/vue/vite-env/vite-env.mdc +61 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.7.1] - 2026-07-16
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- vue: відновлено втрачений guide-контент (composition-api, quasar-ui, structure, testing, tfm, vite-config, vite-env, nheader-layout) + виправлено застарілий шлях у main.mdc
|
|
8
|
+
- restore: відновлено втрачений guide-контент для 21 правила з коміту da05f89d (docs-only guide/ concern-и), виправлено застарілі шляхи в abie/ga/php/python
|
|
9
|
+
- rules: розкладено docs-only guide/ по всіх 22 правилах на окремі per-concern директорії, реалізовано реальні check/policy де це мало сенс (vue tfm-translations, capacitor workspace:*, k8s hpa apiVersion, style admin-table/gap/quasar-fixes, tauri tool_surface), решту залишено docs-only зі звітом де check недоцільний
|
|
10
|
+
|
|
3
11
|
## [1.7.0] - 2026-07-16
|
|
4
12
|
|
|
5
13
|
### Changed
|
package/package.json
CHANGED
|
@@ -6,4 +6,26 @@ Rego-пакет: `abie.http_route_base`
|
|
|
6
6
|
|
|
7
7
|
Перевіряє, що кожен hostname у `spec.hostnames` належить до домену `aiml.live`: точна відповідність `aiml.live`, wildcard `*.aiml.live` або будь-який піддомен `*.aiml.live`. Перевірка case-insensitive. Не є HTTPRoute — пакет не діє.
|
|
8
8
|
|
|
9
|
-
JS-частина (cross-file аналіз ua-overlay, backendRefs, тощо) — у `
|
|
9
|
+
JS-частина (cross-file аналіз ua-overlay, backendRefs, тощо) — у `ua_http_route/main.mjs`.
|
|
10
|
+
|
|
11
|
+
### Приклади
|
|
12
|
+
|
|
13
|
+
```yaml title="…/k8s/base/hr.yaml"
|
|
14
|
+
apiVersion: gateway.networking.k8s.io/v1
|
|
15
|
+
kind: HTTPRoute
|
|
16
|
+
metadata:
|
|
17
|
+
name: my-app
|
|
18
|
+
spec:
|
|
19
|
+
hostnames:
|
|
20
|
+
- "my-app.aiml.live" # ✓ піддомен aiml.live
|
|
21
|
+
parentRefs:
|
|
22
|
+
- name: gateway
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Недозволені в base-шарі (але коректні для ua-overlay — `abie.app`, `vybeerai.com.ua`):
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
hostnames:
|
|
29
|
+
- "abie.app" # ✗ ua-домен, не base
|
|
30
|
+
- "vybeerai.com.ua" # ✗ ua-домен, не base
|
|
31
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema": "https://unpkg.com/@7n/rules/schemas/concern.json"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
## MADR v4 і дві фази файлу
|
|
2
|
+
|
|
3
|
+
ADR живуть у єдиному каталозі **`docs/adr/`**. Clean ADR-и мають формат **MADR v4.0.0 minimal** з **OKF v0.1 frontmatter** і точними section headings англійською:
|
|
4
|
+
|
|
5
|
+
- `## Context and Problem Statement`
|
|
6
|
+
- `## Considered Options`
|
|
7
|
+
- `## Decision Outcome`
|
|
8
|
+
- `### Consequences`
|
|
9
|
+
- `## More Information`
|
|
10
|
+
|
|
11
|
+
Вміст секцій — українською, code identifiers / paths / commands — як у transcript. Якщо transcript не містить альтернатив або підтверджених наслідків, LLM-промпт нормалізатора вимагає явно писати `Інші варіанти в transcript не обговорювалися.` або `transcript не містить підтвердження ...`, а не вигадувати відсутні факти.
|
|
12
|
+
|
|
13
|
+
Є два стани файлу, які відрізняються YAML frontmatter:
|
|
14
|
+
|
|
15
|
+
- **Draft** — файл з frontmatter `session: …`, `captured: …`, `transcript: …` та timestamp-іменем `YYMMDD-HHMM-<sid>.md` (fallback на старі чернетки без розпізнаного heading). Пише `capture-decisions.sh` після кожної сесії.
|
|
16
|
+
- **Clean** — файл з OKF v0.1 frontmatter (`type: ADR`, `title:`, `description:`) і kebab-case-іменем. Заголовок `#` у тілі не потрібен — `title:` у frontmatter вже є заголовком. `normalize-decisions.sh` зберігає timestamp-префікс чернетки → `YYMMDD-HHMM-<slug>.md` (наприклад `260518-0928-ланцюжок-запуску-abie.md`); підтримуються і старіші чернетки з `YYYYMMDD-HHMMSS-` prefix. Створений руками clean-файл може мати просто `<slug>.md`.
|
|
17
|
+
|
|
18
|
+
`normalize-decisions.sh` ніколи не чіпає clean-файли — крім випадку `merge-into`, коли дописує `## Update YYYY-MM-DD` в кінець наявного clean-файлу.
|
|
19
|
+
|
|
20
|
+
**Примітка про історичні файли:** описаний тут MADR v4 minimal — конвенція для файлів, які проходять актуальну версію `normalize-decisions.sh`. Каталог `docs/adr/` цього ж репозиторію містить і файли зі старішими форматами frontmatter/заголовків (напр. `type: ADR` + `topic:`/`spec:` без `title:`, чи заголовки українською на кшталт «Контекст»/«Рішення»), накопичені до впровадження поточної конвенції. Тому автоматизованої перевірки форми clean-ADR тут немає — рекурсивна класифікація "старий/новий формат" по вмісту ненадійна, а lint по всіх файлах масово провалював би легітимні історичні записи.
|
|
21
|
+
|
|
22
|
+
## Фаза 1 — Capture
|
|
23
|
+
|
|
24
|
+
Stop-hook `capture-decisions.sh` зчитує JSONL-транскрипт сесії (через `jq`), витягає текст, `thinking`-блоки та назви `tool_use`-викликів, передає компактний дайджест у LLM-бекенд з evidence-bound промптом і записує результат у **`docs/adr/<timestamp>-<slug-або-sid>.md`**, якщо модель повернула MADR-блок з шапкою `## ...`. Якщо модель повернула `NONE` (тривіальна сесія) або відповідь порожня — нічого не пишеться. Slug для імені файлу виводиться локально (без додаткового LLM-виклику) з першого `## `-заголовка відповіді. Рекурсію з внутрішнього виклику моделі блокує env-var `CAPTURE_DECISIONS_RUNNING=1`.
|
|
25
|
+
|
|
26
|
+
Для Cursor payload скрипт бере `transcript_path`, `conversation_id` / `generation_id` і `workspace_roots[0]`; для Claude Code — `transcript_path`, `session_id` і `CLAUDE_PROJECT_DIR`.
|
|
27
|
+
|
|
28
|
+
Вибір LLM-бекенду для Capture (`CAPTURE_DECISIONS_BACKEND`, дефолт `pi`) описаний окремо в `adr/main.mdc` ("Capture-бекенд"), тут не дублюється.
|
|
29
|
+
|
|
30
|
+
**Cross-project skip:** якщо в сесії редагувалися файли, але жоден не під поточним `PROJECT_ROOT` — це паралельна робота в іншому проєкті, і ADR-чернетка не пишеться (сесії без жодних редагувань, тобто чисте Q&A, цей гейт не відкидає). Вимикається `ADR_CAPTURE_SKIP_CROSS_PROJECT=0`.
|
|
31
|
+
|
|
32
|
+
**Tooling-only skip:** перед викликом LLM `capture-decisions.sh` дивиться у transcript на `tool_use`-правки (`Edit`/`Write`/`MultiEdit`). Якщо всі змінені файли потрапляють у вузький allowlist — `.cspell.json`, `docs/adr/*.md`, `CHANGELOG.md` (в тому числі вкладений `*/CHANGELOG.md`), кореневі `AGENTS.md`/`CLAUDE.md`, або `package.json` з diff виключно по ключу `"version"` — хук виходить з `exit 0` без LLM-виклику. Це розриває петлю «`/n-lint` править `.cspell.json` → з'являється новий ADR-draft → наступний `/n-lint` знов псує правопис у цьому draft». Поведінку вимикає `ADR_NORMALIZE_SKIP_TOOLING_ONLY=0` (той самий прапор ділять capture і normalize).
|
|
33
|
+
|
|
34
|
+
## Фаза 2 — Normalize
|
|
35
|
+
|
|
36
|
+
Stop-hook `normalize-decisions.sh` спрацьовує на тій самій `Stop`-події, але:
|
|
37
|
+
|
|
38
|
+
- Виходить миттєво, якщо чернеток (`session:` у frontmatter) менше ніж **`ADR_NORMALIZE_THRESHOLD`** (default 30).
|
|
39
|
+
- Виходить миттєво, якщо від попередньої спроби пройшло менше **`ADR_NORMALIZE_MIN_INTERVAL_HOURS`** годин (default 6) — щоб не крутитися щоразу, коли поріг постійний.
|
|
40
|
+
- Бере не більше **`ADR_NORMALIZE_BATCH`** чернеток (default 10, найстарші за іменем-timestamp), формує один промпт LLM і чекає JSON-відповідь зі списком операцій.
|
|
41
|
+
- Виходить миттєво, якщо репозиторій у стані `MERGE_HEAD` / `CHERRY_PICK_HEAD` / `REVERT_HEAD` / `rebase-apply` / `rebase-merge` — небезпечно правити файли посеред конфлікту чи rebase.
|
|
42
|
+
- Виходить миттєво, якщо інший normalize-запуск тримає `flock` на `.claude/hooks/.normalize.lock` (тільки де `flock` доступний — macOS без нього просто не блокує паралельний запуск).
|
|
43
|
+
- Перед викликом LLM для кожної чернетки batch'а читає `transcript:` із frontmatter і той самий tool_use-список. Чернетки tooling-only — видаляє без виклику LLM. Якщо після фільтра batch порожній — `exit 0`.
|
|
44
|
+
|
|
45
|
+
LLM повертає масив операцій:
|
|
46
|
+
|
|
47
|
+
| `op` | Семантика | Поля |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| `delete` | Чернетка тривіальна / повністю покрита іншим clean-ADR-ом. | `file`, `reason` |
|
|
50
|
+
| `rewrite` | Чернетка стає окремим clean-файлом MADR v4 minimal: draft-frontmatter (`session:`/`captured:`/`transcript:`) заміняється на OKF v0.1 (`type: ADR`, `title:`), ім'я → `<timestamp>-<slug>.md` (timestamp-префікс чернетки збережено), додаються `**Status:** Accepted`, `**Date:**` з `captured` і canonical MADR headings. Якщо LLM не повернула OKF frontmatter, скрипт сам дописує мінімальне (`type: ADR`, `title:` з першого `# `-рядка або зі `slug`). | `file`, `slug`, `content` |
|
|
51
|
+
| `merge-into` | Чернетка повторює тему вже існуючого clean-файлу; дописуємо `## Update YYYY-MM-DD` у кінець `target`. Скрипт резолвить `target` у три кроки: точна назва в `docs/adr/`, slug свіжо створеного `rewrite` цього ж батчу, або унікальний існуючий файл, що закінчується на `-<slug>.md`. | `file`, `target`, `additions` |
|
|
52
|
+
|
|
53
|
+
`slug` — kebab-case українською (`ланцюжок-запуску-abie`, `npm-publish-flow`); англійські технічні терміни лишаються англійською без транслітерації. До імені clean-файлу скрипт додає `YYMMDD-HHMM-` чернетки, тож запис лишається прив'язаним до часу capture, а `docs/adr/` сортується хронологічно. Колізія імен обробляється детермінованим суфіксом `-2`, `-3`. Старі чернетки з `YYYYMMDD-HHMMSS-` prefix нормалізатор також розпізнає, щоб не ламати наявний inbox.
|
|
54
|
+
|
|
55
|
+
### Жодних git-операцій
|
|
56
|
+
|
|
57
|
+
`normalize-decisions.sh` **не комітить, не `git add`, нічого з git**. Усі зміни — у робочому дереві. Розробник у зручний момент дивиться `git status` / `git diff` і вирішує: `git add` + commit, `git checkout -- <file>` для відкату, або правки руками. Це і є review-вікно.
|
|
58
|
+
|
|
59
|
+
### Recursion guard і ENV-керування
|
|
60
|
+
|
|
61
|
+
Інший LLM-виклик, який запустить normalize, успадковує `ADR_NORMALIZE_RUNNING=1` — внутрішній Stop-hook вийде відразу. `ADR_HOOKS_SKIP=1` (виставляє JS-оркестратор перед `npx @7n/rules lint`/`/n-lint`/`/n-doc-files`/`/n-taze` тощо) також блокує запуск раніше за ці guard'и — деталі в `adr/main.mdc`. Доступні ENV для нормалізації:
|
|
62
|
+
|
|
63
|
+
| Змінна | Default | Призначення |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `ADR_NORMALIZE_THRESHOLD` | `30` | Поріг чернеток для запуску фази. |
|
|
66
|
+
| `ADR_NORMALIZE_BATCH` | `10` | Максимум чернеток у одному виклику LLM. |
|
|
67
|
+
| `ADR_NORMALIZE_MIN_INTERVAL_HOURS` | `6` | Мінімум між спробами (навіть якщо поріг). |
|
|
68
|
+
| `ADR_NORMALIZE_DRY` | `0` | `1` — лише лог запланованих операцій, без змін на диску. |
|
|
69
|
+
| `ADR_NORMALIZE_BACKEND` | автовизначення | `local`/`pi`/`claude`/`cursor` — примусовий вибір бекенду (див. нижче). |
|
|
70
|
+
| `ADR_NORMALIZE_MODEL` | `sonnet` | Модель для бекенду `claude`. |
|
|
71
|
+
| `ADR_NORMALIZE_CURSOR_MODEL` | `claude-4.6-sonnet-medium` | Модель для бекенду `cursor`. |
|
|
72
|
+
| `ADR_NORMALIZE_PI_MODEL` | `$N_CLOUD_AVG_MODEL` (fallback `openai-codex/gpt-5.5`) | Модель для бекенду `pi`. |
|
|
73
|
+
| `ADR_NORMALIZE_LOCAL_CMD` | `npx --no @7n/rules adr-normalize-local` | Команда локального пайплайна (override для тестів/in-repo). |
|
|
74
|
+
| `ADR_NORMALIZE_SKIP_TOOLING_ONLY` | `1` | `0` — вимкнути structural skip tooling-only сесій (той самий прапор ділять capture і normalize). |
|
|
75
|
+
|
|
76
|
+
Для ручного запуску (поза порогом і поза Stop-хуком) є **`/n-adr-normalize`** — slash-команда тимчасово виставляє `ADR_NORMALIZE_THRESHOLD=0` і `ADR_NORMALIZE_MIN_INTERVAL_HOURS=0` та викликає скрипт напряму.
|
|
77
|
+
|
|
78
|
+
## Normalize LLM-бекенд: local → pi → claude → cursor
|
|
79
|
+
|
|
80
|
+
`ADR_NORMALIZE_BACKEND` (якщо не заданий явно) обирається автоматично:
|
|
81
|
+
|
|
82
|
+
1. **`local`** — якщо задано `N_LOCAL_MIN_MODEL`: конвеєр на малій локальній моделі (privacy + $0, `npm/scripts/lib/adr/normalize-pipeline.mjs`, викликається через `ADR_NORMALIZE_LOCAL_CMD`) сам будує дрібні промпти з батча.
|
|
83
|
+
2. **`pi`** — якщо `pi` є в `PATH`: `pi -p --model "$ADR_NORMALIZE_PI_MODEL" --no-prompt-templates`.
|
|
84
|
+
3. **`claude`** — якщо `claude` є в `PATH`: `claude -p --model "$ADR_NORMALIZE_MODEL"`.
|
|
85
|
+
4. **`cursor`** — якщо `cursor-agent` є в `PATH`: `cursor-agent -p --mode ask --output-format text --model "$ADR_NORMALIZE_CURSOR_MODEL"`.
|
|
86
|
+
5. Жодного бекенду — скрипт логує `no LLM backend available` і виходить з кодом `0` без змін.
|
|
87
|
+
|
|
88
|
+
Вибір LLM-бекенду для Capture (`CAPTURE_DECISIONS_BACKEND`: `pi`/`claude`/`cursor-agent`/`auto`, дефолт `pi`) — окремий механізм, задокументований в `adr/main.mdc`.
|
|
89
|
+
|
|
90
|
+
## Структура каталогу
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
docs/adr/
|
|
94
|
+
├── YYMMDD-HHMM-<sid>.md # drafts (frontmatter session:/captured:/transcript:)
|
|
95
|
+
└── YYMMDD-HHMM-<slug>.md # clean ADR-и (без frontmatter, timestamp-префікс чернетки збережено)
|
|
96
|
+
.claude/hooks/
|
|
97
|
+
├── capture-decisions.sh # auto-synced з пакета
|
|
98
|
+
├── normalize-decisions.sh # auto-synced з пакета
|
|
99
|
+
├── capture-decisions.log # лог запусків capture (НЕ коміти)
|
|
100
|
+
├── normalize-decisions.log # лог запусків normalize (НЕ коміти)
|
|
101
|
+
├── .normalize-state # timestamp останнього normalize-запуску (НЕ коміти)
|
|
102
|
+
└── .normalize.lock # lock-файл (НЕ коміти)
|
|
103
|
+
.cursor/
|
|
104
|
+
└── hooks.json # Cursor Agent stop-hooks для тих самих скриптів
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`.gitignore` у корені проєкту повинен містити базові рядки (`node_modules/`, `dist/`, `*.secret`) і патерни для ADR Stop-hook (**`.claude/hooks/*.log`**, `.claude/hooks/.normalize-state`, `.claude/hooks/.normalize.lock`). Канонічний фрагмент (дописується `npx @7n/rules`, коли правило `adr` увімкнене):
|
|
108
|
+
|
|
109
|
+
```gitignore
|
|
110
|
+
node_modules/
|
|
111
|
+
dist/
|
|
112
|
+
*.secret
|
|
113
|
+
|
|
114
|
+
# @7n/rules (adr) — локальні артефакти Stop-hook, не коміти
|
|
115
|
+
.claude/hooks/*.log
|
|
116
|
+
.claude/hooks/.normalize-state
|
|
117
|
+
.claude/hooks/.normalize.lock
|
|
118
|
+
.claude/scheduled_tasks.lock
|
|
119
|
+
```
|
|
@@ -10,3 +10,8 @@ Gate порівнює кожен leaf-ключ у кожній секції ша
|
|
|
10
10
|
|
|
11
11
|
- секція відсутня або не є обʼєктом (`bunfig.toml: відсутня секція [install]`)
|
|
12
12
|
- leaf-ключ має інше значення (`bunfig.toml: у секції [install] має бути linker = "hoisted"`)
|
|
13
|
+
|
|
14
|
+
## Навіщо саме `linker = "hoisted"`
|
|
15
|
+
|
|
16
|
+
Канонічна лінковка для кореневого `bunfig.toml` — **hoisted**: пласке `node_modules`,
|
|
17
|
+
сумісне з інструментами, які не розуміють ізольований (isolated) layout Bun.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## Лінт через n-rules, не package.json-скрипти
|
|
2
|
+
|
|
3
|
+
Лінт запускається через CLI **`n-rules`** (локально) / **`npx @7n/rules`** (без локальної інсталяції),
|
|
4
|
+
**не** через `package.json`-скрипти:
|
|
5
|
+
|
|
6
|
+
- **`npx @7n/rules lint --full`** — весь репо (CI-режим);
|
|
7
|
+
- **`npx @7n/rules lint`** — дельта vs origin (лише змінені файли, типовий задачний прогін);
|
|
8
|
+
- **`n-rules lint <rule…>`** — конкретні правила, напр. **`n-rules lint ga`**.
|
|
9
|
+
|
|
10
|
+
У кореневому `package.json` **не повинно бути** `lint`/`lint-*` скриптів — єдина точка лінту — CLI
|
|
11
|
+
`n-rules` (пакет `@7n/rules`). Цей запис механічно перевіряється Rego-gate'ом
|
|
12
|
+
[`bun.package_json`](../package_json/package_json.mdc) (deny на `scripts.lint` / `scripts.lint-*`) —
|
|
13
|
+
тут лишається лише пояснення *чому* так (єдина точка входу для лінту), без дублювання самого чеку.
|
|
@@ -74,7 +74,8 @@ const RE_COCOAPODS_EXEMPT_ALLOW = /\biosCocoaPodsAllowed\s*:\s*true\b/
|
|
|
74
74
|
/**
|
|
75
75
|
* Мінімальний **major** (нижня межа) для **однієї** OR-частини діапазону npm (без `||` всередині).
|
|
76
76
|
* @param {string} segment одна частина після `||` або весь рядок
|
|
77
|
-
* @returns {number | null}
|
|
77
|
+
* @returns {number | null} `Infinity`, якщо **`workspace:`**-протокол (завжди прийнятний, як у Rego-gate
|
|
78
|
+
* `capacitor.package_json`); `null`, якщо **`*` / `x` / `latest`**, або **major** **нижньої** межі
|
|
78
79
|
*/
|
|
79
80
|
export function capacitorSegmentMinMajor(segment) {
|
|
80
81
|
if (typeof segment !== 'string') {
|
|
@@ -85,6 +86,9 @@ export function capacitorSegmentMinMajor(segment) {
|
|
|
85
86
|
return null
|
|
86
87
|
}
|
|
87
88
|
const low = s0.toLowerCase()
|
|
89
|
+
if (low.startsWith('workspace:')) {
|
|
90
|
+
return Infinity
|
|
91
|
+
}
|
|
88
92
|
if (s0 === '*' || low === 'x' || low === 'latest') {
|
|
89
93
|
return null
|
|
90
94
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
## JS-перевірка: версія Capacitor та iOS SPM/CocoaPods
|
|
2
|
+
|
|
3
|
+
JS-модуль: `platforms/main.mjs` (authoritative check, `capacitor.mdc`) — обходить усі `package.json`
|
|
4
|
+
у дереві та iOS-шар (`Podfile`) одним проходом.
|
|
5
|
+
|
|
6
|
+
### Версія `@capacitor/core`
|
|
7
|
+
|
|
8
|
+
У `package.json` (у **корені** репозиторію чи **workspace**-пакеті) оголошення **`@capacitor/core`**
|
|
9
|
+
має вказувати діапазон, **сумісний лише з мажорною версією 8 і вище** (наприклад `^8.0.0`).
|
|
10
|
+
**`*`**, `latest` і діапазони, де можлива 7-мажор, — неприйнятні.
|
|
11
|
+
|
|
12
|
+
Перевірка обходить усі `package.json` у дереві (крім `node_modules`, `.git`, `dist`, `coverage`,
|
|
13
|
+
`Pods`, `.turbo`, `.next`, `build`) і перевіряє нижню межу діапазону через `capacitorVersionRangeMinMajor`.
|
|
14
|
+
Підтримуються `||`-частини, hyphen-range (`7 - 9`), `^`, `~`, `>=`, `>`, `=`, bare-version.
|
|
15
|
+
|
|
16
|
+
`workspace:*` (і будь-який інший діапазон із префіксом `workspace:`) — **завжди прийнятний**
|
|
17
|
+
(як у Rego-gate `capacitor.package_json`): реальна версія походить від workspace-пакета, який
|
|
18
|
+
перевіряється окремо. `*` / `x` / `latest` (без префікса `workspace:`) — **неприйнятні**: діапазон
|
|
19
|
+
не гарантує мажор ≥ 8.
|
|
20
|
+
|
|
21
|
+
**Приклади допустимих діапазонів:**
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
"@capacitor/core": "^8.0.0"
|
|
25
|
+
"@capacitor/core": ">=8"
|
|
26
|
+
"@capacitor/core": "8.x"
|
|
27
|
+
"@capacitor/core": "workspace:*"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Приклади неприйнятних діапазонів:**
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
"@capacitor/core": "^7.0.0" // мажор 7 — занадто старий
|
|
34
|
+
"@capacitor/core": "*" // будь-яка — неприйнятна
|
|
35
|
+
"@capacitor/core": "latest" // неприйнятна
|
|
36
|
+
"@capacitor/core": ">=6" // нижня межа 6 — занадто старий
|
|
37
|
+
"@capacitor/core": "6 - 9" // hyphen-range: нижня межа 6 — неприйнятна
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### iOS: лише SPM, виняток через Podfile
|
|
41
|
+
|
|
42
|
+
#### Правило за замовчуванням
|
|
43
|
+
|
|
44
|
+
Не залишай `Podfile` (поза `Pods/`) у вихідному iOS-шарі, **якщо** уся потрібна
|
|
45
|
+
iOS-функціональність (нативні плагіни/модулі) може працювати **лише** через **SPM** (Swift Package Manager).
|
|
46
|
+
|
|
47
|
+
Перевірка рекурсивно шукає `Podfile` у `ios/`, пропускаючи `Pods/`, `build/`, `DerivedData/`.
|
|
48
|
+
|
|
49
|
+
#### Плагіни @nitra/
|
|
50
|
+
|
|
51
|
+
Плагіни зі скоупу `@nitra/` за політикою **підтримують SPM** — перевіряти їх на SPM **не потрібно**
|
|
52
|
+
(check не обходить `package.json` на предмет `@nitra/`).
|
|
53
|
+
|
|
54
|
+
#### Коли Podfile дозволений
|
|
55
|
+
|
|
56
|
+
Якщо не вся потрібна iOS-функціональність поза `@nitra/` (сторонні Capacitor-плагіни, інша
|
|
57
|
+
нативна залежність) доступна через SPM — `Podfile` дозволяється, але це **обов'язково** треба
|
|
58
|
+
явно задати в кореневому **`package.json`** або в **`capacitor.config.json` / `capacitor.config.ts` / `capacitor.config.mjs`**:
|
|
59
|
+
|
|
60
|
+
- **`"iosCocoaPodsBecausePluginsLackSpm": true`** — семантика: не вся потрібна нативна частина
|
|
61
|
+
поза `@nitra/` на SPM; `@nitra/` у це не входить;
|
|
62
|
+
- або **`"iosCocoaPodsAllowed": true`** — короткий alias для того самого винятку.
|
|
63
|
+
|
|
64
|
+
Без одного з цих прапорів `true` наявний `Podfile` поза `Pods/` вважається порушенням правила «лише SPM».
|
|
65
|
+
|
|
66
|
+
**Де задати виняток у `package.json`:**
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"nitra": {
|
|
71
|
+
"iosCocoaPodsBecausePluginsLackSpm": true
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
або коротший alias:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"nitra": {
|
|
81
|
+
"iosCocoaPodsAllowed": true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Де задати виняток у `capacitor.config.json`:**
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"nitra": {
|
|
91
|
+
"iosCocoaPodsAllowed": true
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Де задати виняток у `capacitor.config.ts` / `capacitor.config.mjs`:**
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const config = {
|
|
100
|
+
// ...
|
|
101
|
+
nitra: {
|
|
102
|
+
iosCocoaPodsAllowed: true,
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Перевірка читає **лише** кореневі файли: `package.json`, потім capacitor-конфіги у корені.
|
|
108
|
+
У `.ts` / `.mjs`: шукається блок `nitra { ... }` і на його тілі перевіряються ці boolean-поля.
|
|
@@ -0,0 +1,46 @@
|
|
|
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`). За наявності `origin/*` беремо новішу з двох баз (локальна гілка-кандидат vs `origin/`-версія) — застарілий локальний `main`/`dev` не має перекривати вже інтегровану в origin історію, і навпаки.
|
|
23
|
+
4. Drift `version` від бази → **fail** (ручний bump заборонено). Зміни фіксуй change-файлом; bump зробить CI.
|
|
24
|
+
|
|
25
|
+
Якщо немає git або немає `dev`/`main`/`origin/main` — local-only пропускається.
|
|
26
|
+
|
|
27
|
+
Merge-коміт (готовий, з другим предком, або `MERGE_HEAD` під час незавершеного `git commit`) пропускається цілком — changeset документують feature-коміти, а не інтеграційний merge.
|
|
28
|
+
|
|
29
|
+
## Чеклист агента (деталі)
|
|
30
|
+
|
|
31
|
+
Основний робочий алгоритм — «перед фінальною відповіддю виконай `npx @7n/rules lint changelog`, познач результат рядком `Changelog: …`» (AGENTS.md/AGENTS.template.md); тут лише уточнення, що саме перевіряється.
|
|
32
|
+
|
|
33
|
+
**Інверсія (за замовчуванням не вимагають change-файлу):**
|
|
34
|
+
|
|
35
|
+
- зміни **лише** під `docs/` або `doc/`;
|
|
36
|
+
- синхронізований із `@7n/rules` інструментарій під `.cursor/` (канонічні правила й скіли) і `.claude/` (ADR-хуки) — це дзеркало tooling-пакета, а не логіка воркспейсу;
|
|
37
|
+
- будь-які зміни в **корені монорепо** (воркспейс `.` за наявності підпакетів) — корінь веде glue/конфіг/tooling, власного CHANGELOG не має; помітні зміни документують підпакети. Сюди потрапляють і кореневі `AGENTS.md` / `CLAUDE.md`, і bump `@7n/rules` у `devDependencies`;
|
|
38
|
+
- файли під **`.gitignore`**.
|
|
39
|
+
|
|
40
|
+
**Вимагають change-файл** — усі інші зміни в каталозі workspace (код, rego, правила, скіли, конфіги, тести тощо). Виняток `.cursor/` / `.claude/` **не** поширюється на джерело правил у репо `@7n/rules` — воно лежить під `npm/`, тож зміни в ньому далі вимагають change-файлу.
|
|
41
|
+
|
|
42
|
+
Ніколи не редагуй `version` і `CHANGELOG.md` вручну — навіть для hotfix; єдиний артефакт зміни — change-файл (`npx @7n/n ch [--bump <major|minor|patch>] [--section <Added|Changed|Fixed|Removed>] [--message "<…>"]`), bump/секцію CHANGELOG формує `n-rules release` у CI на `main`.
|
|
43
|
+
|
|
44
|
+
Канонічне pre-commit wiring (крок `npm-changelog` у `hk.pkl`, autofix через `N_RULES_CHANGELOG_AUTOFIX=1`) — деталь `npm-module.mdc`, тут не дублюється.
|
|
45
|
+
|
|
46
|
+
Перевірка програмна (`changelog/consistency/main.mjs`, delta-гейт присутності — `changelog/presence/main.mjs`).
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
Механічно `main.mjs` (`checkChangelogFormat`) перевіряє лише наявність рядка `# Changelog` (H1). Точна лексика секцій і порядок версій (новіші зверху) — конвенція, яку check **не** валідує: наявні `CHANGELOG.md` у цьому репо містять і нестандартні секції (`### BREAKING`, `### Notes`, `### TODO` тощо) з історичних причин, тож строга валідація словника секцій зробила б check несумісним із власною практикою репо. Дотримання — на розсуд автора change-файлу.
|
|
30
|
+
|
|
31
|
+
## Post-release інваріант (гарантує CI)
|
|
32
|
+
|
|
33
|
+
Перша (верхня) секція `## [version]` у `CHANGELOG.md` дорівнює полю `version` у маніфесті — але це **post-release** твердження, яке забезпечує `n-rules release` у CI, агрегуючи change-файли (bump `version` + генерація секції + git-тег `<name>@<version>`). **Локально цю рівність руками не підтримують**: у feature-флоу `version`/`CHANGELOG.md` не чіпають, тож верхня секція може відставати від майбутньої версії — це нормально. Drift `version` поза CI (vs реєстр / vs git-база) ловить цей concern (`consistency`) як заборонений ручний bump.
|
|
34
|
+
|
|
35
|
+
Інструкції щодо bump `version` і редагування `CHANGELOG.md` живуть **лише** в правилі `changelog` (деталізація моделі порівняння — `comparison-models.mdc` поряд) — джерелі істини. Інші правила (зокрема `npm-module`) їй підпорядковані щодо формату/моделі й власних інструкцій bump/CHANGELOG не дублюють; команду створення change-файлу (`npx @7n/n ch`) й заборону ручного bump вони лише повторюють як нагадування.
|