From 82e151e3d562f062af88d3cc3a5b856e35e86860 Mon Sep 17 00:00:00 2001 From: vitaliytv Date: Thu, 23 Jul 2026 09:47:02 +0300 Subject: [PATCH] =?UTF-8?q?chore:=20sync=20@7n/rules=20=E2=80=94=20=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D1=81=D1=96=D1=97=20=D0=B9=20=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2=D1=96=20=D0=BF=D0=BE=D0=BA=D1=80=D0=B0=D1=89?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D1=8F=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ручна курація виводу "голого" npx @7n/rules (без застосування автодетекту): Прийнято: - @7n/rules ^1.43.1→^1.44.1, @7n/rules-ci-github ^1.9.0→^1.9.2 - n-changelog.mdc, n-security.mdc — точкові покращення контенту (лінт чистий) - n-adr-normalize/SKILL.md, n-lint/SKILL.md — краще розпізнавання "вже в worktree" (.worktrees/ і .claude/worktrees/) + ідемпотентний bun install у worktree-блоці - .gitignore: .claude/scheduled_tasks.lock (з правила adr) Відкинуто (scope creep — автодетект правил, яких цей docs-only репо не потребує): - нові правила bun/doc-files/js/local-ai/test, skill doc-files, plugin @7n/rules-lang-js (тягнув eslint/oxlint/jscpd/knip/stylelint назад — саме те, що прибрано в PR #60) - rtk-хуки (.claude/settings.json, .cursor/hooks.json, .pi/extensions/rtk.ts) - переписані CLAUDE.md/AGENTS.md і n-taze-файли під multi-ecosystem lang-plugin архітектуру Відкинуто n-ga.mdc/n-text.mdc/n-adr.mdc — контентні покращення там приносили новий лінт-борг (cspell unknown words, MD031/032/038); не варті рефлоу заради формулювань. Не вирішено (винесено окремо в /n-llm-patch): застарілий `npx @7n/mt worktree create` у 3 skill-файлах — зашитий у шаблоні @7n/rules (skills/{taze,lint,adr-normalize}/main.json → worktree:true), не контролюється через .n-rules.json. Co-Authored-By: Claude Fable 5 --- .cursor/rules/n-changelog.mdc | 83 +++++++++++++++++++++++++ .cursor/rules/n-security.mdc | 52 +++------------- .cursor/skills/n-adr-normalize/SKILL.md | 8 ++- .cursor/skills/n-lint/SKILL.md | 8 ++- .gitignore | 2 + bun.lock | 8 +-- package.json | 4 +- 7 files changed, 109 insertions(+), 56 deletions(-) diff --git a/.cursor/rules/n-changelog.mdc b/.cursor/rules/n-changelog.mdc index dd1ecb9..3ebec92 100644 --- a/.cursor/rules/n-changelog.mdc +++ b/.cursor/rules/n-changelog.mdc @@ -5,3 +5,86 @@ alwaysApply: true --- У кожному **пакетному** workspace (каталог із `package.json` або `pyproject.toml`) має бути власний **`CHANGELOG.md`**. Спільного на репозиторій змісту змін **не існує** — кожен пакет веде свій. Маніфест версії: **JS/Bun/npm** — `package.json` (`version`); **Python** — `pyproject.toml` (`[project].version` або `[tool.poetry].version`). + +## Дві моделі бази порівняння + +Режим визначається автоматично з маніфесту. + +### registry-published (npm / PyPI) + +**npm:** непорожнє `name`, не `private: true`, масив `files`. + +**Python:** статичні `project.name` і `project.version` у `pyproject.toml` (або Poetry-секція). + +1. **Локальна `version` ≠ опублікованій** (npm / PyPI): drift поза CI → **fail** (ручний bump заборонено; навіть із change-файлом). Відкоти `version`. +2. **Версії збігаються**, але в git є **релевантні** зміни без change-файлу → fail. Для npm `"CHANGELOG.md"` має бути в `files` (публікується разом із пакетом). +3. **Реєстр недосяжний** — fail-safe pass. +4. **Немає релевантних змін** — pass. + +### local-only + +**npm:** `private: true` або без `files`. **Python:** без пари name+version для реєстру. База залежить від гілки: + +1. На **`dev`** local-only не активний (крім незакомічених registry-published). +2. На **`main`** — diff від **`origin/main`** (попередній опублікований `main`); без remote — від `HEAD~1`. **`dev` не використовується** як база на `main`. +3. На **feature-гілці** — merge-base з **`dev`**, якщо є; інакше з **`main`** (репо без `dev`). За наявності `origin/*` беремо новішу з двох баз (локальна гілка-кандидат vs `origin/`-версія) — застарілий локальний `main`/`dev` не має перекривати вже інтегровану в origin історію, і навпаки. +4. Drift `version` від бази → **fail** (ручний bump заборонено). Зміни фіксуй change-файлом; bump зробить CI. + +Якщо немає git або немає `dev`/`main`/`origin/main` — local-only пропускається. + +Merge-коміт (готовий, з другим предком, або `MERGE_HEAD` під час незавершеного `git commit`) пропускається цілком — changeset документують feature-коміти, а не інтеграційний merge. + +## Чеклист агента (деталі) + +Основний робочий алгоритм — «перед фінальною відповіддю виконай `npx @7n/rules lint changelog`, познач результат рядком `Changelog: …`» (AGENTS.md/AGENTS.template.md); тут лише уточнення, що саме перевіряється. + +**Інверсія (за замовчуванням не вимагають change-файлу):** + +- зміни **лише** під `docs/` або `doc/`; +- синхронізований із `@7n/rules` інструментарій під `.cursor/` (канонічні правила й скіли) і `.claude/` (ADR-хуки) — це дзеркало tooling-пакета, а не логіка воркспейсу; +- будь-які зміни в **корені монорепо** (воркспейс `.` за наявності підпакетів) — корінь веде glue/конфіг/tooling, власного CHANGELOG не має; помітні зміни документують підпакети. Сюди потрапляють і кореневі `AGENTS.md` / `CLAUDE.md`, і bump `@7n/rules` у `devDependencies`; +- файли під **`.gitignore`**. + +**Вимагають change-файл** — усі інші зміни в каталозі workspace (код, rego, правила, скіли, конфіги, тести тощо). Виняток `.cursor/` / `.claude/` **не** поширюється на джерело правил у репо `@7n/rules` — воно лежить під `npm/`, тож зміни в ньому далі вимагають change-файлу. + +Ніколи не редагуй `version` і `CHANGELOG.md` вручну — навіть для hotfix; єдиний артефакт зміни — change-файл (`npx @7n/n ch [--bump ] [--section ] [--message "<…>"]`), bump/секцію CHANGELOG формує `n-rules release` у CI на `main`. + +Канонічне pre-commit wiring (крок `npm-changelog` у `hk.pkl`, autofix через `N_RULES_CHANGELOG_AUTOFIX=1`) — деталь `npm-module.mdc`, тут не дублюється. + +Перевірка програмна (`changelog/consistency/main.mjs`, delta-гейт присутності — `changelog/presence/main.mjs`). + +## Формат CHANGELOG.md + +[Keep a Changelog 1.1.0](https://keepachangelog.com/uk/1.1.0/), мова — українська, новіші версії зверху. + +```md title="/CHANGELOG.md" +# Changelog + +Усі помітні зміни цього пакета документуються тут. + +Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/). + +## [1.2.3] - 2026-05-05 + +### Added + +- ... + +### Changed + +- ... + +### Fixed + +- ... +``` + +Секції — підмножина `### Added`, `### Changed`, `### Fixed`, `### Removed` (одна або кілька). + +Механічно `main.mjs` (`checkChangelogFormat`) перевіряє лише наявність рядка `# Changelog` (H1). Точна лексика секцій і порядок версій (новіші зверху) — конвенція, яку check **не** валідує: наявні `CHANGELOG.md` у цьому репо містять і нестандартні секції (`### BREAKING`, `### Notes`, `### TODO` тощо) з історичних причин, тож строга валідація словника секцій зробила б check несумісним із власною практикою репо. Дотримання — на розсуд автора change-файлу. + +## Post-release інваріант (гарантує CI) + +Перша (верхня) секція `## [version]` у `CHANGELOG.md` дорівнює полю `version` у маніфесті — але це **post-release** твердження, яке забезпечує `n-rules release` у CI, агрегуючи change-файли (bump `version` + генерація секції + git-тег `@`). **Локально цю рівність руками не підтримують**: у feature-флоу `version`/`CHANGELOG.md` не чіпають, тож верхня секція може відставати від майбутньої версії — це нормально. Drift `version` поза CI (vs реєстр / vs git-база) ловить цей concern (`consistency`) як заборонений ручний bump. + +Інструкції щодо bump `version` і редагування `CHANGELOG.md` живуть **лише** в правилі `changelog` (деталізація моделі порівняння — `comparison-models.mdc` поряд) — джерелі істини. Інші правила (зокрема `npm-module`) їй підпорядковані щодо формату/моделі й власних інструкцій bump/CHANGELOG не дублюють; команду створення change-файлу (`npx @7n/n ch`) й заборону ручного bump вони лише повторюють як нагадування. diff --git a/.cursor/rules/n-security.mdc b/.cursor/rules/n-security.mdc index 9fc38b9..d344dc6 100644 --- a/.cursor/rules/n-security.mdc +++ b/.cursor/rules/n-security.mdc @@ -11,42 +11,6 @@ version: '2.1' `npx @7n/rules fix security` -- `lint-security.yml`: - -```yaml -name: Lint Security - -on: - push: - branches: - - dev - - main - - pull_request: - branches: - - dev - - main - -concurrency: - group: ${{ github.ref }}-${{ github.workflow }} - cancel-in-progress: true - -jobs: - security: - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - fetch-depth: 0 - - - uses: trufflesecurity/trufflehog@main - with: - extra_args: --results=verified,unknown -``` - - `package.json`: ```json @@ -56,14 +20,6 @@ jobs: } ``` -## Наявність кроку TruffleHog у CI workflow - -Rego-пакет: `security.lint_security_yml` - -Цільові файли: `.github/workflows/lint-security.yml` - -Перевіряє, що серед `uses:` у workflow присутній крок `trufflesecurity/trufflehog@main`. Очікуваний перелік action-refs (не-`actions/*`) береться з `--data` через [lint-security.yml.snippet.yml](./template/lint-security.yml.snippet.yml). Універсальні кроки (`actions/*`) перевіряє `ga.workflow_common`. - ## Заборона `trufflehog` у залежностях `package.json` Rego-пакет: `security.package_json` @@ -114,3 +70,11 @@ Concern `security.sample_secret` (`js/sample_secret.mjs`) сканує всі п **Важливо:** один regex-pattern на рядок, без TOML-обгортки; коментарі починаються з `#`. Перевірка (JS): `js/trufflehog.mjs` — впевнюється, що файл `.trufflehog-exclude` існує в корені й містить канонічні шаблони. + +## Наявність кроку TruffleHog у CI workflow + +Rego-пакет: `security.lint_security_yml` + +Цільові файли: `.github/workflows/lint-security.yml` + +Перевіряє, що серед `uses:` у workflow присутній крок `trufflesecurity/trufflehog@main`. Очікуваний перелік action-refs (не-`actions/*`) береться з `--data` через [lint-security.yml.snippet.yml](./template/lint-security.yml.snippet.yml). Універсальні кроки (`actions/*`) перевіряє `ga.workflow_common`. diff --git a/.cursor/skills/n-adr-normalize/SKILL.md b/.cursor/skills/n-adr-normalize/SKILL.md index 7b50e73..71e1805 100644 --- a/.cursor/skills/n-adr-normalize/SKILL.md +++ b/.cursor/skills/n-adr-normalize/SKILL.md @@ -20,7 +20,9 @@ git branch --show-current **Root-assert.** Якщо `pwd` **не** збігається з виводом `git rev-parse --show-toplevel` — ти в **піддиректорії** робочого дерева (worktree-шляхи нижче відносні до кореня репо). Спершу перейди в корінь: `cd ` (literal-шлях із виводу), і лише тоді продовжуй preflight. Не створюй worktree з піддиректорії — `cd .worktrees/<…>` звідти впаде. -Якщо `git rev-parse --show-toplevel` показав, що ти **не** в `.worktrees/`, візьми вивід `git branch --show-current` як `` і виконай **literal-команди без shell expansion** (без command substitution, variable expansion чи backticks). Наприклад, якщо поточна гілка `feature/x`: +**Вже ізольований — нічого не створюй.** Якщо `git rev-parse --show-toplevel` містить сегмент `.worktrees/<…>` (репо-конвенція) **або** `.claude/worktrees/<…>` (worktree харнесу Claude Code — туди `npx @7n/mt worktree create` класти заборонено, `n-worktree.mdc`) — ти вже виконуєшся в окремому git-worktree. Preflight пройдено: нічого не створюй, нікого не питай про назву гілки — переходь одразу до Кроку 0.1. + +Інакше, якщо toplevel не містить жодного з цих сегментів, візьми вивід `git branch --show-current` як `` і виконай **literal-команди без shell expansion** (без command substitution, variable expansion чи backticks). Наприклад, якщо поточна гілка `feature/x`: ```bash npx @7n/mt worktree create "feature/x-adr-normal" "n-adr-normal: worktree-only skill" @@ -29,10 +31,10 @@ cd ".worktrees/feature-x-adr-normal" Тобто branch-argument лишає slash як у git-гілці, а шлях для `cd` бере sanitized форму: slash → `-`. -**Крок 0.1 — bootstrap у новому дереві (після `cd`).** Дерево щойно створене й **без** `node_modules`. Постав залежності локально — тоді `npx @7n/rules ` бере локальну копію без походу в реєстр: +**Крок 0.1 — bootstrap (якщо в дереві ще нема `node_modules`).** Свіжостворений worktree (Крок 0) точно без `node_modules`; вже ізольований harness-worktree може мати їх або ні — постав локально, тоді `npx @7n/rules ` бере локальну копію без походу в реєстр: ```bash -bun install +test -d node_modules || bun install ``` diff --git a/.cursor/skills/n-lint/SKILL.md b/.cursor/skills/n-lint/SKILL.md index fad2375..36887de 100644 --- a/.cursor/skills/n-lint/SKILL.md +++ b/.cursor/skills/n-lint/SKILL.md @@ -19,7 +19,9 @@ git branch --show-current **Root-assert.** Якщо `pwd` **не** збігається з виводом `git rev-parse --show-toplevel` — ти в **піддиректорії** робочого дерева (worktree-шляхи нижче відносні до кореня репо). Спершу перейди в корінь: `cd ` (literal-шлях із виводу), і лише тоді продовжуй preflight. Не створюй worktree з піддиректорії — `cd .worktrees/<…>` звідти впаде. -Якщо `git rev-parse --show-toplevel` показав, що ти **не** в `.worktrees/`, візьми вивід `git branch --show-current` як `` і виконай **literal-команди без shell expansion** (без command substitution, variable expansion чи backticks). Наприклад, якщо поточна гілка `feature/x`: +**Вже ізольований — нічого не створюй.** Якщо `git rev-parse --show-toplevel` містить сегмент `.worktrees/<…>` (репо-конвенція) **або** `.claude/worktrees/<…>` (worktree харнесу Claude Code — туди `npx @7n/mt worktree create` класти заборонено, `n-worktree.mdc`) — ти вже виконуєшся в окремому git-worktree. Preflight пройдено: нічого не створюй, нікого не питай про назву гілки — переходь одразу до Кроку 0.1. + +Інакше, якщо toplevel не містить жодного з цих сегментів, візьми вивід `git branch --show-current` як `` і виконай **literal-команди без shell expansion** (без command substitution, variable expansion чи backticks). Наприклад, якщо поточна гілка `feature/x`: ```bash npx @7n/mt worktree create "feature/x-lint" "n-lint: worktree-only skill" @@ -28,10 +30,10 @@ cd ".worktrees/feature-x-lint" Тобто branch-argument лишає slash як у git-гілці, а шлях для `cd` бере sanitized форму: slash → `-`. -**Крок 0.1 — bootstrap у новому дереві (після `cd`).** Дерево щойно створене й **без** `node_modules`. Постав залежності локально — тоді `npx @7n/rules ` бере локальну копію без походу в реєстр: +**Крок 0.1 — bootstrap (якщо в дереві ще нема `node_modules`).** Свіжостворений worktree (Крок 0) точно без `node_modules`; вже ізольований harness-worktree може мати їх або ні — постав локально, тоді `npx @7n/rules ` бере локальну копію без походу в реєстр: ```bash -bun install +test -d node_modules || bun install ``` diff --git a/.gitignore b/.gitignore index 3fc1def..68b5fc6 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ dist/ .codex/hooks/.normalize.lock .claude/worktrees/ .worktrees/ +# @7n/rules (adr) — локальні артефакти Stop-hook, не коміти +.claude/scheduled_tasks.lock diff --git a/bun.lock b/bun.lock index 323e585..d23f4e1 100644 --- a/bun.lock +++ b/bun.lock @@ -5,8 +5,8 @@ "": { "name": "mono", "devDependencies": { - "@7n/rules": "^1.43.1", - "@7n/rules-ci-github": "^1.9.0", + "@7n/rules": "^1.44.1", + "@7n/rules-ci-github": "^1.9.2", "@nitra/cspell-dict": "^2.2.2", }, }, @@ -24,9 +24,9 @@ "@7n/mt-linux-x64": ["@7n/mt-linux-x64@0.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-c4b1c435vfGZcRKkLnWcoKs8O6KTATFFx6gPGnid4ee+JDqzyhtxb5kzxYkcTQySO8iBHPAtUMEOBCHDrF1UjQ=="], - "@7n/rules": ["@7n/rules@1.43.1", "", { "dependencies": { "@7n/llm-lib": "^2.8.2", "@7n/mt": "^0.5.1", "@zed-industries/agent-client-protocol": "^0.4.5", "ajv": "^8.20.0", "cli-progress": "^3.12.0", "github-actionlint": "^1.7.12", "globby": "^16.0.0", "ignore": "^7.0.5", "markdownlint-cli2": "^0.22.1", "oxc-parser": "^0.137.0", "picomatch": "^4.0.4", "smol-toml": "^1.7.0", "v8r": "^6.1.0", "yaml": "^2.9.0", "zod": "^4.4.3" }, "optionalDependencies": { "@agentclientprotocol/claude-agent-acp": "^0.59.0", "@earendil-works/pi-ai": "0.80.10", "@earendil-works/pi-coding-agent": "0.80.10" }, "bin": { "n-rules": "bin/n-rules.js", "n-cursor": "bin/n-rules.js" } }, "sha512-vsZyaEYwePoibaCmh0UQDpb93qMOjL7m76+GvBdxTCopj0Ej2A34iS58wQ4jLL+NPh26R5/xWLJKcT4WqGSPaw=="], + "@7n/rules": ["@7n/rules@1.44.1", "", { "dependencies": { "@7n/llm-lib": "^2.8.2", "@7n/mt": "^0.5.1", "@zed-industries/agent-client-protocol": "^0.4.5", "ajv": "^8.20.0", "cli-progress": "^3.12.0", "github-actionlint": "^1.7.12", "globby": "^16.0.0", "ignore": "^7.0.5", "markdownlint-cli2": "^0.22.1", "oxc-parser": "^0.137.0", "picomatch": "^4.0.4", "smol-toml": "^1.7.0", "v8r": "^6.1.0", "yaml": "^2.9.0", "zod": "^4.4.3" }, "optionalDependencies": { "@agentclientprotocol/claude-agent-acp": "^0.59.0", "@earendil-works/pi-ai": "0.80.10", "@earendil-works/pi-coding-agent": "0.80.10" }, "bin": { "n-rules": "bin/n-rules.js", "n-cursor": "bin/n-rules.js" } }, "sha512-s7I57g5vFQkXrYeTxugwdTR/AVTrsWuRUfOaavhIWP6gdmhbwE5eMzLNLLCpelfbkO41Ae3SmhwmiTzHSYzlAQ=="], - "@7n/rules-ci-github": ["@7n/rules-ci-github@1.9.0", "", { "dependencies": { "yaml": "^2.9.0" }, "peerDependencies": { "@7n/rules": ">=1.2.0" } }, "sha512-X/f2qKb1XT/QDryJXgK2L/vzpJY8lsBLUTazK8p3L9M5pyGYYY3edkRnz9WgavrYi0TXVE6pzhZcOZUagvocYA=="], + "@7n/rules-ci-github": ["@7n/rules-ci-github@1.9.2", "", { "dependencies": { "yaml": "^2.9.0" }, "peerDependencies": { "@7n/rules": ">=1.2.0" } }, "sha512-fPPbwixa/VjSnp6zD7qBWeTAp3CNDEh9CoB0jSo9duUH/w4Kqu1CU1txNYA/QR2iCr88rtjWWwRvGOg3D1Poug=="], "@agentclientprotocol/claude-agent-acp": ["@agentclientprotocol/claude-agent-acp@0.59.0", "", { "dependencies": { "@agentclientprotocol/sdk": "1.2.1", "@anthropic-ai/claude-agent-sdk": "0.3.207", "zod": "^3.25.0 || ^4.0.0" }, "bin": { "claude-agent-acp": "dist/index.js" } }, "sha512-GejLH5qxsI5IoSDfhyOVDEsRNxqi6y0Rcj5FstVeOwMACSht/bUXII0HILbzOQNoA5qlyZle3FRvf+CAjD7Rpg=="], diff --git a/package.json b/package.json index 6cca852..80e532e 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,8 @@ "CHANGELOG.md" ], "devDependencies": { - "@7n/rules": "^1.43.1", - "@7n/rules-ci-github": "^1.9.0", + "@7n/rules": "^1.44.1", + "@7n/rules-ci-github": "^1.9.2", "@nitra/cspell-dict": "^2.2.2" }, "engines": {