@7n/rules 1.3.1 → 1.4.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/.pi-template/extensions/docs/index.md +9 -0
- package/.pi-template/extensions/docs/rtk.md +33 -0
- package/.pi-template/extensions/rtk.ts +102 -0
- package/CHANGELOG.md +12 -0
- package/bin/n-rules.js +1 -0
- package/docs/index.md +0 -2
- package/docs/vitest.config.md +1 -1
- package/package.json +1 -1
- package/rules/abie/clean_merged_ignore_branches/docs/fix-clean_merged_ignore_branches.md +31 -0
- package/rules/abie/clean_merged_ignore_branches/docs/index.md +9 -0
- package/rules/abie/clean_merged_ignore_branches/fix-clean_merged_ignore_branches.mjs +6 -1
- package/rules/docker/lint_docker_yml/docs/fix-lint_docker_yml.md +31 -0
- package/rules/docker/lint_docker_yml/docs/index.md +9 -0
- package/rules/docker/lint_docker_yml/fix-lint_docker_yml.mjs +3 -1
- package/rules/ga/clean_ga_workflows/docs/fix-clean_ga_workflows.md +33 -0
- package/rules/ga/clean_ga_workflows/docs/index.md +9 -0
- package/rules/ga/clean_ga_workflows/fix-clean_ga_workflows.mjs +6 -1
- package/rules/ga/clean_merged_branch/docs/fix-clean_merged_branch.md +27 -0
- package/rules/ga/clean_merged_branch/docs/index.md +9 -0
- package/rules/ga/clean_merged_branch/fix-clean_merged_branch.mjs +6 -1
- package/rules/ga/git_ai/docs/fix-git_ai.md +30 -0
- package/rules/ga/git_ai/docs/index.md +9 -0
- package/rules/ga/git_ai/fix-git_ai.mjs +3 -1
- package/rules/ga/lint_ga/docs/fix-lint_ga.md +32 -0
- package/rules/ga/lint_ga/docs/index.md +9 -0
- package/rules/ga/lint_ga/fix-lint_ga.mjs +3 -1
- package/rules/ga/vscode_settings/docs/fix-vscode_settings.md +26 -0
- package/rules/ga/vscode_settings/docs/index.md +9 -0
- package/rules/ga/vscode_settings/fix-vscode_settings.mjs +3 -1
- package/rules/ga/zizmor_yml/docs/fix-zizmor_yml.md +28 -0
- package/rules/ga/zizmor_yml/docs/index.md +9 -0
- package/rules/js/jscpd_config/docs/fix-jscpd_config.md +26 -0
- package/rules/js/jscpd_config/docs/index.md +9 -0
- package/rules/js/lint_js_yml/docs/fix-lint_js_yml.md +28 -0
- package/rules/js/lint_js_yml/docs/index.md +9 -0
- package/rules/js/package_json/docs/fix-package_json.md +26 -0
- package/rules/js/package_json/docs/index.md +9 -0
- package/rules/js/tooling/data/tooling/knip-canonical.json +1 -0
- package/rules/k8s/lint_k8s_yml/docs/fix-lint_k8s_yml.md +30 -0
- package/rules/k8s/lint_k8s_yml/docs/index.md +9 -0
- package/rules/k8s/lint_k8s_yml/fix-lint_k8s_yml.mjs +3 -1
- package/rules/local-ai/main.json +1 -0
- package/rules/local-ai/main.mdc +85 -0
- package/rules/npm-module/emit_types_config/docs/fix-emit_types_config.md +25 -0
- package/rules/npm-module/emit_types_config/docs/index.md +9 -0
- package/rules/npm-module/emit_types_config/fix-emit_types_config.mjs +3 -1
- package/rules/npm-module/npm_package_json/docs/fix-npm_package_json.md +27 -0
- package/rules/npm-module/npm_package_json/docs/index.md +9 -0
- package/rules/npm-module/npm_package_json/fix-npm_package_json.mjs +3 -1
- package/rules/npm-module/npm_publish_yml/docs/fix-npm_publish_yml.md +28 -0
- package/rules/npm-module/npm_publish_yml/docs/index.md +9 -0
- package/rules/npm-module/npm_publish_yml/fix-npm_publish_yml.mjs +4 -1
- package/rules/npm-module/root_package_json/docs/fix-root_package_json.md +26 -0
- package/rules/npm-module/root_package_json/docs/index.md +9 -0
- package/rules/npm-module/root_package_json/fix-root_package_json.mjs +3 -1
- package/rules/php/lint_php_yml/docs/fix-lint_php_yml.md +28 -0
- package/rules/php/lint_php_yml/docs/index.md +9 -0
- package/rules/php/lint_php_yml/fix-lint_php_yml.mjs +3 -1
- package/rules/python/lint_python_yml/docs/fix-lint_python_yml.md +27 -0
- package/rules/python/lint_python_yml/docs/index.md +9 -0
- package/rules/python/lint_python_yml/fix-lint_python_yml.mjs +3 -1
- package/rules/rego/vscode_settings/docs/fix-vscode_settings.md +27 -0
- package/rules/rego/vscode_settings/docs/index.md +9 -0
- package/rules/rego/vscode_settings/fix-vscode_settings.mjs +3 -1
- package/rules/rust/lint_rust_yml/docs/fix-lint_rust_yml.md +28 -0
- package/rules/rust/lint_rust_yml/docs/index.md +9 -0
- package/rules/security/lint_security_yml/docs/fix-lint_security_yml.md +31 -0
- package/rules/security/lint_security_yml/docs/index.md +9 -0
- package/rules/security/lint_security_yml/fix-lint_security_yml.mjs +6 -1
- package/rules/style/lint_style_yml/docs/fix-lint_style_yml.md +31 -0
- package/rules/style/lint_style_yml/docs/index.md +9 -0
- package/rules/style/lint_style_yml/fix-lint_style_yml.mjs +3 -1
- package/rules/style/package_json/docs/fix-package_json.md +27 -0
- package/rules/style/package_json/docs/index.md +9 -0
- package/rules/style/vscode_settings/docs/fix-vscode_settings.md +27 -0
- package/rules/style/vscode_settings/docs/index.md +9 -0
- package/rules/style/vscode_settings/fix-vscode_settings.mjs +3 -1
- package/rules/text/lint_text/docs/fix-lint_text.md +28 -0
- package/rules/text/lint_text/docs/index.md +9 -0
- package/rules/text/oxfmtrc/docs/fix-oxfmtrc.md +26 -0
- package/rules/text/oxfmtrc/docs/index.md +9 -0
- package/rules/text/vscode_settings/docs/fix-vscode_settings.md +27 -0
- package/rules/text/vscode_settings/docs/index.md +9 -0
- package/rules/text/vscode_settings/fix-vscode_settings.mjs +3 -1
- package/rules/worktree/vscode_settings/docs/fix-vscode_settings.md +28 -0
- package/rules/worktree/vscode_settings/docs/index.md +9 -0
- package/rules/worktree/zed_settings/docs/fix-zed_settings.md +28 -0
- package/rules/worktree/zed_settings/docs/index.md +9 -0
- package/rules/worktree/zed_settings/fix-zed_settings.mjs +3 -1
- package/scripts/docs/skills-cli.md +1 -1
- package/scripts/docs/sync-claude-config.md +9 -4
- package/scripts/lib/fix/docs/index.md +4 -5
- package/scripts/lib/fix/docs/template-deep-merge.md +35 -0
- package/scripts/skills-cli.mjs +15 -4
- package/scripts/sync-claude-config.mjs +145 -36
|
@@ -18,7 +18,13 @@
|
|
|
18
18
|
* - `.claude/hooks/normalize-decisions.sh` — fully owned bash-скрипт ADR normalize
|
|
19
19
|
* Stop-hook (батч-нормалізація чернеток); умови — ті самі, що для `capture`.
|
|
20
20
|
* - `.cursor/hooks.json` — **merge**: користувацькі hooks зберігаються; ADR stop
|
|
21
|
-
* entries додаються, коли правило `adr` увімкнене, і видаляються, коли
|
|
21
|
+
* entries додаються, коли правило `adr` увімкнене, і видаляються, коли вимкнене;
|
|
22
|
+
* rtk preToolUse entry — аналогічно за правилом `local-ai`.
|
|
23
|
+
* - rtk (правило `local-ai`) — fail-open інтеграція компресора виводу CLI-команд:
|
|
24
|
+
* PreToolUse hook у `.claude/settings.json` (`rtk hook claude`), preToolUse у
|
|
25
|
+
* `.cursor/hooks.json` (`rtk hook cursor`) і vendored pi-extension
|
|
26
|
+
* `.pi/extensions/rtk.ts`. Без установленого rtk hooks мовчки пропускають команди,
|
|
27
|
+
* тож конфіг безпечно потрапляє в git — установка зводиться до `brew install rtk-ai/tap/rtk`.
|
|
22
28
|
* - `.gitignore` — **merge** (лише з `adr`): дописує відсутні рядки з канонічного
|
|
23
29
|
* фрагмента `rules/adr/js/hooks/template/.gitignore.snippet` (`node_modules/`, `dist/`,
|
|
24
30
|
* `*.secret`, логи capture/normalize, `.normalize-state`, `.normalize.lock`,
|
|
@@ -52,6 +58,10 @@ export const ADR_NORMALIZE_HOOK_COMMAND_MARKER = '.claude/hooks/normalize-decisi
|
|
|
52
58
|
export const CURSOR_ADR_HOOK_COMMAND_MARKER = '.claude/hooks/capture-decisions.sh'
|
|
53
59
|
/** Маркер Cursor ADR Normalize Stop-hook'а — той самий script path, але в `.cursor/hooks.json`. */
|
|
54
60
|
export const CURSOR_ADR_NORMALIZE_HOOK_COMMAND_MARKER = '.claude/hooks/normalize-decisions.sh'
|
|
61
|
+
/** Маркер rtk PreToolUse hook'а у `.claude/settings.json` (правило `local-ai`). */
|
|
62
|
+
export const RTK_CLAUDE_HOOK_COMMAND_MARKER = 'rtk hook claude'
|
|
63
|
+
/** Маркер rtk preToolUse hook'а у `.cursor/hooks.json` (правило `local-ai`). */
|
|
64
|
+
export const RTK_CURSOR_HOOK_COMMAND_MARKER = 'rtk hook cursor'
|
|
55
65
|
/**
|
|
56
66
|
* Усі маркери managed-hook'ів пакета — за ними відрізняємо свої записи від користувацьких.
|
|
57
67
|
* Legacy stop-hook включений сюди, щоб старі entries автоматично видалялись при наступному sync-у.
|
|
@@ -65,7 +75,8 @@ export const MANAGED_HOOK_COMMAND_MARKERS = Object.freeze([
|
|
|
65
75
|
LEGACY_DOC_FILES_HOOK_COMMAND_MARKER,
|
|
66
76
|
LEGACY_STOP_HOOK_COMMAND_MARKER,
|
|
67
77
|
ADR_HOOK_COMMAND_MARKER,
|
|
68
|
-
ADR_NORMALIZE_HOOK_COMMAND_MARKER
|
|
78
|
+
ADR_NORMALIZE_HOOK_COMMAND_MARKER,
|
|
79
|
+
RTK_CLAUDE_HOOK_COMMAND_MARKER
|
|
69
80
|
])
|
|
70
81
|
|
|
71
82
|
const CLAUDE_DIR = '.claude'
|
|
@@ -130,6 +141,32 @@ const CURSOR_ADR_STOP_HOOK = Object.freeze({
|
|
|
130
141
|
timeout: 180
|
|
131
142
|
})
|
|
132
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Канонічна група hooks для rtk PreToolUse (правило `local-ai`): переписує Bash-команди
|
|
146
|
+
* на rtk-еквіваленти. Fail-open guard: без установленого rtk — `exit 0` без виводу,
|
|
147
|
+
* Claude Code виконує команду без змін.
|
|
148
|
+
*/
|
|
149
|
+
const RTK_PRE_TOOL_USE_HOOK_GROUP = Object.freeze({
|
|
150
|
+
matcher: 'Bash',
|
|
151
|
+
hooks: Object.freeze([
|
|
152
|
+
Object.freeze({
|
|
153
|
+
type: 'command',
|
|
154
|
+
command: `command -v rtk >/dev/null 2>&1 && exec ${RTK_CLAUDE_HOOK_COMMAND_MARKER}; exit 0`,
|
|
155
|
+
timeout: 30
|
|
156
|
+
})
|
|
157
|
+
])
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Канонічний Cursor preToolUse hook для rtk (правило `local-ai`). Cursor вимагає JSON-вивід
|
|
162
|
+
* на всіх code path — без установленого rtk відповідаємо `{}` (passthrough).
|
|
163
|
+
*/
|
|
164
|
+
const CURSOR_RTK_PRE_TOOL_USE_HOOK = Object.freeze({
|
|
165
|
+
command: `bash -lc 'command -v rtk >/dev/null 2>&1 && exec ${RTK_CURSOR_HOOK_COMMAND_MARKER}; echo "{}"'`,
|
|
166
|
+
matcher: 'Shell',
|
|
167
|
+
timeout: 30
|
|
168
|
+
})
|
|
169
|
+
|
|
133
170
|
/** Канонічний Cursor stop-hook для ADR normalize. */
|
|
134
171
|
const CURSOR_ADR_NORMALIZE_STOP_HOOK = Object.freeze({
|
|
135
172
|
command: [
|
|
@@ -188,13 +225,13 @@ function isManagedHookGroup(group) {
|
|
|
188
225
|
/**
|
|
189
226
|
* Чи Cursor hook entry належить пакету `@7n/rules`.
|
|
190
227
|
* @param {CursorHookEntry} entry один entry з `.cursor/hooks.json`
|
|
191
|
-
* @returns {boolean} `true`, якщо command містить managed ADR marker
|
|
228
|
+
* @returns {boolean} `true`, якщо command містить managed ADR або rtk marker
|
|
192
229
|
*/
|
|
193
230
|
function isManagedCursorHookEntry(entry) {
|
|
194
231
|
return (
|
|
195
232
|
typeof entry?.command === 'string' &&
|
|
196
|
-
[CURSOR_ADR_HOOK_COMMAND_MARKER, CURSOR_ADR_NORMALIZE_HOOK_COMMAND_MARKER].some(
|
|
197
|
-
entry.command.includes(marker)
|
|
233
|
+
[CURSOR_ADR_HOOK_COMMAND_MARKER, CURSOR_ADR_NORMALIZE_HOOK_COMMAND_MARKER, RTK_CURSOR_HOOK_COMMAND_MARKER].some(
|
|
234
|
+
marker => entry.command.includes(marker)
|
|
198
235
|
)
|
|
199
236
|
)
|
|
200
237
|
}
|
|
@@ -268,16 +305,36 @@ function templateWithAdrHook(template) {
|
|
|
268
305
|
return { ...template, hooks }
|
|
269
306
|
}
|
|
270
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Будує копію темплейту із додатковою rtk PreToolUse hook-групою (правило `local-ai`).
|
|
310
|
+
* Темплейт залишається незмінним; повертається новий об'єкт з доданою групою.
|
|
311
|
+
* @param {ClaudeSettings} template вихідний темплейт із `.claude-template/settings.template.json`
|
|
312
|
+
* @returns {ClaudeSettings} копія з доданою rtk-групою у `hooks.PreToolUse`
|
|
313
|
+
*/
|
|
314
|
+
function templateWithRtkHook(template) {
|
|
315
|
+
/** @type {Record<string, HookGroup[]>} */
|
|
316
|
+
const hooks = {}
|
|
317
|
+
for (const [event, groups] of Object.entries(template.hooks ?? {})) {
|
|
318
|
+
hooks[event] = Array.isArray(groups) ? [...groups] : []
|
|
319
|
+
}
|
|
320
|
+
hooks.PreToolUse = [...(hooks.PreToolUse ?? []), /** @type {HookGroup} */ (RTK_PRE_TOOL_USE_HOOK_GROUP)]
|
|
321
|
+
return { ...template, hooks }
|
|
322
|
+
}
|
|
323
|
+
|
|
271
324
|
/**
|
|
272
325
|
* Повертає об'єднаний об'єкт settings.json.
|
|
273
326
|
* @param {ClaudeSettings | undefined} existing існуючий вміст `.claude/settings.json` користувача (або undefined, якщо файла нема)
|
|
274
327
|
* @param {ClaudeSettings} template settings із темплейту пакета `@7n/rules`
|
|
275
328
|
* @param {object} [options] опції merge-у
|
|
276
329
|
* @param {boolean} [options.includeAdrHook] чи додати ADR Stop-hook групу до managed-hooks (коли в `.n-rules.json` `rules` присутнє `adr`)
|
|
330
|
+
* @param {boolean} [options.includeLocalAiHook] чи додати rtk PreToolUse-групу (коли в `.n-rules.json` `rules` присутнє `local-ai`)
|
|
277
331
|
* @returns {ClaudeSettings} результат merge-у (користувацькі поля збережено, наші перевизначено)
|
|
278
332
|
*/
|
|
279
333
|
export function mergeSettings(existing, template, options = {}) {
|
|
280
|
-
|
|
334
|
+
let effectiveTemplate = options.includeAdrHook ? templateWithAdrHook(template) : template
|
|
335
|
+
if (options.includeLocalAiHook) {
|
|
336
|
+
effectiveTemplate = templateWithRtkHook(effectiveTemplate)
|
|
337
|
+
}
|
|
281
338
|
/** @type {ClaudeSettings} */
|
|
282
339
|
const merged = { ...existing }
|
|
283
340
|
const mergedAllow = mergeAllowList(existing?.permissions?.allow, effectiveTemplate.permissions?.allow)
|
|
@@ -295,10 +352,12 @@ export function mergeSettings(existing, template, options = {}) {
|
|
|
295
352
|
|
|
296
353
|
/**
|
|
297
354
|
* Зливає `.cursor/hooks.json`: користувацькі entries зберігаються, managed ADR
|
|
298
|
-
* entries у `hooks.stop` перезаписуються або видаляються залежно від `includeAdrHook
|
|
355
|
+
* entries у `hooks.stop` перезаписуються або видаляються залежно від `includeAdrHook`;
|
|
356
|
+
* managed rtk entry у `hooks.preToolUse` — залежно від `includeLocalAiHook`.
|
|
299
357
|
* @param {CursorHooksConfig | undefined} existing поточний Cursor hooks config
|
|
300
358
|
* @param {object} [options] опції merge-у
|
|
301
359
|
* @param {boolean} [options.includeAdrHook] чи додати ADR stop entries
|
|
360
|
+
* @param {boolean} [options.includeLocalAiHook] чи додати rtk preToolUse entry
|
|
302
361
|
* @returns {CursorHooksConfig} результат злиття
|
|
303
362
|
*/
|
|
304
363
|
export function mergeCursorHooksConfig(existing, options = {}) {
|
|
@@ -321,6 +380,15 @@ export function mergeCursorHooksConfig(existing, options = {}) {
|
|
|
321
380
|
} else {
|
|
322
381
|
delete hooks.stop
|
|
323
382
|
}
|
|
383
|
+
const preToolUse = (hooks.preToolUse ?? []).filter(entry => !isManagedCursorHookEntry(entry))
|
|
384
|
+
if (options.includeLocalAiHook) {
|
|
385
|
+
preToolUse.push(/** @type {CursorHookEntry} */ (CURSOR_RTK_PRE_TOOL_USE_HOOK))
|
|
386
|
+
}
|
|
387
|
+
if (preToolUse.length > 0) {
|
|
388
|
+
hooks.preToolUse = preToolUse
|
|
389
|
+
} else {
|
|
390
|
+
delete hooks.preToolUse
|
|
391
|
+
}
|
|
324
392
|
merged.version = typeof merged.version === 'number' ? merged.version : 1
|
|
325
393
|
if (Object.keys(hooks).length > 0) {
|
|
326
394
|
merged.hooks = hooks
|
|
@@ -347,17 +415,18 @@ async function readJsonOrUndefined(path) {
|
|
|
347
415
|
}
|
|
348
416
|
|
|
349
417
|
/**
|
|
350
|
-
* Синхронізує `.cursor/hooks.json` для Cursor Agent
|
|
351
|
-
* project-level config з `.cursor/hooks.json`; hook scripts лишаються
|
|
352
|
-
* з Claude Code у `.claude/hooks/`.
|
|
418
|
+
* Синхронізує `.cursor/hooks.json` для Cursor Agent hooks (ADR stop + rtk preToolUse).
|
|
419
|
+
* Cursor читає project-level config з `.cursor/hooks.json`; hook scripts лишаються
|
|
420
|
+
* спільними з Claude Code у `.claude/hooks/`.
|
|
353
421
|
* @param {string} projectRoot корінь проєкту, куди писати
|
|
354
422
|
* @param {object} [options] опції merge-у
|
|
355
423
|
* @param {boolean} [options.includeAdrHook] чи додавати ADR stop-hook entries
|
|
424
|
+
* @param {boolean} [options.includeLocalAiHook] чи додавати rtk preToolUse entry
|
|
356
425
|
* @returns {Promise<{ written: boolean, path: string }>} результат: чи писали файл, та його відносний шлях
|
|
357
426
|
*/
|
|
358
427
|
export async function syncCursorHooksConfig(projectRoot, options = {}) {
|
|
359
428
|
const hooksPath = join(projectRoot, CURSOR_HOOKS_FILE)
|
|
360
|
-
if (!options.includeAdrHook && !existsSync(hooksPath)) {
|
|
429
|
+
if (!options.includeAdrHook && !options.includeLocalAiHook && !existsSync(hooksPath)) {
|
|
361
430
|
return { written: false, path: '' }
|
|
362
431
|
}
|
|
363
432
|
const existing = /** @type {CursorHooksConfig | undefined} */ (await readJsonOrUndefined(hooksPath))
|
|
@@ -539,6 +608,44 @@ export async function removeOrphanPiExtension(projectRoot) {
|
|
|
539
608
|
return { removed: true, path: `${PI_EXTENSIONS_DIR}/${PI_EXTENSION_NAME}` }
|
|
540
609
|
}
|
|
541
610
|
|
|
611
|
+
/** Ім'я файлу rtk pi-extension — той самий шлях, що пише `rtk init --agent pi` (повторна установка ідемпотентна). */
|
|
612
|
+
export const RTK_PI_EXTENSION_FILE = 'rtk.ts'
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Копіює vendored rtk pi-extension `npm/.pi-template/extensions/rtk.ts` у
|
|
616
|
+
* `.pi/extensions/rtk.ts` проєкту-споживача (правило `local-ai`). Файл fully-owned:
|
|
617
|
+
* при кожному sync-у перезаписується. Якщо bundled template відсутній — `{written: false}`.
|
|
618
|
+
* @param {string} projectRoot корінь проєкту-споживача
|
|
619
|
+
* @param {string} bundledPackageRoot корінь установленого `@7n/rules` (із `.pi-template/`)
|
|
620
|
+
* @returns {Promise<{ written: boolean, path: string }>} чи писали та відносний шлях файла
|
|
621
|
+
*/
|
|
622
|
+
export async function syncRtkPiExtension(projectRoot, bundledPackageRoot) {
|
|
623
|
+
const srcPath = join(bundledPackageRoot, PI_TEMPLATE_DIR_NAME, 'extensions', RTK_PI_EXTENSION_FILE)
|
|
624
|
+
if (!existsSync(srcPath)) {
|
|
625
|
+
return { written: false, path: '' }
|
|
626
|
+
}
|
|
627
|
+
const destDir = join(projectRoot, PI_EXTENSIONS_DIR)
|
|
628
|
+
await mkdir(destDir, { recursive: true })
|
|
629
|
+
await writeFile(join(destDir, RTK_PI_EXTENSION_FILE), await readFile(srcPath, 'utf8'), 'utf8')
|
|
630
|
+
return { written: true, path: `${PI_EXTENSIONS_DIR}/${RTK_PI_EXTENSION_FILE}` }
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Видаляє `.pi/extensions/rtk.ts` з проєкту-споживача. Викликається, коли правило
|
|
635
|
+
* `local-ai` вимкнено у `.n-rules.json` (симетрично до cleanup-у hook-записів).
|
|
636
|
+
* Шлях спільний з `rtk init --agent pi`, тож прибереться і встановлений вручну файл.
|
|
637
|
+
* @param {string} projectRoot корінь проєкту-споживача
|
|
638
|
+
* @returns {Promise<{ removed: boolean, path: string }>} чи було щось видалено та відносний шлях
|
|
639
|
+
*/
|
|
640
|
+
export async function removeOrphanRtkPiExtension(projectRoot) {
|
|
641
|
+
const extPath = join(projectRoot, PI_EXTENSIONS_DIR, RTK_PI_EXTENSION_FILE)
|
|
642
|
+
if (!existsSync(extPath)) {
|
|
643
|
+
return { removed: false, path: '' }
|
|
644
|
+
}
|
|
645
|
+
await rm(extPath, { force: true })
|
|
646
|
+
return { removed: true, path: `${PI_EXTENSIONS_DIR}/${RTK_PI_EXTENSION_FILE}` }
|
|
647
|
+
}
|
|
648
|
+
|
|
542
649
|
/**
|
|
543
650
|
* Повертає змістовні (не коментар, не порожній) рядки з text-фрагмента `.gitignore`.
|
|
544
651
|
* @param {string} raw вміст snippet-файлу
|
|
@@ -627,36 +734,30 @@ export async function syncClaudeCommands(projectRoot, templateDir) {
|
|
|
627
734
|
* @param {string} options.projectRoot корінь проєкту-споживача
|
|
628
735
|
* @param {string} options.bundledPackageRoot корінь установленого `@7n/rules`
|
|
629
736
|
* @param {boolean} options.enabled чи увімкнено sync (з `.n-rules.json` `claude-config`)
|
|
630
|
-
* @param {string[]} [options.rules] список увімкнених правил із `.n-rules.json` — впливає на ADR Stop-hook (`adr`)
|
|
631
|
-
* @returns {Promise<{ settings: boolean, cursorHooks: boolean, commands: string[], adrHook: boolean, adrNormalizeHook: boolean, adrHookLib: string[], gitignoreAdr: boolean, piExtension: boolean }>} прапорці записів settings/Cursor hooks/ADR-hook(s)/`.gitignore`/pi-extension, перелік lib-файлів і список slash-команд
|
|
737
|
+
* @param {string[]} [options.rules] список увімкнених правил із `.n-rules.json` — впливає на ADR Stop-hook (`adr`) і rtk hooks (`local-ai`)
|
|
738
|
+
* @returns {Promise<{ settings: boolean, cursorHooks: boolean, commands: string[], adrHook: boolean, adrNormalizeHook: boolean, adrHookLib: string[], gitignoreAdr: boolean, piExtension: boolean, rtkPiExtension: boolean }>} прапорці записів settings/Cursor hooks/ADR-hook(s)/`.gitignore`/pi-extension(s), перелік lib-файлів і список slash-команд
|
|
632
739
|
*/
|
|
633
740
|
export async function syncClaudeConfig({ projectRoot, bundledPackageRoot, enabled, rules = [] }) {
|
|
741
|
+
const noop = {
|
|
742
|
+
settings: false,
|
|
743
|
+
cursorHooks: false,
|
|
744
|
+
commands: [],
|
|
745
|
+
adrHook: false,
|
|
746
|
+
adrNormalizeHook: false,
|
|
747
|
+
adrHookLib: [],
|
|
748
|
+
gitignoreAdr: false,
|
|
749
|
+
piExtension: false,
|
|
750
|
+
rtkPiExtension: false
|
|
751
|
+
}
|
|
634
752
|
if (!enabled) {
|
|
635
|
-
return
|
|
636
|
-
settings: false,
|
|
637
|
-
cursorHooks: false,
|
|
638
|
-
commands: [],
|
|
639
|
-
adrHook: false,
|
|
640
|
-
adrNormalizeHook: false,
|
|
641
|
-
adrHookLib: [],
|
|
642
|
-
gitignoreAdr: false,
|
|
643
|
-
piExtension: false
|
|
644
|
-
}
|
|
753
|
+
return noop
|
|
645
754
|
}
|
|
646
755
|
const templateDir = join(bundledPackageRoot, TEMPLATE_DIR_NAME)
|
|
647
756
|
if (!existsSync(templateDir)) {
|
|
648
|
-
return
|
|
649
|
-
settings: false,
|
|
650
|
-
cursorHooks: false,
|
|
651
|
-
commands: [],
|
|
652
|
-
adrHook: false,
|
|
653
|
-
adrNormalizeHook: false,
|
|
654
|
-
adrHookLib: [],
|
|
655
|
-
gitignoreAdr: false,
|
|
656
|
-
piExtension: false
|
|
657
|
-
}
|
|
757
|
+
return noop
|
|
658
758
|
}
|
|
659
759
|
const includeAdrHook = Array.isArray(rules) && rules.includes('adr')
|
|
760
|
+
const includeLocalAiHook = Array.isArray(rules) && rules.includes('local-ai')
|
|
660
761
|
const adrHook = includeAdrHook ? await syncAdrHookScript(projectRoot, templateDir) : { written: false, path: '' }
|
|
661
762
|
const adrNormalizeHook = includeAdrHook
|
|
662
763
|
? await syncAdrNormalizeHookScript(projectRoot, templateDir)
|
|
@@ -676,8 +777,15 @@ export async function syncClaudeConfig({ projectRoot, bundledPackageRoot, enable
|
|
|
676
777
|
const removed = await removeOrphanPiExtension(projectRoot)
|
|
677
778
|
piExtension = { written: false, path: removed.path }
|
|
678
779
|
}
|
|
679
|
-
|
|
680
|
-
|
|
780
|
+
let rtkPiExtension
|
|
781
|
+
if (includeLocalAiHook) {
|
|
782
|
+
rtkPiExtension = await syncRtkPiExtension(projectRoot, bundledPackageRoot)
|
|
783
|
+
} else {
|
|
784
|
+
const removed = await removeOrphanRtkPiExtension(projectRoot)
|
|
785
|
+
rtkPiExtension = { written: false, path: removed.path }
|
|
786
|
+
}
|
|
787
|
+
const settings = await syncClaudeSettings(projectRoot, templateDir, { includeAdrHook, includeLocalAiHook })
|
|
788
|
+
const cursorHooks = await syncCursorHooksConfig(projectRoot, { includeAdrHook, includeLocalAiHook })
|
|
681
789
|
const commands = await syncClaudeCommands(projectRoot, templateDir)
|
|
682
790
|
return {
|
|
683
791
|
settings: settings.written,
|
|
@@ -687,6 +795,7 @@ export async function syncClaudeConfig({ projectRoot, bundledPackageRoot, enable
|
|
|
687
795
|
adrNormalizeHook: adrNormalizeHook.written,
|
|
688
796
|
adrHookLib: adrHookLibEntries.map(e => e.path),
|
|
689
797
|
gitignoreAdr: gitignoreAdr.written,
|
|
690
|
-
piExtension: piExtension.written
|
|
798
|
+
piExtension: piExtension.written,
|
|
799
|
+
rtkPiExtension: rtkPiExtension.written
|
|
691
800
|
}
|
|
692
801
|
}
|