@7n/rules 1.31.0 → 1.32.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 +12 -0
- package/bin/n-rules.js +9 -2
- package/package.json +1 -1
- package/rules/{test → k8s/dremio_logging}/docs/index.md +2 -4
- package/rules/k8s/dremio_logging/docs/main.md +31 -0
- package/scripts/docs/skills-cli.md +1 -1
- package/scripts/lib/auto-worktree.mjs +72 -15
- package/scripts/lib/docs/auto-worktree.md +1 -1
- package/scripts/skills-cli.mjs +17 -0
- package/skills/taze/js/docs/index.md +4 -6
- package/skills/taze/js/docs/migration-cache.md +38 -0
- package/skills/taze/js/docs/orchestrate.md +1 -1
- package/skills/taze/js/orchestrate.mjs +37 -2
- package/rules/test/cargo_mutants_config/cargo_mutants_config.mdc +0 -18
- package/rules/test/cargo_mutants_config/concern.json +0 -8
- package/rules/test/cargo_mutants_config/data/cargo_mutants_config/mutants.toml.baseline +0 -7
- package/rules/test/cargo_mutants_config/docs/fix-cargo_mutants_config.md +0 -31
- package/rules/test/cargo_mutants_config/docs/index.md +0 -12
- package/rules/test/cargo_mutants_config/docs/main.md +0 -36
- package/rules/test/cargo_mutants_config/fix-cargo_mutants_config.mjs +0 -49
- package/rules/test/cargo_mutants_config/main.mjs +0 -62
- package/rules/test/lib/collect-test-file-offenders.mjs +0 -49
- package/rules/test/lib/docs/collect-test-file-offenders.md +0 -30
- package/rules/test/lib/docs/index.md +0 -9
- package/rules/test/location/concern.json +0 -8
- package/rules/test/location/docs/index.md +0 -11
- package/rules/test/location/docs/main.md +0 -36
- package/rules/test/location/location.mdc +0 -52
- package/rules/test/location/main.mjs +0 -70
- package/rules/test/main.json +0 -1
- package/rules/test/main.mdc +0 -26
- package/rules/test/no-bun-test-import/concern.json +0 -7
- package/rules/test/no-bun-test-import/docs/fix-no-bun-test-import.md +0 -31
- package/rules/test/no-bun-test-import/docs/index.md +0 -10
- package/rules/test/no-bun-test-import/docs/main.md +0 -36
- package/rules/test/no-bun-test-import/fix-no-bun-test-import.mjs +0 -51
- package/rules/test/no-bun-test-import/main.mjs +0 -105
- package/rules/test/no-bun-test-import/no-bun-test-import.mdc +0 -9
- package/rules/test/no-console-store-restore/concern.json +0 -7
- package/rules/test/no-console-store-restore/docs/index.md +0 -11
- package/rules/test/no-console-store-restore/docs/main.md +0 -44
- package/rules/test/no-console-store-restore/main.mjs +0 -55
- package/rules/test/no-console-store-restore/no-console-store-restore.mdc +0 -11
- package/rules/test/no-process-chdir/concern.json +0 -7
- package/rules/test/no-process-chdir/docs/index.md +0 -11
- package/rules/test/no-process-chdir/docs/main.md +0 -32
- package/rules/test/no-process-chdir/main.mjs +0 -40
- package/rules/test/no-process-chdir/no-process-chdir.mdc +0 -15
- package/rules/test/no-relative-fs-path/concern.json +0 -7
- package/rules/test/no-relative-fs-path/docs/index.md +0 -11
- package/rules/test/no-relative-fs-path/docs/main.md +0 -34
- package/rules/test/no-relative-fs-path/main.mjs +0 -239
- package/rules/test/no-relative-fs-path/no-relative-fs-path.mdc +0 -22
- package/rules/test/package_json/concern.json +0 -11
- package/rules/test/package_json/package_json.mdc +0 -18
- package/rules/test/package_json/package_json.rego +0 -25
- package/rules/test/package_json/template/package.json.contains.json +0 -6
- package/rules/test/sandbox-aware-test/concern.json +0 -7
- package/rules/test/sandbox-aware-test/docs/index.md +0 -11
- package/rules/test/sandbox-aware-test/docs/main.md +0 -55
- package/rules/test/sandbox-aware-test/main.mjs +0 -89
- package/rules/test/sandbox-aware-test/sandbox-aware-test.mdc +0 -28
- package/rules/test/stryker_config/concern.json +0 -8
- package/rules/test/stryker_config/data/stryker_config/docs/index.md +0 -13
- package/rules/test/stryker_config/data/stryker_config/docs/stryker-vue-macros-ignorer.md +0 -31
- package/rules/test/stryker_config/data/stryker_config/docs/stryker.config.baseline.md +0 -31
- package/rules/test/stryker_config/data/stryker_config/docs/stryker.config.vue.baseline.md +0 -35
- package/rules/test/stryker_config/data/stryker_config/stryker-vue-macros-ignorer.mjs +0 -48
- package/rules/test/stryker_config/data/stryker_config/stryker.config.baseline.mjs +0 -18
- package/rules/test/stryker_config/data/stryker_config/stryker.config.vue.baseline.mjs +0 -23
- package/rules/test/stryker_config/data/vitest_config/docs/index.md +0 -11
- package/rules/test/stryker_config/data/vitest_config/docs/vitest.config.baseline.md +0 -35
- package/rules/test/stryker_config/data/vitest_config/vitest.config.baseline.js +0 -22
- package/rules/test/stryker_config/docs/fix-stryker_config.md +0 -42
- package/rules/test/stryker_config/docs/index.md +0 -12
- package/rules/test/stryker_config/docs/main.md +0 -38
- package/rules/test/stryker_config/fix-stryker_config.mjs +0 -76
- package/rules/test/stryker_config/main.mjs +0 -504
- package/rules/test/stryker_config/stryker_config.mdc +0 -26
- package/rules/test/vitest-api-conventions/concern.json +0 -7
- package/rules/test/vitest-api-conventions/docs/index.md +0 -9
- package/rules/test/vitest-api-conventions/docs/main.md +0 -40
- package/rules/test/vitest-api-conventions/main.mjs +0 -153
- package/rules/test/vitest-api-conventions/vitest-api-conventions.mdc +0 -129
- package/rules/test/vitest-config-pool-forks/concern.json +0 -7
- package/rules/test/vitest-config-pool-forks/docs/index.md +0 -11
- package/rules/test/vitest-config-pool-forks/docs/main.md +0 -42
- package/rules/test/vitest-config-pool-forks/main.mjs +0 -41
- package/rules/test/vitest-config-pool-forks/vitest-config-pool-forks.mdc +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.32.1] - 2026-07-20
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- fix(n-rules/n-taze): не забруднювати дерево перед worktree-гейтом і не губити untracked-директорії при поверненні змін (#160)
|
|
8
|
+
|
|
9
|
+
## [1.32.0] - 2026-07-19
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- lint: правило test переїхало у @7n/rules-lang-js (vitest/stryker/ізоляція тестів), а концерн cargo_mutants_config — у правило rust плагіна @7n/rules-lang-rust (семантично канон rust-тулчейну; активація за glob Cargo.toml, у rust-only репо працює без lang-js) — у ядрі 23 правила, мовних правил не лишилось
|
|
14
|
+
|
|
3
15
|
## [1.31.0] - 2026-07-19
|
|
4
16
|
|
|
5
17
|
### Changed
|
package/bin/n-rules.js
CHANGED
|
@@ -101,7 +101,7 @@ import { syncClaudeConfig } from '../scripts/sync-claude-config.mjs'
|
|
|
101
101
|
import { syncGitignoreWorktree } from '../scripts/lib/sync-gitignore-worktree.mjs'
|
|
102
102
|
import { upgradeNRulesToLatestAndBunInstall } from '../scripts/upgrade-n-rules-and-install.mjs'
|
|
103
103
|
import { runRenameYamlExtensionsCli } from './rename-yaml-extensions.mjs'
|
|
104
|
-
import { runSkillsCli } from '../scripts/skills-cli.mjs'
|
|
104
|
+
import { isTazeOrchestratorSkillArgs, runSkillsCli } from '../scripts/skills-cli.mjs'
|
|
105
105
|
import { syncSetupBunDepsAction } from '../scripts/sync-setup-bun-deps-action.mjs'
|
|
106
106
|
|
|
107
107
|
const PACKAGE_NAME = '@7n/rules'
|
|
@@ -1729,7 +1729,14 @@ try {
|
|
|
1729
1729
|
}
|
|
1730
1730
|
// `ci` (plan) — read-only гейт-команда для CI-джоб: не мутує package.json
|
|
1731
1731
|
// (ensure дописав би devDependency прямо в чекауті pipeline-агента).
|
|
1732
|
-
|
|
1732
|
+
// `skill <runner> taze` — JS-оркестрований worktree-only шлях
|
|
1733
|
+
// (skills/taze/js/orchestrate.mjs): сам створює worktree і гейтить на
|
|
1734
|
+
// чистоту дерева ДО checkout. Мутація тут забруднила б дерево прямо
|
|
1735
|
+
// перед тим гейтом і провалювала б auto-create на інакше чистому дереві —
|
|
1736
|
+
// оркестратор сам робить self-upgrade devDependency вже ВСЕРЕДИНІ
|
|
1737
|
+
// щойно створеного worktree, після власного гейту чистоти.
|
|
1738
|
+
const skipDevDepsEnsure = command === 'skill' && isTazeOrchestratorSkillArgs(args)
|
|
1739
|
+
if (command !== 'ci' && !skipDevDepsEnsure) await ensureNRulesInRootDevDependencies(effectiveRoot)
|
|
1733
1740
|
// Підкоманди-оркестратори (hook/lint/skill/adr-normalize-local/taze/release тощо)
|
|
1734
1741
|
// можуть спавнити внутрішню agent/LLM-сесію — ADR Stop-hooks (capture/normalize)
|
|
1735
1742
|
// мають пропустити її як технічний шум, не людську думку (spec 2026-06-30).
|
package/package.json
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: Directory Index
|
|
3
|
-
title: npm/rules/
|
|
4
|
-
resource: npm/rules/
|
|
3
|
+
title: npm/rules/k8s/dremio_logging
|
|
4
|
+
resource: npm/rules/k8s/dremio_logging/
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# npm/rules/test
|
|
8
|
-
|
|
9
7
|
| Файл | Тип |
|
|
10
8
|
| ------------------- | --------- |
|
|
11
9
|
| [main.mjs](main.md) | JS Module |
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: main.mjs
|
|
4
|
+
resource: npm/rules/k8s/dremio_logging/main.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: a89abb0a
|
|
7
|
+
model: openai-codex/gpt-5.4-mini
|
|
8
|
+
tier: cloud-min
|
|
9
|
+
score: 95
|
|
10
|
+
issues: anchor-miss:(k8s.mdc),judge:inaccurate:0.99
|
|
11
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Огляд
|
|
15
|
+
|
|
16
|
+
`zkLogbackRootLevelViolation` перевіряє повідомлення, пов’язані з надто гучним `root` у `k8s.mdc`, а `lint` збирає ці перевірки в один pass і повертає результат без зупинки процесу. Поведінка read-only: правило не пише у ФС чи БД, працює fail-safe й не кидає винятків назовні.
|
|
17
|
+
|
|
18
|
+
## Поведінка
|
|
19
|
+
|
|
20
|
+
- `zkLogbackRootLevelViolation` — перевіряє, чи вбудований `logback.xml` у ZooKeeper-templated файлі не тримає `root` на надто гучному рівні; повертає текст порушення з міткою `` або `null`, якщо перевірка не потрібна або рівень уже прийнятний.
|
|
21
|
+
- `lint` — проходить по вказаних файлах у read-only режимі, збирає порушення для `k8s.dremio_logging` і не виводить помилки назовні; пропускає файли, які не вдалося прочитати, та позначає знайдені проблеми міткою ``.
|
|
22
|
+
|
|
23
|
+
## Публічний API
|
|
24
|
+
|
|
25
|
+
- zkLogbackRootLevelViolation — позначає ZooKeeper Helm-темплейт, якщо вбудований `logback.xml` має занадто гучний `root`-рівень (`info`, `debug`, `trace`) або взагалі не задає `root`; не чіпає файли, де `root` уже `warn`, `error` чи `off`.
|
|
26
|
+
- lint — запускає `k8s.dremio_logging` для ZooKeeper-темплейтів у межах одного файлу, лише для читання.
|
|
27
|
+
|
|
28
|
+
## Гарантії поведінки
|
|
29
|
+
|
|
30
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
31
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/** @see ./docs/auto-worktree.md */
|
|
2
|
-
import { existsSync } from 'node:fs'
|
|
3
|
-
import { copyFile, mkdir, rm } from 'node:fs/promises'
|
|
4
|
-
import { dirname, join } from 'node:path'
|
|
2
|
+
import { existsSync, statSync } from 'node:fs'
|
|
3
|
+
import { copyFile, mkdir, readdir, rm } from 'node:fs/promises'
|
|
4
|
+
import { dirname, join, relative } from 'node:path'
|
|
5
5
|
import { createInterface } from 'node:readline/promises'
|
|
6
6
|
|
|
7
7
|
const YES_RE = /^y(es)?$/i
|
|
8
|
+
const TRAILING_SLASH_RE = /\/$/
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Питає y/N у терміналі. Поза TTY (CI, неінтерактивний виклик) — одразу `false`,
|
|
@@ -112,6 +113,31 @@ export async function ensureRunningInWorktree(
|
|
|
112
113
|
return { cwd: newCwd, autoCreated: true, branchArg }
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Рекурсивно копіює вміст директорії (лише файли-листки; підкаталоги
|
|
118
|
+
* створюються по дорозі через `makeDir`). `listDir` — інжектований `readdir`
|
|
119
|
+
* з `{ recursive: true, withFileTypes: true }` (Node ≥20.1): кожен `Dirent`
|
|
120
|
+
* несе `parentPath`/`path` — реальний каталог, у якому файл лежить.
|
|
121
|
+
* @param {string} srcDir директорія-джерело (у worktree)
|
|
122
|
+
* @param {string} destDir директорія-призначення (у оригінальному дереві)
|
|
123
|
+
* @param {{ copy: (src: string, dest: string) => Promise<void>, makeDir: (path: string, opts?: object) => Promise<void>, listDir: (path: string, opts?: object) => Promise<Array<{ name: string, parentPath?: string, path?: string, isFile: () => boolean }>> }} io інжекти
|
|
124
|
+
* @returns {Promise<string[]>} шляхи скопійованих файлів відносно `srcDir` (forward-slash)
|
|
125
|
+
*/
|
|
126
|
+
async function copyDirectoryRecursive(srcDir, destDir, { copy, makeDir, listDir }) {
|
|
127
|
+
const entries = await listDir(srcDir, { recursive: true, withFileTypes: true })
|
|
128
|
+
const copiedRelPaths = []
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
if (!entry.isFile()) continue
|
|
131
|
+
const entryDir = entry.parentPath ?? entry.path ?? srcDir
|
|
132
|
+
const relFromSrc = relative(srcDir, join(entryDir, entry.name)).replaceAll('\\', '/')
|
|
133
|
+
const dest = join(destDir, relFromSrc)
|
|
134
|
+
await makeDir(dirname(dest), { recursive: true })
|
|
135
|
+
await copy(join(entryDir, entry.name), dest)
|
|
136
|
+
copiedRelPaths.push(relFromSrc)
|
|
137
|
+
}
|
|
138
|
+
return copiedRelPaths
|
|
139
|
+
}
|
|
140
|
+
|
|
115
141
|
/**
|
|
116
142
|
* Переносить зміни з автоствореного worktree назад у вихідне дерево як
|
|
117
143
|
* **untracked/незакомічені** правки (просте копіювання файлів, без git
|
|
@@ -122,48 +148,79 @@ export async function ensureRunningInWorktree(
|
|
|
122
148
|
* лише нову назву — стара лишається як була, прийнятний компроміс для
|
|
123
149
|
* інструментів, що самі файли не перейменовують (ефект можливий лише як
|
|
124
150
|
* побічний результат LLM-рефакторингу чи форматера).
|
|
151
|
+
*
|
|
152
|
+
* **Untracked-директорія цілком.** Git схлопує щойно створену untracked
|
|
153
|
+
* директорію в один porcelain-рядок із `/` на кінці (якщо в ній нема жодного
|
|
154
|
+
* файлу, вже відомого git) — `copyFile` на такий шлях впав би (`EISDIR`/`ENOENT`)
|
|
155
|
+
* і обривав би цикл, гублячи все, що йшло по порядку ітерації ДАЛІ. Такий
|
|
156
|
+
* рядок (суфікс `/` або реальний каталог на диску) копіюється рекурсивно
|
|
157
|
+
* (`copyDirectoryRecursive`) — весь вміст, а не один `copyFile`.
|
|
125
158
|
* @param {string} worktreeCwd автостворений worktree, з якого переносимо
|
|
126
159
|
* @param {string} originalCwd вихідне дерево, куди переносимо
|
|
127
160
|
* @param {typeof import('node:child_process').spawnSync} spawnFn інжект для тестів
|
|
128
161
|
* @param {(line: string) => void} log колбек прогресу
|
|
129
|
-
* @param {{ copyFile?: (src: string, dest: string) => Promise<void>, rm?: (path: string, opts?: object) => Promise<void>, mkdir?: (path: string, opts?: object) => Promise<void
|
|
130
|
-
* @returns {Promise<string[]>} відносні шляхи перенесених файлів
|
|
162
|
+
* @param {{ copyFile?: (src: string, dest: string) => Promise<void>, rm?: (path: string, opts?: object) => Promise<void>, mkdir?: (path: string, opts?: object) => Promise<void>, readdir?: (path: string, opts?: object) => Promise<Array<object>> }} [deps] інжекти для тестів
|
|
163
|
+
* @returns {Promise<{ brought: string[], failed: boolean }>} відносні шляхи перенесених файлів і чи стався провал (частковий чи повний)
|
|
131
164
|
*/
|
|
132
165
|
export async function bringChangesBackToOriginal(worktreeCwd, originalCwd, spawnFn, log, deps = {}) {
|
|
133
166
|
const copy = deps.copyFile ?? copyFile
|
|
134
167
|
const removeFile = deps.rm ?? rm
|
|
135
168
|
const makeDir = deps.mkdir ?? mkdir
|
|
169
|
+
const listDir = deps.readdir ?? readdir
|
|
136
170
|
|
|
137
171
|
const statusResult = spawnFn('git', ['status', '--porcelain'], { cwd: worktreeCwd, encoding: 'utf8' })
|
|
138
172
|
if (statusResult.status !== 0) {
|
|
139
173
|
log(
|
|
140
174
|
`⚠️ Не вдалось прочитати git status у "${worktreeCwd}" — зміни НЕ перенесені назад, worktree лишиться для ручного розбору.`
|
|
141
175
|
)
|
|
142
|
-
return []
|
|
176
|
+
return { brought: [], failed: true }
|
|
143
177
|
}
|
|
144
178
|
|
|
145
179
|
const lines = statusResult.stdout.split('\n').filter(Boolean)
|
|
146
180
|
if (lines.length === 0) {
|
|
147
181
|
log('ℹ️ Worktree без змін — нічого переносити назад.')
|
|
148
|
-
return []
|
|
182
|
+
return { brought: [], failed: false }
|
|
149
183
|
}
|
|
150
184
|
|
|
151
185
|
const brought = []
|
|
186
|
+
let failed = false
|
|
152
187
|
for (const line of lines) {
|
|
153
188
|
const rest = line.slice(3)
|
|
154
189
|
const relPath = rest.includes(' -> ') ? rest.split(' -> ', 2)[1] : rest
|
|
155
190
|
const srcPath = join(worktreeCwd, relPath)
|
|
156
191
|
const destPath = join(originalCwd, relPath)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
192
|
+
try {
|
|
193
|
+
if (!existsSync(srcPath)) {
|
|
194
|
+
await removeFile(destPath, { force: true, recursive: true })
|
|
195
|
+
brought.push(relPath)
|
|
196
|
+
continue
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const isDir = relPath.endsWith('/') || statSync(srcPath).isDirectory()
|
|
200
|
+
if (isDir) {
|
|
201
|
+
const trimmedRelPath = relPath.replace(TRAILING_SLASH_RE, '')
|
|
202
|
+
const copiedRelPaths = await copyDirectoryRecursive(srcPath, destPath, { copy, makeDir, listDir })
|
|
203
|
+
for (const nestedRelPath of copiedRelPaths) {
|
|
204
|
+
brought.push(`${trimmedRelPath}/${nestedRelPath}`)
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
await makeDir(dirname(destPath), { recursive: true })
|
|
208
|
+
await copy(srcPath, destPath)
|
|
209
|
+
brought.push(relPath)
|
|
210
|
+
}
|
|
211
|
+
} catch (error) {
|
|
212
|
+
failed = true
|
|
213
|
+
log(
|
|
214
|
+
`⚠️ Не вдалось перенести "${relPath}" назад у "${originalCwd}" — ${error instanceof Error ? error.message : String(error)}`
|
|
215
|
+
)
|
|
162
216
|
}
|
|
163
|
-
brought.push(relPath)
|
|
164
217
|
}
|
|
165
|
-
log(
|
|
166
|
-
|
|
218
|
+
log(
|
|
219
|
+
failed
|
|
220
|
+
? `⚠️ Перенесення назад у "${originalCwd}" частково провалилось — перенесено: ${brought.join(', ') || '(нічого)'}`
|
|
221
|
+
: `📤 Перенесено назад у "${originalCwd}" як untracked: ${brought.join(', ')}`
|
|
222
|
+
)
|
|
223
|
+
return { brought, failed }
|
|
167
224
|
}
|
|
168
225
|
|
|
169
226
|
/**
|
package/scripts/skills-cli.mjs
CHANGED
|
@@ -244,6 +244,23 @@ export function resolveBundledPackageRoot(fromModuleUrl = import.meta.url) {
|
|
|
244
244
|
return join(dirname(fileURLToPath(fromModuleUrl)), '..')
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Чи `argv` (аргументи після `skill`) резолвиться в JS-оркестрований
|
|
249
|
+
* worktree-only `taze`-шлях (`runTazeOrchestratorCli`) — той самий критерій,
|
|
250
|
+
* що й нижче в `runSkillsCli`. Використовується `n-rules.js`, щоб не мутувати
|
|
251
|
+
* root `package.json` (self-upgrade `@7n/rules`) ДО власного worktree-гейту
|
|
252
|
+
* оркестратора: той сам створює worktree і перевіряє чистоту дерева
|
|
253
|
+
* (`ensureRunningInWorktree`, `requireCleanTree: true`) — мутація package.json
|
|
254
|
+
* прямо перед цим викликом примусово провалила б auto-create там, де дерево
|
|
255
|
+
* інакше було б чисте.
|
|
256
|
+
* @param {string[]} argv аргументи після `skill`
|
|
257
|
+
* @returns {boolean} `true`, якщо запуск піде через `runTazeOrchestratorCli`
|
|
258
|
+
*/
|
|
259
|
+
export function isTazeOrchestratorSkillArgs(argv) {
|
|
260
|
+
const [first, second] = argv
|
|
261
|
+
return RUNNERS.has(first) && first !== 'claude' && normalizeSkillId(second) === 'taze'
|
|
262
|
+
}
|
|
263
|
+
|
|
247
264
|
/**
|
|
248
265
|
* @param {string[]} argv аргументи після `skill` у `n-rules`
|
|
249
266
|
* @param {{ packageRoot?: string, projectDir?: string, log?: (line: string) => void, logError?: (line: string) => void, deps?: { runPiAgentSkill?: (prompt: string, opts?: object) => Promise<{ ok: boolean, error: string|null }> } }} [options] перевизначення кореня пакета, каталогу проєкту, функцій виводу та інжектів (для тестів)
|
|
@@ -4,9 +4,7 @@ title: npm/skills/taze/js
|
|
|
4
4
|
resource: npm/skills/taze/js/
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
| Файл
|
|
8
|
-
|
|
|
9
|
-
| [
|
|
10
|
-
| [
|
|
11
|
-
| [orchestrate.mjs](orchestrate.md) | JS Module |
|
|
12
|
-
| [rust-provider.mjs](rust-provider.md) | JS Module |
|
|
7
|
+
| Файл | Тип |
|
|
8
|
+
| ----------------------------------------- | --------- |
|
|
9
|
+
| [migration-cache.mjs](migration-cache.md) | JS Module |
|
|
10
|
+
| [orchestrate.mjs](orchestrate.md) | JS Module |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: migration-cache.mjs
|
|
4
|
+
resource: npm/skills/taze/js/migration-cache.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 5f264929
|
|
7
|
+
model: openai-codex/gpt-5.4-mini
|
|
8
|
+
tier: cloud-min
|
|
9
|
+
score: 100
|
|
10
|
+
issues: judge:inaccurate:0.98
|
|
11
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Огляд
|
|
15
|
+
|
|
16
|
+
Модуль надає `DEFAULT_CACHE_DIR`, `migrationCacheKey`, `readMigrationCache`, `writeMigrationCache` і `withKnownMigrationNotes` для роботи з локальним кешем відомих результатів аналізу міграцій у межах прогону. Він зберігає та повторно читає вже відомі записи, а `withKnownMigrationNotes` підставляє їх у промпт, щоб не повторювати дослідження там, де результат уже є. Модуль fail-safe: не кидає винятків назовні й за окремих помилок повертає порожнє значення, зокрема `null`.
|
|
17
|
+
|
|
18
|
+
## Поведінка
|
|
19
|
+
|
|
20
|
+
- `DEFAULT_CACHE_DIR` — стандартний спільний каталог для кешу міграцій на цій машині, щоб різні репозиторії могли повторно використовувати вже відомі результати.
|
|
21
|
+
- `migrationCacheKey` — перетворює назву пакета і діапазон версій на стабільний безпечний ключ для кешу.
|
|
22
|
+
- `readMigrationCache` — читає кешований запис міграції для потрібної пари версій; якщо запису немає або він пошкоджений, мовчки повертає `null`.
|
|
23
|
+
- `writeMigrationCache` — зберігає результат аналізу міграції в кеш, щоб наступні запуски на цій машині могли його повторити без нового дослідження.
|
|
24
|
+
- `withKnownMigrationNotes` — додає до промпта підсумок уже відомої міграції з кешу й підказує пропустити повторне дослідження та перейти до перевірки використання в поточному проєкті.
|
|
25
|
+
|
|
26
|
+
## Публічний API
|
|
27
|
+
|
|
28
|
+
- DEFAULT_CACHE_DIR — Спільний каталог кешу міграцій для всіх репозиторіїв на цій машині; не прив’язаний до конкретного worktree.
|
|
29
|
+
- migrationCacheKey — Перетворює `` на безпечне ім’я файлу для спільного кешу між репо.
|
|
30
|
+
- readMigrationCache — Повертає збережений запис міграції для ``, якщо його вже обробляли в іншому repo/worktree; відсутній або пошкоджений запис дає `null`.
|
|
31
|
+
- writeMigrationCache — Записує результат LLM-обробки для ``, щоб не повторювати дослідження changelog для такого ж bump-а.
|
|
32
|
+
- withKnownMigrationNotes — Додає до prompt короткий підсумок знайденої міграції і, якщо є кеш, пропускає дослідження changelog/diff на користь пошуку використання в поточному проєкті.
|
|
33
|
+
|
|
34
|
+
## Гарантії поведінки
|
|
35
|
+
|
|
36
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
37
|
+
- За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
|
|
38
|
+
- Кешує результати в межах одного прогону.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { spawnSync } from 'node:child_process'
|
|
3
3
|
import { pathToFileURL } from 'node:url'
|
|
4
4
|
|
|
5
|
+
import { ensureNRulesInRootDevDependencies } from '../../../scripts/ensure-n-rules-dev-dependencies.mjs'
|
|
5
6
|
import {
|
|
6
7
|
bringChangesBackToOriginal,
|
|
7
8
|
ensureRunningInWorktree,
|
|
@@ -138,7 +139,13 @@ async function runEcosystem(provider, { cwd, runner, log, deps, spawnFn, call })
|
|
|
138
139
|
eco.results.push({ ...entry, ...outcome })
|
|
139
140
|
log(outcome.ok ? ` ✅ ${entry.pkg}` : ` ❌ ${entry.pkg}: ${outcome.error}`)
|
|
140
141
|
if (outcome.ok && outcome.text) {
|
|
141
|
-
await writeCache(
|
|
142
|
+
await writeCache(
|
|
143
|
+
entry.pkg,
|
|
144
|
+
entry.from,
|
|
145
|
+
entry.to,
|
|
146
|
+
{ notes: outcome.text, sourceRepo: cwd, updatedAt: new Date().toISOString() },
|
|
147
|
+
deps
|
|
148
|
+
)
|
|
142
149
|
}
|
|
143
150
|
}
|
|
144
151
|
|
|
@@ -241,22 +248,50 @@ export async function runTazeOrchestrator(options = {}) {
|
|
|
241
248
|
})
|
|
242
249
|
const cwd = worktree.cwd
|
|
243
250
|
|
|
251
|
+
// Self-upgrade root package.json devDependency (`@7n/rules`) — навмисно
|
|
252
|
+
// ВСЕРЕДИНІ worktree, ПІСЛЯ гейту `ensureRunningInWorktree` на чистоту
|
|
253
|
+
// дерева: та ж мутація у `originalCwd` ДО цього гейту забруднила б дерево
|
|
254
|
+
// прямо перед перевіркою, яку сам гейт щойно пройшов (n-rules.js навмисно
|
|
255
|
+
// пропускає її для цього шляху). Провал — не критичний для taze-прогону,
|
|
256
|
+
// лише лог.
|
|
257
|
+
try {
|
|
258
|
+
const ensureDevDeps = deps.ensureNRulesInRootDevDependencies ?? ensureNRulesInRootDevDependencies
|
|
259
|
+
await ensureDevDeps(cwd, { silent: true })
|
|
260
|
+
} catch (error) {
|
|
261
|
+
log(
|
|
262
|
+
`⚠️ Self-upgrade @7n/rules у "${cwd}" провалився — пропускаю (${error instanceof Error ? error.message : String(error)})`
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
244
266
|
let cleanedUp = false
|
|
245
267
|
/**
|
|
246
268
|
* Переносить зміни назад і прибирає автостворений worktree — не більше
|
|
247
269
|
* одного разу (idempotent), щоб і сигнальний обробник, і `finally` могли
|
|
248
270
|
* безпечно кликати те саме без подвійного `bringChangesBackToOriginal`/
|
|
249
271
|
* `removeAutoCreatedWorktree`.
|
|
272
|
+
*
|
|
273
|
+
* **Захист від втрати даних.** Якщо перенесення провалилось (частково чи
|
|
274
|
+
* повністю — `bringChangesBackToOriginal` кидає або повертає
|
|
275
|
+
* `failed: true`), worktree НЕ прибирається: джерело ще не перенесених
|
|
276
|
+
* файлів лишається на диску для ручного відновлення замість того, щоб
|
|
277
|
+
* зникнути разом з `npx \@7n/mt worktree remove`.
|
|
250
278
|
* @returns {Promise<void>}
|
|
251
279
|
*/
|
|
252
280
|
const cleanupAutoCreatedWorktree = async () => {
|
|
253
281
|
if (cleanedUp) return
|
|
254
282
|
cleanedUp = true
|
|
283
|
+
let failed = true
|
|
255
284
|
try {
|
|
256
|
-
await bringChangesBackToOriginal(cwd, originalCwd, spawnFn, log, deps)
|
|
285
|
+
;({ failed } = await bringChangesBackToOriginal(cwd, originalCwd, spawnFn, log, deps))
|
|
257
286
|
} catch (error) {
|
|
258
287
|
log(`⚠️ Перенесення змін назад провалилось: ${error instanceof Error ? error.message : String(error)}`)
|
|
259
288
|
}
|
|
289
|
+
if (failed) {
|
|
290
|
+
log(
|
|
291
|
+
`⚠️ Перенесення назад не вдалось повністю — worktree "${cwd}" (гілка "${worktree.branchArg}") НЕ прибирається, розберіться вручну.`
|
|
292
|
+
)
|
|
293
|
+
return
|
|
294
|
+
}
|
|
260
295
|
removeAutoCreatedWorktree(worktree.branchArg, originalCwd, spawnFn, log)
|
|
261
296
|
}
|
|
262
297
|
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
## Налаштування mutation-testing для Rust: `.cargo/mutants.toml`
|
|
2
|
-
|
|
3
|
-
Якщо у `.n-rules.json#rules` присутнє правило `rust` — правило `test` створює `.cargo/mutants.toml` у каталозі **кожного** Cargo.toml-маніфесту: кореневий `Cargo.toml`, `<workspace>/src-tauri/Cargo.toml` (Tauri-патерн) і `<workspace>/Cargo.toml` (flat workspace).
|
|
4
|
-
|
|
5
|
-
Канон cargo-mutants config: [mutants.toml.baseline](./data/cargo_mutants_config/mutants.toml.baseline)
|
|
6
|
-
|
|
7
|
-
**Customization** (mutate patterns, exclude rules, timeout) — відповідальність проєкту-споживача; концерн лише забезпечує наявність файлу як стартового baseline в кожному з виявлених workspace-каталогів.
|
|
8
|
-
|
|
9
|
-
### Універсальний baseline і framework-specific tuning
|
|
10
|
-
|
|
11
|
-
Правило `test` відповідає лише за **універсальний baseline** mutation-testing:
|
|
12
|
-
|
|
13
|
-
- створює `.cargo/mutants.toml` для Rust crates (порожній, з коментарем);
|
|
14
|
-
- не робить припущень про framework/app shell;
|
|
15
|
-
- не виключає platform glue, generated wrappers або binary entrypoints;
|
|
16
|
-
- framework-specific tuning (`--lib`/`--tests`, `exclude_globs` для app-shell і platform bridge файлів) належить відповідним правилам (`tauri`, `capacitor` тощо).
|
|
17
|
-
|
|
18
|
-
Якщо інше правило спеціалізує mutation-behavior — воно зобов'язане **доповнювати** існуючий `.cargo/mutants.toml` без дублювання (додавати лише відсутні ключі) і **не перетирати** ручні налаштування. Послідовний запуск `npx @7n/rules fix test` після `fix tauri` не має скидати tauri-tuning, і навпаки — повторний `fix tauri` не дублює секції.
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
# .cargo/mutants.toml — universal cargo-mutants baseline (test.mdc).
|
|
2
|
-
# Цей baseline нейтральний: він не робить припущень про framework/app shell,
|
|
3
|
-
# не виключає platform glue, generated wrappers або binary entrypoints.
|
|
4
|
-
# Framework-specific tuning (Tauri, Capacitor тощо) належить відповідним
|
|
5
|
-
# правилам — вони без дублювання доповнюють цей файл, не перетирають його.
|
|
6
|
-
# cargo-mutants має робочі defaults; цей файл — стартова точка для customization.
|
|
7
|
-
# Документація: https://mutants.rs/
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: fix-cargo_mutants_config.mjs
|
|
4
|
-
resource: npm/rules/test/cargo_mutants_config/fix-cargo_mutants_config.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: cf485f41
|
|
7
|
-
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
tier: local-min-retry
|
|
9
|
-
score: 100
|
|
10
|
-
issues: judge:inaccurate:0.99
|
|
11
|
-
judgeModel: openai-codex/gpt-5.4-mini
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Огляд
|
|
15
|
-
|
|
16
|
-
Для виконання завдання мені потрібен **код або контекст** файлу `test/cargo_mutants_config`, щоб я міг переписати секцію "Overview" відповідно до наданих "ВІДОМИХ ФАКТІВ" та уникнути помилок із "ЧЕРНЕТКОЮ".
|
|
17
|
-
|
|
18
|
-
## Поведінка
|
|
19
|
-
|
|
20
|
-
1. Перевіряє, чи не виявлено порушення, пов'язане з відсутністю конфігураційного файлу для мутантів у Cargo-проекті.
|
|
21
|
-
2. Якщо конфігураційний файл не існує в каталозі з резервною копією, виконується наступне:
|
|
22
|
-
3. Збираються шляхи до всіх Cargo-маніфестів у робочому каталозі.
|
|
23
|
-
4. Для кожного знайденого Cargo-маніфеста визначається цільовий шлях до файлу `mutants.toml`.
|
|
24
|
-
5. Якщо цільовий файл не існує, система позначає його для запису.
|
|
25
|
-
6. Створюється директорія, якщо вона відсутня.
|
|
26
|
-
7. З резервної копії копіюється вміст конфігураційного файлу до цільового шляху.
|
|
27
|
-
8. Якщо жоден файл не було створено, операція завершується без змін.
|
|
28
|
-
|
|
29
|
-
## Гарантії поведінки
|
|
30
|
-
|
|
31
|
-
- (специфічних машинно-виведених гарантій немає)
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: Directory Index
|
|
3
|
-
title: npm/rules/test/cargo_mutants_config
|
|
4
|
-
resource: npm/rules/test/cargo_mutants_config/
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# npm/rules/test/cargo_mutants_config
|
|
8
|
-
|
|
9
|
-
| Файл | Тип |
|
|
10
|
-
| ----------------------------------------------------------- | --------- |
|
|
11
|
-
| [fix-cargo_mutants_config.mjs](fix-cargo_mutants_config.md) | JS Module |
|
|
12
|
-
| [main.mjs](main.md) | JS Module |
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: main.mjs
|
|
4
|
-
resource: npm/rules/test/cargo_mutants_config/main.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: 817124cc
|
|
7
|
-
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score: 95
|
|
9
|
-
judgeModel: openai-codex/gpt-5.4-mini
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Огляд
|
|
13
|
-
|
|
14
|
-
Огляд: Файл перевіряє конфігурацію проекту, зокрема налаштування для мови Rust. Він забезпечує, що у директоріях, що містять файли маніфестів Cargo, існує файл `mutants.toml`, копіюючи базовий файл, якщо він відсутній.
|
|
15
|
-
|
|
16
|
-
Поведінка:
|
|
17
|
-
|
|
18
|
-
1. Перевіряється, чи активована логіка для мови Rust.
|
|
19
|
-
2. Ідентифікуються всі файли маніфестів Cargo.
|
|
20
|
-
3. Перевіряється наявність базового файлу `mutants.toml.baseline`.
|
|
21
|
-
4. Для кожного знайденого файлу маніфесту Cargo, якщо файл `.cargo/mutants.toml` відсутній, він створюється, копіюючи вміст базового файлу. **[test.mdc]**
|
|
22
|
-
5. Процес завершується з кодом 0, якщо всі перевірки виконані або відповідні файли не виявлені/немає порушень; з кодом 1 у разі виявлення порушень.
|
|
23
|
-
|
|
24
|
-
## Поведінка
|
|
25
|
-
|
|
26
|
-
Поведінка:
|
|
27
|
-
|
|
28
|
-
1. Перевіряється, увімкнено чи ні правило для мови Rust.
|
|
29
|
-
2. З'ясовуються всі файли маніфестів Cargo.
|
|
30
|
-
3. Перевіряється наявність канонічного базового файлу `mutants.toml.baseline`.
|
|
31
|
-
4. Для кожного знайденого файлу маніфесту Cargo, якщо файл `.cargo/mutants.toml` відсутній, він створюється, копіюючи вміст базового файлу.
|
|
32
|
-
5. Процес завершується з кодом 0, якщо всі перевірки пройдені або якщо відповідні файли не знайдено/немає порушень; з кодом 1 у разі виявлення порушень.
|
|
33
|
-
|
|
34
|
-
## Гарантії поведінки
|
|
35
|
-
|
|
36
|
-
- (специфічних машинно-виведених гарантій немає)
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* T0-autofix для `test/cargo_mutants_config` — детерміноване створення
|
|
3
|
-
* `<cargoDir>/.cargo/mutants.toml` з canonical neutral baseline там, де його ще
|
|
4
|
-
* немає. Логіку перенесено з detector-а (read-only contract: detector лише звітує
|
|
5
|
-
* `mutants-config-missing`, запис — тут).
|
|
6
|
-
*
|
|
7
|
-
* Unified lint surface: structured violations (test(violations)/apply(violations,ctx)).
|
|
8
|
-
* Цільові каталоги резолвимо повторним скануванням Cargo-маніфестів від `ctx.cwd`
|
|
9
|
-
* (idempotent: existing target пропускаємо → touchedFiles лишається порожнім).
|
|
10
|
-
*/
|
|
11
|
-
import { existsSync } from 'node:fs'
|
|
12
|
-
import { copyFile, mkdir } from 'node:fs/promises'
|
|
13
|
-
import { dirname, join } from 'node:path'
|
|
14
|
-
import { fileURLToPath } from 'node:url'
|
|
15
|
-
|
|
16
|
-
import { resolveAllCargoManifests } from '../../../scripts/utils/resolve-cargo-manifest.mjs'
|
|
17
|
-
|
|
18
|
-
import { MUTANTS_CONFIG_MISSING } from './main.mjs'
|
|
19
|
-
|
|
20
|
-
const BASELINE_PATH = join(
|
|
21
|
-
dirname(fileURLToPath(import.meta.url)),
|
|
22
|
-
'data',
|
|
23
|
-
'cargo_mutants_config',
|
|
24
|
-
'mutants.toml.baseline'
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
/** @type {import('../../../scripts/lib/lint-surface/types.mjs').T0Pattern[]} */
|
|
28
|
-
export const patterns = [
|
|
29
|
-
{
|
|
30
|
-
id: 'test-cargo-mutants-config-create',
|
|
31
|
-
test: violations => violations.some(v => v.reason === MUTANTS_CONFIG_MISSING),
|
|
32
|
-
apply: async (violations, ctx) => {
|
|
33
|
-
if (!existsSync(BASELINE_PATH)) return { touchedFiles: [] }
|
|
34
|
-
const cwd = ctx.cwd
|
|
35
|
-
const manifests = await resolveAllCargoManifests(cwd)
|
|
36
|
-
const touchedFiles = []
|
|
37
|
-
for (const manifestPath of manifests) {
|
|
38
|
-
const target = join(dirname(manifestPath), '.cargo', 'mutants.toml')
|
|
39
|
-
if (existsSync(target)) continue
|
|
40
|
-
ctx.recordWrite?.(target)
|
|
41
|
-
await mkdir(dirname(target), { recursive: true })
|
|
42
|
-
await copyFile(BASELINE_PATH, target)
|
|
43
|
-
touchedFiles.push(target)
|
|
44
|
-
}
|
|
45
|
-
if (touchedFiles.length === 0) return { touchedFiles: [] }
|
|
46
|
-
return { touchedFiles, message: `створено .cargo/mutants.toml: ${touchedFiles.join(', ')}` }
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
]
|