@nitra/cursor 1.8.158 → 1.8.159

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.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: >-
3
+ Запустити всі програмні перевірки правил (`npx @nitra/cursor check`)
4
+ ---
5
+
6
+ # n-check
7
+
8
+ Запусти `npx @nitra/cursor check` і пройдися по результатах.
9
+
10
+ - Якщо є помилки — виправи відповідно до правила, на яке вказує перевірка.
11
+ - Якщо все чисто — підтверди коротким повідомленням і переходь до наступного кроку.
@@ -0,0 +1,23 @@
1
+ <!-- Файл генерується автоматично через `npx @nitra/cursor`. Не редагуй вручну. -->
2
+
3
+ # Робота в `npm/`
4
+
5
+ Path-scoped нагадування для агента: підвантажується автоматично, коли редагуємо файли під `npm/`.
6
+
7
+ ## Перед коміт-релевантними змінами в `npm/`
8
+
9
+ 1. Підвищ `version` у `npm/package.json` (build-bump, не більше одного кроку відносно `HEAD`).
10
+ 2. Додай запис у `npm/CHANGELOG.md` форматом Keep a Changelog: `## [версія] - YYYY-MM-DD` + секції `### Added/Changed/Fixed/Removed`.
11
+
12
+ Без обох пунктів `npx @nitra/cursor check npm-module` падає, а `Stop` hook блокує завершення ходу.
13
+
14
+ ## Перевірка локально
15
+
16
+ ```bash
17
+ npx @nitra/cursor check npm-module
18
+ ```
19
+
20
+ ## Джерело правил
21
+
22
+ - `.cursor/rules/n-npm-module.mdc` — повний текст правила
23
+ - `npm/scripts/check-npm-module.mjs` — алгоритм перевірки
@@ -0,0 +1,26 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(bun *)",
5
+ "Bash(bun run *)",
6
+ "Bash(bun test *)",
7
+ "Bash(bunx *)",
8
+ "Bash(npx --no @nitra/cursor *)",
9
+ "Bash(npx @nitra/cursor *)"
10
+ ]
11
+ },
12
+ "hooks": {
13
+ "Stop": [
14
+ {
15
+ "matcher": "",
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "npx --no @nitra/cursor stop-hook",
20
+ "timeout": 60
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+ }
26
+ }
package/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.8.159] - 2026-05-01
8
+
9
+ ### Added
10
+
11
+ - Інтеграція з Claude Code: новий каталог `npm/.claude-template/` із `settings.template.json` (Stop hook + permissions allowlist), `npm-CLAUDE.md` (path-scoped нагадування для роботи в `npm/`) і slash-команду `/n-check`.
12
+ - `sync-claude-config.mjs`: під час `npx @nitra/cursor` синхронізує `.claude/settings.json` (merge — користувацькі поля зберігаються, наші hooks ідентифікуються маркером і перезаписуються), `npm/CLAUDE.md` і slash-команди checks.
13
+ - Subcommand `npx @nitra/cursor stop-hook` — точка входу Stop hook Claude Code (читає stdin, виходить 0 при `stop_hook_active=true` для захисту від рекурсії, інакше викликає `check`).
14
+ - Поле `claude-config` у `.n-cursor.json` (default `true`) для опт-ауту.
15
+ - Тести `npm/tests/sync-claude-config.test.mjs` — merge allow-list/hooks, інтеграція, ідемпотентність, опт-аут (12 кейсів).
16
+
17
+ ### Changed
18
+
19
+ - `npm/schemas/n-cursor.json`: додано опис поля `claude-config`.
20
+ - `npm/package.json`: `.claude-template` додано в масив `files`, щоб публікувався з пакетом.
21
+
7
22
  ## [1.8.158] - 2026-05-01
8
23
 
9
24
  ### Changed
package/bin/n-cursor.js CHANGED
@@ -9,6 +9,13 @@
9
9
  * якщо в корені вже є `.n-cursor.json`, спочатку зчитується конфіг і за потреби дописується `$schema`
10
10
  * `npx \@nitra/cursor check bun` — перевірити лише вказані правила (ігнорує AGENTS.md)
11
11
  * `npx \@nitra/cursor rename-yaml-extensions` — k8s `*.yml` → `*.yaml`, `.github` `*.yaml` → `*.yml` (опції: `--dry-run`, `--root=…`; див. bin/rename-yaml-extensions.mjs)
12
+ * `npx \@nitra/cursor stop-hook` — точка входу Stop hook Claude Code (читає stdin, виходить 0 при `stop_hook_active`,
13
+ * інакше викликає `check`); прописується автоматично в `.claude/settings.json`
14
+ *
15
+ * Claude Code інтеграція: під час синку, окрім `.cursor/rules` і `.claude/commands` (з skills), CLI ще раз
16
+ * синхронізує `.claude/settings.json` (hooks + permissions; merge — користувацькі поля зберігаються),
17
+ * `npm/CLAUDE.md` (path-scoped нагадування для роботи в `npm/`) і slash-команди checks (`/n-check`).
18
+ * Опт-аут — поле `claude-config: false` у `.n-cursor.json`.
12
19
  *
13
20
  * Якщо у корені репозиторію немає .n-cursor.json, спочатку перейменовується за наявності nitra-cursor.json;
14
21
  * у `.cursor/rules` файли `nitra-*.mdc` перейменовуються на `n-*.mdc`; інакше конфіг створюється автоматично
@@ -48,7 +55,9 @@ import { fileURLToPath } from 'node:url'
48
55
 
49
56
  import { buildAgentsCommandBulletItems } from '../scripts/build-agents-commands.mjs'
50
57
  import { detectAutoRulesAndSkills, mergeConfigWithAutoDetected, normalizeIdList } from '../scripts/auto-rules.mjs'
58
+ import { runStopHookCli } from '../scripts/claude-stop-hook.mjs'
51
59
  import { ensureNitraCursorInRootDevDependencies } from '../scripts/ensure-nitra-cursor-dev-dependencies.mjs'
60
+ import { syncClaudeConfig } from '../scripts/sync-claude-config.mjs'
52
61
  import { upgradeNitraCursorToLatestAndBunInstall } from '../scripts/upgrade-nitra-cursor-and-install.mjs'
53
62
  import { runRenameYamlExtensionsCli } from './rename-yaml-extensions.mjs'
54
63
  import { syncSetupBunDepsAction } from '../scripts/sync-setup-bun-deps-action.mjs'
@@ -1097,6 +1106,7 @@ async function runSync() {
1097
1106
  const config = await runSyncStep('❌ ', () => readConfig({ bundledMdcDir, bundledSkillsDir }))
1098
1107
 
1099
1108
  const { rules, skills, version, ignore } = config
1109
+ const claudeConfigEnabled = config['claude-config'] !== false
1100
1110
  const bundledVer = await readBundledVersionAt(effectivePackageRoot)
1101
1111
  if (bundledVer) {
1102
1112
  const line =
@@ -1160,6 +1170,25 @@ async function runSync() {
1160
1170
  syncClaudeMd(/** @type {string[] | undefined} */ (ignore))
1161
1171
  )
1162
1172
 
1173
+ await runSyncStep('❌ Не вдалося синхронізувати Claude-конфіг: ', async () => {
1174
+ const result = await syncClaudeConfig({
1175
+ projectRoot: cwd(),
1176
+ bundledPackageRoot: effectivePackageRoot,
1177
+ enabled: claudeConfigEnabled
1178
+ })
1179
+ if (!claudeConfigEnabled) {
1180
+ console.log('🤖 Claude-конфіг: пропущено (claude-config: false у .n-cursor.json)')
1181
+ return
1182
+ }
1183
+ const parts = []
1184
+ if (result.settings) parts.push('.claude/settings.json')
1185
+ if (result.npmClaudeMd) parts.push('npm/CLAUDE.md')
1186
+ if (result.commands.length > 0) parts.push(`${result.commands.length} slash-commands`)
1187
+ if (parts.length > 0) {
1188
+ console.log(`🤖 Claude-конфіг: ${parts.join(', ')}`)
1189
+ }
1190
+ })
1191
+
1163
1192
  console.log(`\n✨ Готово: ${successCount} завантажено, ${failCount} з помилками\n`)
1164
1193
  if (failCount > 0) {
1165
1194
  throw new Error(`Не вдалося завантажити ${failCount} з ${rules.length} правил`)
@@ -1185,6 +1214,14 @@ try {
1185
1214
 
1186
1215
  break
1187
1216
  }
1217
+ case 'stop-hook': {
1218
+ // Викликається з .claude/settings.json як Stop hook Claude Code.
1219
+ // Прокидає `check` і поважає stop_hook_active, щоб не зациклюватись.
1220
+ const code = await runStopHookCli()
1221
+ process.exitCode = code
1222
+
1223
+ break
1224
+ }
1188
1225
  case undefined:
1189
1226
  case '': {
1190
1227
  await runSync()
@@ -1193,7 +1230,9 @@ try {
1193
1230
  }
1194
1231
  default: {
1195
1232
  console.error(`❌ Невідома команда: ${command}`)
1196
- console.error(` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions`)
1233
+ console.error(
1234
+ ` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, stop-hook`
1235
+ )
1197
1236
  process.exitCode = 1
1198
1237
  }
1199
1238
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.8.158",
3
+ "version": "1.8.159",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -30,6 +30,7 @@
30
30
  "schemas",
31
31
  "scripts",
32
32
  "skills",
33
+ ".claude-template",
33
34
  "AGENTS.template.md",
34
35
  "CHANGELOG.md"
35
36
  ],
@@ -54,6 +54,11 @@
54
54
  "version": {
55
55
  "type": "string",
56
56
  "description": "Застаріле поле, ігнорується CLI. Правила завжди копіюються з каталогу mdc/ установленого пакету (node_modules або кеш npx); змініть версію через оновлення залежності."
57
+ },
58
+ "claude-config": {
59
+ "type": "boolean",
60
+ "description": "Чи синхронізувати `.claude/settings.json` (hooks + permissions, merge зі збереженням користувацьких полів), `npm/CLAUDE.md` (path-scoped нагадування для роботи в `npm/`) і slash-команди checks. За замовчуванням true.",
61
+ "default": true
57
62
  }
58
63
  },
59
64
  "required": ["rules"]
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Stop-hook для Claude Code: запускається hook'ом із `.claude/settings.json` після того,
3
+ * як агент сигналізує завершення ходу. Прозоро прокидає `npx @nitra/cursor check`
4
+ * і повертає його exit code, щоб помилки правил блокували завершення.
5
+ *
6
+ * Захист від нескінченної рекурсії: якщо stdin містить `"stop_hook_active": true`
7
+ * (Claude Code позначає цей прапорець, коли hook сам спричинив повторний Stop),
8
+ * виходимо з кодом 0 без повторного запуску перевірок.
9
+ *
10
+ * Виклик з `bin/n-cursor.js`:
11
+ * `npx --no @nitra/cursor stop-hook`
12
+ */
13
+ import { spawn } from 'node:child_process'
14
+
15
+ /**
16
+ * Зчитує stdin до EOF як utf8 рядок. Якщо stdin порожній (TTY) — повертає '' одразу.
17
+ * @returns {Promise<string>} вміст stdin
18
+ */
19
+ function readStdin() {
20
+ return new Promise(resolve => {
21
+ if (process.stdin.isTTY) {
22
+ resolve('')
23
+ return
24
+ }
25
+ let data = ''
26
+ process.stdin.setEncoding('utf8')
27
+ process.stdin.on('data', chunk => {
28
+ data += chunk
29
+ })
30
+ process.stdin.on('end', () => resolve(data))
31
+ process.stdin.on('error', () => resolve(data))
32
+ })
33
+ }
34
+
35
+ /**
36
+ * Чи stdin вказує, що поточний Stop вже виник через попередній Stop hook
37
+ * (Claude Code передає `stop_hook_active: true`). У такому випадку повторний
38
+ * запуск перевірок створив би нескінченний цикл — пропускаємо.
39
+ * @param {string} stdin сирий вміст stdin
40
+ * @returns {boolean} true, якщо рекурсивний виклик
41
+ */
42
+ export function isRecursiveStopHookCall(stdin) {
43
+ if (!stdin) {
44
+ return false
45
+ }
46
+ try {
47
+ const obj = JSON.parse(stdin)
48
+ return obj?.stop_hook_active === true
49
+ } catch {
50
+ return false
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Точка входу. Викликається з `bin/n-cursor.js` коли argv[0] === 'stop-hook'.
56
+ * @returns {Promise<number>} exit code (0 — OK / пропуск, 1 — помилки правил)
57
+ */
58
+ export async function runStopHookCli() {
59
+ const stdin = await readStdin()
60
+ if (isRecursiveStopHookCall(stdin)) {
61
+ return 0
62
+ }
63
+ return new Promise(resolve => {
64
+ const child = spawn('npx', ['--no', '@nitra/cursor', 'check'], { stdio: 'inherit' })
65
+ child.on('exit', code => resolve(code ?? 1))
66
+ child.on('error', err => {
67
+ process.stderr.write(`stop-hook: не вдалося запустити npx @nitra/cursor check — ${err.message}\n`)
68
+ resolve(1)
69
+ })
70
+ })
71
+ }
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Синхронізує конфігурацію Claude Code (`.claude/settings.json`, `npm/CLAUDE.md`,
3
+ * slash-команди для checks) у поточний проєкт із темплейтів пакету
4
+ * `npm/.claude-template/`.
5
+ *
6
+ * Архітектура:
7
+ * - `settings.json` — **merge**: користувацькі поля зберігаються; наші hooks
8
+ * ідентифікуються командою-маркером (`MANAGED_HOOK_COMMAND_MARKER`) і
9
+ * перезаписуються; permissions.allow зливається через union (із дедублікацією).
10
+ * - `npm/CLAUDE.md` — **fully owned**: завжди перезаписується; пропускається,
11
+ * якщо в проєкті немає каталогу `npm/`.
12
+ * - `.claude/commands/n-check.md` — fully owned slash-команда.
13
+ *
14
+ * Опт-аут — `claude-config: false` у `.n-cursor.json`.
15
+ */
16
+ import { existsSync } from 'node:fs'
17
+ import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises'
18
+ import { join } from 'node:path'
19
+
20
+ /** Маркер у command нашого managed-hook'а — за ним відрізняємо свої записи від користувацьких */
21
+ export const MANAGED_HOOK_COMMAND_MARKER = '@nitra/cursor stop-hook'
22
+
23
+ const CLAUDE_DIR = '.claude'
24
+ const CLAUDE_SETTINGS_FILE = `${CLAUDE_DIR}/settings.json`
25
+ const CLAUDE_COMMANDS_DIR = `${CLAUDE_DIR}/commands`
26
+ const NPM_CLAUDE_MD_FILE = 'npm/CLAUDE.md'
27
+ const TEMPLATE_DIR_NAME = '.claude-template'
28
+
29
+ /**
30
+ * @typedef {object} HookEntry
31
+ * @property {string} type
32
+ * @property {string} command
33
+ * @property {number} [timeout]
34
+ */
35
+
36
+ /**
37
+ * @typedef {object} HookGroup
38
+ * @property {string} [matcher]
39
+ * @property {HookEntry[]} hooks
40
+ */
41
+
42
+ /**
43
+ * @typedef {object} ClaudeSettings
44
+ * @property {{ allow?: string[] }} [permissions]
45
+ * @property {Record<string, HookGroup[]>} [hooks]
46
+ */
47
+
48
+ /**
49
+ * Чи hook-група містить лише наші managed-команди (за маркером).
50
+ * @param {HookGroup} group
51
+ * @returns {boolean}
52
+ */
53
+ function isManagedHookGroup(group) {
54
+ if (!group?.hooks?.length) {
55
+ return false
56
+ }
57
+ return group.hooks.every(h => typeof h?.command === 'string' && h.command.includes(MANAGED_HOOK_COMMAND_MARKER))
58
+ }
59
+
60
+ /**
61
+ * Зливає список allow-permissions: union існуючого і темплейтного без дублікатів,
62
+ * порядок — спочатку існуючі (щоб не міняти користувацький порядок), потім нові.
63
+ * @param {string[] | undefined} existing
64
+ * @param {string[] | undefined} fromTemplate
65
+ * @returns {string[]}
66
+ */
67
+ export function mergeAllowList(existing, fromTemplate) {
68
+ const out = []
69
+ const seen = new Set()
70
+ for (const arr of [existing ?? [], fromTemplate ?? []]) {
71
+ for (const item of arr) {
72
+ if (typeof item !== 'string' || seen.has(item)) {
73
+ continue
74
+ }
75
+ seen.add(item)
76
+ out.push(item)
77
+ }
78
+ }
79
+ return out
80
+ }
81
+
82
+ /**
83
+ * Зливає hooks-секцію: для кожної події в темплейті видаляємо managed-групи
84
+ * з існуючої конфігурації і додаємо актуальні з темплейту. Немені події в
85
+ * темплейті не чіпаються.
86
+ * @param {Record<string, HookGroup[]> | undefined} existing
87
+ * @param {Record<string, HookGroup[]> | undefined} fromTemplate
88
+ * @returns {Record<string, HookGroup[]>}
89
+ */
90
+ export function mergeHooks(existing, fromTemplate) {
91
+ /** @type {Record<string, HookGroup[]>} */
92
+ const out = {}
93
+ for (const [event, groups] of Object.entries(existing ?? {})) {
94
+ out[event] = Array.isArray(groups) ? groups.slice() : []
95
+ }
96
+ for (const [event, templateGroups] of Object.entries(fromTemplate ?? {})) {
97
+ const existingGroups = (out[event] ?? []).filter(g => !isManagedHookGroup(g))
98
+ out[event] = [...existingGroups, ...(templateGroups ?? [])]
99
+ if (out[event].length === 0) {
100
+ delete out[event]
101
+ }
102
+ }
103
+ return out
104
+ }
105
+
106
+ /**
107
+ * Повертає об'єднаний об'єкт settings.json.
108
+ * @param {ClaudeSettings | undefined} existing
109
+ * @param {ClaudeSettings} template
110
+ * @returns {ClaudeSettings}
111
+ */
112
+ export function mergeSettings(existing, template) {
113
+ /** @type {ClaudeSettings} */
114
+ const merged = { ...(existing ?? {}) }
115
+ const mergedAllow = mergeAllowList(existing?.permissions?.allow, template.permissions?.allow)
116
+ if (mergedAllow.length > 0) {
117
+ merged.permissions = { ...(existing?.permissions ?? {}), allow: mergedAllow }
118
+ }
119
+ const mergedHooks = mergeHooks(existing?.hooks, template.hooks)
120
+ if (Object.keys(mergedHooks).length > 0) {
121
+ merged.hooks = mergedHooks
122
+ } else {
123
+ delete merged.hooks
124
+ }
125
+ return merged
126
+ }
127
+
128
+ /**
129
+ * Читає JSON-файл; якщо файл відсутній або не валідний — повертає `undefined`.
130
+ * @param {string} path
131
+ * @returns {Promise<ClaudeSettings | undefined>}
132
+ */
133
+ async function readJsonOrUndefined(path) {
134
+ if (!existsSync(path)) {
135
+ return undefined
136
+ }
137
+ try {
138
+ return JSON.parse(await readFile(path, 'utf8'))
139
+ } catch {
140
+ return undefined
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Синхронізує `.claude/settings.json` за темплейтом, зберігаючи решту
146
+ * користувацьких полів.
147
+ * @param {string} projectRoot корінь проєкту, куди писати
148
+ * @param {string} templateDir каталог `.claude-template/` усередині пакету
149
+ * @returns {Promise<{ written: boolean, path: string }>}
150
+ */
151
+ export async function syncClaudeSettings(projectRoot, templateDir) {
152
+ const templatePath = join(templateDir, 'settings.template.json')
153
+ if (!existsSync(templatePath)) {
154
+ return { written: false, path: '' }
155
+ }
156
+ const template = /** @type {ClaudeSettings} */ (JSON.parse(await readFile(templatePath, 'utf8')))
157
+ const settingsPath = join(projectRoot, CLAUDE_SETTINGS_FILE)
158
+ const existing = await readJsonOrUndefined(settingsPath)
159
+ const merged = mergeSettings(existing, template)
160
+ await mkdir(join(projectRoot, CLAUDE_DIR), { recursive: true })
161
+ await writeFile(settingsPath, `${JSON.stringify(merged, null, 2)}\n`, 'utf8')
162
+ return { written: true, path: CLAUDE_SETTINGS_FILE }
163
+ }
164
+
165
+ /**
166
+ * Копіює `npm/CLAUDE.md` з темплейту, якщо в проєкті є каталог `npm/`.
167
+ * @param {string} projectRoot
168
+ * @param {string} templateDir
169
+ * @returns {Promise<{ written: boolean, path: string }>}
170
+ */
171
+ export async function syncNpmClaudeMd(projectRoot, templateDir) {
172
+ if (!existsSync(join(projectRoot, 'npm'))) {
173
+ return { written: false, path: '' }
174
+ }
175
+ const templatePath = join(templateDir, 'npm-CLAUDE.md')
176
+ if (!existsSync(templatePath)) {
177
+ return { written: false, path: '' }
178
+ }
179
+ const content = await readFile(templatePath, 'utf8')
180
+ await writeFile(join(projectRoot, NPM_CLAUDE_MD_FILE), content, 'utf8')
181
+ return { written: true, path: NPM_CLAUDE_MD_FILE }
182
+ }
183
+
184
+ /**
185
+ * Копіює всі slash-команди з `templateDir/commands/` у `.claude/commands/`.
186
+ * Команди ідентифікуються тим, що вони лежать у темплейті — не перетинаються
187
+ * з командами скілів (n-fix, n-lint, ...).
188
+ * @param {string} projectRoot
189
+ * @param {string} templateDir
190
+ * @returns {Promise<string[]>} масив відносних шляхів записаних файлів
191
+ */
192
+ export async function syncClaudeCommands(projectRoot, templateDir) {
193
+ const commandsTemplateDir = join(templateDir, 'commands')
194
+ if (!existsSync(commandsTemplateDir)) {
195
+ return []
196
+ }
197
+ const targetDir = join(projectRoot, CLAUDE_COMMANDS_DIR)
198
+ await mkdir(targetDir, { recursive: true })
199
+ const written = []
200
+ for (const name of await readdir(commandsTemplateDir)) {
201
+ if (!name.endsWith('.md')) {
202
+ continue
203
+ }
204
+ const content = await readFile(join(commandsTemplateDir, name), 'utf8')
205
+ await writeFile(join(targetDir, name), content, 'utf8')
206
+ written.push(`${CLAUDE_COMMANDS_DIR}/${name}`)
207
+ }
208
+ return written
209
+ }
210
+
211
+ /**
212
+ * Виконує повну синхронізацію Claude Code-конфігу з темплейту пакету в проєкт.
213
+ * Використовується з `bin/n-cursor.js` після інших синків.
214
+ * @param {object} options
215
+ * @param {string} options.projectRoot корінь проєкту-споживача
216
+ * @param {string} options.bundledPackageRoot корінь установленого `@nitra/cursor`
217
+ * @param {boolean} options.enabled чи увімкнено sync (з `.n-cursor.json` `claude-config`)
218
+ * @returns {Promise<{ settings: boolean, npmClaudeMd: boolean, commands: string[] }>}
219
+ */
220
+ export async function syncClaudeConfig({ projectRoot, bundledPackageRoot, enabled }) {
221
+ if (!enabled) {
222
+ return { settings: false, npmClaudeMd: false, commands: [] }
223
+ }
224
+ const templateDir = join(bundledPackageRoot, TEMPLATE_DIR_NAME)
225
+ if (!existsSync(templateDir)) {
226
+ return { settings: false, npmClaudeMd: false, commands: [] }
227
+ }
228
+ const settings = await syncClaudeSettings(projectRoot, templateDir)
229
+ const npmClaudeMd = await syncNpmClaudeMd(projectRoot, templateDir)
230
+ const commands = await syncClaudeCommands(projectRoot, templateDir)
231
+ return { settings: settings.written, npmClaudeMd: npmClaudeMd.written, commands }
232
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "./node_modules/oxlint/configuration_schema.json",
3
- "plugins": ["unicorn", "oxc", "import", "jsdoc", "promise", "node"],
3
+ "plugins": ["unicorn", "oxc", "import", "jsdoc", "promise", "node", "vue"],
4
4
  "jsPlugins": ["@e18e/eslint-plugin"],
5
5
  "categories": {},
6
6
  "rules": {},
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "./node_modules/oxlint/configuration_schema.json",
3
- "plugins": ["unicorn", "oxc", "import", "jsdoc", "promise", "node"],
3
+ "plugins": ["unicorn", "oxc", "import", "jsdoc", "promise", "node", "vue"],
4
4
  "jsPlugins": ["@e18e/eslint-plugin"],
5
5
  "categories": {},
6
6
  "rules": {