@7n/rules 1.6.0 → 1.7.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.
Files changed (113) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +1 -2
  3. package/rules/abie/http_route_base/http_route_base.mdc +23 -1
  4. package/rules/adr/madr_format/concern.json +1 -0
  5. package/rules/adr/madr_format/madr_format.mdc +119 -0
  6. package/rules/bun/bunfig/bunfig.mdc +5 -0
  7. package/rules/bun/lint-surface/concern.json +3 -0
  8. package/rules/bun/lint-surface/lint-surface.mdc +13 -0
  9. package/rules/capacitor/platforms/docs/main.md +1 -1
  10. package/rules/capacitor/platforms/main.mjs +5 -1
  11. package/rules/capacitor/platforms/platforms.mdc +108 -0
  12. package/rules/changelog/consistency/comparison-models.mdc +46 -0
  13. package/rules/changelog/consistency/consistency.mdc +35 -0
  14. package/rules/docker/main.mdc +235 -2
  15. package/rules/image-avif/avif_generation/avif_generation.mdc +14 -0
  16. package/rules/js/check/check.mdc +26 -0
  17. package/rules/js/file-extensions/concern.json +3 -0
  18. package/rules/js/file-extensions/file-extensions.mdc +12 -0
  19. package/rules/js/jscpd_config/jscpd_config.mdc +28 -0
  20. package/rules/js/knip/knip.mdc +15 -0
  21. package/rules/js/utils_imports/utils_imports.mdc +15 -0
  22. package/rules/js-bun-db/connection/concern.json +3 -0
  23. package/rules/js-bun-db/connection/connection.mdc +42 -0
  24. package/rules/js-bun-db/package_json/package_json.mdc +15 -1
  25. package/rules/js-bun-db/pg_format_identifiers/concern.json +3 -0
  26. package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +104 -0
  27. package/rules/js-bun-db/safety/safety.mdc +458 -0
  28. package/rules/js-mssql/main.mdc +130 -0
  29. package/rules/js-mssql/mssql-tvp/concern.json +3 -0
  30. package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +77 -0
  31. package/rules/js-run/configmap/configmap.mdc +6 -0
  32. package/rules/js-run/jsconfig/jsconfig.mdc +23 -0
  33. package/rules/js-run/package_json/package_json.mdc +6 -0
  34. package/rules/js-run/project-structure/concern.json +3 -0
  35. package/rules/js-run/project-structure/project-structure.mdc +11 -0
  36. package/rules/js-run/runtime/runtime.mdc +170 -0
  37. package/rules/js-run/scope/concern.json +3 -0
  38. package/rules/js-run/scope/scope.mdc +11 -0
  39. package/rules/k8s/hasura_configmap/hasura_configmap.mdc +6 -0
  40. package/rules/k8s/hpa_pdb/hpa_pdb.mdc +134 -0
  41. package/rules/k8s/kubeconform/kubeconform.mdc +38 -0
  42. package/rules/k8s/kustomization/kustomization.mdc +73 -0
  43. package/rules/k8s/main.mdc +68 -0
  44. package/rules/k8s/manifest/manifest.mdc +37 -0
  45. package/rules/k8s/manifests/docs/fix-manifests.md +3 -1
  46. package/rules/k8s/manifests/fix-manifests.mjs +11 -0
  47. package/rules/k8s/manifests/main.mjs +28 -0
  48. package/rules/k8s/network_policy/network_policy.mdc +33 -0
  49. package/rules/nginx-default-tpl/http-route/concern.json +1 -0
  50. package/rules/nginx-default-tpl/http-route/http-route.mdc +54 -0
  51. package/rules/nginx-default-tpl/template/template.mdc +152 -0
  52. package/rules/php/tooling/tooling.mdc +7 -6
  53. package/rules/python/pyproject_toml/pyproject_toml.mdc +17 -1
  54. package/rules/python/tooling/tooling.mdc +9 -10
  55. package/rules/rego/main.mdc +14 -0
  56. package/rules/rust/check/check.mdc +16 -0
  57. package/rules/style/admin_table/admin_table.mdc +88 -0
  58. package/rules/style/admin_table/concern.json +7 -0
  59. package/rules/style/admin_table/docs/index.md +9 -0
  60. package/rules/style/admin_table/docs/main.md +14 -0
  61. package/rules/style/admin_table/main.mjs +46 -0
  62. package/rules/style/colors/colors.mdc +21 -0
  63. package/rules/style/colors/concern.json +3 -0
  64. package/rules/style/gap/concern.json +7 -0
  65. package/rules/style/gap/docs/index.md +9 -0
  66. package/rules/style/gap/docs/main.md +15 -0
  67. package/rules/style/gap/gap.mdc +22 -0
  68. package/rules/style/gap/main.mjs +51 -0
  69. package/rules/style/quasar/concern.json +3 -0
  70. package/rules/style/quasar/quasar.mdc +7 -0
  71. package/rules/style/quasar_fixes/concern.json +7 -0
  72. package/rules/style/quasar_fixes/docs/index.md +9 -0
  73. package/rules/style/quasar_fixes/docs/main.md +16 -0
  74. package/rules/style/quasar_fixes/main.mjs +57 -0
  75. package/rules/style/quasar_fixes/quasar_fixes.mdc +32 -0
  76. package/rules/tauri/tool_surface/concern.json +16 -0
  77. package/rules/tauri/tool_surface/docs/index.md +9 -0
  78. package/rules/tauri/tool_surface/docs/main.md +24 -0
  79. package/rules/tauri/tool_surface/main.mjs +145 -0
  80. package/rules/tauri/tool_surface/tool_surface.mdc +29 -0
  81. package/rules/test/vitest-api-conventions/concern.json +7 -0
  82. package/rules/test/vitest-api-conventions/docs/index.md +9 -0
  83. package/rules/test/vitest-api-conventions/docs/main.md +40 -0
  84. package/rules/test/vitest-api-conventions/main.mjs +186 -0
  85. package/rules/test/vitest-api-conventions/vitest-api-conventions.mdc +129 -0
  86. package/rules/text/cspell/cspell.mdc +18 -0
  87. package/rules/text/markdownlint/markdownlint.mdc +4 -0
  88. package/rules/text/run-dotenv-linter/run-dotenv-linter.mdc +17 -0
  89. package/rules/text/run-shellcheck/run-shellcheck.mdc +17 -0
  90. package/rules/text/run-v8r/run-v8r.mdc +23 -0
  91. package/rules/vue/composition-api/composition-api.mdc +82 -0
  92. package/rules/vue/composition-api/concern.json +3 -0
  93. package/rules/vue/main.mdc +1 -1
  94. package/rules/vue/nheader-layout/concern.json +3 -0
  95. package/rules/vue/nheader-layout/nheader-layout.mdc +171 -0
  96. package/rules/vue/packages/packages.mdc +56 -0
  97. package/rules/vue/quasar-ui/concern.json +3 -0
  98. package/rules/vue/quasar-ui/quasar-ui.mdc +32 -0
  99. package/rules/vue/structure/concern.json +3 -0
  100. package/rules/vue/structure/structure.mdc +101 -0
  101. package/rules/vue/testing/concern.json +3 -0
  102. package/rules/vue/testing/testing.mdc +40 -0
  103. package/rules/vue/tfm-translations/concern.json +7 -0
  104. package/rules/vue/tfm-translations/docs/main.md +29 -0
  105. package/rules/vue/tfm-translations/main.mjs +55 -0
  106. package/rules/vue/tfm-translations/tfm-translations.mdc +32 -0
  107. package/rules/vue/vite-config/concern.json +3 -0
  108. package/rules/vue/vite-config/vite-config.mdc +153 -0
  109. package/rules/vue/vite-env/concern.json +3 -0
  110. package/rules/vue/vite-env/vite-env.mdc +61 -0
  111. package/scripts/docs/skills-cli.md +3 -3
  112. package/scripts/lib/acp-runner.mjs +23 -28
  113. package/scripts/skills-cli.mjs +35 -26
@@ -0,0 +1,153 @@
1
+ ## Vite-конфігурація: VueMacros, AutoImport, Layouts
2
+
3
+ Потрібно використовувати **Vite версії 8.1 та вище** для frontend проекту на Vue.
4
+
5
+ Потрібно використовувати **`unplugin-auto-import`** для автоматичного імпортування компонентів, composables, utils та інших функцій і прибирати з файлів усередині Vite проектів відповідні ручні імпорти, зокрема рядки виду `import { … } from 'vue'` — API Vue (`ref`, `computed`, `watch` тощо) мають підставлятися через auto-import, а не дублюватися явним імпортом з модуля `vue`.
6
+
7
+ **Виняток — бібліотеки компонентів (`vue` у `peerDependencies`).** Увесь стек auto-import застосовується лише коли `vue` підключено як звичайну `dependencies`. Якщо ж пакет оголошує `vue` у `peerDependencies` — це проєкт-бібліотека компонентів: його джерела споживаються Vite-додатками і **не** проходять через `unplugin-auto-import` споживача. Тому до таких пакетів **не** застосовуються: заборона явних `import { ref, computed, … } from 'vue'` (вони обовʼязкові), вимога `'vue'` у `AutoImport.imports`, а також вимоги наявності `VueMacros` / `AutoImport` у `vite.config`. Решта перевірок (заборона `esbuild`, `process.env.npm_lifecycle_event` тощо) лишаються. Тригер винятку — `isVueComponentLibraryPkg` у `npm/rules/vue/packages/main.mjs`.
8
+
9
+ Потрібно використовувати **`vite-plugin-vue-layouts-next`** для автоматичного імпортування layout компонентів.
10
+
11
+ ### Це вже перевіряється автоматично
12
+
13
+ Увесь зміст цього файлу — вже реалізовані, а не лише документовані, перевірки:
14
+
15
+ - **Обов'язкові залежності** (`vite` мажор ≥ 8 і мінор ≥ 1, `@vitejs/plugin-vue`, `vue-macros`, `unplugin-auto-import`, `vite-plugin-vue-layouts-next`) і **заборона** `esbuild`/`vitest`/`jsdom` у `package.json` — Rego-пакет `vue.package_json` (`npm/rules/vue/package_json/package_json.rego`, концерн `package_json`).
16
+ - **`vite.config.{js,ts,mjs}`**: наявність `VueMacros`, `AutoImport` (і `'vue'` у його `imports`, якщо застосовний), `css: { transformer: 'lightningcss' }`, заборона `esbuild`, заборона `process.env.npm_lifecycle_event` (Bun-сумісність) — JS-концерн `packages` (`npm/rules/vue/packages/main.mjs`, функція `checkViteConfig`).
17
+ - **`.vscode/extensions.json`** з рекомендацією `Vue.volar` — той самий концерн `packages` (`checkVueVolarRecommendation`).
18
+
19
+ Цей concern (`vite-config`) залишається **docs-only** — він не додає нової перевірки, а лише зберігає розгорнутий еталонний приклад `vite.config.js`/`package.json` і пояснення для людини; фактичний gate — вище.
20
+
21
+ ### Еталонний `vite.config.js`
22
+
23
+ ```javascript title="vite.config.js"
24
+ import Vue from '@vitejs/plugin-vue'
25
+ import VueMacros from 'vue-macros/vite'
26
+ import { defineConfig } from 'vite'
27
+ import AutoImport from 'unplugin-auto-import/vite'
28
+ import Layouts from 'vite-plugin-vue-layouts-next'
29
+
30
+ export default defineConfig({
31
+ plugins: [
32
+ AutoImport({
33
+ imports: [
34
+ // presets
35
+ 'vue',
36
+ 'vue-router',
37
+ 'quasar',
38
+ 'pinia',
39
+ // custom
40
+ {
41
+ '@nitra/vite-boot/apollo': [
42
+ // named imports
43
+ 'gql',
44
+ 'useQuery',
45
+ 'useMutation',
46
+ 'useSubscription'
47
+ ],
48
+ '@nitra/consola': [
49
+ // named imports
50
+ 'createLogger' // import { createLogger } from '@nitra/consola'
51
+ ]
52
+ }
53
+ ]
54
+ }),
55
+ VueMacros({
56
+ plugins: {
57
+ vue: Vue()
58
+ }
59
+ }),
60
+ Layouts()
61
+ ]
62
+ })
63
+ ```
64
+
65
+ ### `npm_lifecycle_event` (Bun-сумісність)
66
+
67
+ У більшості проектів у файлі `vite.config.js` є конструкція виду:
68
+
69
+ ```javascript
70
+ switch (process.env.npm_lifecycle_event) {
71
+ case 'start-remote-tr': {
72
+ ```
73
+
74
+ Вона перестала працювати з новим Bun. Тепер її треба обрамити у функцію і передавати `mode`:
75
+
76
+ ```javascript title="vite.config.js"
77
+ function getProxy(mode) {
78
+ const proxy = {}
79
+
80
+ switch (mode) {
81
+ case 'remote-tr': {
82
+ proxy['^/auth/.*'] = 'https://tr.efes.cloud'
83
+ proxy['/file-link/'] = 'https://tr.efes.cloud'
84
+ break
85
+ }
86
+ default: {
87
+ proxy['^/auth/.*'] = 'https://dev.efes.cloud'
88
+ proxy['/file-link/'] = 'https://dev.efes.cloud'
89
+ }
90
+ }
91
+ return proxy
92
+ }
93
+ ```
94
+
95
+ ```javascript title="vite.config.js"
96
+ export default defineConfig(({ mode, command }) => {
97
+ // ...
98
+ server: {
99
+ proxy: getProxy(mode)
100
+ }
101
+ })
102
+ ```
103
+
104
+ ### CSS-трансформер: lightningcss (обов'язково)
105
+
106
+ У `vite.config` має бути `css: { transformer: 'lightningcss' }`:
107
+
108
+ ```javascript title="vite.config.js"
109
+ export default defineConfig({
110
+ css: {
111
+ transformer: 'lightningcss'
112
+ },
113
+ // ...
114
+ })
115
+ ```
116
+
117
+ Lightningcss ~18% швидше за sass-трансформер на типовому Vue+Quasar-проєкті. Потрібний `lightningcss` у `devDependencies`.
118
+
119
+ **Виняток:** бібліотеки компонентів (`vue` у `peerDependencies`) від цієї вимоги звільнені — вони не збираються напряму.
120
+
121
+ ### esbuild заборонено
122
+
123
+ У проєкті не має бути залежності `esbuild` і згадок `esbuild` у конфігах/коді. Якщо десь є налаштування або інструкції під `esbuild` — заміни на **rolldown**.
124
+
125
+ ### Еталонний `package.json`
126
+
127
+ мінімальний повний `package.json` для Vite + Vue + `vue-macros` (версії підлаштуй під проєкт):
128
+
129
+ ```json title="package.json"
130
+ {
131
+ "name": "my-vue-app",
132
+ "private": true,
133
+ "type": "module",
134
+ "dependencies": {
135
+ "vue": "^3.6.12"
136
+ },
137
+ "devDependencies": {
138
+ "vite": "^8.1.0",
139
+ "@vitejs/plugin-vue": "^6.0.0",
140
+ "vue-macros": "^3.1.2"
141
+ }
142
+ }
143
+ ```
144
+
145
+ ### VS Code розширення
146
+
147
+ У файлі `.vscode/extensions.json` потрібна рекомендація для Vue:
148
+
149
+ ```json title=".vscode/extensions.json"
150
+ {
151
+ "recommendations": ["Vue.volar"]
152
+ }
153
+ ```
@@ -0,0 +1,3 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json"
3
+ }
@@ -0,0 +1,61 @@
1
+ ## Vite client types (Volar, імпорти асетів)
2
+
3
+ Без типів **Vite** редактор (Volar / TypeScript) не знає, що імпорт статичного файлу (`import url from './hero.avif'`, `*.png`, `*.svg` тощо) відповідає модулю з `string` URL. Тоді у `.vue` з'являється помилка на кшталт **Cannot find module '…' or its corresponding type declarations**.
4
+
5
+ У **кожному** workspace-пакеті з **Vue + Vite** обов'язково:
6
+
7
+ 1. **`src/vite-env.d.ts`** — рівно з посиланням на клієнтські типи Vite (одного рядка достатньо):
8
+
9
+ ```ts title="src/vite-env.d.ts"
10
+ /// <reference types="vite/client" />
11
+ ```
12
+
13
+ Так підтягуються декларації з `vite/client.d.ts` (`declare module '*.avif'`, `*.png`, …).
14
+
15
+ 2. **Корінь пакета:** **`jsconfig.json`** із **`include`**, що охоплює `src` (наприклад `"include": ["src/**/*"]`), щоб мова служби бачила `vite-env.d.ts` і SFC.
16
+
17
+ Мінімальний приклад для JS-пакета:
18
+
19
+ ```json title="jsconfig.json"
20
+ {
21
+ "compilerOptions": {
22
+ "target": "ESNext",
23
+ "module": "ESNext",
24
+ "moduleResolution": "bundler",
25
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
26
+ "jsx": "preserve",
27
+ "strict": true,
28
+ "noEmit": true,
29
+ "skipLibCheck": true,
30
+ "resolveJsonModule": true,
31
+ "isolatedModules": true,
32
+ "allowJs": true
33
+ },
34
+ "include": ["src/**/*"]
35
+ }
36
+ ```
37
+
38
+ **Не** звужуй без потреби **`compilerOptions.types`** до `["vite/client"]`: це може відрізати інші пакети з `@types` і зламати інші підказки. Достатньо `/// <reference types="vite/client" />` у `vite-env.d.ts` і коректного `include`.
39
+
40
+ ### Ця частина вже перевіряється автоматично
41
+
42
+ Наявність `src/vite-env.d.ts` з коректним `/// <reference types="vite/client" />` і наявність `jsconfig.json` у корені пакета — вже реалізований JS-концерн `packages` (`npm/rules/vue/packages/main.mjs`, функція `checkViteClientEnvAndEditorConfig`). Цей concern (`vite-env`) не дублює перевірку — лишається docs-only довідкою з еталонним прикладом.
43
+
44
+ ## Статичні файли — BASE_URL
45
+
46
+ Для підключення статичних файлів не використовуй відносні шляхи. Завжди будуй URL через `import.meta.env.BASE_URL`:
47
+
48
+ ```vue
49
+ <!-- Погано -->
50
+ <img src="/logo.png" />
51
+ <img src="./assets/logo.png" />
52
+
53
+ <!-- Добре -->
54
+ <img :src="baseUrl + 'logo.png'" />
55
+ ```
56
+
57
+ ```javascript
58
+ const baseUrl = import.meta.env.BASE_URL
59
+ ```
60
+
61
+ Ця вимога **не** перевіряється механічно: рядок-специфікатор `src` може легітимно вказувати на зовнішній URL, дата-URI чи інший валідний шлях, і однозначно відрізнити «статичний асет проекту» від інших випадків без хибних спрацювань неможливо — залишається на розсуд ревʼю.
@@ -3,7 +3,7 @@ type: JS Module
3
3
  title: skills-cli.mjs
4
4
  resource: npm/scripts/skills-cli.mjs
5
5
  docgen:
6
- crc: ce3d7d60
6
+ crc: 1a8f83e4
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  ---
9
9
 
@@ -30,7 +30,7 @@ runSkillsCli виконує логіку командного інтерфейс
30
30
 
31
31
  ## Гарантії поведінки
32
32
 
33
- - Сам модуль лише читає файли й збирає промпт; **виконання** делегується агенту: `pi` (вбудований, мутує дерево, запускає bash) або зовнішньому ACP-агенту `cursor`/`codex`/`claude` через `runAcpRunner` (`./lib/acp-runner.mjs`) — JSON-RPC поверх stdio (`cursor-agent acp`, вбудовані адаптери `@agentclientprotocol/codex-acp` і `@agentclientprotocol/claude-agent-acp`), а не сирий `stdin`/`stdout`-pipe-передавання.
33
+ - Сам модуль лише читає файли й збирає промпт; **виконання** делегується агенту: `pi` (вбудований, мутує дерево, запускає bash) або зовнішньому ACP-агенту `cursor`/`codex`/`claude` через `runAcpRunner` (`./lib/acp-runner.mjs`) — JSON-RPC поверх stdio (`cursor-agent acp`, бандловані адаптери `@agentclientprotocol/codex-acp` і `@agentclientprotocol/claude-agent-acp`), а не сирий `stdin`/`stdout`-піпінг.
34
34
  - Тира моделі для `pi`-runner береться з `main.json.tier` скіла (дефолт `max`).
35
- - ACP-раннер автоматично схвалює `session/request_permission` (без інтерактивних питань) — паритет із колишнім non-interactive режимом; `Client`-реалізація читає/пише файли напряму через `node:fs`.
35
+ - ACP-раннер автоапрувляє `session/request_permission` (без інтерактивних питань) — паритет із колишнім non-interactive режимом; `Client`-реалізація читає/пише файли напряму через `node:fs`.
36
36
  - Лише `claude` — deprecated: друкує попередження й лишається як fallback, доки не налаштовано pi-модель. `cursor`/`codex` — повноцінні раннери без попередження.
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Виконання скіла через зовнішнього ACP-агента (Agent Client Protocol,
3
- * agentclientprotocol.com) — JSON-RPC поверх stdio замість сирого
4
- * `stdin`/`stdout`-передавання через pipe CLI.
2
+ * Deprecated JS ACP-шим для `skill claude` (Agent Client Protocol,
3
+ * agentclientprotocol.com) — JSON-RPC поверх stdio.
5
4
  *
6
- * Заміняє колишній `runLlmCli` (spawnSync + `-p`/`exec -`) для `cursor`/`codex`/`claude`:
7
- * підʼєднання йде через офіційний TS SDK `@zed-industries/agent-client-protocol`,
8
- * а дозволи на tool-calls (`session/request_permission`) автоматично схвалюються без участі
9
- * людини паритет із сьогоднішнім non-interactive `-p`-режимом (full user-trust,
10
- * без write-guard, як задокументовано в `@7n/llm-lib/agent-skill`).
5
+ * `cursor`/`codex` мігрували на `@7n/llm-lib/acp` (napi-міст до
6
+ * `llm_cascade::acp` та сама протокольна логіка, але в Rust, без
7
+ * дублювання в JS). `claude` лишається тут як окремий JS-шим, бо Rust-крейт
8
+ * `AcpAgentKind` його не моделює (лише `Cursor`/`Codex`); підʼєднання йде
9
+ * через офіційний TS SDK `@zed-industries/agent-client-protocol`, а дозволи
10
+ * на tool-calls (`session/request_permission`) автоматично схвалюються без
11
+ * участі людини — паритет із колишнім non-interactive `-p`-режимом
12
+ * (full user-trust, без write-guard, як задокументовано в `@7n/llm-lib/agent-skill`).
11
13
  *
12
- * `cursor` — нативний ACP-режим самого `cursor-agent` (`cursor-agent acp`).
13
- * `codex`/`claude` офіційні адаптери (`@agentclientprotocol/codex-acp`,
14
- * `@agentclientprotocol/claude-agent-acp`), що вбудовують свій рушій — зовнішній
15
- * бінарник `codex`/`claude` у PATH більше не потрібен.
14
+ * `claude` — офіційний адаптер (`@agentclientprotocol/claude-agent-acp`), що
15
+ * вбудовує свій рушій — зовнішній бінарник `claude` у PATH не потрібен.
16
16
  */
17
17
 
18
18
  import { spawn } from 'node:child_process'
@@ -25,13 +25,11 @@ import { Readable, Writable } from 'node:stream'
25
25
  const require = createRequire(import.meta.url)
26
26
 
27
27
  /**
28
- * Команда запуску ACP-агента на провайдер. `cursor` зовнішній бінарник у PATH;
29
- * `codex`/`claude` — резолвляться з `bin`-запису вбудованого npm-пакета-адаптера.
30
- * @type {Record<'cursor' | 'codex' | 'claude', { bin: string, args: string[] } | { adapterPackage: string }>}
28
+ * Команда запуску ACP-агента на провайдер. Резолвиться з `bin`-запису
29
+ * вбудованого npm-пакета-адаптера.
30
+ * @type {Record<'claude', { adapterPackage: string }>}
31
31
  */
32
32
  export const ACP_AGENT_COMMANDS = {
33
- cursor: { bin: 'cursor-agent', args: ['acp'] },
34
- codex: { adapterPackage: '@agentclientprotocol/codex-acp' },
35
33
  claude: { adapterPackage: '@agentclientprotocol/claude-agent-acp' }
36
34
  }
37
35
 
@@ -46,7 +44,7 @@ export function stopReasonToExitCode(stopReason) {
46
44
 
47
45
  /**
48
46
  * Резолвить абсолютний шлях до bin-файлу вбудованого ACP-адаптера з його `package.json`.
49
- * @param {string} adapterPackage назва npm-пакета адаптера (`@agentclientprotocol/codex-acp`, …)
47
+ * @param {string} adapterPackage назва npm-пакета адаптера (`@agentclientprotocol/claude-agent-acp`)
50
48
  * @returns {string} абсолютний шлях до виконуваного файлу адаптера
51
49
  */
52
50
  export function resolveAdapterBin(adapterPackage) {
@@ -121,12 +119,13 @@ class AcpSkillClient {
121
119
  }
122
120
 
123
121
  /**
124
- * Виконує скіл через зовнішнього ACP-агента (`cursor`/`codex`/`claude`).
125
- * @param {'cursor' | 'codex' | 'claude'} kind провайдер
122
+ * Виконує скіл через зовнішнього ACP-агента. Єдиний лишений провайдер —
123
+ * deprecated `claude` (`cursor`/`codex` див. `@7n/llm-lib/acp`).
124
+ * @param {'claude'} kind провайдер
126
125
  * @param {string} prompt готовий промпт скіла
127
126
  * @param {string} projectDir робочий каталог сесії агента
128
127
  * @param {(line: string) => void} logError вивід помилок
129
- * @param {{ acp?: object, spawnFn?: typeof spawn, out?: (chunk: string) => void, resolveAdapterBin?: (pkg: string) => string, isBinaryInPath?: (bin: string) => boolean }} [deps] інжекти для тестів
128
+ * @param {{ acp?: object, spawnFn?: typeof spawn, out?: (chunk: string) => void, resolveAdapterBin?: (pkg: string) => string }} [deps] інжекти для тестів
130
129
  * @returns {Promise<number>} exit code (0 — `end_turn`, 1 — інакше)
131
130
  */
132
131
  export async function runAcpRunner(kind, prompt, projectDir, logError, deps = {}) {
@@ -134,13 +133,9 @@ export async function runAcpRunner(kind, prompt, projectDir, logError, deps = {}
134
133
  const out = deps.out ?? (chunk => stdout.write(chunk))
135
134
  const resolveBin = deps.resolveAdapterBin ?? resolveAdapterBin
136
135
 
137
- const command = ACP_AGENT_COMMANDS[kind]
138
- const { bin, args } =
139
- 'adapterPackage' in command ? { bin: process.execPath, args: [resolveBin(command.adapterPackage)] } : command
140
-
141
- if ('bin' in command && deps.isBinaryInPath && !deps.isBinaryInPath(command.bin)) {
142
- throw new Error(`\`${command.bin}\` not found in PATH. Install ${kind} CLI (ACP mode) or use \`skill pi\`.`)
143
- }
136
+ const { adapterPackage } = ACP_AGENT_COMMANDS[kind]
137
+ const bin = process.execPath
138
+ const args = [resolveBin(adapterPackage)]
144
139
 
145
140
  const acp = deps.acp ?? (await import('@zed-industries/agent-client-protocol'))
146
141
  const child = spawnFn(bin, args, { cwd: projectDir, stdio: ['pipe', 'pipe', 'inherit'], env: process.env })
@@ -4,7 +4,9 @@
4
4
  * Скіли читаються з `npm/skills/<id>/SKILL.md` установленого пакета (або кешу `npx`).
5
5
  * Промпт збирає інструкцію скілу + контекст поточного CWD (`package.json`, `tsconfig.json`,
6
6
  * `.n-rules.json`) — далі stdout або виконання через один з раннерів: вбудований
7
- * pi-агент, чи зовнішній ACP-агент (`cursor-agent acp`, `codex-acp`, або deprecated `claude`).
7
+ * pi-агент, чи зовнішній ACP-агент. `cursor`/`codex` через `@7n/llm-lib/acp`
8
+ * (napi-міст до `llm_cascade::acp`, без власного JSON-RPC у JS); deprecated
9
+ * `claude` — окремий JS-шим (`./lib/acp-runner.mjs`), бо Rust-крейт його не моделює.
8
10
  *
9
11
  * Підтримувані формати:
10
12
  * `npx \@7n/rules skill list`
@@ -12,14 +14,14 @@
12
14
  * `npx \@7n/rules skill pi taze` — виконати через вбудований pi-агент (рекомендовано)
13
15
  * `npx \@7n/rules skill pi taze "онови залежності"`
14
16
  * `npx \@7n/rules skill cursor taze` — Cursor CLI через ACP (`cursor-agent acp`)
15
- * `npx \@7n/rules skill codex taze` — Codex через ACP-адаптер (`@agentclientprotocol/codex-acp`)
17
+ * `npx \@7n/rules skill codex taze` — Codex через ACP (napi-міст `@7n/llm-lib/acp`, Rust спавнить `npx \@agentclientprotocol/codex-acp`)
16
18
  * `npx \@7n/rules skill claude taze` — deprecated: Claude Code через ACP-адаптер
17
19
  */
18
20
 
19
- import { spawnSync } from 'node:child_process'
21
+ import { runAcpAgent } from '@7n/llm-lib/acp'
20
22
  import { existsSync, readFileSync, readdirSync } from 'node:fs'
21
23
  import { dirname, join } from 'node:path'
22
- import { cwd } from 'node:process'
24
+ import { cwd, stdout } from 'node:process'
23
25
  import { fileURLToPath } from 'node:url'
24
26
 
25
27
  import { runAcpRunner } from './lib/acp-runner.mjs'
@@ -28,7 +30,11 @@ import { readSkillMetaRaw, skillTier } from './lib/skill-meta.mjs'
28
30
  /** Виконавці скіла. `pi` — вбудований (рекомендований); `cursor`/`codex`/`claude` — зовнішні ACP-агенти (`claude` — deprecated). */
29
31
  const RUNNERS = new Set(['pi', 'cursor', 'codex', 'claude'])
30
32
 
31
- /** Раннери, що йдуть через зовнішнього ACP-агента (`runAcpRunner`), і чи deprecated (друкує попередження перед запуском). */
33
+ /**
34
+ * Раннери, що йдуть через зовнішнього ACP-агента, і чи deprecated (друкує попередження
35
+ * перед запуском). `cursor`/`codex` — napi-міст `@7n/llm-lib/acp`; `claude` — окремий
36
+ * JS-шим `runAcpRunner` (Rust-крейт `llm_cascade::acp` `claude` не моделює).
37
+ */
32
38
  const ACP_RUNNERS = {
33
39
  claude: { deprecated: true },
34
40
  cursor: { deprecated: false },
@@ -47,17 +53,6 @@ const USAGE_LINES = [
47
53
  'Skill id: каталог у пакеті (lint, taze, …) або з префіксом n- (n-lint → lint).'
48
54
  ]
49
55
 
50
- /**
51
- * @param {string} name ім'я бінарника
52
- * @returns {boolean} чи знайдено бінарник у PATH
53
- */
54
- function isBinaryInPath(name) {
55
- // Явний `env: process.env` — Bun без нього дає дітям snapshot оточення зі старту процесу
56
- // і не бачить runtime-змін `process.env.PATH` (та сама причина, що в resolve-cmd.mjs).
57
- const probe = spawnSync('command', ['-v', name], { shell: true, encoding: 'utf8', env: process.env })
58
- return probe.status === 0
59
- }
60
-
61
56
  /**
62
57
  * @param {string} name ім'я скілу з CLI або каталогу `.cursor/skills/n-*`
63
58
  * @returns {string} id каталогу в `npm/skills/<id>/`
@@ -170,27 +165,41 @@ async function runPiRunner(prompt, rawSkillName, skillsRoot, projectDir, logErro
170
165
  }
171
166
 
172
167
  /**
173
- * Делегує виконання скіла зовнішньому ACP-агенту (`cursor-agent acp`, `codex-acp`,
174
- * або deprecated `claude`-адаптер) через `runAcpRunner` — JSON-RPC поверх stdio,
175
- * дозволи на tool-calls автоматично схвалюються (без інтерактивних питань, паритет із
176
- * колишнім non-interactive `-p`). `claude` лишається як deprecated fallback для
177
- * тих, у кого pi-модель ще не налаштована; буде прибрано (мігруй на `skill pi`).
168
+ * Делегує виконання скіла зовнішньому ACP-агенту. `cursor`/`codex` через
169
+ * `@7n/llm-lib/acp` (napi-міст до `llm_cascade::acp`: спавн, `session/prompt`,
170
+ * автоапрув дозволів усе в Rust, без JSON-RPC у JS). `claude` — deprecated
171
+ * JS-шим `runAcpRunner` (Rust його не моделює); буде прибрано (мігруй на `skill pi`).
172
+ * На відміну від колишнього стрімінгу по чанках, napi-шлях повертає повний текст
173
+ * лише по завершенню ходу — Rust-функція не стрімить проміжні токени в JS.
178
174
  * @param {'claude' | 'cursor' | 'codex'} kind якого ACP-агента запускати
179
175
  * @param {string} prompt промпт скіла
180
176
  * @param {string} projectDir робочий каталог агента
181
177
  * @param {(line: string) => void} logError вивід попередження/помилок
182
- * @param {{ runAcpRunner?: typeof runAcpRunner }} [deps] інжект для тестів
183
- * @returns {Promise<number>} exit code (0 — `end_turn`, 1 — інакше)
178
+ * @param {{ runAcpRunner?: typeof runAcpRunner, runAcpAgent?: typeof runAcpAgent, out?: (chunk: string) => void }} [deps] інжекти для тестів
179
+ * @returns {Promise<number>} exit code (0 — успіх, 1 — інакше)
184
180
  */
185
- function runLlmCli(kind, prompt, projectDir, logError, deps = {}) {
181
+ async function runLlmCli(kind, prompt, projectDir, logError, deps = {}) {
186
182
  const runner = ACP_RUNNERS[kind]
187
- const runAcp = deps.runAcpRunner ?? runAcpRunner
188
183
 
189
184
  if (runner.deprecated) {
190
185
  logError(`[deprecated] skill ${kind} → use 'skill pi'; зовнішній ACP-агент буде прибрано`)
191
186
  }
192
187
 
193
- return runAcp(kind, prompt, projectDir, logError, { isBinaryInPath })
188
+ if (kind === 'claude') {
189
+ const runAcp = deps.runAcpRunner ?? runAcpRunner
190
+ return runAcp(kind, prompt, projectDir, logError)
191
+ }
192
+
193
+ const runNativeAcp = deps.runAcpAgent ?? runAcpAgent
194
+ const out = deps.out ?? (chunk => stdout.write(chunk))
195
+
196
+ try {
197
+ out(await runNativeAcp(kind, prompt, projectDir))
198
+ return 0
199
+ } catch (error) {
200
+ logError(error instanceof Error ? error.message : String(error))
201
+ return 1
202
+ }
194
203
  }
195
204
 
196
205
  /**