@nitra/cursor 1.37.0 → 1.38.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/CHANGELOG.md +6 -0
- package/README.md +6 -0
- package/bin/n-cursor.js +7 -1
- package/package.json +1 -1
- package/rules/worktree/worktree.mdc +34 -0
- package/scripts/lib/worktree.mjs +73 -0
- package/scripts/worktree-cli.mjs +200 -0
- package/skills/worktree/SKILL.md +38 -0
- package/skills/worktree/meta.json +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.38.0] - 2026-05-31
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- worktree: кросплатформний CLI n-cursor worktree (add/remove/list/prune) — виконавець конвенції .worktrees/ з інвентарним файлом-описом; тонкий skill worktree; pure-doc правило worktree
|
|
8
|
+
|
|
3
9
|
## [1.37.0] - 2026-05-31
|
|
4
10
|
|
|
5
11
|
### Changed
|
package/README.md
CHANGED
|
@@ -68,6 +68,12 @@
|
|
|
68
68
|
npx @nitra/cursor
|
|
69
69
|
npx @nitra/cursor fix
|
|
70
70
|
npx @nitra/cursor fix bun ga
|
|
71
|
+
|
|
72
|
+
# Керування git-worktree (.worktrees/ + інвентарний файл-опис)
|
|
73
|
+
npx @nitra/cursor worktree add <branch> "<опис>"
|
|
74
|
+
npx @nitra/cursor worktree list
|
|
75
|
+
npx @nitra/cursor worktree remove <branch> [--force]
|
|
76
|
+
npx @nitra/cursor worktree prune
|
|
71
77
|
```
|
|
72
78
|
|
|
73
79
|
Команда `check` запускає programmatic перевірки з каталогу `scripts/` пакету. Якщо в корені репозиторію вже є `.n-cursor.json`, перед перевірками виконується зчитування конфігу — зокрема додається або виправляється поле `$schema`, якщо воно відсутнє або не збігається з очікуваним URL.
|
package/bin/n-cursor.js
CHANGED
|
@@ -103,6 +103,7 @@ import { syncClaudeConfig } from '../scripts/sync-claude-config.mjs'
|
|
|
103
103
|
import { upgradeNitraCursorToLatestAndBunInstall } from '../scripts/upgrade-nitra-cursor-and-install.mjs'
|
|
104
104
|
import { runRenameYamlExtensionsCli } from './rename-yaml-extensions.mjs'
|
|
105
105
|
import { runSkillsCli } from '../scripts/skills-cli.mjs'
|
|
106
|
+
import { runWorktreeCli } from '../scripts/worktree-cli.mjs'
|
|
106
107
|
import { syncSetupBunDepsAction } from '../scripts/sync-setup-bun-deps-action.mjs'
|
|
107
108
|
import { runLintCli } from '../scripts/lib/run-lint-cli.mjs'
|
|
108
109
|
import { formatTimingSummary } from '../scripts/lib/timing-summary.mjs'
|
|
@@ -1525,6 +1526,11 @@ try {
|
|
|
1525
1526
|
|
|
1526
1527
|
break
|
|
1527
1528
|
}
|
|
1529
|
+
case 'worktree': {
|
|
1530
|
+
process.exitCode = await runWorktreeCli(args)
|
|
1531
|
+
|
|
1532
|
+
break
|
|
1533
|
+
}
|
|
1528
1534
|
case undefined:
|
|
1529
1535
|
case '': {
|
|
1530
1536
|
await runSync()
|
|
@@ -1534,7 +1540,7 @@ try {
|
|
|
1534
1540
|
default: {
|
|
1535
1541
|
console.error(`❌ Невідома команда: ${command}`)
|
|
1536
1542
|
console.error(
|
|
1537
|
-
` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, post-tool-use-fix, lint, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text, coverage, change, release, skill`
|
|
1543
|
+
` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, post-tool-use-fix, lint, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text, coverage, change, release, skill, worktree`
|
|
1538
1544
|
)
|
|
1539
1545
|
process.exitCode = 1
|
|
1540
1546
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Конвенція git-worktree у цьому репо — створення, інвентаризація та прибирання через n-cursor worktree CLI.
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Worktree-конвенція
|
|
8
|
+
|
|
9
|
+
Усі git-worktree створюй і прибирай через CLI `n-cursor worktree` — він кладе їх у
|
|
10
|
+
`.worktrees/` (gitignored) і веде інвентарний файл-опис поруч.
|
|
11
|
+
|
|
12
|
+
## Розташування
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
.worktrees/
|
|
16
|
+
feat-skill-meta/ ← git worktree checkout
|
|
17
|
+
feat-skill-meta.md ← інвентарний опис поруч (gitignored через .worktrees/)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Слеш у гілці перетворюється на дефіс: `feat/skill-meta` → `.worktrees/feat-skill-meta/`.
|
|
21
|
+
Git-гілка лишається з оригінальним імʼям (`feat/skill-meta`).
|
|
22
|
+
|
|
23
|
+
## Команди
|
|
24
|
+
|
|
25
|
+
- **Створити** (опис обовʼязковий): `npx @nitra/cursor worktree add <branch> "<навіщо>"`
|
|
26
|
+
- **Інвентаризація**: `npx @nitra/cursor worktree list`
|
|
27
|
+
- **Прибрати**: `npx @nitra/cursor worktree remove <branch> [--force]`
|
|
28
|
+
- **Прибрати осиротілі**: `npx @nitra/cursor worktree prune`
|
|
29
|
+
|
|
30
|
+
## Заборони
|
|
31
|
+
|
|
32
|
+
- Не клади worktree в `.claude/worktrees/` — це приватна директорія харнесу Claude Code.
|
|
33
|
+
- Не клади worktree в батьківський каталог `../cursor-<name>` — ускладнює інвентаризацію.
|
|
34
|
+
- Не створюй worktree вручну (`git worktree add`) повз CLI — інакше не буде інвентарного опису.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Чиста логіка worktree-tool `n-cursor worktree` (без git/fs side-effects).
|
|
3
|
+
*
|
|
4
|
+
* Тут — детерміновані, тестовані без git функції:
|
|
5
|
+
* - `sanitizeBranch` — імʼя гілки → безпечне імʼя каталогу/файла (слеш та інші
|
|
6
|
+
* небезпечні для шляху символи → дефіс), щоб структура `.worktrees/` лишалась пласкою;
|
|
7
|
+
* - `worktreePaths` — шляхи checkout і файла-опису поруч;
|
|
8
|
+
* - `buildDescription` — текст інвентарного `.worktrees/<name>.md` за конвенцією worktree.mdc;
|
|
9
|
+
* - `findOrphanDescFiles` — `.md`-описи без зареєстрованого worktree (для `prune`).
|
|
10
|
+
*
|
|
11
|
+
* Оркестрація (виклики git, запис файлів, argv) — у `npm/scripts/worktree-cli.mjs`.
|
|
12
|
+
*/
|
|
13
|
+
import { basename, join } from 'node:path'
|
|
14
|
+
|
|
15
|
+
/** Символи, безпечні для імені каталогу/файла; решта → дефіс. */
|
|
16
|
+
const UNSAFE_PATH_CHARS_RE = /[^a-zA-Z0-9._-]+/gu
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Перетворює імʼя git-гілки на безпечне імʼя каталогу/файла для `.worktrees/`.
|
|
20
|
+
* @param {string} branch імʼя git-гілки (наприклад `feat/skill-meta`)
|
|
21
|
+
* @returns {string} пласке імʼя (наприклад `feat-skill-meta`)
|
|
22
|
+
*/
|
|
23
|
+
export function sanitizeBranch(branch) {
|
|
24
|
+
if (typeof branch !== 'string' || branch.trim() === '') {
|
|
25
|
+
throw new Error('worktree: імʼя гілки обовʼязкове')
|
|
26
|
+
}
|
|
27
|
+
const sanitized = branch.trim().replace(UNSAFE_PATH_CHARS_RE, '-').replace(/^-+|-+$/gu, '')
|
|
28
|
+
if (sanitized === '') {
|
|
29
|
+
throw new Error(`worktree: імʼя гілки "${branch}" не містить допустимих символів`)
|
|
30
|
+
}
|
|
31
|
+
return sanitized
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Детерміновані шляхи checkout і файла-опису для гілки.
|
|
36
|
+
* @param {string} repoRoot абсолютний корінь репозиторію
|
|
37
|
+
* @param {string} branch імʼя git-гілки
|
|
38
|
+
* @returns {{ checkout: string, descFile: string }} абсолютні шляхи
|
|
39
|
+
*/
|
|
40
|
+
export function worktreePaths(repoRoot, branch) {
|
|
41
|
+
const name = sanitizeBranch(branch)
|
|
42
|
+
const dir = join(repoRoot, '.worktrees')
|
|
43
|
+
return { checkout: join(dir, name), descFile: join(dir, `${name}.md`) }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Текст інвентарного файла-опису worktree.
|
|
48
|
+
* @param {{ branch: string, task: string, baseCommit: string, date: string }} params поля опису
|
|
49
|
+
* @returns {string} markdown-вміст `.worktrees/<name>.md`
|
|
50
|
+
*/
|
|
51
|
+
export function buildDescription({ branch, task, baseCommit, date }) {
|
|
52
|
+
return [
|
|
53
|
+
`# ${branch}`,
|
|
54
|
+
'',
|
|
55
|
+
`**Задача:** ${task}`,
|
|
56
|
+
`**Дата:** ${date}`,
|
|
57
|
+
`**База (коміт):** ${baseCommit}`,
|
|
58
|
+
'',
|
|
59
|
+
'Прибрати: ' + '`' + `npx @nitra/cursor worktree remove ${branch}` + '`',
|
|
60
|
+
''
|
|
61
|
+
].join('\n')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* `.md`-описи без відповідного зареєстрованого worktree-checkout.
|
|
66
|
+
* @param {string[]} descFiles абсолютні шляхи `.worktrees/*.md`
|
|
67
|
+
* @param {string[]} registeredCheckouts абсолютні шляхи зареєстрованих worktree-checkout
|
|
68
|
+
* @returns {string[]} осиротілі `.md` (підмножина `descFiles`)
|
|
69
|
+
*/
|
|
70
|
+
export function findOrphanDescFiles(descFiles, registeredCheckouts) {
|
|
71
|
+
const checkoutBasenames = new Set(registeredCheckouts.map(c => basename(c)))
|
|
72
|
+
return descFiles.filter(md => !checkoutBasenames.has(basename(md).replace(/\.md$/u, '')))
|
|
73
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-оркестратор worktree-tool `n-cursor worktree` (виконавець конвенції `.worktrees/`).
|
|
3
|
+
*
|
|
4
|
+
* Підкоманди:
|
|
5
|
+
* add <branch> "<опис>" — git worktree add .worktrees/<sanit> -b <branch> (від HEAD) + .md-опис
|
|
6
|
+
* remove <branch> [--force] — прибрати checkout + .md (гілку лишає)
|
|
7
|
+
* list — git worktree list + вміст .md-описів
|
|
8
|
+
* prune — git worktree prune + видалити осиротілі .md
|
|
9
|
+
*
|
|
10
|
+
* Чисті функції (санітизація, шляхи, текст опису, осиротілі) — у `lib/worktree.mjs`.
|
|
11
|
+
* Тут лише git-виклики, запис файлів, парсинг argv і звіт.
|
|
12
|
+
*/
|
|
13
|
+
import { spawnSync } from 'node:child_process'
|
|
14
|
+
import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
15
|
+
import { join } from 'node:path'
|
|
16
|
+
import { cwd as processCwd } from 'node:process'
|
|
17
|
+
|
|
18
|
+
import { buildDescription, findOrphanDescFiles, worktreePaths } from './lib/worktree.mjs'
|
|
19
|
+
|
|
20
|
+
const USAGE = [
|
|
21
|
+
'Usage:',
|
|
22
|
+
' npx @nitra/cursor worktree add <branch> "<опис>"',
|
|
23
|
+
' npx @nitra/cursor worktree remove <branch> [--force]',
|
|
24
|
+
' npx @nitra/cursor worktree list',
|
|
25
|
+
' npx @nitra/cursor worktree prune'
|
|
26
|
+
].join('\n')
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Запускає git, повертає { status, stdout, stderr }.
|
|
30
|
+
* @param {string[]} args аргументи git
|
|
31
|
+
* @param {string} cwd робочий каталог
|
|
32
|
+
* @returns {{ status: number, stdout: string, stderr: string }} результат
|
|
33
|
+
*/
|
|
34
|
+
function git(args, cwd) {
|
|
35
|
+
const r = spawnSync('git', args, { cwd, encoding: 'utf8' })
|
|
36
|
+
return { status: r.status ?? 1, stdout: r.stdout ?? '', stderr: r.stderr ?? '' }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Поточна дата YYYY-MM-DD (ін'єкція через ctx.now для тестів).
|
|
41
|
+
* @param {() => Date} now фабрика дати
|
|
42
|
+
* @returns {string} дата у форматі YYYY-MM-DD
|
|
43
|
+
*/
|
|
44
|
+
function today(now) {
|
|
45
|
+
return now().toISOString().slice(0, 10)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Реєстровані worktree-checkout (абсолютні шляхи) з `git worktree list --porcelain`.
|
|
50
|
+
* @param {string} cwd корінь репо
|
|
51
|
+
* @returns {string[]} абсолютні шляхи checkout
|
|
52
|
+
*/
|
|
53
|
+
function listRegisteredCheckouts(cwd) {
|
|
54
|
+
return git(['worktree', 'list', '--porcelain'], cwd)
|
|
55
|
+
.stdout.split('\n')
|
|
56
|
+
.filter(line => line.startsWith('worktree '))
|
|
57
|
+
.map(line => line.slice('worktree '.length).trim())
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Абсолютні шляхи `.worktrees/*.md`.
|
|
62
|
+
* @param {string} cwd корінь репо
|
|
63
|
+
* @returns {string[]} шляхи файлів-описів
|
|
64
|
+
*/
|
|
65
|
+
function listDescFiles(cwd) {
|
|
66
|
+
const dir = join(cwd, '.worktrees')
|
|
67
|
+
if (!existsSync(dir)) return []
|
|
68
|
+
return readdirSync(dir)
|
|
69
|
+
.filter(n => n.endsWith('.md'))
|
|
70
|
+
.map(n => join(dir, n))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* add: створити worktree від HEAD + .md-опис.
|
|
75
|
+
* @param {string[]} rest [branch, ...descParts]
|
|
76
|
+
* @param {{ cwd: string, log: Function, logError: Function, now: () => Date }} ctx контекст
|
|
77
|
+
* @returns {number} exit code
|
|
78
|
+
*/
|
|
79
|
+
function cmdAdd(rest, ctx) {
|
|
80
|
+
const [branch, ...descParts] = rest
|
|
81
|
+
const task = descParts.join(' ').trim()
|
|
82
|
+
if (!branch) {
|
|
83
|
+
ctx.logError('worktree add: потрібне імʼя гілки')
|
|
84
|
+
ctx.logError(USAGE)
|
|
85
|
+
return 1
|
|
86
|
+
}
|
|
87
|
+
if (!task) {
|
|
88
|
+
ctx.logError('worktree add: опис обовʼязковий — `worktree add <branch> "<опис>"`')
|
|
89
|
+
return 1
|
|
90
|
+
}
|
|
91
|
+
let paths
|
|
92
|
+
try {
|
|
93
|
+
paths = worktreePaths(ctx.cwd, branch)
|
|
94
|
+
} catch (error) {
|
|
95
|
+
ctx.logError(error.message)
|
|
96
|
+
return 1
|
|
97
|
+
}
|
|
98
|
+
const added = git(['worktree', 'add', paths.checkout, '-b', branch], ctx.cwd)
|
|
99
|
+
if (added.status !== 0) {
|
|
100
|
+
ctx.logError(`worktree add не вдався: ${added.stderr.trim()}`)
|
|
101
|
+
return 1
|
|
102
|
+
}
|
|
103
|
+
const baseCommit = git(['rev-parse', '--short', 'HEAD'], ctx.cwd).stdout.trim()
|
|
104
|
+
const md = buildDescription({ branch, task, baseCommit, date: today(ctx.now) })
|
|
105
|
+
writeFileSync(paths.descFile, md, 'utf8')
|
|
106
|
+
ctx.log(`✅ worktree: ${paths.checkout}`)
|
|
107
|
+
ctx.log(` опис: ${paths.descFile}`)
|
|
108
|
+
return 0
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* remove: прибрати checkout + .md (гілку лишає).
|
|
113
|
+
* @param {string[]} rest [branch, ...flags]
|
|
114
|
+
* @param {{ cwd: string, log: Function, logError: Function }} ctx контекст
|
|
115
|
+
* @returns {number} exit code
|
|
116
|
+
*/
|
|
117
|
+
function cmdRemove(rest, ctx) {
|
|
118
|
+
const branch = rest.find(a => !a.startsWith('--'))
|
|
119
|
+
const force = rest.includes('--force')
|
|
120
|
+
if (!branch) {
|
|
121
|
+
ctx.logError('worktree remove: потрібне імʼя гілки')
|
|
122
|
+
return 1
|
|
123
|
+
}
|
|
124
|
+
let paths
|
|
125
|
+
try {
|
|
126
|
+
paths = worktreePaths(ctx.cwd, branch)
|
|
127
|
+
} catch (error) {
|
|
128
|
+
ctx.logError(error.message)
|
|
129
|
+
return 1
|
|
130
|
+
}
|
|
131
|
+
const args = ['worktree', 'remove', paths.checkout]
|
|
132
|
+
if (force) args.push('--force')
|
|
133
|
+
const removed = git(args, ctx.cwd)
|
|
134
|
+
if (removed.status !== 0) {
|
|
135
|
+
ctx.logError(`worktree remove не вдався: ${removed.stderr.trim()} (спробуй --force, якщо дерево брудне)`)
|
|
136
|
+
return 1
|
|
137
|
+
}
|
|
138
|
+
if (existsSync(paths.descFile)) rmSync(paths.descFile, { force: true })
|
|
139
|
+
ctx.log(`✅ прибрано: ${paths.checkout} (гілку ${branch} лишено)`)
|
|
140
|
+
return 0
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* list: git worktree list + вміст .md-описів.
|
|
145
|
+
* @param {{ cwd: string, log: Function }} ctx контекст
|
|
146
|
+
* @returns {number} exit code
|
|
147
|
+
*/
|
|
148
|
+
function cmdList(ctx) {
|
|
149
|
+
ctx.log(git(['worktree', 'list'], ctx.cwd).stdout.trimEnd())
|
|
150
|
+
for (const md of listDescFiles(ctx.cwd)) {
|
|
151
|
+
ctx.log(`\n--- ${md} ---`)
|
|
152
|
+
ctx.log(readFileSync(md, 'utf8').trimEnd())
|
|
153
|
+
}
|
|
154
|
+
return 0
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* prune: git worktree prune + видалити осиротілі .md.
|
|
159
|
+
* @param {{ cwd: string, log: Function }} ctx контекст
|
|
160
|
+
* @returns {number} exit code
|
|
161
|
+
*/
|
|
162
|
+
function cmdPrune(ctx) {
|
|
163
|
+
git(['worktree', 'prune'], ctx.cwd)
|
|
164
|
+
const orphans = findOrphanDescFiles(listDescFiles(ctx.cwd), listRegisteredCheckouts(ctx.cwd))
|
|
165
|
+
for (const md of orphans) {
|
|
166
|
+
rmSync(md, { force: true })
|
|
167
|
+
ctx.log(`🧹 видалено осиротілий опис: ${md}`)
|
|
168
|
+
}
|
|
169
|
+
ctx.log(`prune завершено (осиротілих описів: ${orphans.length})`)
|
|
170
|
+
return 0
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Точка входу підкоманди worktree.
|
|
175
|
+
* @param {string[]} argv аргументи після `worktree`
|
|
176
|
+
* @param {{ cwd?: string, log?: Function, logError?: Function, now?: () => Date }} [options] ін'єкція для тестів
|
|
177
|
+
* @returns {Promise<number>} exit code
|
|
178
|
+
*/
|
|
179
|
+
export function runWorktreeCli(argv, options = {}) {
|
|
180
|
+
const ctx = {
|
|
181
|
+
cwd: options.cwd ?? processCwd(),
|
|
182
|
+
log: options.log ?? (line => console.log(line)),
|
|
183
|
+
logError: options.logError ?? (line => console.error(line)),
|
|
184
|
+
now: options.now ?? (() => new Date())
|
|
185
|
+
}
|
|
186
|
+
const [sub, ...rest] = argv
|
|
187
|
+
switch (sub) {
|
|
188
|
+
case 'add':
|
|
189
|
+
return Promise.resolve(cmdAdd(rest, ctx))
|
|
190
|
+
case 'remove':
|
|
191
|
+
return Promise.resolve(cmdRemove(rest, ctx))
|
|
192
|
+
case 'list':
|
|
193
|
+
return Promise.resolve(cmdList(ctx))
|
|
194
|
+
case 'prune':
|
|
195
|
+
return Promise.resolve(cmdPrune(ctx))
|
|
196
|
+
default:
|
|
197
|
+
ctx.logError(USAGE)
|
|
198
|
+
return Promise.resolve(1)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: worktree
|
|
3
|
+
description: >-
|
|
4
|
+
Створення та керування git-worktree через n-cursor worktree CLI: ізольований
|
|
5
|
+
workspace у .worktrees/<branch>/ з інвентарним файлом-описом
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# worktree — ізольований workspace через CLI
|
|
9
|
+
|
|
10
|
+
Для роботи в окремому git-worktree використовуй CLI `n-cursor worktree` — він
|
|
11
|
+
однаковий у Claude і Cursor, кладе worktree у `.worktrees/` (gitignored) і сам
|
|
12
|
+
створює інвентарний файл-опис поруч.
|
|
13
|
+
|
|
14
|
+
## Команди
|
|
15
|
+
|
|
16
|
+
- Створити (опис **обовʼязковий**):
|
|
17
|
+
`npx @nitra/cursor worktree add <branch> "<навіщо цей worktree>"`
|
|
18
|
+
- Список активних з описами:
|
|
19
|
+
`npx @nitra/cursor worktree list`
|
|
20
|
+
- Прибрати (гілку лишає; `--force` для брудного дерева):
|
|
21
|
+
`npx @nitra/cursor worktree remove <branch> [--force]`
|
|
22
|
+
- Прибрати осиротілі описи / метадані:
|
|
23
|
+
`npx @nitra/cursor worktree prune`
|
|
24
|
+
|
|
25
|
+
## Приклад
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @nitra/cursor worktree add feat/skill-meta "реалізація Spec A: meta.json"
|
|
29
|
+
cd .worktrees/feat-skill-meta
|
|
30
|
+
# … робота в ізоляції …
|
|
31
|
+
cd -
|
|
32
|
+
npx @nitra/cursor worktree remove feat/skill-meta
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Слеш у гілці перетворюється на дефіс для пласкої структури: `feat/skill-meta`
|
|
36
|
+
→ `.worktrees/feat-skill-meta/`. Git-гілка лишається `feat/skill-meta`.
|
|
37
|
+
|
|
38
|
+
Конвенція й заборони (де НЕ створювати worktree) — `.cursor/rules/n-worktree.mdc`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "worktree": false }
|