@7n/rules 1.5.0 → 1.6.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.
Files changed (113) hide show
  1. package/.claude-template/hooks/.gitignore.snippet +9 -0
  2. package/CHANGELOG.md +12 -0
  3. package/bin/n-rules.js +83 -21
  4. package/package.json +1 -1
  5. package/rules/abie/main.mdc +0 -6
  6. package/rules/js/check/docs/main.md +1 -1
  7. package/rules/js/check/main.mjs +6 -10
  8. package/rules/js/main.mdc +0 -1
  9. package/rules/js/vscode_extensions/template/extensions.json.snippet.json +4 -1
  10. package/rules/npm-module/main.mdc +0 -2
  11. package/rules/php/tooling/docs/main.md +1 -1
  12. package/rules/php/tooling/main.mjs +2 -7
  13. package/rules/python/tooling/docs/main.md +1 -1
  14. package/rules/python/tooling/main.mjs +2 -7
  15. package/rules/rego/lib/docs/run-external-tool.md +1 -2
  16. package/rules/rego/lib/run-external-tool.mjs +4 -1
  17. package/rules/security/main.mdc +0 -1
  18. package/rules/style/main.mdc +0 -1
  19. package/rules/style/tooling/docs/main.md +1 -1
  20. package/rules/style/tooling/main.mjs +2 -7
  21. package/rules/tauri/release/release.mdc +1 -1
  22. package/rules/text/formatting/docs/main.md +1 -1
  23. package/rules/text/formatting/main.mjs +11 -11
  24. package/rules/text/main.mdc +0 -1
  25. package/scripts/docs/skills-cli.md +3 -3
  26. package/scripts/docs/sync-claude-config.md +1 -1
  27. package/scripts/lib/acp-runner.mjs +8 -10
  28. package/scripts/lib/docs/acp-runner.md +5 -5
  29. package/scripts/lib/docs/mirror-parity.md +1 -1
  30. package/scripts/lib/mirror-parity.mjs +49 -16
  31. package/scripts/skills-cli.mjs +1 -1
  32. package/scripts/sync-claude-config.mjs +2 -2
  33. package/rules/abie/clean_merged_ignore_branches/clean_merged_ignore_branches.mdc +0 -19
  34. package/rules/abie/clean_merged_ignore_branches/clean_merged_ignore_branches.rego +0 -81
  35. package/rules/abie/clean_merged_ignore_branches/concern.json +0 -11
  36. package/rules/abie/clean_merged_ignore_branches/docs/fix-clean_merged_ignore_branches.md +0 -31
  37. package/rules/abie/clean_merged_ignore_branches/docs/index.md +0 -9
  38. package/rules/abie/clean_merged_ignore_branches/fix-clean_merged_ignore_branches.mjs +0 -8
  39. package/rules/abie/clean_merged_ignore_branches/template/clean-merged-branch.yml.snippet.yml +0 -9
  40. package/rules/docker/lint_docker_yml/concern.json +0 -12
  41. package/rules/docker/lint_docker_yml/docs/fix-lint_docker_yml.md +0 -31
  42. package/rules/docker/lint_docker_yml/docs/index.md +0 -9
  43. package/rules/docker/lint_docker_yml/fix-lint_docker_yml.mjs +0 -5
  44. package/rules/docker/lint_docker_yml/lint_docker_yml.mdc +0 -14
  45. package/rules/docker/lint_docker_yml/lint_docker_yml.rego +0 -79
  46. package/rules/docker/lint_docker_yml/template/lint-docker.yml.snippet.yml +0 -41
  47. package/rules/js/lint_js_yml/concern.json +0 -12
  48. package/rules/js/lint_js_yml/docs/fix-lint_js_yml.md +0 -28
  49. package/rules/js/lint_js_yml/docs/index.md +0 -9
  50. package/rules/js/lint_js_yml/fix-lint_js_yml.mjs +0 -5
  51. package/rules/js/lint_js_yml/lint_js_yml.mdc +0 -14
  52. package/rules/js/lint_js_yml/lint_js_yml.rego +0 -117
  53. package/rules/js/lint_js_yml/template/lint-js.yml.snippet.yml +0 -40
  54. package/rules/k8s/lint_k8s_yml/concern.json +0 -12
  55. package/rules/k8s/lint_k8s_yml/docs/fix-lint_k8s_yml.md +0 -30
  56. package/rules/k8s/lint_k8s_yml/docs/index.md +0 -9
  57. package/rules/k8s/lint_k8s_yml/fix-lint_k8s_yml.mjs +0 -5
  58. package/rules/k8s/lint_k8s_yml/lint_k8s_yml.rego +0 -57
  59. package/rules/k8s/lint_k8s_yml/template/lint-k8s.yml.snippet.yml +0 -43
  60. package/rules/npm-module/npm_publish_yml/concern.json +0 -13
  61. package/rules/npm-module/npm_publish_yml/docs/fix-npm_publish_yml.md +0 -28
  62. package/rules/npm-module/npm_publish_yml/docs/index.md +0 -9
  63. package/rules/npm-module/npm_publish_yml/fix-npm_publish_yml.mjs +0 -8
  64. package/rules/npm-module/npm_publish_yml/template/npm-publish.yml.snippet.yml +0 -49
  65. package/rules/php/lint_php_yml/concern.json +0 -12
  66. package/rules/php/lint_php_yml/docs/fix-lint_php_yml.md +0 -28
  67. package/rules/php/lint_php_yml/docs/index.md +0 -9
  68. package/rules/php/lint_php_yml/fix-lint_php_yml.mjs +0 -5
  69. package/rules/php/lint_php_yml/lint_php_yml.mdc +0 -23
  70. package/rules/php/lint_php_yml/lint_php_yml.rego +0 -34
  71. package/rules/php/lint_php_yml/template/lint-php.yml.snippet.yml +0 -47
  72. package/rules/python/lint_python_yml/concern.json +0 -12
  73. package/rules/python/lint_python_yml/docs/fix-lint_python_yml.md +0 -27
  74. package/rules/python/lint_python_yml/docs/index.md +0 -9
  75. package/rules/python/lint_python_yml/fix-lint_python_yml.mjs +0 -5
  76. package/rules/python/lint_python_yml/lint_python_yml.mdc +0 -12
  77. package/rules/python/lint_python_yml/lint_python_yml.rego +0 -68
  78. package/rules/python/lint_python_yml/template/lint-python.yml.snippet.yml +0 -41
  79. package/rules/rust/lint_rust_yml/concern.json +0 -14
  80. package/rules/rust/lint_rust_yml/docs/fix-lint_rust_yml.md +0 -28
  81. package/rules/rust/lint_rust_yml/docs/index.md +0 -9
  82. package/rules/rust/lint_rust_yml/fix-lint_rust_yml.mjs +0 -5
  83. package/rules/rust/lint_rust_yml/lint_rust_yml.mdc +0 -17
  84. package/rules/rust/lint_rust_yml/lint_rust_yml.rego +0 -79
  85. package/rules/rust/lint_rust_yml/template/lint-rust.yml.snippet.yml +0 -44
  86. package/rules/rust/toolchain_cache/concern.json +0 -14
  87. package/rules/rust/toolchain_cache/docs/fix-toolchain_cache.md +0 -35
  88. package/rules/rust/toolchain_cache/docs/index.md +0 -12
  89. package/rules/rust/toolchain_cache/docs/main.md +0 -57
  90. package/rules/rust/toolchain_cache/fix-toolchain_cache.mjs +0 -117
  91. package/rules/rust/toolchain_cache/main.mjs +0 -177
  92. package/rules/rust/toolchain_cache/toolchain_cache.mdc +0 -23
  93. package/rules/security/lint_security_yml/concern.json +0 -14
  94. package/rules/security/lint_security_yml/docs/fix-lint_security_yml.md +0 -31
  95. package/rules/security/lint_security_yml/docs/index.md +0 -9
  96. package/rules/security/lint_security_yml/fix-lint_security_yml.mjs +0 -8
  97. package/rules/security/lint_security_yml/lint_security_yml.mdc +0 -7
  98. package/rules/security/lint_security_yml/lint_security_yml.rego +0 -33
  99. package/rules/security/lint_security_yml/template/lint-security.yml.snippet.yml +0 -31
  100. package/rules/style/lint_style_yml/concern.json +0 -12
  101. package/rules/style/lint_style_yml/docs/fix-lint_style_yml.md +0 -31
  102. package/rules/style/lint_style_yml/docs/index.md +0 -9
  103. package/rules/style/lint_style_yml/fix-lint_style_yml.mjs +0 -5
  104. package/rules/style/lint_style_yml/lint_style_yml.mdc +0 -13
  105. package/rules/style/lint_style_yml/lint_style_yml.rego +0 -38
  106. package/rules/style/lint_style_yml/template/lint-style.yml.snippet.yml +0 -39
  107. package/rules/text/lint_text/concern.json +0 -11
  108. package/rules/text/lint_text/docs/fix-lint_text.md +0 -28
  109. package/rules/text/lint_text/docs/index.md +0 -9
  110. package/rules/text/lint_text/fix-lint_text.mjs +0 -5
  111. package/rules/text/lint_text/lint_text.mdc +0 -19
  112. package/rules/text/lint_text/lint_text.rego +0 -104
  113. package/rules/text/lint_text/template/lint-text.yml.snippet.yml +0 -61
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * Parity дзеркала правил: `.cursor/rules/n-<id>.mdc` має дорівнювати канонічному
3
- * `npm/rules/<id>/<id>.mdc` з inlined-шаблонами — тим самим трансформом, що його
4
- * застосовує синк (`readBundledRuleContent` → `inlineTemplateLinks`). Дрейф виникає,
5
- * коли канонічний `.mdc` змінюють, не регенерувавши дзеркало (беклог адаптації flow #10).
3
+ * `main.mdc` правила з inlined-шаблонами — тим самим трансформом, що його
4
+ * застосовує синк (`readBundledRuleContent` → `inlineTemplateLinks` → mixin-extras).
5
+ * Дрейф виникає, коли канонічний `.mdc` змінюють, не регенерувавши дзеркало.
6
+ *
7
+ * Multi-dir: власник правила шукається у `npm/rules/<id>` і `plugins/<p>/rules/<id>`
8
+ * (перший з `main.mdc`); решта тек `rules/<id>` інших джерел — mixin-extras, їхні
9
+ * concern-mdc доінлайнюються після концернів власника (як у синку).
6
10
  *
7
11
  * Використовується і тестом-гардом (drift === []), і разовою регенерацією.
8
12
  */
@@ -10,41 +14,70 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs'
10
14
  import { dirname, join } from 'node:path'
11
15
 
12
16
  import { appendDiscoveredMdcFiles, inlineTemplateLinks } from './inline-template-links.mjs'
17
+ import { readNRulesConfigLite } from './read-n-rules-config-lite.mjs'
18
+ import { resolvePlugins } from './resolve-plugins.mjs'
13
19
 
14
20
  const MIRROR_PREFIX = 'n-'
15
21
  const MDC_EXT = '.mdc'
16
22
 
17
23
  /**
18
- * Керовані дзеркала `.cursor/rules/n-<id>.mdc`, що мають канонічне джерело
19
- * `npm/rules/<id>/<id>.mdc`. Дзеркала без канону (зовнішні) пропускаються.
24
+ * Rules-джерела репо у порядку пріоритету: ядро, потім АКТИВНІ плагіни (з `.n-rules.json`,
25
+ * резолв через node_modules той самий шлях, що у синку; неактивні plugins/* монорепо
26
+ * не враховуються, бо їх нема і в дзеркалах).
27
+ * @param {string} repoRoot корінь репо
28
+ * @returns {Promise<string[]>} абсолютні шляхи rules-каталогів
29
+ */
30
+ async function repoRulesDirs(repoRoot) {
31
+ const dirs = [join(repoRoot, 'npm/rules')]
32
+ const config = await readNRulesConfigLite(repoRoot)
33
+ for (const plugin of resolvePlugins(repoRoot, { plugins: config.plugins }, { allowInstall: false, quiet: true })) {
34
+ dirs.push(plugin.rulesDir)
35
+ }
36
+ return dirs
37
+ }
38
+
39
+ /**
40
+ * Керовані дзеркала `.cursor/rules/n-<id>.mdc` з канонічним джерелом у ядрі або плагіні.
41
+ * Дзеркала без канону (зовнішні) пропускаються.
20
42
  * @param {string} repoRoot корінь репо
21
- * @returns {{ id: string, mirrorPath: string, canonicalPath: string }[]} список
43
+ * @returns {Promise<{ id: string, mirrorPath: string, canonicalPath: string, extraDirs: string[] }[]>} список
22
44
  */
23
- export function listManagedMirrors(repoRoot) {
45
+ export async function listManagedMirrors(repoRoot) {
24
46
  const rulesDir = join(repoRoot, '.cursor/rules')
25
47
  if (!existsSync(rulesDir)) return []
48
+ const sources = await repoRulesDirs(repoRoot)
26
49
  return readdirSync(rulesDir)
27
50
  .filter(f => f.startsWith(MIRROR_PREFIX) && f.endsWith(MDC_EXT))
28
51
  .map(f => {
29
52
  const id = f.slice(MIRROR_PREFIX.length, -MDC_EXT.length)
53
+ const candidates = sources.map(s => join(s, id))
54
+ const owner = candidates.find(dir => existsSync(join(dir, `main${MDC_EXT}`)))
55
+ const extraDirs = owner === undefined ? [] : candidates.filter(dir => dir !== owner && existsSync(dir))
30
56
  return {
31
57
  id,
32
58
  mirrorPath: join(rulesDir, f),
33
- canonicalPath: join(repoRoot, 'npm/rules', id, `main${MDC_EXT}`)
59
+ canonicalPath: owner === undefined ? '' : join(owner, `main${MDC_EXT}`),
60
+ extraDirs
34
61
  }
35
62
  })
36
- .filter(m => existsSync(m.canonicalPath))
63
+ .filter(m => m.canonicalPath !== '')
37
64
  }
38
65
 
39
66
  /**
40
- * Очікуваний вміст дзеркала = канон з inlined-шаблонами (трансформ синку).
41
- * @param {string} canonicalPath абсолютний шлях `npm/rules/<id>/<id>.mdc`
67
+ * Очікуваний вміст дзеркала = канон з inlined-шаблонами + concern-mdc mixin-джерел
68
+ * (той самий трансформ, що у синку).
69
+ * @param {string} canonicalPath абсолютний шлях `rules/<id>/main.mdc` власника
70
+ * @param {string[]} [extraDirs] теки `rules/<id>` mixin-джерел
42
71
  * @returns {Promise<string>} очікуваний текст дзеркала
43
72
  */
44
- export async function expectedMirrorContent(canonicalPath) {
73
+ export async function expectedMirrorContent(canonicalPath, extraDirs = []) {
45
74
  const dir = dirname(canonicalPath)
46
- const withTemplates = await inlineTemplateLinks(readFileSync(canonicalPath, 'utf8'), dir)
47
- return appendDiscoveredMdcFiles(withTemplates, dir)
75
+ let out = await inlineTemplateLinks(readFileSync(canonicalPath, 'utf8'), dir)
76
+ out = await appendDiscoveredMdcFiles(out, dir)
77
+ for (const extra of extraDirs) {
78
+ out = await appendDiscoveredMdcFiles(out, extra)
79
+ }
80
+ return out
48
81
  }
49
82
 
50
83
  /**
@@ -54,8 +87,8 @@ export async function expectedMirrorContent(canonicalPath) {
54
87
  */
55
88
  export async function findMirrorDrift(repoRoot) {
56
89
  const drift = []
57
- for (const m of listManagedMirrors(repoRoot)) {
58
- const expected = await expectedMirrorContent(m.canonicalPath)
90
+ for (const m of await listManagedMirrors(repoRoot)) {
91
+ const expected = await expectedMirrorContent(m.canonicalPath, m.extraDirs)
59
92
  if (readFileSync(m.mirrorPath, 'utf8') !== expected) drift.push(m.id)
60
93
  }
61
94
  return drift.toSorted()
@@ -172,7 +172,7 @@ async function runPiRunner(prompt, rawSkillName, skillsRoot, projectDir, logErro
172
172
  /**
173
173
  * Делегує виконання скіла зовнішньому ACP-агенту (`cursor-agent acp`, `codex-acp`,
174
174
  * або deprecated `claude`-адаптер) через `runAcpRunner` — JSON-RPC поверх stdio,
175
- * дозволи на tool-calls автоапрувляться (без інтерактивних питань, паритет із
175
+ * дозволи на tool-calls автоматично схвалюються (без інтерактивних питань, паритет із
176
176
  * колишнім non-interactive `-p`). `claude` лишається як deprecated fallback для
177
177
  * тих, у кого pi-модель ще не налаштована; буде прибрано (мігруй на `skill pi`).
178
178
  * @param {'claude' | 'cursor' | 'codex'} kind якого ACP-агента запускати
@@ -26,7 +26,7 @@
26
26
  * `.pi/extensions/rtk.ts`. Без установленого rtk hooks мовчки пропускають команди,
27
27
  * тож конфіг безпечно потрапляє в git — установка зводиться до `brew install rtk-ai/tap/rtk`.
28
28
  * - `.gitignore` — **merge** (лише з `adr`): дописує відсутні рядки з канонічного
29
- * фрагмента `rules/adr/js/hooks/template/.gitignore.snippet` (`node_modules/`, `dist/`,
29
+ * фрагмента `.claude-template/hooks/.gitignore.snippet` (`node_modules/`, `dist/`,
30
30
  * `*.secret`, логи capture/normalize, `.normalize-state`, `.normalize.lock`,
31
31
  * `.claude/scheduled_tasks.lock`); існуючі рядки не перезаписуються.
32
32
  *
@@ -101,7 +101,7 @@ export const PI_EXTENSION_NAME = 'n-rules-adr'
101
101
  /** @deprecated — ім'я extension-теки до перейменування пакету; лишається для cleanup при ресинку. */
102
102
  export const LEGACY_PI_EXTENSION_NAME = 'n-cursor-adr'
103
103
  /** Відносний шлях до канонічного фрагмента `.gitignore` для ADR Stop-hook'ів у tarball пакета. */
104
- export const ADR_GITIGNORE_SNIPPET_REL = 'rules/adr/js/templates/hooks/.gitignore.snippet'
104
+ export const ADR_GITIGNORE_SNIPPET_REL = '.claude-template/hooks/.gitignore.snippet'
105
105
  const GITIGNORE_FILE = '.gitignore'
106
106
  const EOL_RE = /\r?\n/u
107
107
 
@@ -1,19 +0,0 @@
1
- ## Rego: `ignore_branches` у `clean-merged-branch.yml`
2
-
3
- Rego-пакет: `abie.clean_merged_ignore_branches`
4
-
5
- Цільовий файл: `.github/workflows/clean-merged-branch.yml`.
6
-
7
- Перевіряє, що у workflow-файлі є крок з `uses: phpdocker-io/github-actions-delete-abandoned-branches` і що його `with.ignore_branches` містить обидва токени `dev` та `ua` (case-insensitive, пробіли навколо токенів обрізаються). Токени зчитуються з template-файлу `data.template.snippet` — зміна канону автоматично змінює вимоги.
8
-
9
- Канонічний snippet: [clean-merged-branch.yml.snippet.yml](./template/clean-merged-branch.yml.snippet.yml)
10
-
11
- ```yaml title=".github/workflows/clean-merged-branch.yml (фрагмент)"
12
- - uses: phpdocker-io/github-actions-delete-abandoned-branches@v2
13
- with:
14
- ignore_branches: "dev,ua,main" # ✓ містить dev та ua
15
- ```
16
-
17
- ```yaml
18
- ignore_branches: "main" # ✗ не вистачає dev,ua
19
- ```
@@ -1,81 +0,0 @@
1
- # Перевірка `clean-merged-branch.yml` для abie-проєктів (abie.mdc).
2
- #
3
- # Канон надходить через --data: { "template": { "snippet": ... } }
4
- # Структура --data сформована з template/clean-merged-branch.yml.snippet.yml.
5
- # Action-маркер (`uses:` substring) і required branches (parsed з template's
6
- # `ignore_branches`) читаються з template. ga.clean_merged_branch перевіряє
7
- # повний канон workflow окремо; цей пакет — лише abie-specific шар.
8
- package abie.clean_merged_ignore_branches
9
-
10
- import rego.v1
11
-
12
- # Експектації з template's step (першого з steps).
13
- expected_step := step if some step in data.template.snippet.jobs.cleanup_old_branches.steps
14
-
15
- target_action_marker := expected_step.uses
16
-
17
- required_branches := parsed_ignore_tokens(expected_step.with.ignore_branches)
18
-
19
- step_missing_msg := sprintf(
20
- "clean-merged-branch.yml: не знайдено крок з uses: %s (abie.mdc)",
21
- [target_action_marker],
22
- )
23
-
24
- ignore_branches_missing_msg := sprintf(
25
- "clean-merged-branch.yml: не знайдено with.ignore_branches у кроці %s (abie.mdc)",
26
- [target_action_marker],
27
- )
28
-
29
- # ── deny ─────────────────────────────────────────────────────────────────
30
-
31
- deny contains step_missing_msg if {
32
- count(target_steps) == 0
33
- }
34
-
35
- deny contains ignore_branches_missing_msg if {
36
- count(target_steps) > 0
37
- not has_ignore_branches_value
38
- }
39
-
40
- deny contains msg if {
41
- count(target_steps) > 0
42
- ignore_branches_value != ""
43
- missing := required_branches - parsed_ignore_tokens(ignore_branches_value)
44
- count(missing) > 0
45
- msg := sprintf(
46
- "clean-merged-branch.yml: ignore_branches має містити %v (зараз: %q; не вистачає: %v) (abie.mdc)",
47
- [concat(",", sort(required_branches)), ignore_branches_value, concat(",", sort(missing))],
48
- )
49
- }
50
-
51
- # ── helpers ──────────────────────────────────────────────────────────────
52
-
53
- target_steps contains step if {
54
- some job in object.get(input, "jobs", {})
55
- some step in object.get(job, "steps", [])
56
- uses := object.get(step, "uses", "")
57
- is_string(uses)
58
- contains(uses, target_action_marker)
59
- }
60
-
61
- has_ignore_branches_value if {
62
- some step in target_steps
63
- v := object.get(object.get(step, "with", {}), "ignore_branches", null)
64
- is_string(v)
65
- }
66
-
67
- default ignore_branches_value := ""
68
-
69
- ignore_branches_value := values[0] if {
70
- values := [v |
71
- some step in target_steps
72
- v := object.get(object.get(step, "with", {}), "ignore_branches", null)
73
- is_string(v)
74
- ]
75
- count(values) > 0
76
- }
77
-
78
- parsed_ignore_tokens(value) := {lower(trim_space(part)) |
79
- some part in split(value, ",")
80
- trim_space(part) != ""
81
- }
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "policy": {
4
- "files": {
5
- "single": ".github/workflows/clean-merged-branch.yml"
6
- }
7
- },
8
- "requires": {
9
- "capability": "ci:github"
10
- }
11
- }
@@ -1,31 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-clean_merged_ignore_branches.mjs
4
- resource: npm/rules/abie/clean_merged_ignore_branches/fix-clean_merged_ignore_branches.mjs
5
- docgen:
6
- crc: 9fe2cc60
7
- model: openai-codex/gpt-5.5
8
- tier: cloud-avg
9
- score: 100
10
- issues: judge:inaccurate:0.99
11
- judgeModel: openai-codex/gpt-5.4-mini
12
- ---
13
-
14
- ## Огляд
15
-
16
- `patterns` визначає набір шляхів репозиторію, які правило має враховувати під час перевірки. Воно свідомо не охоплює `.git` і `.github`, щоб не аналізувати службові дані Git та конфігурацію GitHub. Правило працює read-only: лише повідомляє про невідповідність очікуваній структурі, не змінюючи файли чи зовнішні сховища.
17
-
18
- ## Поведінка
19
-
20
- 1. `patterns` визначає правило виправлення, яке забезпечує наявність workflow для очищення merged branches.
21
-
22
- 2. Правило орієнтується на шаблонний стан файлу `.github/workflows/clean-merged-branch.yml`, щоб уніфікувати поведінку репозиторіїв щодо прибирання вже злитих гілок.
23
-
24
- 3. Під час перевірки свідомо не обходить службові шляхи `.github` і `.git`, окрім цільового workflow-файлу, який є предметом виправлення.
25
-
26
- 4. `patterns` лише описує очікуване виправлення й не виконує самостійного запису у файлову систему чи інші сховища.
27
-
28
- ## Гарантії поведінки
29
-
30
- - Read-only: не виконує операцій запису (ФС/БД).
31
- - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -1,9 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/abie/clean_merged_ignore_branches
4
- resource: npm/rules/abie/clean_merged_ignore_branches/
5
- ---
6
-
7
- | Файл | Тип |
8
- | --------------------------------------------------------------------------- | --------- |
9
- | [fix-clean_merged_ignore_branches.mjs](fix-clean_merged_ignore_branches.md) | JS Module |
@@ -1,8 +0,0 @@
1
- import { createTemplateFixPattern } from '../../../scripts/lib/fix/template-deep-merge.mjs'
2
-
3
- export const patterns = [
4
- createTemplateFixPattern({
5
- id: 'abie-clean_merged_ignore_branches-template',
6
- targetPath: '.github/workflows/clean-merged-branch.yml'
7
- })
8
- ]
@@ -1,9 +0,0 @@
1
- # abie-specific шар поверх ga.clean_merged_branch:
2
- # у `clean-merged-branch.yml` крок phpdocker-io/github-actions-delete-abandoned-branches
3
- # має містити `ignore_branches: dev,ua` (обовʼязкові гілки для abie-проєктів).
4
- jobs:
5
- cleanup_old_branches:
6
- steps:
7
- - uses: phpdocker-io/github-actions-delete-abandoned-branches
8
- with:
9
- ignore_branches: dev,ua
@@ -1,12 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "policy": {
5
- "files": {
6
- "single": ".github/workflows/lint-docker.yml"
7
- }
8
- },
9
- "requires": {
10
- "capability": "ci:github"
11
- }
12
- }
@@ -1,31 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-lint_docker_yml.mjs
4
- resource: npm/rules/docker/lint_docker_yml/fix-lint_docker_yml.mjs
5
- docgen:
6
- crc: 551913c9
7
- model: openai-codex/gpt-5.5
8
- tier: cloud-avg
9
- score: 100
10
- issues: judge:inaccurate:0.98
11
- judgeModel: openai-codex/gpt-5.4-mini
12
- ---
13
-
14
- ## Огляд
15
-
16
- `patterns` визначає поведінку правила для обходу проєкту без змін у файловій системі чи зовнішніх сховищах. Службові директорії `.github` і `.git` свідомо не входять до перевірюваних шляхів, щоб правило працювало лише з релевантним кодом проєкту.
17
-
18
- ## Поведінка
19
-
20
- 1. `patterns` визначає правило автоматичного виправлення для workflow перевірки Docker.
21
-
22
- 2. `patterns` забезпечує наявність стандартного файлу `.github/workflows/lint-docker.yml`, щоб проєкт мав єдиний CI-контроль Docker-конфігурації.
23
-
24
- 3. `patterns` працює як read-only опис виправлення: сам модуль не змінює файлову систему чи зовнішні сховища.
25
-
26
- 4. Під час супровідного обходу шляхів свідомо не аналізуються `.github` і `.git`, щоб не змішувати службові директорії з основним кодом проєкту.
27
-
28
- ## Гарантії поведінки
29
-
30
- - Read-only: не виконує операцій запису (ФС/БД).
31
- - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -1,9 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/docker/lint_docker_yml
4
- resource: npm/rules/docker/lint_docker_yml/
5
- ---
6
-
7
- | Файл | Тип |
8
- | ------------------------------------------------- | --------- |
9
- | [fix-lint_docker_yml.mjs](fix-lint_docker_yml.md) | JS Module |
@@ -1,5 +0,0 @@
1
- import { createTemplateFixPattern } from '../../../scripts/lib/fix/template-deep-merge.mjs'
2
-
3
- export const patterns = [
4
- createTemplateFixPattern({ id: 'docker-lint_docker_yml-template', targetPath: '.github/workflows/lint-docker.yml' })
5
- ]
@@ -1,14 +0,0 @@
1
- ## Перевірка `.github/workflows/lint-docker.yml`
2
-
3
- Rego-пакет: `docker.lint_docker_yml`
4
-
5
- Цільовий файл: `.github/workflows/lint-docker.yml`
6
-
7
- Перевіряє, що CI-workflow lint-docker відповідає канонічному сніпету:
8
-
9
- - `on.push.paths` містить усі три glob-маски: `**/Dockerfile`, `**/*.Dockerfile`, `**/*.dockerfile`
10
- - присутні кроки `uses: actions/checkout@v6` та `uses: ./.github/actions/setup-bun-deps`
11
- - крок install hadolint містить рядок із версією `v2.12.0` (substring-перевірка)
12
- - крок lint містить `n-rules lint docker --no-fix`
13
-
14
- Канонічний шаблон: [lint-docker.yml.snippet.yml](./template/lint-docker.yml.snippet.yml)
@@ -1,79 +0,0 @@
1
- # Перевірка `.github/workflows/lint-docker.yml` (docker.mdc).
2
- #
3
- # Канон надходить через --data: { "template": { "snippet": ... } }
4
- # Структура --data сформована з template/lint-docker.yml.snippet.yml.
5
- # Path-маркери, hadolint версія (substring "v2.12.0" у run), setup-bun-deps
6
- # composite, `n-rules lint docker --no-fix` substring — все читається з template's snippet.
7
- # Універсальні workflow-перевірки — у `ga.workflow_common`.
8
- package docker.lint_docker_yml
9
-
10
- import rego.v1
11
-
12
- # Очікувані літерали з template.
13
- expected_paths := {p | some p in data.template.snippet.on.push.paths}
14
-
15
- # Required uses set — з template's steps.
16
- expected_uses_set contains u if {
17
- some step in data.template.snippet.jobs["lint-docker"].steps
18
- u := object.get(step, "uses", "")
19
- u != ""
20
- }
21
-
22
- # Required run substrings — collected per-step з template.
23
- expected_run_substrings contains r if {
24
- some step in data.template.snippet.jobs["lint-docker"].steps
25
- r := object.get(step, "run", "")
26
- r != ""
27
- }
28
-
29
- # Аліаси на input.
30
- all_step_uses contains u if {
31
- some job in object.get(input, "jobs", {})
32
- some step in object.get(job, "steps", [])
33
- u := object.get(step, "uses", "")
34
- u != ""
35
- }
36
-
37
- all_run_text := concat("\n", [run_text |
38
- some job in object.get(input, "jobs", {})
39
- some step in object.get(job, "steps", [])
40
- run_text := step_run_to_text(step)
41
- ])
42
-
43
- # conftest парсить YAML 1.1, тож канонічний `on:` без лапок стає булевим ключем
44
- # `true` (як у `ga.lint_ga`). Тому читаємо через `input["true"]`.
45
- push_paths_set := {p |
46
- some p in object.get(object.get(object.get(input, "true", {}), "push", {}), "paths", [])
47
- }
48
-
49
- # ── deny: on.push.paths subset-of ──────────────────────────────────────
50
-
51
- deny contains msg if {
52
- some required in expected_paths
53
- not required in push_paths_set
54
- msg := sprintf("lint-docker.yml: on.push.paths має містити %q (docker.mdc)", [required])
55
- }
56
-
57
- # ── deny: required uses present ────────────────────────────────────────
58
-
59
- deny contains msg if {
60
- some required_use in expected_uses_set
61
- not required_use in all_step_uses
62
- msg := sprintf("lint-docker.yml: відсутній крок `uses: %s` (docker.mdc)", [required_use])
63
- }
64
-
65
- # ── deny: required run substrings ──────────────────────────────────────
66
-
67
- deny contains msg if {
68
- some required_run in expected_run_substrings
69
- not contains(all_run_text, required_run)
70
- msg := sprintf("lint-docker.yml: жоден крок run не містить %q (docker.mdc)", [required_run])
71
- }
72
-
73
- # ── helpers ────────────────────────────────────────────────────────────
74
-
75
- step_run_to_text(step) := step.run if is_string(step.run)
76
-
77
- else := concat("\n", [s | some s in step.run]) if is_array(step.run)
78
-
79
- else := ""
@@ -1,41 +0,0 @@
1
- name: Lint Docker
2
-
3
- on:
4
- push:
5
- branches:
6
- - dev
7
- - main
8
- paths:
9
- - '**/Dockerfile'
10
- - '**/*.Dockerfile'
11
- - '**/*.dockerfile'
12
-
13
- pull_request:
14
- branches:
15
- - dev
16
- - main
17
-
18
- concurrency:
19
- group: ${{ github.ref }}-${{ github.workflow }}
20
- cancel-in-progress: true
21
-
22
- jobs:
23
- lint-docker:
24
- runs-on: ubuntu-latest
25
- permissions:
26
- contents: read
27
- steps:
28
- - uses: actions/checkout@v6
29
- with:
30
- persist-credentials: false
31
-
32
- - name: Install hadolint
33
- run: |
34
- curl -sSL -o /tmp/hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64
35
- chmod +x /tmp/hadolint
36
- sudo mv /tmp/hadolint /usr/local/bin/hadolint
37
-
38
- - uses: ./.github/actions/setup-bun-deps
39
-
40
- - name: Lint Docker
41
- run: bunx n-rules lint docker --no-fix
@@ -1,12 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "policy": {
5
- "files": {
6
- "single": ".github/workflows/lint-js.yml"
7
- }
8
- },
9
- "requires": {
10
- "capability": "ci:github"
11
- }
12
- }
@@ -1,28 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-lint_js_yml.mjs
4
- resource: npm/rules/js/lint_js_yml/fix-lint_js_yml.mjs
5
- docgen:
6
- crc: 39aecc30
7
- model: openai-codex/gpt-5.4-mini
8
- tier: cloud-min
9
- score: 100
10
- issues: judge:inaccurate:0.99
11
- judgeModel: openai-codex/gpt-5.4-mini
12
- ---
13
-
14
- ## Огляд
15
-
16
- `patterns` — read-only набір правил для аналізу й уніфікації JavaScript lint-конфігурації в межах доступних шляхів. Він свідомо пропускає `.github` і `.git`, щоб не зачіпати службові та git-специфічні файли.
17
-
18
- ## Поведінка
19
-
20
- 1. `patterns` формує набір правил для виправлення lint-конфігурації JavaScript-проєкту.
21
- 2. Для кожного правила прив’язує оновлення до цільового workflow-файлу `.github/workflows/lint-js.yml`.
22
- 3. У межах своєї роботи свідомо не зачіпає `.github` і `.git`, щоб уникати змін у службових та внутрішніх областях.
23
- 4. Працює в read-only режимі щодо ФС і БД: сам нічого не записує, а лише описує, що треба виправити.
24
-
25
- ## Гарантії поведінки
26
-
27
- - Read-only: не виконує операцій запису (ФС/БД).
28
- - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -1,9 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/js/lint_js_yml
4
- resource: npm/rules/js/lint_js_yml/
5
- ---
6
-
7
- | Файл | Тип |
8
- | ----------------------------------------- | --------- |
9
- | [fix-lint_js_yml.mjs](fix-lint_js_yml.md) | JS Module |
@@ -1,5 +0,0 @@
1
- import { createTemplateFixPattern } from '../../../scripts/lib/fix/template-deep-merge.mjs'
2
-
3
- export const patterns = [
4
- createTemplateFixPattern({ id: 'js-lint_js_yml-template', targetPath: '.github/workflows/lint-js.yml' })
5
- ]
@@ -1,14 +0,0 @@
1
- ## Rego-gate `.github/workflows/lint-js.yml`
2
-
3
- Rego-пакет: `js_lint.lint_js_yml`
4
-
5
- Цільовий файл: `.github/workflows/lint-js.yml`.
6
-
7
- Що перевіряється:
8
-
9
- - Наявність усіх `uses:`-кроків з канону (subset-of по `jobs.eslint.steps`); SHA-пін того самого action (`owner/action@<40-hex SHA>`, з тег-коментарем `# vX` чи без) задовольняє канонічний тег — НЕ замінюй наявний SHA-пін тегом
10
- - Наявність усіх рядків `run:` з канону (substring-match по всіх кроках)
11
- - `actions/checkout` (будь-який ref) має мати `with.persist-credentials: false` (inverse-check)
12
- - Заборона `--fix` у CI: `bunx oxlint ... --fix` або `eslint --fix` у будь-якому `run:` → deny
13
-
14
- Канон-snippet: [lint-js.yml.snippet.yml](./template/lint-js.yml.snippet.yml)