@nitra/cursor 1.13.0 → 1.13.2
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/.claude-template/hooks/capture-decisions.sh +38 -15
- package/.claude-template/hooks/normalize-decisions.sh +15 -4
- package/CHANGELOG.md +12 -0
- package/bin/n-cursor.js +3 -1
- package/package.json +1 -1
- package/rules/abie/abie.mdc +1 -9
- package/rules/adr/adr.mdc +36 -17
- package/rules/adr/fix/hooks/check.mjs +70 -0
- package/rules/bun/bun.mdc +0 -18
- package/rules/capacitor/capacitor.mdc +1 -6
- package/rules/changelog/changelog.mdc +1 -5
- package/rules/ci4/ci4.mdc +0 -4
- package/rules/docker/docker.mdc +1 -19
- package/rules/ga/ga.mdc +1 -26
- package/rules/graphql/graphql.mdc +0 -8
- package/rules/hasura/hasura.mdc +0 -6
- package/rules/image-avif/image-avif.mdc +1 -13
- package/rules/image-compress/image-compress.mdc +7 -33
- package/rules/js-bun-db/js-bun-db.mdc +0 -4
- package/rules/js-bun-redis/js-bun-redis.mdc +0 -4
- package/rules/js-lint/js-lint.mdc +0 -14
- package/rules/js-mssql/js-mssql.mdc +0 -2
- package/rules/js-run/js-run.mdc +0 -8
- package/rules/k8s/k8s.mdc +2 -21
- package/rules/nginx-default-tpl/nginx-default-tpl.mdc +0 -25
- package/rules/npm-module/npm-module.mdc +1 -7
- package/rules/rego/rego.mdc +2 -38
- package/rules/style-lint/style-lint.mdc +0 -23
- package/rules/tauri/tauri.mdc +0 -4
- package/scripts/sync-claude-config.mjs +133 -4
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
# (default: claude-4.6-sonnet-medium)
|
|
10
10
|
# neither — exit 0 silently
|
|
11
11
|
#
|
|
12
|
+
# Hook payloads:
|
|
13
|
+
# - Claude Code Stop: `transcript_path`, `session_id`, `CLAUDE_PROJECT_DIR`
|
|
14
|
+
# - Cursor stop: `transcript_path`, `conversation_id` / `generation_id`, `workspace_roots[]`
|
|
15
|
+
#
|
|
12
16
|
# Bundled with @nitra/cursor; project copy is auto-synced by the `adr` rule.
|
|
13
17
|
set -euo pipefail
|
|
14
18
|
|
|
@@ -19,9 +23,10 @@ export CAPTURE_DECISIONS_RUNNING=1
|
|
|
19
23
|
|
|
20
24
|
INPUT=$(cat)
|
|
21
25
|
TRANSCRIPT_PATH=$(printf '%s' "$INPUT" | jq -r '.transcript_path // empty')
|
|
22
|
-
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // "unknown"')
|
|
26
|
+
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // .conversation_id // .generation_id // "unknown"')
|
|
27
|
+
CURSOR_WORKSPACE_ROOT=$(printf '%s' "$INPUT" | jq -r '.workspace_roots[0] // empty')
|
|
23
28
|
|
|
24
|
-
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
29
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-${CURSOR_WORKSPACE_ROOT:-$PWD}}"
|
|
25
30
|
ADR_DIR="$PROJECT_ROOT/docs/adr"
|
|
26
31
|
LOG_DIR="$PROJECT_ROOT/.claude/hooks"
|
|
27
32
|
LOG="$LOG_DIR/capture-decisions.log"
|
|
@@ -78,33 +83,51 @@ if [[ -z "$TRANSCRIPT" ]]; then
|
|
|
78
83
|
fi
|
|
79
84
|
|
|
80
85
|
PROMPT=$(cat <<'EOF'
|
|
81
|
-
You analyze
|
|
86
|
+
You analyze an AI coding session transcript and produce durable decision documentation.
|
|
82
87
|
|
|
83
|
-
LANGUAGE: Write the
|
|
88
|
+
LANGUAGE: Write the content in Ukrainian. Keep MADR section headings in English exactly as shown below. Keep code identifiers, file paths, commands, and tool or library names in their original form (do not translate `walkDir`, `package.json`, `npm`, etc.).
|
|
84
89
|
|
|
85
90
|
IMPORTANT: by "decision" we mean the design choice expressed in the session — even if the user pre-specified it in their brief. The user dictating the approach IS the decision; capture it, including the rationale they gave or that became apparent during implementation. Do NOT return NONE just because the user gave detailed instructions upfront.
|
|
86
91
|
|
|
92
|
+
ANTI-HALLUCINATION RULES:
|
|
93
|
+
- Use only facts present in the transcript, tool calls, changed file paths, or direct implications of those facts.
|
|
94
|
+
- Do not invent decision makers, stakeholders, business context, requirements, alternatives, or consequences.
|
|
95
|
+
- If alternatives were not discussed, write exactly: "Інші варіанти в transcript не обговорювалися."
|
|
96
|
+
- If a consequence is unknown, write it as "Neutral, because transcript не містить підтвердження наслідку."
|
|
97
|
+
- Prefer specific file paths and commands from the transcript over generic prose.
|
|
98
|
+
|
|
87
99
|
OUTPUT RULES:
|
|
88
100
|
- Emit one or more markdown blocks in this exact shape (no preamble, no trailing prose):
|
|
89
101
|
|
|
90
|
-
##
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
102
|
+
## ADR <короткий заголовок українською>
|
|
103
|
+
|
|
104
|
+
## Context and Problem Statement
|
|
105
|
+
<1-3 речення: яка проблема / ситуація спричинила рішення.>
|
|
106
|
+
|
|
107
|
+
## Considered Options
|
|
108
|
+
* <назва явно обговореного варіанта>
|
|
109
|
+
* <або "Інші варіанти в transcript не обговорювалися.">
|
|
110
|
+
|
|
111
|
+
## Decision Outcome
|
|
112
|
+
Chosen option: "<назва обраного варіанта>", because <коротке обґрунтування з transcript>.
|
|
113
|
+
|
|
114
|
+
### Consequences
|
|
115
|
+
* Good, because <підтверджений позитивний наслідок або "transcript фіксує очікувану користь: ...">.
|
|
116
|
+
* Bad, because <підтверджений негативний наслідок або "transcript не містить підтверджених негативних наслідків.">.
|
|
117
|
+
|
|
118
|
+
## More Information
|
|
119
|
+
<файли, команди, публічні API, конфіги, transcript facts. Якщо нема — "Додаткової інформації в transcript не зафіксовано.">
|
|
96
120
|
|
|
97
121
|
WHEN TO PICK EACH TYPE:
|
|
98
|
-
- ADR
|
|
99
|
-
- Runbook
|
|
100
|
-
- Knowledge: a non-obvious constraint, gotcha, or invariant uncovered (without a corresponding code change).
|
|
122
|
+
- Emit ADR for design choices: library, schema, pattern, file layout, hook semantics, API behavior, validation semantics.
|
|
123
|
+
- Do not emit Runbook or Knowledge blocks here. This hook stores MADR-style decision records only.
|
|
101
124
|
|
|
102
125
|
OUTPUT NONE ONLY IF the session is genuinely trivial:
|
|
103
126
|
- A single typo fix, comment edit, or lint cleanup with no design content
|
|
104
|
-
- A pure question/answer with no
|
|
127
|
+
- A pure question/answer with no durable decision
|
|
105
128
|
- An aborted/empty session
|
|
106
129
|
|
|
107
|
-
When in doubt, emit a
|
|
130
|
+
When in doubt, emit a conservative ADR with explicit "not discussed" placeholders rather than inventing missing details.
|
|
108
131
|
|
|
109
132
|
TRANSCRIPT FOLLOWS:
|
|
110
133
|
---
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
# (default: claude-4.6-sonnet-medium)
|
|
13
13
|
# neither — exit 0 silently
|
|
14
14
|
#
|
|
15
|
+
# Hook payloads:
|
|
16
|
+
# - Claude Code Stop: `CLAUDE_PROJECT_DIR`
|
|
17
|
+
# - Cursor stop: `workspace_roots[]`
|
|
18
|
+
#
|
|
15
19
|
# Portable bash 3.2 (macOS /bin/bash): no `mapfile`, no associative arrays.
|
|
16
20
|
#
|
|
17
21
|
# Bundled with @nitra/cursor; project copy is auto-synced by the `adr` rule.
|
|
@@ -23,7 +27,9 @@ if [ -n "${ADR_NORMALIZE_RUNNING:-}" ]; then
|
|
|
23
27
|
fi
|
|
24
28
|
export ADR_NORMALIZE_RUNNING=1
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
INPUT=$(cat || true)
|
|
31
|
+
CURSOR_WORKSPACE_ROOT=$(printf '%s' "$INPUT" | jq -r '.workspace_roots[0] // empty' 2>/dev/null || true)
|
|
32
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-${CURSOR_WORKSPACE_ROOT:-$PWD}}"
|
|
27
33
|
ADR_DIR="$PROJECT_ROOT/docs/adr"
|
|
28
34
|
LOG_DIR="$PROJECT_ROOT/.claude/hooks"
|
|
29
35
|
LOG="$LOG_DIR/normalize-decisions.log"
|
|
@@ -154,7 +160,7 @@ PROMPT_HEADER=$(cat <<'EOF'
|
|
|
154
160
|
{
|
|
155
161
|
"operations": [
|
|
156
162
|
{ "op": "delete", "file": "<basename>.md", "reason": "..." },
|
|
157
|
-
{ "op": "rewrite", "file": "<basename>.md", "slug": "<kebab-case-ukrainian>", "content": "<повний markdown
|
|
163
|
+
{ "op": "rewrite", "file": "<basename>.md", "slug": "<kebab-case-ukrainian>", "content": "<повний markdown файлу у MADR 4.0.0>" },
|
|
158
164
|
{ "op": "merge-into", "file": "<basename>.md", "target": "<slug>.md", "additions": "<markdown для дописування>" }
|
|
159
165
|
]
|
|
160
166
|
}
|
|
@@ -163,11 +169,15 @@ PROMPT_HEADER=$(cat <<'EOF'
|
|
|
163
169
|
|
|
164
170
|
1. `delete` — драфт тривіальний / повністю покритий іншим існуючим clean-ADR-ом / порожній. Поясни короткою причиною українською.
|
|
165
171
|
|
|
166
|
-
2. `rewrite` — драфт має самостійну
|
|
172
|
+
2. `rewrite` — драфт має самостійну цінність як decision record. Повертай у `content` повний фінальний вміст файлу у форматі MADR 4.0.0 minimal:
|
|
167
173
|
- Без YAML frontmatter (жодного `session:`, `captured:`, `transcript:`).
|
|
168
174
|
- Заголовок `# <Title>` українською.
|
|
169
175
|
- Один рядок `**Status:** Accepted` і один рядок `**Date:** YYYY-MM-DD` — дату беремо з поля `captured:` оригінальної чернетки (перші 10 символів ISO-дати).
|
|
170
|
-
- Далі
|
|
176
|
+
- Далі секції з точними MADR headings англійською: `## Context and Problem Statement`, `## Considered Options`, `## Decision Outcome`, `### Consequences`, `## More Information`.
|
|
177
|
+
- У `## Considered Options` перелічуй лише варіанти, які є в драфті/transcript. Якщо альтернатив не було, додай bullet `Інші варіанти в transcript не обговорювалися.`
|
|
178
|
+
- У `## Decision Outcome` використовуй форму `Chosen option: "<option>", because <reason>.` Причина має спиратися на драфт/transcript, без вигаданого business/context.
|
|
179
|
+
- У `### Consequences` пиши bullets `Good, because ...`, `Bad, because ...`, `Neutral, because ...`. Якщо наслідок не зафіксований, явно пиши `transcript не містить підтвердження ...`, не вигадуй.
|
|
180
|
+
- У `## More Information` перенеси файли, команди, публічні API, конфіги й transcript facts. Якщо нема — `Додаткової інформації в transcript не зафіксовано.`
|
|
171
181
|
- `slug` — kebab-case українською (наприклад `ланцюжок-запуску-abie`, `npm-publish-flow`). Без розширення `.md`. Літери малі, дозволено цифри, дефіс, кирилиця. Якщо тема технічна англійською (назва пакету, ключове слово) — лиши англійською без транслітерації.
|
|
172
182
|
|
|
173
183
|
3. `merge-into` — драфт повторює тему вже існуючого clean-файлу зі списку нижче. `target` — точна назва файлу зі списку (з `.md`). `additions` — лише новий зміст, який варто дописати в кінець target-файлу під підзаголовком `## Update YYYY-MM-DD` (date з `captured` драфта). Якщо нічого нового додати — використовуй `delete`.
|
|
@@ -178,6 +188,7 @@ PROMPT_HEADER=$(cat <<'EOF'
|
|
|
178
188
|
- Кожен файл з вхідного списку має зʼявитися у `operations` рівно один раз.
|
|
179
189
|
- Слаги не повторювати між операціями того самого батча. Якщо дві чернетки про одну тему — одна `rewrite`, інша `merge-into target: <slug>.md` з тим самим slug-ом.
|
|
180
190
|
- Не вигадуй target, якого нема у списку clean-файлів.
|
|
191
|
+
- Не вигадуй альтернативи, decision drivers, наслідки, людей або зовнішній контекст. Якщо даних бракує — явно напиши, що transcript цього не містить.
|
|
181
192
|
|
|
182
193
|
Вхідні драфти і clean-список — нижче.
|
|
183
194
|
EOF
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.13.2] - 2026-05-17
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **`adr` hook output тепер MADR v4.0.0 minimal** — capture/normalize prompts генерують ADR-и з canonical headings `Context and Problem Statement`, `Considered Options`, `Decision Outcome`, `Consequences`, `More Information`. Prompts стали evidence-bound: якщо transcript не містить альтернатив або підтверджених наслідків, hook явно пише, що даних немає, замість вигадування деталей.
|
|
12
|
+
|
|
13
|
+
## [1.13.1] - 2026-05-17
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **`adr` rule: Cursor Agent Stop-hook support** — `npx @nitra/cursor` тепер merge-ить project-level `.cursor/hooks.json` і додає managed `hooks.stop` entries для `.claude/hooks/capture-decisions.sh` та `.claude/hooks/normalize-decisions.sh`. Hook-скрипти приймають Cursor payload (`transcript_path`, `conversation_id` / `generation_id`, `workspace_roots[]`) і використовують той самий ADR capture/normalize pipeline, що й Claude Code.
|
|
18
|
+
|
|
7
19
|
## [1.13.0] - 2026-05-17
|
|
8
20
|
|
|
9
21
|
### Changed
|
package/bin/n-cursor.js
CHANGED
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
* `npx \@nitra/cursor lint-text` — канонічний lint-text (text.mdc): `cspell` → `shellcheck` (з auto-fix) →
|
|
21
21
|
* `markdownlint-cli2 --fix` → `v8r` (json/json5/yaml/yml/toml)
|
|
22
22
|
*
|
|
23
|
-
*
|
|
23
|
+
* Agent інтеграція: під час синку, окрім `.cursor/rules` і `.claude/commands` (з skills), CLI ще раз
|
|
24
24
|
* синхронізує `.claude/settings.json` (hooks + permissions; merge — користувацькі поля зберігаються),
|
|
25
|
+
* `.cursor/hooks.json` (Cursor Agent hooks; merge — користувацькі hooks зберігаються),
|
|
25
26
|
* `npm/CLAUDE.md` (path-scoped нагадування для роботи в `npm/`) і slash-команди checks (`/n-check`).
|
|
26
27
|
* Опт-аут — поле `claude-config: false` у `.n-cursor.json`.
|
|
27
28
|
*
|
|
@@ -1311,6 +1312,7 @@ async function runSync() {
|
|
|
1311
1312
|
}
|
|
1312
1313
|
const parts = []
|
|
1313
1314
|
if (result.settings) parts.push('.claude/settings.json')
|
|
1315
|
+
if (result.cursorHooks) parts.push('.cursor/hooks.json')
|
|
1314
1316
|
if (result.npmClaudeMd) parts.push('npm/CLAUDE.md')
|
|
1315
1317
|
if (result.commands.length > 0) parts.push(`${result.commands.length} slash-commands`)
|
|
1316
1318
|
if (result.adrHook) parts.push('.claude/hooks/capture-decisions.sh')
|
package/package.json
CHANGED
package/rules/abie/abie.mdc
CHANGED
|
@@ -6,10 +6,6 @@ version: '1.20'
|
|
|
6
6
|
|
|
7
7
|
Правило **abie** для споживачів **@nitra/cursor**: **k8s** (Deployment + **HealthCheckPolicy** у **`hc.yaml`**, overlay **ua** — **nodeSelector**, **HTTPRoute** (будь-який непорожній **`target.name`**, для спільних сервісів **`auth-run-hl`** / **`file-link-hl`** — **`namespace: dev`** у base та patch **`…/backendRefs/…/namespace`** у **ua**)), гілки **dev**, **ua** у **clean-merged-branch**, а також заборона тримати артефакти **Firebase Hosting** у **підкаталогах першого рівня** (безпосередні діти кореня репозиторію; у самому корені ці імена не вимагаються до видалення).
|
|
8
8
|
|
|
9
|
-
**`npx @nitra/cursor check abie`** виконується лише якщо в **`.n-cursor.json`** у **`rules`** є **`abie`** — інакше вихід **0** без зауважень.
|
|
10
|
-
|
|
11
|
-
**Канон перевірки** — **`npm/scripts/check-abie.mjs`** у пакеті **`@nitra/cursor`**: верхній JSDoc і реалізація задають точні умови, допустимі домени для hostnames, тексти помилок. Нижче — зміст правила й орієнтовні фрагменти YAML; не дублюй тут покроковий алгоритм зі скрипта.
|
|
12
|
-
|
|
13
9
|
## k8s: `hc.yaml` поруч із Deployment
|
|
14
10
|
|
|
15
11
|
Якщо під **`k8s`** є **Deployment**, у **тій самій директорії** має бути **`hc.yaml`** з **HealthCheckPolicy** (**`networking.gke.io/v1`**): коректний modeline **`$schema`**, **`httpHealthCheck.requestPath`** — непорожній шлях від кореня (рядок, що починається з **`/`**: канонічно **`/healthz`**, але також допустимі **`/IsAlive`**, **`/api/live`** тощо — узгоджується з реальним endpoint сервісу), порт **8080**, **`targetRef.name`** — **headless** **Service** з суфіксом **`-hl`** (узгоджено з парою **`svc.yaml`** / **`svc-hl.yaml`** у **k8s.mdc**): або **`${metadata.name}-hl`**, або те саме ім’я, якщо **`metadata.name`** уже з **`-hl`**.
|
|
@@ -157,11 +153,7 @@ with:
|
|
|
157
153
|
ignore_branches: main,dev,ua
|
|
158
154
|
```
|
|
159
155
|
|
|
160
|
-
##
|
|
161
|
-
|
|
162
|
-
**`npx @nitra/cursor check abie`**
|
|
163
|
-
|
|
164
|
-
### Швидкий gate через conftest (Rego)
|
|
156
|
+
## Швидкий gate через conftest (Rego)
|
|
165
157
|
|
|
166
158
|
Підмножину пер-документних правил продубльовано як rego-полісі у **`npm/rules/abie/policy/`** (запускається через **`bun run lint-rego`** для `*_test.rego` юніт-тестів і через **`npx @nitra/cursor check abie`** для прогону по реальних YAML — деталі в **conftest.mdc** / **n-rego.mdc**). JS у **`fix/<concern>/check.mjs`** authoritative — rego тільки швидкий gate для одиничного маніфеста (зокрема через IDE-розширення `tsandall.opa`).
|
|
167
159
|
|
package/rules/adr/adr.mdc
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Автоматичний збір ADR/Runbook/Knowledge-чернеток і батч-нормалізація у `docs/adr/` через Stop
|
|
2
|
+
description: Автоматичний збір ADR/Runbook/Knowledge-чернеток і батч-нормалізація у `docs/adr/` через Stop-хуки Claude Code та Cursor Agent
|
|
3
3
|
alwaysApply: true
|
|
4
4
|
version: '2.0'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## MADR v4 і дві фази
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
ADR живуть у єдиному каталозі **`docs/adr/`**. Clean ADR-и мають формат **MADR v4.0.0 minimal** з точними section headings англійською:
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
11
|
+
- `## Context and Problem Statement`
|
|
12
|
+
- `## Considered Options`
|
|
13
|
+
- `## Decision Outcome`
|
|
14
|
+
- `### Consequences`
|
|
15
|
+
- `## More Information`
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
Вміст секцій — українською, code identifiers / paths / commands — як у transcript. Якщо transcript не містить альтернатив або підтверджених наслідків, hook має явно писати `Інші варіанти в transcript не обговорювалися.` або `transcript не містить підтвердження ...`, а не вигадувати відсутні факти.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
Є два стани файлу, які відрізняються YAML frontmatter:
|
|
19
20
|
|
|
20
21
|
- **Draft** — файл з frontmatter `session: …`, `captured: …`, `transcript: …` та timestamp-іменем `YYYYMMDD-HHMMSS-<sid>.md`. Пише `capture-decisions.sh` після кожної сесії.
|
|
21
22
|
- **Clean** — файл без frontmatter, з kebab-case-іменем `<slug>.md` (наприклад `ланцюжок-запуску-abie.md`). Створює `normalize-decisions.sh` або людина руками.
|
|
@@ -24,7 +25,9 @@ ADR живуть у єдиному каталозі **`docs/adr/`**. Є два
|
|
|
24
25
|
|
|
25
26
|
### Фаза 1 — Capture
|
|
26
27
|
|
|
27
|
-
Stop-hook `capture-decisions.sh` зчитує JSONL-транскрипт сесії (через `jq`), витягає текст, `thinking`-блоки та назви `tool_use`-викликів, передає компактний дайджест у LLM CLI з промптом
|
|
28
|
+
Stop-hook `capture-decisions.sh` зчитує JSONL-транскрипт сесії (через `jq`), витягає текст, `thinking`-блоки та назви `tool_use`-викликів, передає компактний дайджест у LLM CLI з evidence-bound промптом і записує результат у **`docs/adr/<timestamp>-<session>.md`**, якщо модель повернула MADR-блок з шапкою `## ADR …`. Якщо модель повернула `NONE` (тривіальна сесія) — нічого не пишеться. Рекурсію з внутрішнього виклику моделі блокує env-var `CAPTURE_DECISIONS_RUNNING=1`.
|
|
29
|
+
|
|
30
|
+
Для Cursor payload скрипт бере `transcript_path`, `conversation_id` / `generation_id` і `workspace_roots[0]`; для Claude Code — `transcript_path`, `session_id` і `CLAUDE_PROJECT_DIR`.
|
|
28
31
|
|
|
29
32
|
### Фаза 2 — Normalize
|
|
30
33
|
|
|
@@ -41,7 +44,7 @@ LLM повертає масив операцій:
|
|
|
41
44
|
| `op` | Семантика | Поля |
|
|
42
45
|
| --- | --- | --- |
|
|
43
46
|
| `delete` | Чернетка тривіальна / повністю покрита іншим clean-ADR-ом. | `file`, `reason` |
|
|
44
|
-
| `rewrite` | Чернетка стає окремим clean
|
|
47
|
+
| `rewrite` | Чернетка стає окремим clean-файлом MADR v4 minimal: frontmatter знімається, ім'я → `<slug>.md`, додаються `**Status:** Accepted`, `**Date:**` з `captured` і canonical MADR headings. | `file`, `slug`, `content` |
|
|
45
48
|
| `merge-into` | Чернетка повторює тему вже існуючого clean-файлу; дописуємо `## Update YYYY-MM-DD` у кінець `target`. | `file`, `target`, `additions` |
|
|
46
49
|
|
|
47
50
|
`slug` — kebab-case українською (`ланцюжок-запуску-abie`, `npm-publish-flow`); англійські технічні терміни лишаються англійською без транслітерації. Колізія slug-ів обробляється детермінованим суфіксом `-2`, `-3`.
|
|
@@ -88,12 +91,12 @@ docs/adr/
|
|
|
88
91
|
├── normalize-decisions.log # лог запусків normalize (НЕ коміти)
|
|
89
92
|
├── .normalize-state # timestamp останнього normalize-запуску (НЕ коміти)
|
|
90
93
|
└── .normalize.lock # lock-файл (НЕ коміти)
|
|
94
|
+
.cursor/
|
|
95
|
+
└── hooks.json # Cursor Agent stop-hooks для тих самих скриптів
|
|
91
96
|
```
|
|
92
97
|
|
|
93
98
|
`.gitignore` повинен містити рядки, що покривають **`.claude/hooks/*.log`** і службові файли normalize (`.claude/hooks/.normalize-*`).
|
|
94
99
|
|
|
95
|
-
> Якщо в репозиторії лишився старий каталог **`docs/adr/_inbox/`** з попередньої версії правила — `normalize-decisions.sh` бачить його рекурсивно й поступово розчистить. Можна також одразу `git mv docs/adr/_inbox/*.md docs/adr/` і прибрати порожній каталог.
|
|
96
|
-
|
|
97
100
|
## Stop-hook у `.claude/settings.json`
|
|
98
101
|
|
|
99
102
|
Канонічний запис, який вставляє sync (поряд із lint stop-hook):
|
|
@@ -141,10 +144,26 @@ docs/adr/
|
|
|
141
144
|
|
|
142
145
|
Усі три групи ідентифікуються пакетом за маркером у `command` (`@nitra/cursor stop-hook`, `.claude/hooks/capture-decisions.sh`, `.claude/hooks/normalize-decisions.sh`) — користувацькі hook-групи поряд не чіпаються. Якщо `adr` прибрати з `rules`, обидві ADR-групи автоматично видаляються на наступному `npx @nitra/cursor`.
|
|
143
146
|
|
|
144
|
-
##
|
|
147
|
+
## Stop-hook у `.cursor/hooks.json`
|
|
145
148
|
|
|
146
|
-
|
|
149
|
+
Cursor Agent читає project-level **`.cursor/hooks.json`**. `npx @nitra/cursor` merge-ить файл: користувацькі hooks лишаються, managed entries для `capture-decisions.sh` і `normalize-decisions.sh` перезаписуються або видаляються разом із правилом `adr`.
|
|
147
150
|
|
|
148
|
-
|
|
151
|
+
```json title=".cursor/hooks.json"
|
|
152
|
+
{
|
|
153
|
+
"version": 1,
|
|
154
|
+
"hooks": {
|
|
155
|
+
"stop": [
|
|
156
|
+
{
|
|
157
|
+
"command": "bash -lc 'root=\"$PWD\"; if [ ! -f \"$root/.claude/hooks/capture-decisions.sh\" ] && [ -f \"$root/../.claude/hooks/capture-decisions.sh\" ]; then root=\"$root/..\"; fi; bash \"$root/.claude/hooks/capture-decisions.sh\"'",
|
|
158
|
+
"timeout": 180
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"command": "bash -lc 'root=\"$PWD\"; if [ ! -f \"$root/.claude/hooks/normalize-decisions.sh\" ] && [ -f \"$root/../.claude/hooks/normalize-decisions.sh\" ]; then root=\"$root/..\"; fi; bash \"$root/.claude/hooks/normalize-decisions.sh\"'",
|
|
162
|
+
"timeout": 600
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
149
168
|
|
|
150
|
-
`
|
|
169
|
+
Обидва Stop-hook'и ADR живуть у **project-shared** `.claude/settings.json` (закомічений), щоб механізм працював у всіх членів команди. Якщо хук колись був у `.claude/settings.local.json` — прибери дубль вручну: project-shared і local-копія створили б два запуски на одну подію.
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* пакета (sync керує файлами повністю).
|
|
9
9
|
* - `.claude/settings.json` (project-shared) має managed-групи у `hooks.Stop` для
|
|
10
10
|
* обох скриптів (маркери у `command` — самі шляхи до скриптів).
|
|
11
|
+
* - `.cursor/hooks.json` має managed entries у `hooks.stop` для обох скриптів, щоб
|
|
12
|
+
* Cursor Agent теж запускав ADR capture/normalize після завершення відповіді.
|
|
11
13
|
* - `.claude/settings.local.json` (якщо існує) НЕ має дублів цих managed-груп —
|
|
12
14
|
* після переходу на project-shared такі записи створили б два запуски на одну подію.
|
|
13
15
|
* - `.gitignore` у корені містить шаблон, який покриває
|
|
@@ -31,6 +33,7 @@ const HOOK_ARTIFACTS = /** @type {const} */ ([
|
|
|
31
33
|
])
|
|
32
34
|
|
|
33
35
|
const PROJECT_SETTINGS_PATH = '.claude/settings.json'
|
|
36
|
+
const CURSOR_HOOKS_PATH = '.cursor/hooks.json'
|
|
34
37
|
const EOL_RE = /\r?\n/u
|
|
35
38
|
|
|
36
39
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
@@ -119,6 +122,72 @@ function checkProjectSettings(reporter) {
|
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Читає JSON-файл із диска без винятку.
|
|
127
|
+
* @param {string} path відносний шлях до JSON-файлу
|
|
128
|
+
* @returns {Promise<unknown | null>} розпарсений JSON або null
|
|
129
|
+
*/
|
|
130
|
+
async function readJsonSafe(path) {
|
|
131
|
+
try {
|
|
132
|
+
return JSON.parse(await readFile(path, 'utf8'))
|
|
133
|
+
} catch {
|
|
134
|
+
return null
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Чи має Cursor hooks config stop-entry з потрібним command marker.
|
|
140
|
+
* @param {unknown} config розпарсений `.cursor/hooks.json`
|
|
141
|
+
* @param {string} marker підрядок, який має бути в `command`
|
|
142
|
+
* @returns {boolean} true, якщо marker знайдено у `hooks.stop[]`
|
|
143
|
+
*/
|
|
144
|
+
function cursorConfigHasStopHook(config, marker) {
|
|
145
|
+
if (config === null || typeof config !== 'object' || Array.isArray(config)) {
|
|
146
|
+
return false
|
|
147
|
+
}
|
|
148
|
+
const hooks = /** @type {{ hooks?: unknown }} */ (config).hooks
|
|
149
|
+
if (hooks === null || typeof hooks !== 'object' || Array.isArray(hooks)) {
|
|
150
|
+
return false
|
|
151
|
+
}
|
|
152
|
+
const stop = /** @type {{ stop?: unknown }} */ (hooks).stop
|
|
153
|
+
if (!Array.isArray(stop)) {
|
|
154
|
+
return false
|
|
155
|
+
}
|
|
156
|
+
return stop.some(entry => {
|
|
157
|
+
if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
158
|
+
return false
|
|
159
|
+
}
|
|
160
|
+
const command = /** @type {{ command?: unknown }} */ (entry).command
|
|
161
|
+
return typeof command === 'string' && command.includes(marker)
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Перевіряє project-level Cursor hooks config для ADR stop-hooks.
|
|
167
|
+
* @param {import('../../../../scripts/utils/check-reporter.mjs').CheckReporter} reporter репортер
|
|
168
|
+
* @returns {Promise<void>}
|
|
169
|
+
*/
|
|
170
|
+
async function checkCursorHooks(reporter) {
|
|
171
|
+
const { pass, fail } = reporter
|
|
172
|
+
if (!existsSync(CURSOR_HOOKS_PATH)) {
|
|
173
|
+
fail(`${CURSOR_HOOKS_PATH} не існує — запусти \`npx @nitra/cursor\``)
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
const config = await readJsonSafe(CURSOR_HOOKS_PATH)
|
|
177
|
+
if (config === null) {
|
|
178
|
+
fail(`${CURSOR_HOOKS_PATH} не парситься як JSON — запусти \`npx @nitra/cursor\` або виправ файл`)
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
for (const { scriptName } of HOOK_ARTIFACTS) {
|
|
182
|
+
const marker = projectHookPath(scriptName)
|
|
183
|
+
if (cursorConfigHasStopHook(config, marker)) {
|
|
184
|
+
pass(`${CURSOR_HOOKS_PATH} має stop-hook для ${marker}`)
|
|
185
|
+
} else {
|
|
186
|
+
fail(`${CURSOR_HOOKS_PATH}: відсутній stop-hook для \`${marker}\` (adr.mdc)`)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
122
191
|
/**
|
|
123
192
|
* Перевіряє `.gitignore` на ігнорування лог-файлу одного хука.
|
|
124
193
|
* @param {import('../../../../scripts/utils/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
@@ -212,6 +281,7 @@ export async function check() {
|
|
|
212
281
|
await checkHookScript(reporter, scriptName)
|
|
213
282
|
}
|
|
214
283
|
checkProjectSettings(reporter)
|
|
284
|
+
await checkCursorHooks(reporter)
|
|
215
285
|
await checkGitignore(reporter)
|
|
216
286
|
checkLlmCliAvailable(reporter)
|
|
217
287
|
return reporter.getExitCode()
|
package/rules/bun/bun.mdc
CHANGED
|
@@ -26,21 +26,8 @@ version: '1.8'
|
|
|
26
26
|
- `bunx <tool>`
|
|
27
27
|
- `npx <tool>`
|
|
28
28
|
|
|
29
|
-
- Для встановлення залежностей використовуй `bun i`.
|
|
30
|
-
- Для запуску скриптів використовуй `bun run <script>`.
|
|
31
|
-
- Для додавання залежностей:
|
|
32
|
-
- `bun add <pkg>`
|
|
33
|
-
- `bun add -d <pkg>` для devDependencies
|
|
34
|
-
- Для одноразових CLI-команд використовуй `bunx <tool>`.
|
|
35
|
-
|
|
36
29
|
**Не додавай** у `dependencies` / `devDependencies` пакети, які **використовуються лише як CLI** і їх достатньо викликати через **`bunx <pkg>`** (або **`npx`**, якщо в проєкті так прийнято): наприклад **oxlint**, **jscpd**, **eslint** у корені тощо. Виняток — пакет потрібен як **бібліотека** (імпорт у коді), peer для іншого пакета, або інструмент **не** покривається `bunx` у вашому CI.
|
|
37
30
|
|
|
38
|
-
Заборонено використовувати:
|
|
39
|
-
|
|
40
|
-
- `npm`
|
|
41
|
-
- `yarn`
|
|
42
|
-
- `pnpm`
|
|
43
|
-
|
|
44
31
|
Lockfile у репозиторії: `bun.lock`.
|
|
45
32
|
Не створювати `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`.
|
|
46
33
|
Видалити якщо вони є. Видалити .yarn та .yarnrc.yml якщо вони є.
|
|
@@ -62,7 +49,6 @@ linker = "hoisted"
|
|
|
62
49
|
|
|
63
50
|
Якщо в package.json є поля `packageManager`, то прибрати їх, також прибрати всі директорії та файли для yarn
|
|
64
51
|
|
|
65
|
-
Якщо в проекті використовується npx, то не замінювати його на bunx, а використовувати npx.
|
|
66
52
|
Коли зміна відбувається в Dockerfile, то використовувати
|
|
67
53
|
|
|
68
54
|
```dockerfile
|
|
@@ -81,10 +67,6 @@ FROM oven/bun:alpine AS build-env
|
|
|
81
67
|
|
|
82
68
|
Якщо в репозиторії action збережено під **`./npm/github-actions/setup-bun-deps`**, у `uses:` вкажи цей шлях замість `.github/actions/…` (**ga.mdc**).
|
|
83
69
|
|
|
84
|
-
## Перевірка
|
|
85
|
-
|
|
86
|
-
`npx @nitra/cursor check bun`
|
|
87
|
-
|
|
88
70
|
## lint
|
|
89
71
|
|
|
90
72
|
Якщо в кореневому @package.json існують скрипти з префіксом `lint-`, обов'язково створюй `lint` скрипт, який буде запускати всі ці скрипти з лінт-префіксом.
|
|
@@ -9,8 +9,7 @@ version: '1.1'
|
|
|
9
9
|
|
|
10
10
|
У `package.json` (у **корені** репозиторію чи **workspace**-пакеті) оголошення **`@capacitor/core`**
|
|
11
11
|
має вказувати діапазон, **сумісний лише з мажорною версією 8 і вище** (наприклад `^8.0.0`).
|
|
12
|
-
**`*`**, `latest` і діапазони, де можлива 7-мажор, — неприйнятні.
|
|
13
|
-
(репозиторій **@nitra/cursor**).
|
|
12
|
+
**`*`**, `latest` і діапазони, де можлива 7-мажор, — неприйнятні.
|
|
14
13
|
|
|
15
14
|
## iOS: зазвичай лише SPM, виняток Podfile
|
|
16
15
|
|
|
@@ -33,7 +32,3 @@ version: '1.1'
|
|
|
33
32
|
|
|
34
33
|
- Перевірка читає **лише** кореневі файли: **`package.json`**, потім **capacitor-конфіги** у **корені** (див. вище).
|
|
35
34
|
У **`.ts` / `.mjs`**: шукається блок **nitra** `{ ... }` і **на його тілі** перевіряються ці **boolean**-поля.
|
|
36
|
-
|
|
37
|
-
## Перевірка
|
|
38
|
-
|
|
39
|
-
`npx @nitra/cursor check capacitor` (коли **check-скрипт** підключено до цієї **ruleset**).
|
|
@@ -5,7 +5,7 @@ globs: "**/{CHANGELOG.md,package.json}"
|
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Bun monorepo: у кожному workspace із кореневого `package.json.workspaces` (плюс кореневий пакет, плюс `npm/`) має бути власний **`CHANGELOG.md`**. Спільного на репозиторій змісту змін **не існує** — кожен пакет веде свій.
|
|
8
|
+
Bun monorepo: у кожному workspace із кореневого `package.json.workspaces` (плюс кореневий пакет, плюс `npm/`) має бути власний **`CHANGELOG.md`**. Спільного на репозиторій змісту змін **не існує** — кожен пакет веде свій.
|
|
9
9
|
|
|
10
10
|
## Дві моделі бази порівняння
|
|
11
11
|
|
|
@@ -59,7 +59,3 @@ Git у цьому режимі не використовується — пор
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
Секції — підмножина `### Added`, `### Changed`, `### Fixed`, `### Removed` (одна або кілька).
|
|
62
|
-
|
|
63
|
-
## Перевірка
|
|
64
|
-
|
|
65
|
-
`npx @nitra/cursor check changelog`
|
package/rules/ci4/ci4.mdc
CHANGED
|
@@ -101,7 +101,3 @@ C4-схеми потрібно оновити, і робимо це у тому
|
|
|
101
101
|
C4-схеми — частина **користувацької документації**, а не закритий артефакт для команди.
|
|
102
102
|
Контекстна діаграма (рівень 1) і контейнерна (рівень 2) живуть там, де читач шукає вступ у
|
|
103
103
|
проєкт, а не у відокремленій теці «for-architects».
|
|
104
|
-
|
|
105
|
-
## Перевірка
|
|
106
|
-
|
|
107
|
-
`npx @nitra/cursor check ci4`
|
package/rules/docker/docker.mdc
CHANGED
|
@@ -7,8 +7,6 @@ alwaysApply: false
|
|
|
7
7
|
|
|
8
8
|
# Docker — hadolint
|
|
9
9
|
|
|
10
|
-
[hadolint](https://github.com/hadolint/hadolint) перевіряє Dockerfile на типові помилки та рекомендації (`FROM`, `RUN`, `COPY`, shell form тощо).
|
|
11
|
-
|
|
12
10
|
Для образів з Docker Hub — **`oven/bun`**, **`alpine`**, **`nginxinc/nginx-unprivileged`**, **`node`** — у **`FROM`** треба вказувати дзеркало GCR, а не pull напряму з Hub: **`mirror.gcr.io/oven/bun`**, **`mirror.gcr.io/library/alpine`**, **`mirror.gcr.io/nginxinc/nginx-unprivileged`**, **`mirror.gcr.io/library/node`**. Перевіряє **`check-docker.mjs`**, деталі в **`npm/scripts/utils/docker-mirror.mjs`**.
|
|
13
11
|
|
|
14
12
|
Також Dockerfile/Containerfile **має бути multistage build**: окремий build stage (залежності/компіляція) і окремий runtime stage. У фінальному stage дозволені лише мінімальні базові образи:
|
|
@@ -181,20 +179,4 @@ ignored:
|
|
|
181
179
|
|
|
182
180
|
Якщо немає файлів у межах відповідного набору (**`lint-docker`** або **`check docker`**) — перевірка пропускається (exit 0).
|
|
183
181
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
- Після правок у Dockerfile проганяй **`bun run lint-docker`** і/або **`check docker`**.
|
|
187
|
-
- Винятки: **`# hadolint ignore=DL3008`** (або інший код) у Dockerfile або **`ignored`** у **`.hadolint.yaml`** (наприклад **DL3007** для **`:latest`** — див. вище).
|
|
188
|
-
- Образи на базі Bun — див. **`n-bun.mdc`**.
|
|
189
|
-
|
|
190
|
-
## Редактор
|
|
191
|
-
|
|
192
|
-
Розширення VS Code / Cursor: **`exiasr.hadolint`** (потрібна утиліта hadolint) — за потреби в **`.vscode/extensions.json`**.
|
|
193
|
-
|
|
194
|
-
## Перевірка
|
|
195
|
-
|
|
196
|
-
`npx @nitra/cursor check docker`
|
|
197
|
-
|
|
198
|
-
Після змін у Dockerfile: **`bun run lint-docker`** (обов'язково для проєктів з правилом **`docker`** у **`.n-cursor.json`**).
|
|
199
|
-
|
|
200
|
-
Kubernetes YAML і `$schema` у `k8s/` — окреме правило **`k8s.mdc`**, **`check k8s`**.
|
|
182
|
+
Винятки: **`# hadolint ignore=DL3008`** (або інший код) у Dockerfile або **`ignored`** у **`.hadolint.yaml`** (наприклад **DL3007** для **`:latest`** — див. вище).
|
package/rules/ga/ga.mdc
CHANGED
|
@@ -166,20 +166,6 @@ jobs:
|
|
|
166
166
|
|
|
167
167
|
**Локальний composite** (`uses: ./.github/actions/setup-bun-deps` або `./npm/github-actions/setup-bun-deps`): **спочатку** обов’язковий крок **`actions/checkout@v6`** (`persist-credentials: false`), інакше runner не знайде `action.yml`. Сам composite: **`actions/setup-node@v6`** (**Node 24**), **Bun**, **`actions/cache@v5`**, **`bun install --frozen-lockfile`**.
|
|
168
168
|
|
|
169
|
-
```json title=".vscode/extensions.json"
|
|
170
|
-
{
|
|
171
|
-
"recommendations": ["github.vscode-github-actions"]
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
У **`.vscode/settings.json`** для мови **`github-actions-workflow`** (workflow з розширення GitHub Actions) задай **oxc** як formatter:
|
|
176
|
-
|
|
177
|
-
```json
|
|
178
|
-
"[github-actions-workflow]": {
|
|
179
|
-
"editor.defaultFormatter": "oxc.oxc-vscode"
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
169
|
**ЗАБОРОНЕНО** дублювати кроки встановлення Bun та кешування безпосередньо у workflow файлах. Завжди використовуй локальний composite action.
|
|
184
170
|
|
|
185
171
|
**Кроки `run`:** не розбивай команду shell-продовженням через зворотний сліш у кінці рядка (`… \` у `run: |`). Замість багаторядкового буквального блока з `\\` оформ довгу одну shell-команду як **folded block** `>-` (рядки з’єднаються в один рядок із пробілами).
|
|
@@ -264,13 +250,7 @@ jobs:
|
|
|
264
250
|
|
|
265
251
|
> Не використовуй `npx --no @nitra/cursor lint-ga` — `bun run` автоматично транслює `npx` у `bun x`, а `bun x` для скоупованого пакету з одним bin-ім’ям повертає 0 без виконання. Виклик через bin-ім’я `n-cursor` працює і у `bun run`, і у `npm run`.
|
|
266
252
|
|
|
267
|
-
CLI
|
|
268
|
-
|
|
269
|
-
1. Preflight на [`shellcheck`](https://www.shellcheck.net/) у `PATH` — без нього `actionlint` мовчки пропускає shell-перевірки в `run:` блоках, тож локальний прогін зеленіє, а CI на `ubuntu-latest` (де shellcheck передвстановлений) падає на тих самих workflow. Встановлення: `brew install shellcheck` (macOS), `sudo apt-get install -y shellcheck` (Debian/Ubuntu), `sudo pacman -S shellcheck` (Arch).
|
|
270
|
-
2. Preflight на [`uv`](https://docs.astral.sh/uv/) у `PATH` — постачає `uvx`, без якого не запуститься `uvx zizmor`. Встановлення: `brew install uv` (macOS), `curl -LsSf https://astral.sh/uv/install.sh | sh` (universal), `pip install uv`.
|
|
271
|
-
3. Якщо хоча б один preflight не пройшов — exit 1 (підказки для всіх відсутніх залежностей друкуються разом, а не лише для першої).
|
|
272
|
-
4. `bunx github-actionlint`.
|
|
273
|
-
5. `uvx zizmor --offline --collect=workflows .`.
|
|
253
|
+
CLI робить preflight на `shellcheck` і `uv` (`uvx`) у `PATH`, потім запускає `bunx github-actionlint` і `uvx zizmor --offline --collect=workflows .`.
|
|
274
254
|
|
|
275
255
|
**`.github/zizmor.yml`:** для [unpinned-uses](https://docs.zizmor.sh/audits/#unpinned-uses) — політика **`ref-pin`**, якщо в `uses:` семантичні теги. За потреби вимкни [template-injection](https://docs.zizmor.sh/audits/#template-injection):
|
|
276
256
|
|
|
@@ -288,8 +268,3 @@ rules:
|
|
|
288
268
|
**MegaLinter:** не використовувати; прибрати workflow, конфіги (`.mega-linter.yml`, `.megalinter.yaml`, `.mega-linter.yaml`), залежності та згадки в CI / pre-commit / документації.
|
|
289
269
|
|
|
290
270
|
**`depcheck`:** не використовувати у `.github/workflows/*.yml` — мігровано на `knip` (див. `js-lint.mdc`). Перевірка невикористаних залежностей виконується разом з рештою лінтерів у `lint-js`, окремий крок `npx depcheck` у workflow не потрібен і блокується полісі `ga.workflow_common`.
|
|
291
|
-
|
|
292
|
-
## Перевірка
|
|
293
|
-
|
|
294
|
-
- `bun run lint-ga`
|
|
295
|
-
- `npx @nitra/cursor check ga`
|
|
@@ -10,10 +10,6 @@ alwaysApply: false
|
|
|
10
10
|
- файл **`.graphqlrc.yml`** ([GraphQL Config](https://the-guild.dev/graphql/config/docs));
|
|
11
11
|
- у **`.vscode/extensions.json`** в масиві **`recommendations`** — запис **`graphql.vscode-graphql`**.
|
|
12
12
|
|
|
13
|
-
Це забезпечує підсвітку та діагностику GraphQL в редакторі.
|
|
14
|
-
|
|
15
|
-
Деталі виявлення `gql` у скриптах (у т.ч. лише `<script>` у SFC) — **`npm/scripts/check-graphql.mjs`** / **`npm/scripts/utils/graphql-gql-scan.mjs`**.
|
|
16
|
-
|
|
17
13
|
## `.graphqlrc.yml`
|
|
18
14
|
|
|
19
15
|
Підстав свої шляхи до схеми та до файлів з операціями; приклад орієнтиру:
|
|
@@ -33,7 +29,3 @@ documents:
|
|
|
33
29
|
"recommendations": ["graphql.vscode-graphql"]
|
|
34
30
|
}
|
|
35
31
|
```
|
|
36
|
-
|
|
37
|
-
## Перевірка
|
|
38
|
-
|
|
39
|
-
`npx @nitra/cursor check graphql`
|