@7n/rules 1.4.1 → 1.5.0
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 +65 -17
- package/package.json +11 -1
- package/rules/abie/clean_merged_ignore_branches/concern.json +3 -0
- package/rules/docker/lint_docker_yml/concern.json +3 -0
- package/rules/js/lint_js_yml/concern.json +3 -0
- package/rules/k8s/lint_k8s_yml/concern.json +3 -0
- package/rules/npm-module/npm_publish_yml/concern.json +3 -0
- package/rules/php/lint_php_yml/concern.json +3 -0
- package/rules/python/lint_python_yml/concern.json +3 -0
- package/rules/rust/lint_rust_yml/concern.json +3 -0
- package/rules/rust/toolchain_cache/concern.json +10 -1
- package/rules/security/lint_security_yml/concern.json +3 -0
- package/rules/style/lint_style_yml/concern.json +3 -0
- package/rules/tauri/main.mdc +1 -1
- package/rules/tauri/release/release.mdc +15 -0
- package/rules/text/lint_text/concern.json +3 -0
- package/schemas/concern.json +86 -16
- package/schemas/n-rules.json +11 -1
- package/schemas/v8r-catalog.json +123 -27
- package/scripts/auto-rules.mjs +47 -6
- package/scripts/docs/auto-rules.md +3 -1
- package/scripts/docs/skills-cli.md +3 -2
- package/scripts/docs/upgrade-n-rules-and-install.md +3 -1
- package/scripts/lib/acp-runner.mjs +174 -0
- package/scripts/lib/concern-meta.mjs +8 -1
- package/scripts/lib/docs/acp-runner.md +58 -0
- package/scripts/lib/docs/concern-meta.md +1 -1
- package/scripts/lib/docs/read-n-rules-config-lite.md +3 -1
- package/scripts/lib/docs/resolve-plugins.md +17 -0
- package/scripts/lib/docs/run-conftest-batch.md +3 -1
- package/scripts/lib/lint-surface/docs/policy-lint-adapter.md +3 -1
- package/scripts/lib/lint-surface/docs/run-detectors.md +5 -1
- package/scripts/lib/lint-surface/policy-lint-adapter.mjs +2 -0
- package/scripts/lib/lint-surface/run-detectors.mjs +72 -5
- package/scripts/lib/read-n-rules-config-lite.mjs +5 -3
- package/scripts/lib/resolve-plugins.mjs +248 -0
- package/scripts/lib/run-conftest-batch.mjs +3 -1
- package/scripts/skills-cli.mjs +30 -52
- package/scripts/upgrade-n-rules-and-install.mjs +5 -4
- package/rules/ga/clean_ga_workflows/clean_ga_workflows.mdc +0 -20
- package/rules/ga/clean_ga_workflows/clean_ga_workflows.rego +0 -112
- package/rules/ga/clean_ga_workflows/concern.json +0 -10
- package/rules/ga/clean_ga_workflows/docs/fix-clean_ga_workflows.md +0 -33
- package/rules/ga/clean_ga_workflows/docs/index.md +0 -9
- package/rules/ga/clean_ga_workflows/fix-clean_ga_workflows.mjs +0 -8
- package/rules/ga/clean_ga_workflows/template/clean-ga-workflows.yml.snippet.yml +0 -26
- package/rules/ga/clean_merged_branch/clean_merged_branch.mdc +0 -24
- package/rules/ga/clean_merged_branch/clean_merged_branch.rego +0 -163
- package/rules/ga/clean_merged_branch/concern.json +0 -10
- package/rules/ga/clean_merged_branch/docs/fix-clean_merged_branch.md +0 -27
- package/rules/ga/clean_merged_branch/docs/index.md +0 -9
- package/rules/ga/clean_merged_branch/fix-clean_merged_branch.mjs +0 -8
- package/rules/ga/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +0 -37
- package/rules/ga/docs/index.md +0 -11
- package/rules/ga/git_ai/concern.json +0 -11
- package/rules/ga/git_ai/docs/fix-git_ai.md +0 -30
- package/rules/ga/git_ai/docs/index.md +0 -9
- package/rules/ga/git_ai/fix-git_ai.mjs +0 -5
- package/rules/ga/git_ai/git_ai.mdc +0 -19
- package/rules/ga/git_ai/git_ai.rego +0 -76
- package/rules/ga/git_ai/template/git-ai.yml.snippet.yml +0 -30
- package/rules/ga/lint_ga/concern.json +0 -10
- package/rules/ga/lint_ga/docs/fix-lint_ga.md +0 -32
- package/rules/ga/lint_ga/docs/index.md +0 -9
- package/rules/ga/lint_ga/fix-lint_ga.mjs +0 -5
- package/rules/ga/lint_ga/lint_ga.mdc +0 -21
- package/rules/ga/lint_ga/lint_ga.rego +0 -132
- package/rules/ga/lint_ga/template/lint-ga.yml.snippet.yml +0 -41
- package/rules/ga/main.json +0 -1
- package/rules/ga/main.mdc +0 -17
- package/rules/ga/vscode_extensions/concern.json +0 -11
- package/rules/ga/vscode_extensions/docs/fix-vscode_extensions.md +0 -25
- package/rules/ga/vscode_extensions/docs/index.md +0 -11
- package/rules/ga/vscode_extensions/fix-vscode_extensions.mjs +0 -1
- package/rules/ga/vscode_extensions/template/extensions.json.snippet.json +0 -1
- package/rules/ga/vscode_extensions/vscode_extensions.mdc +0 -9
- package/rules/ga/vscode_extensions/vscode_extensions.rego +0 -18
- package/rules/ga/vscode_settings/concern.json +0 -11
- package/rules/ga/vscode_settings/docs/fix-vscode_settings.md +0 -26
- package/rules/ga/vscode_settings/docs/index.md +0 -9
- package/rules/ga/vscode_settings/fix-vscode_settings.mjs +0 -5
- package/rules/ga/vscode_settings/template/settings.json.snippet.json +0 -1
- package/rules/ga/vscode_settings/vscode_settings.mdc +0 -9
- package/rules/ga/vscode_settings/vscode_settings.rego +0 -22
- package/rules/ga/workflow_common/concern.json +0 -9
- package/rules/ga/workflow_common/template/uses-min-versions.snippet.json +0 -4
- package/rules/ga/workflow_common/workflow_common.mdc +0 -18
- package/rules/ga/workflow_common/workflow_common.rego +0 -288
- package/rules/ga/workflows/concern.json +0 -8
- package/rules/ga/workflows/docs/fix-workflows.md +0 -37
- package/rules/ga/workflows/docs/main.md +0 -38
- package/rules/ga/workflows/fix-workflows.mjs +0 -212
- package/rules/ga/workflows/main.mjs +0 -433
- package/rules/ga/workflows/workflows.mdc +0 -33
- package/rules/ga/zizmor_yml/concern.json +0 -11
- package/rules/ga/zizmor_yml/docs/fix-zizmor_yml.md +0 -28
- package/rules/ga/zizmor_yml/docs/index.md +0 -9
- package/rules/ga/zizmor_yml/fix-zizmor_yml.mjs +0 -3
- package/rules/ga/zizmor_yml/template/zizmor.yml.snippet.yml +0 -5
- package/rules/ga/zizmor_yml/zizmor_yml.mdc +0 -9
- package/rules/ga/zizmor_yml/zizmor_yml.rego +0 -27
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.0] - 2026-07-15
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Нове правило `local-ai` (auto: завжди): інтеграція rtk (Rust Token Killer) для стискання виводу CLI-команд агентів. Sync вставляє fail-open PreToolUse hook у `.claude/settings.json` (`rtk hook claude`), preToolUse у `.cursor/hooks.json` (`rtk hook cursor`) та vendored pi-extension `.pi/extensions/rtk.ts`; від користувача потрібен лише `brew install rtk-ai/tap/rtk`. Вимкнення — `disable-rules: ["local-ai"]`.
|
|
8
|
+
- Механізм плагінів: поле `plugins` у .n-rules.json + автодетект CI-провайдера (файли CI → repository.url) з авто-встановленням devDependency; multi-dir завантаження правил (ядро + плагіни, merge концернів), маніфест плагіна `n-rules` (capabilities/handlers); правило `ga` винесено у @7n/rules-ci-github
|
|
9
|
+
- tauri/release: change-файл — обовʼязковий крок вирішення задачі (як lint/doc-files), не post-hoc фікс на сигнал `changelog/presence`.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- docs(adr): merge pending ADR normalization batch
|
|
14
|
+
|
|
3
15
|
## [1.4.1] - 2026-07-15
|
|
4
16
|
|
|
5
17
|
### Fixed
|
package/bin/n-rules.js
CHANGED
|
@@ -83,6 +83,7 @@ import { injectWorktreeNotice } from '../scripts/lib/worktree-notice.mjs'
|
|
|
83
83
|
import { injectRootNotice } from '../scripts/lib/root-notice.mjs'
|
|
84
84
|
import { listProjectRulesMdcFiles } from '../scripts/lib/list-project-rules-mdc.mjs'
|
|
85
85
|
import { ensureNRulesInRootDevDependencies } from '../scripts/ensure-n-rules-dev-dependencies.mjs'
|
|
86
|
+
import { resolvePluginList, resolveRulesDirs } from '../scripts/lib/resolve-plugins.mjs'
|
|
86
87
|
import { assertCwdIsProjectRoot } from '../scripts/lib/assert-project-root.mjs'
|
|
87
88
|
import { syncClaudeConfig } from '../scripts/sync-claude-config.mjs'
|
|
88
89
|
import { syncGitignoreWorktree } from '../scripts/lib/sync-gitignore-worktree.mjs'
|
|
@@ -253,6 +254,33 @@ async function readRootPackageJsonSafe() {
|
|
|
253
254
|
}
|
|
254
255
|
}
|
|
255
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Агрегує імена правил по rules-каталогах (ядро перше, перший власник виграє)
|
|
259
|
+
* і будує мапу `ruleId → rulesDir` для копіювання mdc.
|
|
260
|
+
* @param {string[]} rulesDirs упорядковані rules-каталоги (ядро + плагіни)
|
|
261
|
+
* @returns {Promise<{ names: string[], sources: Map<string, string> }>} імена правил і їх джерела
|
|
262
|
+
*/
|
|
263
|
+
async function aggregateRuleSources(rulesDirs) {
|
|
264
|
+
/** @type {Map<string, string>} */
|
|
265
|
+
const sources = new Map()
|
|
266
|
+
for (const [i, dir] of rulesDirs.entries()) {
|
|
267
|
+
let names
|
|
268
|
+
if (i === 0) {
|
|
269
|
+
names = await discoverBundledRuleNames(dir) // ядро: відсутність каталогу — фатальна помилка
|
|
270
|
+
} else {
|
|
271
|
+
try {
|
|
272
|
+
names = await discoverBundledRuleNames(dir)
|
|
273
|
+
} catch {
|
|
274
|
+
continue // плагін без валідного rules/ — уже повідомлено у resolve-plugins
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
for (const name of names) {
|
|
278
|
+
if (!sources.has(name)) sources.set(name, dir)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return { names: sources.keys().toArray(), sources }
|
|
282
|
+
}
|
|
283
|
+
|
|
256
284
|
/**
|
|
257
285
|
* Зчитує конфіг .n-rules.json з поточної директорії
|
|
258
286
|
* @param {{ bundledRulesDir?: string, bundledSkillsDir?: string }} [paths] каталоги з пакету-джерела (після `bun i` — зазвичай `node_modules/@7n/rules`)
|
|
@@ -263,7 +291,22 @@ async function readConfig(paths = {}) {
|
|
|
263
291
|
const bundledSkillsDir = paths.bundledSkillsDir ?? BUNDLED_SKILLS_DIR
|
|
264
292
|
await migrateLegacyConfigIfNeeded()
|
|
265
293
|
const configPath = join(cwd(), CONFIG_FILE)
|
|
266
|
-
|
|
294
|
+
|
|
295
|
+
/** @type {Record<string, unknown> | null} */
|
|
296
|
+
let rawConfig = null
|
|
297
|
+
if (existsSync(configPath)) {
|
|
298
|
+
try {
|
|
299
|
+
rawConfig = JSON.parse(await readFile(configPath, 'utf8'))
|
|
300
|
+
} catch {
|
|
301
|
+
throw new Error(`Невірний JSON у файлі ${CONFIG_FILE}`)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Плагіни: явне поле plugins конфігу або автодетект; sync-контекст — з установкою devDep.
|
|
306
|
+
// Результат детекту записуємо у конфіг (нижче), щоб hook/lint не залежали від детекту й мережі.
|
|
307
|
+
const detectedPlugins = Array.isArray(rawConfig?.plugins) ? null : resolvePluginList(cwd(), rawConfig)
|
|
308
|
+
const rulesDirs = resolveRulesDirs(cwd(), rawConfig, bundledRulesDir).map(d => d.rulesDir)
|
|
309
|
+
const { names: availableRules } = await aggregateRuleSources(rulesDirs)
|
|
267
310
|
const availableSkills = await discoverBundledSkillNames(bundledSkillsDir)
|
|
268
311
|
|
|
269
312
|
/**
|
|
@@ -291,7 +334,8 @@ async function readConfig(paths = {}) {
|
|
|
291
334
|
root: cwd(),
|
|
292
335
|
availableRules,
|
|
293
336
|
packageJsonParsed: rootPkg,
|
|
294
|
-
disableRules
|
|
337
|
+
disableRules,
|
|
338
|
+
rulesDirs
|
|
295
339
|
})
|
|
296
340
|
// Skills залежать від ефективного списку правил, який буде у конфізі після merge:
|
|
297
341
|
// вже існуючі (опт-ін вручну) + auto-detected, мінус `disable-rules`. Без цього
|
|
@@ -335,15 +379,20 @@ async function readConfig(paths = {}) {
|
|
|
335
379
|
if (merged['disable-skills']?.length) {
|
|
336
380
|
normalized['disable-skills'] = merged['disable-skills']
|
|
337
381
|
}
|
|
382
|
+
if (!('plugins' in parsedConfig) && detectedPlugins && detectedPlugins.length > 0) {
|
|
383
|
+
normalized.plugins = detectedPlugins
|
|
384
|
+
console.log(`🔌 Автодетект плагінів: ${detectedPlugins.join(', ')} — записано у ${CONFIG_FILE}\n`)
|
|
385
|
+
}
|
|
338
386
|
return sortConfigIdArrays(normalized)
|
|
339
387
|
}
|
|
340
388
|
|
|
341
|
-
if (
|
|
389
|
+
if (rawConfig === null) {
|
|
342
390
|
const rootPkg = await readRootPackageJsonSafe()
|
|
343
391
|
const autoDetectedRules = await detectAutoRules({
|
|
344
392
|
root: cwd(),
|
|
345
393
|
availableRules,
|
|
346
|
-
packageJsonParsed: rootPkg
|
|
394
|
+
packageJsonParsed: rootPkg,
|
|
395
|
+
rulesDirs
|
|
347
396
|
})
|
|
348
397
|
const autoDetectedSkills = detectAutoSkills({
|
|
349
398
|
availableSkills,
|
|
@@ -352,7 +401,8 @@ async function readConfig(paths = {}) {
|
|
|
352
401
|
const defaultConfig = sortConfigIdArrays({
|
|
353
402
|
$schema: CONFIG_SCHEMA_URL,
|
|
354
403
|
rules: autoDetectedRules.rules,
|
|
355
|
-
skills: autoDetectedSkills.skills
|
|
404
|
+
skills: autoDetectedSkills.skills,
|
|
405
|
+
...(detectedPlugins && detectedPlugins.length > 0 && { plugins: detectedPlugins })
|
|
356
406
|
})
|
|
357
407
|
await writeFile(configPath, `${JSON.stringify(defaultConfig, null, 2)}\n`, 'utf8')
|
|
358
408
|
console.log(
|
|
@@ -360,14 +410,7 @@ async function readConfig(paths = {}) {
|
|
|
360
410
|
)
|
|
361
411
|
return defaultConfig
|
|
362
412
|
}
|
|
363
|
-
const
|
|
364
|
-
/** @type {Record<string, unknown>} */
|
|
365
|
-
let config
|
|
366
|
-
try {
|
|
367
|
-
config = JSON.parse(raw)
|
|
368
|
-
} catch {
|
|
369
|
-
throw new Error(`Невірний JSON у файлі ${CONFIG_FILE}`)
|
|
370
|
-
}
|
|
413
|
+
const config = rawConfig
|
|
371
414
|
logRuleMigrationsIfAny(config)
|
|
372
415
|
const normalized = await normalizeConfigWithAutoRules(config)
|
|
373
416
|
if (JSON.stringify(normalized) !== JSON.stringify(config)) {
|
|
@@ -1197,11 +1240,12 @@ async function captureOutput(action) {
|
|
|
1197
1240
|
/**
|
|
1198
1241
|
* Копіює керовані `.mdc` файли з пакету до `.cursor/rules`.
|
|
1199
1242
|
* @param {string[]} rules список rules з конфігу
|
|
1200
|
-
* @param {string} bundledRulesDir каталог `rules` пакету-джерела
|
|
1243
|
+
* @param {string} bundledRulesDir каталог `rules` пакету-джерела (fallback для правил без явного джерела)
|
|
1201
1244
|
* @param {string} rulesDir абсолютний шлях до `.cursor/rules`
|
|
1245
|
+
* @param {Map<string, string>} [ruleSources] мапа `ruleId → rulesDir` власника правила (ядро/плагін)
|
|
1202
1246
|
* @returns {Promise<{ successCount: number, failCount: number }>} статистика копіювання
|
|
1203
1247
|
*/
|
|
1204
|
-
async function syncManagedRuleFiles(rules, bundledRulesDir, rulesDir) {
|
|
1248
|
+
async function syncManagedRuleFiles(rules, bundledRulesDir, rulesDir, ruleSources = new Map()) {
|
|
1205
1249
|
let successCount = 0
|
|
1206
1250
|
let failCount = 0
|
|
1207
1251
|
for (const rule of rules) {
|
|
@@ -1209,7 +1253,8 @@ async function syncManagedRuleFiles(rules, bundledRulesDir, rulesDir) {
|
|
|
1209
1253
|
const destPath = join(rulesDir, fileName)
|
|
1210
1254
|
try {
|
|
1211
1255
|
process.stdout.write(` ⬇ ${rule} → ${RULES_DIR}/${fileName} ... `)
|
|
1212
|
-
const
|
|
1256
|
+
const sourceDir = ruleSources.get(normalizeRuleName(rule)) ?? bundledRulesDir
|
|
1257
|
+
const content = await readBundledRuleContent(rule, sourceDir)
|
|
1213
1258
|
await writeFile(destPath, content, 'utf8')
|
|
1214
1259
|
console.log(`✅`)
|
|
1215
1260
|
successCount++
|
|
@@ -1359,8 +1404,11 @@ async function runSync() {
|
|
|
1359
1404
|
|
|
1360
1405
|
const rulesDir = join(cwd(), RULES_DIR)
|
|
1361
1406
|
await mkdir(rulesDir, { recursive: true })
|
|
1407
|
+
// Джерела правил: ядро + rules/ плагінів (резолв кешований після readConfig).
|
|
1408
|
+
const allRulesDirs = resolveRulesDirs(projectRoot, config, bundledRulesDir).map(d => d.rulesDir)
|
|
1409
|
+
const { sources: ruleSources } = await aggregateRuleSources(allRulesDirs)
|
|
1362
1410
|
const { successCount, failCount } = await captureOutput(async () => {
|
|
1363
|
-
const stats = await syncManagedRuleFiles(rules, bundledRulesDir, rulesDir)
|
|
1411
|
+
const stats = await syncManagedRuleFiles(rules, bundledRulesDir, rulesDir, ruleSources)
|
|
1364
1412
|
return { ...stats, fail: stats.failCount }
|
|
1365
1413
|
})
|
|
1366
1414
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@7n/rules",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "CLI еталонних правил і skills (префікс n-): синк у репозиторій, дельта-lint, конформність",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -48,6 +48,13 @@
|
|
|
48
48
|
],
|
|
49
49
|
"type": "module",
|
|
50
50
|
"types": "./types/bin/n-rules.d.ts",
|
|
51
|
+
"exports": {
|
|
52
|
+
".": "./bin/n-rules.js",
|
|
53
|
+
"./package.json": "./package.json",
|
|
54
|
+
"./scripts/*": "./scripts/*",
|
|
55
|
+
"./schemas/*": "./schemas/*",
|
|
56
|
+
"./rules/*": "./rules/*"
|
|
57
|
+
},
|
|
51
58
|
"scripts": {
|
|
52
59
|
"test": "vitest run",
|
|
53
60
|
"test:watch": "vitest",
|
|
@@ -58,6 +65,7 @@
|
|
|
58
65
|
"dependencies": {
|
|
59
66
|
"@7n/mt": "^0.5.1",
|
|
60
67
|
"@7n/llm-lib": "^2.0.0",
|
|
68
|
+
"@zed-industries/agent-client-protocol": "^0.4.5",
|
|
61
69
|
"ajv": "^8.20.0",
|
|
62
70
|
"cli-progress": "^3.12.0",
|
|
63
71
|
"eslint": "^10.4.1",
|
|
@@ -76,6 +84,8 @@
|
|
|
76
84
|
"zod": "^4.4.3"
|
|
77
85
|
},
|
|
78
86
|
"optionalDependencies": {
|
|
87
|
+
"@agentclientprotocol/claude-agent-acp": "^0.59.0",
|
|
88
|
+
"@agentclientprotocol/codex-acp": "^1.1.2",
|
|
79
89
|
"@earendil-works/pi-ai": "0.80.2",
|
|
80
90
|
"@earendil-works/pi-coding-agent": "0.80.2"
|
|
81
91
|
},
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
|
|
3
3
|
"fixability": "config",
|
|
4
|
-
"lint": {
|
|
4
|
+
"lint": {
|
|
5
|
+
"scope": "full",
|
|
6
|
+
"glob": [
|
|
7
|
+
".github/workflows/*.yml",
|
|
8
|
+
".github/workflows/*.yaml"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"requires": {
|
|
12
|
+
"capability": "ci:github"
|
|
13
|
+
}
|
|
5
14
|
}
|
package/rules/tauri/main.mdc
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: Tauri
|
|
3
3
|
globs: "**/src-tauri/**,**/tauri.conf.json"
|
|
4
4
|
alwaysApply: false
|
|
5
|
-
version: '1.
|
|
5
|
+
version: '1.9'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Правило встановлює канонічні вимоги для Tauri-проєктів: виявлення маркерів у монорепо, VS Code-розширення, налаштування mutation-testing для platform bridge, компіляційну ізоляцію agent/LLM-логіки від app-shell, реалізацію Tool Surface поверх Tauri+Rust, реліз-flow через change-файли (тег → DMG/updater-артефакти), автооновлення через спільний `useUpdater()` з `@7n/tauri-components` та верифікацію застосунку LLM-агентом через `tauri-plugin-mcp-bridge`.
|
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
Канон складається з двох workflow-файлів і відповідного `tauri.conf.json`-конфігу; референс — `.github/workflows/{changelog-release,release}.yml` та `src-tauri/tauri.conf.json` чотирьох production Tauri-репо, де цей flow вже впроваджено.
|
|
6
6
|
|
|
7
|
+
### Change-файл — крок вирішення задачі, а не post-hoc lint-фікс
|
|
8
|
+
|
|
9
|
+
Change-файл (`<app>/.changes/<ts>.md`) створюється **одразу під час вирішення задачі**, що
|
|
10
|
+
чіпає код Tauri-workspace — той самий момент, що lint і doc-files, а не окремий крок після
|
|
11
|
+
сигналу `changeset-missing` від delta-lint concern `changelog/presence`. Без нього мердж у
|
|
12
|
+
`main` не тригерить `changelog-release.yml` (тригер — `paths: [app/.changes/**]`), і зміна
|
|
13
|
+
осідає в репозиторії без релізу, доки хтось не помітить і не докомітить change-файл окремо.
|
|
14
|
+
|
|
15
|
+
Команда: `npx @7n/n ch --bump <major|minor|patch> --section <Added|Changed|Fixed|Removed> --message "<опис>"`
|
|
16
|
+
(інтерактивно — `npx @7n/n ch`). Повна модель (база порівняння, формат CHANGELOG,
|
|
17
|
+
post-release-інваріант) — у `n-changelog.mdc` і `release/main.mdc`.
|
|
18
|
+
|
|
19
|
+
`changelog/presence` лишається gate-запобіжником на випадок, якщо крок пропущено (delta-lint
|
|
20
|
+
у CI/hook), а не первинним механізмом створення change-файлу.
|
|
21
|
+
|
|
7
22
|
### `changelog-release.yml` — бамп версії з change-файлів
|
|
8
23
|
|
|
9
24
|
Тригериться на push у `main` за наявності change-файлів і на ручний dispatch:
|
package/schemas/concern.json
CHANGED
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
},
|
|
13
13
|
"fixability": {
|
|
14
14
|
"type": "string",
|
|
15
|
-
"enum": [
|
|
15
|
+
"enum": [
|
|
16
|
+
"code",
|
|
17
|
+
"config",
|
|
18
|
+
"structural"
|
|
19
|
+
],
|
|
16
20
|
"description": "Маршрутизація fix-движка. Усі мітки виконують детерміновану фазу T0; різниця — що після неї. `code` (дефолт, поле можна опускати) — джерельні порушення, які LLM може осмислено переписати → повна pi-agent-ladder (local→cloud) до чистого re-detect. `config` — canon/settings/tooling конфіг з єдиною правильною формою (jscpd/oxfmtrc/tooling/package_json/*_yml): фікс детермінований (T0/regen з канону), LLM лише вгадав би невалідне → після T0 fail-fast без ladder. `structural` — дублікати/розташування/структура (jscpd_duplicates/test-location/package-structure): авто-правка ризикована й потребує людського розсуду → після T0 fail-fast без ladder. config vs structural різняться мотивом пропуску (детермінований канон vs надто ризиковано) — для телеметрії й майбутнього routing."
|
|
17
21
|
},
|
|
18
22
|
"check": {
|
|
@@ -27,7 +31,10 @@
|
|
|
27
31
|
"properties": {
|
|
28
32
|
"engine": {
|
|
29
33
|
"type": "string",
|
|
30
|
-
"enum": [
|
|
34
|
+
"enum": [
|
|
35
|
+
"rego",
|
|
36
|
+
"template"
|
|
37
|
+
],
|
|
31
38
|
"description": "rego → .rego через conftest; template → generic deep-subset. Канонічна заміна поля `check`."
|
|
32
39
|
},
|
|
33
40
|
"namespace": {
|
|
@@ -39,21 +46,40 @@
|
|
|
39
46
|
{
|
|
40
47
|
"type": "object",
|
|
41
48
|
"additionalProperties": false,
|
|
42
|
-
"required": [
|
|
49
|
+
"required": [
|
|
50
|
+
"single"
|
|
51
|
+
],
|
|
43
52
|
"properties": {
|
|
44
|
-
"single": {
|
|
45
|
-
|
|
53
|
+
"single": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"minLength": 1
|
|
56
|
+
},
|
|
57
|
+
"required": {
|
|
58
|
+
"type": "boolean"
|
|
59
|
+
}
|
|
46
60
|
}
|
|
47
61
|
},
|
|
48
62
|
{
|
|
49
63
|
"type": "object",
|
|
50
64
|
"additionalProperties": false,
|
|
51
|
-
"required": [
|
|
65
|
+
"required": [
|
|
66
|
+
"walkGlob"
|
|
67
|
+
],
|
|
52
68
|
"properties": {
|
|
53
69
|
"walkGlob": {
|
|
54
70
|
"oneOf": [
|
|
55
|
-
{
|
|
56
|
-
|
|
71
|
+
{
|
|
72
|
+
"type": "string",
|
|
73
|
+
"minLength": 1
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"type": "array",
|
|
77
|
+
"minItems": 1,
|
|
78
|
+
"items": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"minLength": 1
|
|
81
|
+
}
|
|
82
|
+
}
|
|
57
83
|
]
|
|
58
84
|
}
|
|
59
85
|
}
|
|
@@ -61,12 +87,24 @@
|
|
|
61
87
|
{
|
|
62
88
|
"type": "object",
|
|
63
89
|
"additionalProperties": false,
|
|
64
|
-
"required": [
|
|
90
|
+
"required": [
|
|
91
|
+
"glob"
|
|
92
|
+
],
|
|
65
93
|
"properties": {
|
|
66
94
|
"glob": {
|
|
67
95
|
"oneOf": [
|
|
68
|
-
{
|
|
69
|
-
|
|
96
|
+
{
|
|
97
|
+
"type": "string",
|
|
98
|
+
"minLength": 1
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"type": "array",
|
|
102
|
+
"minItems": 1,
|
|
103
|
+
"items": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"minLength": 1
|
|
106
|
+
}
|
|
107
|
+
}
|
|
70
108
|
]
|
|
71
109
|
}
|
|
72
110
|
}
|
|
@@ -75,7 +113,9 @@
|
|
|
75
113
|
},
|
|
76
114
|
"check": {
|
|
77
115
|
"type": "string",
|
|
78
|
-
"enum": [
|
|
116
|
+
"enum": [
|
|
117
|
+
"template"
|
|
118
|
+
],
|
|
79
119
|
"description": "template → generic deep-subset без .rego; без поля → Rego mode."
|
|
80
120
|
},
|
|
81
121
|
"missingMessage": {
|
|
@@ -87,22 +127,52 @@
|
|
|
87
127
|
"lint": {
|
|
88
128
|
"type": "object",
|
|
89
129
|
"additionalProperties": false,
|
|
90
|
-
"required": [
|
|
130
|
+
"required": [
|
|
131
|
+
"scope"
|
|
132
|
+
],
|
|
91
133
|
"description": "Lint поверхня. main.mjs має експортувати lint(changed, cwd, opts).",
|
|
92
134
|
"properties": {
|
|
93
135
|
"scope": {
|
|
94
136
|
"type": "string",
|
|
95
|
-
"enum": [
|
|
137
|
+
"enum": [
|
|
138
|
+
"per-file",
|
|
139
|
+
"full"
|
|
140
|
+
],
|
|
96
141
|
"description": "per-file: delta-режим; full: крос-файловий."
|
|
97
142
|
},
|
|
98
143
|
"glob": {
|
|
99
144
|
"oneOf": [
|
|
100
|
-
{
|
|
101
|
-
|
|
145
|
+
{
|
|
146
|
+
"type": "string",
|
|
147
|
+
"minLength": 1
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"type": "array",
|
|
151
|
+
"minItems": 1,
|
|
152
|
+
"items": {
|
|
153
|
+
"type": "string",
|
|
154
|
+
"minLength": 1
|
|
155
|
+
}
|
|
156
|
+
}
|
|
102
157
|
],
|
|
103
158
|
"description": "per-file: фільтр delta-файлів; full: delta-тригер."
|
|
104
159
|
}
|
|
105
160
|
}
|
|
161
|
+
},
|
|
162
|
+
"requires": {
|
|
163
|
+
"type": "object",
|
|
164
|
+
"description": "Гейт активації: концерн виконується лише коли задекларована capability доступна (її надає встановлений плагін через блок n-rules.capabilities у своєму package.json). Приклад: {\"capability\": \"ci:github\"} — концерн вимагає GitHub Actions і спить у репозиторіях без плагіна @7n/rules-ci-github.",
|
|
165
|
+
"additionalProperties": false,
|
|
166
|
+
"required": [
|
|
167
|
+
"capability"
|
|
168
|
+
],
|
|
169
|
+
"properties": {
|
|
170
|
+
"capability": {
|
|
171
|
+
"type": "string",
|
|
172
|
+
"pattern": "^[a-z][a-z0-9:-]*$",
|
|
173
|
+
"description": "Ім'я capability (конвенція <тип>:<назва>, напр. ci:github, ci:azure, lang:rust)"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
106
176
|
}
|
|
107
177
|
}
|
|
108
178
|
}
|
package/schemas/n-rules.json
CHANGED
|
@@ -27,6 +27,14 @@
|
|
|
27
27
|
"minLength": 1
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
|
+
"plugins": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"description": "npm-пакети-плагіни з додатковими правилами (rules/<id>/ за контрактом ядра). Якщо поле відсутнє — CLI автоматично визначає CI-провайдера (файли CI, потім repository.url) і дописує сюди результат. Порожній масив — плагіни вимкнено.",
|
|
33
|
+
"items": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"pattern": "^(@[a-z0-9-~][a-z0-9-._~]*/)?[a-z0-9-~][a-z0-9-._~]*$"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
30
38
|
"disable-rules": {
|
|
31
39
|
"type": "array",
|
|
32
40
|
"description": "Список rule id (без префікса n-), які не потрібно автододавати в `.n-rules.json` під час запуску CLI.",
|
|
@@ -61,5 +69,7 @@
|
|
|
61
69
|
"default": true
|
|
62
70
|
}
|
|
63
71
|
},
|
|
64
|
-
"required": [
|
|
72
|
+
"required": [
|
|
73
|
+
"rules"
|
|
74
|
+
]
|
|
65
75
|
}
|