@7n/rules 1.3.2 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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/package.json +1 -1
- package/rules/js/check/docs/main.md +1 -1
- package/rules/js/check/main.mjs +1 -1
- package/rules/js/tooling/data/tooling/knip-canonical.json +1 -0
- package/rules/js/tooling/docs/main.md +1 -1
- package/rules/js/tooling/main.mjs +1 -1
- package/rules/local-ai/main.json +1 -0
- package/rules/local-ai/main.mdc +85 -0
- package/scripts/docs/sync-claude-config.md +9 -4
- package/scripts/sync-claude-config.mjs +145 -36
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: TS Module
|
|
3
|
+
title: rtk.ts
|
|
4
|
+
resource: npm/.pi-template/extensions/rtk.ts
|
|
5
|
+
docgen:
|
|
6
|
+
crc: f00b429f
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
Pi.dev-extension для rtk (Rust Token Killer): прозоро переписує bash-команди агента на
|
|
12
|
+
rtk-еквіваленти, щоб стискати вивід CLI-команд і економити токени. Vendored із rtk
|
|
13
|
+
(`rtk init --agent pi`), адаптований під конвенції репо; доставляється sync-ом пакета
|
|
14
|
+
`@7n/rules` у `.pi/extensions/rtk.ts` проєктів-споживачів, коли увімкнене правило
|
|
15
|
+
`local-ai`. Шлях збігається зі шляхом установки самого rtk, тож ручний
|
|
16
|
+
`rtk init --agent pi` поверх — ідемпотентний.
|
|
17
|
+
|
|
18
|
+
## Поведінка
|
|
19
|
+
|
|
20
|
+
- Тонкий делегат: рішення про переписування ухвалює `rtk rewrite <cmd>` (exit 0 або 3 +
|
|
21
|
+
stdout → команда мутується через `event.input.command`; exit 1 — passthrough).
|
|
22
|
+
- При завантаженні пробує `rtk --version`: без бінарника в PATH або з rtk < 0.23.0
|
|
23
|
+
extension сам вимикається з `console.warn` (fail-open) — установка конфігу безпечна
|
|
24
|
+
до `brew install rtk-ai/tap/rtk`.
|
|
25
|
+
- Пропускає без змін: не-bash tool_call, порожні команди, команди, що вже починаються
|
|
26
|
+
з `rtk `, і будь-що при `RTK_DISABLED=1` в env.
|
|
27
|
+
- Виклики rtk обмежені таймаутом 2 с і сигналом скасування контексту; будь-яка
|
|
28
|
+
неочікувана помилка в обробнику лише логується — команда виконується без переписування.
|
|
29
|
+
|
|
30
|
+
## Гарантії поведінки
|
|
31
|
+
|
|
32
|
+
- Fail-open: жоден збій rtk/extension не блокує виконання команди агента.
|
|
33
|
+
- Мутує лише `event.input.command` у pi tool_call — жодних записів у ФС чи мережевих викликів.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// RTK Pi extension — переписує bash-команди на rtk-еквіваленти для економії токенів.
|
|
2
|
+
// Vendored з rtk (`rtk init --agent pi`, rtk 0.43.0), адаптований під конвенції репо:
|
|
3
|
+
// без імпортів з '@earendil-works/pi-coding-agent' (пакет резолвиться лише в runtime pi,
|
|
4
|
+
// а knip/TS-server проєктів-споживачів його не бачать) — типи описані локально, як у
|
|
5
|
+
// n-rules-adr. Шлях установки `.pi/extensions/rtk.ts` збігається зі шляхом самого rtk —
|
|
6
|
+
// повторний `rtk init --agent pi` ідемпотентний. Потребує rtk >= 0.23.0 у PATH; без
|
|
7
|
+
// нього extension сам вимикається (fail-open).
|
|
8
|
+
//
|
|
9
|
+
// Тонкий делегат: уся rewrite-логіка живе в `rtk rewrite` (єдине джерело правди).
|
|
10
|
+
//
|
|
11
|
+
// Контракт exit-кодів `rtk rewrite`:
|
|
12
|
+
// 0 + stdout rewrite знайдено → мутуємо команду
|
|
13
|
+
// 1 rtk-еквівалента немає → passthrough
|
|
14
|
+
// 3 + stdout rewrite (advisory) → мутуємо команду
|
|
15
|
+
|
|
16
|
+
import { env } from 'node:process'
|
|
17
|
+
|
|
18
|
+
interface PiToolCallEvent {
|
|
19
|
+
toolName?: string
|
|
20
|
+
input: { command?: unknown }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface PiContext {
|
|
24
|
+
signal?: AbortSignal
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface PiExecResult {
|
|
28
|
+
code: number
|
|
29
|
+
stdout: string
|
|
30
|
+
killed?: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface PiApi {
|
|
34
|
+
exec: (cmd: string, args: string[], opts?: { timeout?: number; signal?: AbortSignal }) => Promise<PiExecResult>
|
|
35
|
+
on: (event: string, handler: (event: PiToolCallEvent, ctx: PiContext) => Promise<void> | void) => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const REWRITE_TIMEOUT_MS = 2_000
|
|
39
|
+
const MIN_SUPPORTED_RTK_MINOR = 23
|
|
40
|
+
|
|
41
|
+
const SEMVER_RE = /(\d+)\.(\d+)\.(\d+)/
|
|
42
|
+
const RTK_VERSION_PREFIX_RE = /^rtk\s+/
|
|
43
|
+
|
|
44
|
+
// Парсить "X.Y.Z" semver → [major, minor, patch] або null.
|
|
45
|
+
function parseSemver(raw: string): [number, number, number] | null {
|
|
46
|
+
const m = raw.trim().match(SEMVER_RE)
|
|
47
|
+
if (!m) return null
|
|
48
|
+
return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Викликає `rtk rewrite`; повертає переписану команду або null (passthrough).
|
|
52
|
+
async function rewriteCommand(pi: PiApi, cmd: string, signal?: AbortSignal): Promise<string | null> {
|
|
53
|
+
const result = await pi.exec('rtk', ['rewrite', cmd], {
|
|
54
|
+
timeout: REWRITE_TIMEOUT_MS,
|
|
55
|
+
signal
|
|
56
|
+
})
|
|
57
|
+
if (result.killed) return null
|
|
58
|
+
if (result.code !== 0 && result.code !== 3) return null
|
|
59
|
+
return result.stdout.trim() || null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default async function (pi: PiApi) {
|
|
63
|
+
// Проба rtk при завантаженні; без бінарника (або із застарим) extension вимикається.
|
|
64
|
+
const ver = await pi.exec('rtk', ['--version'], { timeout: REWRITE_TIMEOUT_MS })
|
|
65
|
+
if (ver.code !== 0) {
|
|
66
|
+
console.warn('[rtk] rtk binary not found in PATH — extension disabled')
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// `rtk rewrite` з'явився у 0.23.0 — старіші версії не підтримуються.
|
|
71
|
+
const parsed = parseSemver(ver.stdout.replace(RTK_VERSION_PREFIX_RE, ''))
|
|
72
|
+
if (parsed) {
|
|
73
|
+
const [major, minor] = parsed
|
|
74
|
+
if (major === 0 && minor < MIN_SUPPORTED_RTK_MINOR) {
|
|
75
|
+
console.warn(`[rtk] rtk ${ver.stdout.trim()} is too old (need >= 0.23.0) — extension disabled`)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pi.on('tool_call', async (event, ctx) => {
|
|
81
|
+
try {
|
|
82
|
+
// В upstream це isToolCallEventType('bash', event) — просте порівняння toolName.
|
|
83
|
+
if (event?.toolName !== 'bash') return
|
|
84
|
+
|
|
85
|
+
const cmd = event.input.command
|
|
86
|
+
if (typeof cmd !== 'string' || cmd.trim() === '') return
|
|
87
|
+
|
|
88
|
+
if (cmd.startsWith('rtk ')) return
|
|
89
|
+
if (env.RTK_DISABLED === '1') return
|
|
90
|
+
|
|
91
|
+
// Делегуємо rewrite-рішення rtk.
|
|
92
|
+
const rewritten = await rewriteCommand(pi, cmd, ctx.signal)
|
|
93
|
+
if (rewritten && rewritten !== cmd) {
|
|
94
|
+
event.input.command = rewritten
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
// Fail open: ніколи не блокуємо виконання через неочікувану помилку.
|
|
98
|
+
console.warn('[rtk] unexpected error in tool_call handler; passing through command', error)
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.1] - 2026-07-15
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Виправлено застарілі шляхи npm/rules/js/js/data/tooling у повідомленнях перевірок tooling/check (пост-реструктуризація da05f89d)
|
|
8
|
+
|
|
9
|
+
## [1.4.0] - 2026-07-15
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Нове правило `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"]`.
|
|
14
|
+
|
|
3
15
|
## [1.3.2] - 2026-07-15
|
|
4
16
|
|
|
5
17
|
### Fixed
|
package/bin/n-rules.js
CHANGED
|
@@ -1458,6 +1458,7 @@ async function runSync() {
|
|
|
1458
1458
|
}
|
|
1459
1459
|
if (result.gitignoreAdr) parts.push('.gitignore (adr fragment)')
|
|
1460
1460
|
if (result.piExtension) parts.push('.pi/extensions/n-rules-adr/')
|
|
1461
|
+
if (result.rtkPiExtension) parts.push('.pi/extensions/rtk.ts')
|
|
1461
1462
|
if (parts.length > 0) {
|
|
1462
1463
|
console.log(`🤖 Claude-конфіг: ${parts.join(', ')}`)
|
|
1463
1464
|
}
|
package/package.json
CHANGED
package/rules/js/check/main.mjs
CHANGED
|
@@ -285,7 +285,7 @@ async function checkKnipConfig(passFn, failFn, cwd) {
|
|
|
285
285
|
return
|
|
286
286
|
}
|
|
287
287
|
await copyFile(KNIP_CANONICAL_JSON_PATH, knipPath)
|
|
288
|
-
passFn('knip.json створено з канонічного npm/rules/js/
|
|
288
|
+
passFn('knip.json створено з канонічного npm/rules/js/tooling/data/tooling/knip-canonical.json (js.mdc)')
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
/**
|
|
@@ -140,7 +140,7 @@ export function verifyOxlintRcAgainstCanonical(cfg, canonical) {
|
|
|
140
140
|
|
|
141
141
|
if (!deepEqualOxlintCanonical(actual, expected)) {
|
|
142
142
|
failures.push(
|
|
143
|
-
`.oxlintrc.json: поле "${key}" має збігатися з каноном пакета @7n/rules (npm/rules/js/
|
|
143
|
+
`.oxlintrc.json: поле "${key}" має збігатися з каноном пакета @7n/rules (npm/rules/js/tooling/data/tooling/oxlint-canonical.json)`
|
|
144
144
|
)
|
|
145
145
|
}
|
|
146
146
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "auto": "завжди" }
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Локальні AI-інструменти на машині розробника — rtk (Rust Token Killer) стискає вивід CLI-команд на 60–90% токенів; агенти застосовують rtk автоматично через hooks, установка лише `brew install rtk-ai/tap/rtk`
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
version: '1.0'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Правило `local-ai` інтегрує локальні AI-інструменти розробника. Зараз покриває **rtk** —
|
|
8
|
+
компресор виводу CLI-команд (тести, git, build: 60–90% економії токенів). Розраховане на
|
|
9
|
+
розширення іншими локальними інструментами в майбутньому.
|
|
10
|
+
|
|
11
|
+
## Установка (єдиний крок користувача)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
brew install rtk-ai/tap/rtk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Більше нічого: hook-записи і extension нижче вставляє sync пакета, і всі вони **fail-open** —
|
|
18
|
+
без установленого rtk мовчки пропускають команди без змін. `rtk init` запускати не треба.
|
|
19
|
+
|
|
20
|
+
## Як агенту користуватись rtk
|
|
21
|
+
|
|
22
|
+
- Клієнти з rewrite-hook — **Claude Code, Cursor, pi** — переписують shell-команди на
|
|
23
|
+
rtk-еквіваленти автоматично (див. канонічні записи нижче); робити нічого не треба.
|
|
24
|
+
- Агенти без rewrite-hook (Codex тощо): якщо `command -v rtk` успішний — додавай префікс
|
|
25
|
+
`rtk` до команд: `rtk git status`, `rtk vitest`, `rtk tsc`, у ланцюжках теж
|
|
26
|
+
(`rtk git add … && rtk git commit …`). Невідомі rtk команди проходять без змін
|
|
27
|
+
(passthrough), тож префікс безпечний. Не дублюй префікс для команд, що вже починаються з `rtk`.
|
|
28
|
+
- Разово вимкнути переписування: `RTK_DISABLED=1` в env; запуск без фільтра —
|
|
29
|
+
`rtk proxy <cmd>`; статистика заощаджень — `rtk gain`.
|
|
30
|
+
|
|
31
|
+
## Канонічні записи (вставляє sync, коли правило увімкнене)
|
|
32
|
+
|
|
33
|
+
### `.claude/settings.json` — PreToolUse
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"hooks": {
|
|
38
|
+
"PreToolUse": [
|
|
39
|
+
{
|
|
40
|
+
"matcher": "Bash",
|
|
41
|
+
"hooks": [
|
|
42
|
+
{
|
|
43
|
+
"type": "command",
|
|
44
|
+
"command": "command -v rtk >/dev/null 2>&1 && exec rtk hook claude; exit 0",
|
|
45
|
+
"timeout": 30
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Без rtk hook завершується `exit 0` без виводу — Claude Code виконує команду як є.
|
|
55
|
+
|
|
56
|
+
### `.cursor/hooks.json` — preToolUse
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"version": 1,
|
|
61
|
+
"hooks": {
|
|
62
|
+
"preToolUse": [
|
|
63
|
+
{
|
|
64
|
+
"command": "bash -lc 'command -v rtk >/dev/null 2>&1 && exec rtk hook cursor; echo \"{}\"'",
|
|
65
|
+
"matcher": "Shell",
|
|
66
|
+
"timeout": 30
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Cursor вимагає JSON-вивід на всіх шляхах — без rtk hook відповідає `{}` (passthrough).
|
|
74
|
+
|
|
75
|
+
### `.pi/extensions/rtk.ts`
|
|
76
|
+
|
|
77
|
+
Vendored extension зі складу rtk (`rtk init --agent pi` генерує ідентичний файл у той самий
|
|
78
|
+
шлях — повторна установка ідемпотентна). Тонкий делегат: на `tool_call` bash викликає
|
|
79
|
+
`rtk rewrite <cmd>`; сам вимикається, якщо rtk відсутній або старіший за 0.23.0.
|
|
80
|
+
|
|
81
|
+
## Вимкнення
|
|
82
|
+
|
|
83
|
+
`disable-rules: ["local-ai"]` у `.n-rules.json` — наступний sync прибере hook-записи з
|
|
84
|
+
`.claude/settings.json` і `.cursor/hooks.json` та видалить `.pi/extensions/rtk.ts`
|
|
85
|
+
(включно з установленим вручну через `rtk init --agent pi` — шлях спільний).
|
|
@@ -3,12 +3,12 @@ type: JS Module
|
|
|
3
3
|
title: sync-claude-config.mjs
|
|
4
4
|
resource: npm/scripts/sync-claude-config.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: e4513e7a
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 85
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
Синхронізує конфігурацію Claude Code (`.claude/settings.json`, slash-команди з `commands/` темплейту, ADR Stop-hook) та Cursor hooks (`.cursor/hooks.json`) у поточний проєкт із темплейтів пакету `npm/.claude-template/`. Здійснює злиття конфігурацій: користувацькі поля зберігаються у `.claude/settings.json`, а дозволи (`permissions.allow`) зливаються через union. Керовані хуки, ідентифіковані командою-маркером `MANAGED_HOOK_COMMAND_MARKERS`, перезаписуються. Копіює ADR Stop-hook (`.claude/hooks/capture-decisions.sh`) та ADR normalize Stop-hook (`.claude/hooks/normalize-decisions.sh`) залежно від налаштувань у `.n-rules.json`. Також зливає фрагмент `.gitignore` з канонічного шаблону, додаючи необхідні записи для ADR.
|
|
11
|
+
Синхронізує конфігурацію Claude Code (`.claude/settings.json`, slash-команди з `commands/` темплейту, ADR Stop-hook) та Cursor hooks (`.cursor/hooks.json`) у поточний проєкт із темплейтів пакету `npm/.claude-template/`. Здійснює злиття конфігурацій: користувацькі поля зберігаються у `.claude/settings.json`, а дозволи (`permissions.allow`) зливаються через union. Керовані хуки, ідентифіковані командою-маркером `MANAGED_HOOK_COMMAND_MARKERS`, перезаписуються. Копіює ADR Stop-hook (`.claude/hooks/capture-decisions.sh`) та ADR normalize Stop-hook (`.claude/hooks/normalize-decisions.sh`) залежно від налаштувань у `.n-rules.json`. Також зливає фрагмент `.gitignore` з канонічного шаблону, додаючи необхідні записи для ADR. За правилом `local-ai` керує rtk-інтеграцією (fail-open, працює лише за наявності бінарника rtk): PreToolUse hook `rtk hook claude` у `.claude/settings.json`, preToolUse entry `rtk hook cursor` у `.cursor/hooks.json` і vendored pi-extension `.pi/extensions/rtk.ts`; вимкнення правила прибирає записи та видаляє extension.
|
|
12
12
|
|
|
13
13
|
## Поведінка
|
|
14
14
|
|
|
@@ -22,6 +22,8 @@ ADR_HOOK_COMMAND_MARKER — Визначає маркер шляху до bash-
|
|
|
22
22
|
ADR_NORMALIZE_HOOK_COMMAND_MARKER — Визначає маркер шляху до bash-скрипта ADR normalize Stop-hook.
|
|
23
23
|
CURSOR_ADR_HOOK_COMMAND_MARKER — Визначає маркер шляху до bash-скрипта ADR capture Stop-hook у `.cursor/hooks.json`.
|
|
24
24
|
CURSOR_ADR_NORMALIZE_HOOK_COMMAND_MARKER — Визначає маркер шляху до bash-скрипта ADR normalize Stop-hook у `.cursor/hooks.json`.
|
|
25
|
+
RTK_CLAUDE_HOOK_COMMAND_MARKER — Визначає маркер rtk PreToolUse hook'а (`rtk hook claude`) у `.claude/settings.json` (правило `local-ai`).
|
|
26
|
+
RTK_CURSOR_HOOK_COMMAND_MARKER — Визначає маркер rtk preToolUse hook'а (`rtk hook cursor`) у `.cursor/hooks.json` (правило `local-ai`).
|
|
25
27
|
MANAGED_HOOK_COMMAND_MARKERS — Містить список усіх маркерів, що ідентифікують керовані хуки пакета.
|
|
26
28
|
PI_DIR — Визначає кореневу директорію для артефактів pi.dev у проєкті-споживачі.
|
|
27
29
|
PI_EXTENSIONS_DIR — Визначає директорію для TS-extensions pi.dev у проєкті-споживачі.
|
|
@@ -31,8 +33,8 @@ ADR_GITIGNORE_SNIPPET_REL — Визначає відносний шлях до
|
|
|
31
33
|
mergeAllowList — Об'єднує списки дозволених дозволів, зберігаючи користувацькі записи першими.
|
|
32
34
|
mergeHooks — Зливає секцію `hooks` з `.claude/settings.json`, видаляючи керовані групи з існуючої конфігурації.
|
|
33
35
|
mergeSettings — Зливає конфігурацію `.claude/settings.json` з темплейту, зберігаючи користувацькі поля та оновлюючи керовані хуки.
|
|
34
|
-
mergeCursorHooksConfig — Зливає конфігурацію `.cursor/hooks.json`, зберігаючи користувацькі записи та додаючи/видаляючи керовані ADR stop entries.
|
|
35
|
-
syncCursorHooksConfig — Синхронізує `.cursor/hooks.json` для Cursor Agent
|
|
36
|
+
mergeCursorHooksConfig — Зливає конфігурацію `.cursor/hooks.json`, зберігаючи користувацькі записи та додаючи/видаляючи керовані ADR stop entries і rtk preToolUse entry.
|
|
37
|
+
syncCursorHooksConfig — Синхронізує `.cursor/hooks.json` для Cursor Agent hooks, додаючи ADR stop і rtk preToolUse entries за умовою.
|
|
36
38
|
syncClaudeSettings — Синхронізує `.claude/settings.json` за темплейтом, зберігаючи користувацькі налаштування.
|
|
37
39
|
syncAdrHookScript — Копіює канонічний bash-скрипт ADR capture Stop-hook з темплейту пакета у `.claude/hooks/`.
|
|
38
40
|
syncAdrNormalizeHookScript — Копіює канонічний bash-скрипт ADR normalize Stop-hook з темплейту пакета у `.claude/hooks/`.
|
|
@@ -40,6 +42,9 @@ syncAdrHookLibScripts — Копіює всі bash-скрипти з `lib/` те
|
|
|
40
42
|
removeOrphanAdrHookLib — Видаляє директорію `.claude/hooks/lib/` з проєкту, якщо ADR-хуки вимкнені.
|
|
41
43
|
syncPiExtensions — Копіює bundled pi.dev TS-extension `n-rules-adr` у `.pi/extensions/n-rules-adr/` проєкту.
|
|
42
44
|
removeOrphanPiExtension — Видаляє директорію `.pi/extensions/n-rules-adr/` з проєкту, якщо ADR-хуки вимкнені.
|
|
45
|
+
RTK_PI_EXTENSION_FILE — Визначає ім'я файлу rtk pi-extension (`rtk.ts`) — шлях спільний із `rtk init --agent pi`.
|
|
46
|
+
syncRtkPiExtension — Копіює vendored rtk pi-extension у `.pi/extensions/rtk.ts` проєкту (правило `local-ai`).
|
|
47
|
+
removeOrphanRtkPiExtension — Видаляє `.pi/extensions/rtk.ts` з проєкту, якщо правило `local-ai` вимкнене.
|
|
43
48
|
syncGitignoreAdrFragment — Дописує відсутні рядки з канонічного ADR-фрагмента до кореневого `.gitignore` проєкту.
|
|
44
49
|
syncClaudeCommands — Копіює всі slash-команди з `.claude-template/commands/` у `.claude/commands/` проєкту.
|
|
45
50
|
syncClaudeConfig — Виконує повну синхронізацію Claude Code-конфігу, включаючи ADR-хуки, `.gitignore` та pi-extension, залежно від правил.
|
|
@@ -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
|
}
|