Imported from nitra/7n-test (
.cursor/skills/n-taze/SKILL.md). Install upstream withnpx skills add nitra/7n-test --skill n-taze. Copyright stays with the author.
[!IMPORTANT] Worktree-only skill. Виконується виключно в окремому git-worktree (
.worktrees/<current-branch>-taze/) і не паралелиться — один інстанс за раз.
Крок 0 — preflight (обовʼязковий, перед будь-якими іншими діями). Якщо перевірка падає — STOP: не питай користувача про назву гілки, а сам створи worktree від поточної гілки за конвенцією <current-branch>-taze. Суфікс taze — коротка (до 10 символів) транслітерація задачі. Не виконуй жоден наступний крок скіла, поки preflight не завершився успіхом.
pwd
git rev-parse --show-toplevel
git branch --show-current
Root-assert. Якщо pwd не збігається з виводом git rev-parse --show-toplevel — ти в піддиректорії робочого дерева (worktree-шляхи нижче відносні до кореня репо). Спершу перейди в корінь: cd <toplevel> (literal-шлях із виводу), і лише тоді продовжуй preflight. Не створюй worktree з піддиректорії — cd .worktrees/<…> звідти впаде.
Якщо git rev-parse --show-toplevel показав, що ти не в .worktrees/, візьми вивід git branch --show-current як <current-branch> і виконай literal-команди без shell expansion (без command substitution, variable expansion чи backticks). Наприклад, якщо поточна гілка feature/x:
npx @7n/mt worktree create "feature/x-taze" "n-taze: worktree-only skill"
cd ".worktrees/feature-x-taze"
Тобто branch-argument лишає slash як у git-гілці, а шлях для cd бере sanitized форму: slash → -.
Крок 0.1 — bootstrap у новому дереві (після cd). Дерево щойно створене й без node_modules. Постав залежності локально — тоді npx @7n/rules <cmd> бере локальну копію без походу в реєстр:
bun install
n-taze — Оновлення версій проекту
Мета
Оновити всі модулі проекту (npm/bun-залежності, а за наявності Cargo.toml/pyproject.toml — і Rust-крейти/Python-пакети) до останніх версій, виявити major-оновлення, перевірити сумісність змін з кодом проекту і за потреби зрефакторити несумісні місця.
Оркестрація (npm/bun-, Rust- і Python-гілка) — не одним промптом
npx @7n/rules skill pi|cursor|codex taze не передає цей файл одним суцільним
промптом в один агентський хід — так робив старий дизайн, і саме тому реальний
прогін міг зависати без жодної діагностики (один величезний непрозорий хід на
весь монорепо, без проміжних зупинок для перевірки прогресу). Замість цього npm/skills/taze/js/orchestrate.mjs:
- Детерміновано, без LLM, виконує кроки 1-3 нижче для всіх гілок: npm/bun (бекап →
bunx taze -w -r latest→bun install→n-rules taze diff); якщо знайденіCargo.toml, Rust (бекап →cargo upgrade --incompatible allow→cargo update→collectCargoDiffзcargo-diff.mjs, детермінований cargo-еквівалентn-rules taze diff— парсить коженCargo.tomlчерезsmol-toml, класифікує major за тим самим правилом caret-семантики, включно з Cargo-скороченими версіями"1"/"0.4"); якщо знайдений кореневийpyproject.toml, Python (бекап → по кожній прямій залежностіuv remove <pkg>+uv add <pkg>[extras] --bounds lower→collectUvDiffзuv-diff.mjs, парсить PEP 508/PEP 440 черезsmol-toml, той самий caret-принцип класифікації). - Для кожного окремого major-пакета/крейта з diff-у — один ізольований, обмежений виклик обраного раннера (кроки 4-6, лише для цього запису; промпт генерує
buildDependencyPrompt/buildCargoDependencyPrompt/buildUvDependencyPrompt, не цей SKILL.md). - Детерміновано прибирає бекапи (крок 7) і компонує звіт (крок 8) з результатів усіх ітерацій.
Якщо Cargo.toml знайдені, але cargo-edit не встановлено — Rust-гілка не
блокує інші гілки: оркестратор пропускає Rust-обробку і в звіті перелічує
знайдені Cargo.toml як такі, що потребують ручного прогону (нижче). Так само,
якщо pyproject.toml знайдений, але uv не встановлено — Python-гілка не
блокує інші гілки.
Переваги: падіння/timeout на одному пакеті/крейті не втрачає прогрес по інших; кожен виклик успадковує власний timeout раннера (короткий, бо скоуп малий — один запис, не весь монорепо); видно прогрес по записах, а не чорну скриньку.
Кроки 1-8 нижче лишаються джерелом правди щодо ЗМІСТУ роботи (що саме робить кожен крок) — оркестратор їх виконує програмно (1-3/7/8) або як промпт на один запис (4-6) для всіх гілок.
Передумови
- Чисте робоче дерево (
git statusбез незакомічених змін уpackage.json/bun.lock/node_modules) — інакше різницю не відрізнити від оновлення. - Встановлений
bunі доступнийbunx. - Запуск з кореня проекту (де лежить
package.json/bun.lock). - Якщо в проекті є хоч один
Cargo.toml(не вnode_modules/.worktrees) — бажано встановленийcargo-edit(cargo install cargo-edit, дає командуcargo upgrade). Без нього major-бампи Rust-залежностей неможливо застосувати детерміновано (голийcargo updateпіднімає лише semver-сумісні версії) — оркестратор пропускає Rust-гілку (без блокування інших гілок) і перелічує знайденіCargo.tomlу звіті як такі, що потребують ручного прогону кроків 2-8 нижче. - Якщо в проекті є кореневий
pyproject.toml— бажано встановленийuv. Без нього major-бампи Python-залежностей неможливо застосувати детерміновано — оркестратор пропускає Python-гілку (без блокування інших гілок) і перелічуєpyproject.tomlу звіті як такий, що потребує ручного прогону кроків 2-8 нижче.
0.2. Детекція Rust-крейтів
find . -name Cargo.toml -not -path "*/node_modules/*" -not -path "*/.worktrees/*" -not -path "*/target/*"
Якщо список непорожній — паралельно з npm-гілкою виконуються кроки 1–8 у Rust-варіанті (позначені нижче як «Rust-гілка»). Якщо порожній — Rust-кроки повністю пропускаються.
0.3. Детекція Python/uv-проекту
test -f pyproject.toml && echo pyproject.toml
v1: лише кореневий pyproject.toml (uv-конвенція single-project, без обходу workspace-членів, на відміну від Cargo). Якщо файл існує — паралельно виконуються кроки 1–8 у Python-варіанті (позначені нижче як «Python-гілка»). Якщо відсутній — Python-кроки повністю пропускаються.
Workflow
1. Зафіксувати стартовий стан
Перед оновленням зберегти список поточних версій, щоб потім обчислити, які модулі стрибнули через major:
cp package.json package.json.taze-bak
cp bun.lock bun.lock.taze-bak
(У monorepo — також усі */package.json воркспейсів. Файли тимчасові, видалити в кінці.)
Rust-гілка — для кожного знайденого на кроці 0.2 Cargo.toml (включно з кореневим, якщо є):
cp Cargo.toml Cargo.toml.taze-bak
cp Cargo.lock Cargo.lock.taze-bak # якщо lock-файл спільний на workspace — достатньо одного бекапу в корені
Python-гілка — якщо на кроці 0.3 знайдений pyproject.toml:
cp pyproject.toml pyproject.toml.taze-bak
cp uv.lock uv.lock.taze-bak # якщо є
2. Запустити оновлення
bunx taze -w -r latest
bun install
-w— записати нові версії уpackage.json.-r— рекурсивно по всіх воркспейсах.latest— піднімати навіть major.
Rust-гілка:
cargo upgrade --incompatible allow
cargo update
cargo upgrade(зcargo-edit) переписує вимоги версій у кожномуCargo.tomlworkspace-у на останні;--incompatible allowявно дозволяє перетинати major-межу (аналог-r latestу taze) — без цього флага incompatible-оновлення за замовчуванням ігноруються.cargo updateпісля цього синхронізуєCargo.lockз новими вимогами.
Python-гілка:
for pkg in $(<список прямих залежностей із [project].dependencies>); do
uv remove "$pkg"
uv add "$pkg" --bounds lower
done
uv не має єдиної команди "підняти все до latest, навіть через major" (на відміну від bunx taze -w -r latest/cargo upgrade --incompatible allow) — підтверджено емпірично: uv add <pkg> на вже присутній залежності НЕ переписує specifier без попереднього uv remove. --bounds lower записує нижню межу без верхньої (>=<latest>), коректно зберігає [extras]. Провал одного пакета (мережа/резолюція) не втрачає прогрес по інших — оркестратор best-effort відновлює оригінальний рядок, якщо uv add не вдався після uv remove.
3. Виявити major-оновлення
Не порівнюй
package.jsonвручну. Класифікацію semver несе CLI — детерміновано, по всіх воркспейсах.
n-rules taze diff
Друкує компактний JSON: { "major": [{workspace, pkg, from, to}], "minorPatch": <N>, "totalChanged": <N> }. major — список залежностей, у яких змінилась найлівіша ненульова компонента semver (1.x→2.x, 0.4.x→0.5.x, 0.0.3→0.0.4); саме він іде в кроки 4–6. minorPatch — лічба сумісних (для звіту в кроці 8).
Покриває прямі залежності з package.json (root + воркспейси). Транзитивні major-стрибки (bun.lock) — за потреби переглянь окремо; основний ризик breaking-змін — у прямих.
Rust-гілка (оркестровано) — collectCargoDiff (npm/skills/taze/js/cargo-diff.mjs) робить те саме детерміновано: парсить кожен Cargo.toml.taze-bak/Cargo.toml через smol-toml, порівнює dependencies/dev-dependencies/build-dependencies (рядок чи { version = "...", features = [...] }), класифікує за тим самим правилом caret-семантики — Cargo-скорочені версії ("1", "0.4") трактуються як відсутні компоненти = 0. Ручний прогін поза оркестратором — той самий принцип: diff Cargo.toml.taze-bak Cargo.toml по рядках <name> = "<version>".
Python-гілка (оркестровано) — collectUvDiff (npm/skills/taze/js/uv-diff.mjs) робить те саме детерміновано: парсить pyproject.toml.taze-bak/pyproject.toml через smol-toml, порівнює [project].dependencies (масив PEP 508-рядків, матчинг за іменем пакета — не за позицією), дістає нижню межу PEP 440-специфікатора (extractLowerBoundVersion) і класифікує за тим самим правилом caret-семантики. Ручний прогін поза оркестратором — той самий принцип: diff pyproject.toml.taze-bak pyproject.toml по записах [project].dependencies.
4. Зібрати breaking changes по кожному major-оновленню
Для кожного модуля зі списку зібрати фактичні відмінності одним з джерел (у порядку пріоритету):
- CHANGELOG / Releases репозиторію модуля — найшвидше і найточніше. Адресу репозиторію взяти з
package.jsonмодуля уnode_modules/<name>/package.json(полеrepository). Дістати релізи між старою і новою версією. - Git-різниця в
node_modules/<name>— якщо CHANGELOG відсутній або неінформативний, порівняти попередню версію (з кешу bun:~/.bun/install/cache/<name>@<old-version>/) з новою (node_modules/<name>/) черезdiff -rпоdist//*.d.ts/ публічних entry-points зexports. - Якщо немає кешованої старої версії — встановити її окремо в тимчасову теку (
mkdir -p /tmp/taze-old && cd /tmp/taze-old && bun add <name>@<old-version>) і порівняти.
Цікавлять: видалені/перейменовані експорти, змінені сигнатури функцій, змінені типи, змінена поведінка за замовчуванням, видалені CLI-прапорці.
Rust-гілка — адресу репозиторію взяти з поля repository/documentation крейта на crates.io (https://crates.io/crates/<name>) або з [package.metadata]; CHANGELOG зазвичай у CHANGELOG.md репозиторію або в GitHub Releases. Якщо немає — cargo doc різниця по публічному API (pub fn/pub struct/pub trait) між закешованою старою версією (~/.cargo/registry/src/*/<name>-<old-version>/) і новою (~/.cargo/registry/src/*/<name>-<new-version>/) через diff -r src/.
Python-гілка — адресу репозиторію взяти зі сторінки https://pypi.org/project/<name>/ (поле "Homepage"/"Source"); CHANGELOG зазвичай у CHANGELOG.md/HISTORY.md репозиторію або в GitHub Releases. Якщо немає — різниця по публічному API між закешованою старою версією (~/.cache/uv/) і новою (.venv/lib/python*/site-packages/<name>/).
5. Перевірити сумісність з кодом проекту
Для кожного breaking change знайти його використання в коді проекту:
rg -n "<імпорт|функція|опція>" --type ts --type js --type vue
Класифікувати:
- сумісно — проект не використовує зачеплене API → нічого не робити.
- несумісно — використання знайдено → перейти до п. 6.
Rust-гілка:
rg -n "<use-шлях|функція|макрос>" --type rust
Та сама класифікація сумісно/несумісно.
Python-гілка:
rg -n "<імпорт|функція|клас>" --type py
Та сама класифікація сумісно/несумісно.
6. Рефакторинг несумісних місць
Для кожного несумісного місця — застосувати міграцію згідно з changelog модуля (перейменувати імпорт, оновити сигнатуру виклику, замінити видалену опцію еквівалентом тощо). Після правок:
npx @7n/rules lint
bun run typecheck # якщо є
bun test # якщо є
Rust-гілка — після правок:
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
Python-гілка — після правок (залежно від того, що реально налаштовано в проєкті):
ruff check .
mypy .
pytest
Якщо міграція нетривіальна або неоднозначна — не вгадувати, залишити TODO у коді з посиланням на CHANGELOG і винести в підсумковий звіт як ручну дію.
7. Прибрати тимчасові файли
rm package.json.taze-bak bun.lock.taze-bak
(І решту бекапів воркспейсів, якщо створювались.)
Rust-гілка:
rm Cargo.toml.taze-bak Cargo.lock.taze-bak
(І бекапи по кожному workspace-члену, якщо створювались окремо.)
Python-гілка:
rm pyproject.toml.taze-bak uv.lock.taze-bak
8. Звіт користувачу
Коротко в одному повідомленні:
- Оновлено (minor/patch): кількість пакетів (
minorPatchіз кроку 3), без деталей. - Major-оновлення: список
<name>: <old> → <new>з посиланням на release notes. - Зрефакторено автоматично: список файлів і коротко що саме змінено.
- Потребує ручного втручання: список TODO з причиною (нетривіальна міграція / неоднозначність / падіння тестів).
- Стан перевірок:
lint/typecheck/test— pass/fail з номером рядка, де впало.
Якщо на кроці 0.2 знайдені Rust-крейти — додати окрему секцію Rust-крейти з тим самим переліком (оновлено / major / зрефакторено / потребує ручного втручання), і в Стан перевірок — окремо cargo fmt / cargo clippy / cargo test. Якщо на кроці 0.3 знайдений pyproject.toml — так само окрема секція Python-пакети (uv), і в Стан перевірок — окремо ruff / mypy / pytest.
Примітка
- Не запускати
npx @7n/rules lintпаралельно з іншими ESLint-задачами — діє правило з кореневогоCLAUDE.md. - Якщо проект —
npm/пакет цього репо, після змін уpackage.json/ коді треба піднятиversionі додати запис уCHANGELOG.mdзгідно зnpm/CLAUDE.md. - При великій кількості major-оновлень розбити PR по одному модулю на коміт — щоб
git bisectзалишався корисним. Це стосується і Rust-крейтів, і Python-пакетів окремо від npm-пакетів. cargo upgrade --incompatible allowредагуєCargo.tomlнавіть для залежностей без доступних breaking changes в змінах API — завжди звіряй крок 4 (CHANGELOG) перед тим, як вважати оновлення безпечним, а не лише факт успішної компіляції.uv add <pkg>на вже присутній залежності — no-op (specifier НЕ переписується); детерміновану гілку це не блокує (вона робитьuv removeспершу), але при ручному прогоні кроків 2-8 легко про це забути.