@nitra/cursor 3.2.0 → 3.2.2
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 +16 -0
- package/bin/n-cursor.js +1 -1
- package/package.json +1 -1
- package/rules/k8s/js/manifests.mjs +30 -4
- package/rules/k8s/k8s.mdc +1 -1
- package/scripts/lib/worktree-notice.mjs +12 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.2.2] - 2026-06-01
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- js-lint: додано e18e/\* deny-правила та канонічні ignorePatterns у .oxlintrc.json
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- k8s NetworkPolicy: мітку `app` для Job беремо з `spec.template.metadata.labels.app`, для CronJob — з `spec.jobTemplate.spec.template.metadata.labels.app` (ручний `spec.selector` у Job/CronJob невалідний без `manualSelector: true`)
|
|
12
|
+
|
|
13
|
+
## [3.2.1] - 2026-06-01
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- worktree-only skills: прибрано shell expansion з preflight, щоб агент створював worktree literal-командами без confirmation prompt
|
|
18
|
+
|
|
3
19
|
## [3.2.0] - 2026-06-01
|
|
4
20
|
|
|
5
21
|
### Added
|
package/bin/n-cursor.js
CHANGED
|
@@ -651,7 +651,7 @@ function buildClaudeWorktreeEnforcementSectionLines() {
|
|
|
651
651
|
'',
|
|
652
652
|
'## Worktree-only skills (`meta.json` → `worktree: true`)',
|
|
653
653
|
'',
|
|
654
|
-
'Скіл із **`worktree: true`** у `meta.json` запускається **виключно** в окремому git-worktree (`.worktrees/<current-branch>-<suffix>/`) — **не** в основному дереві й **не** паралельно. Перший крок такого скіла (блок `n-cursor:worktree:start` у його `SKILL.md`) — **preflight**: якщо `git rev-parse --show-toplevel` не вказує під `.worktrees/`, **STOP** і не питай користувача про назву гілки; створи worktree від поточної гілки готовим snippet з `SKILL.md` за конвенцією `<current-branch>-<suffix
|
|
654
|
+
'Скіл із **`worktree: true`** у `meta.json` запускається **виключно** в окремому git-worktree (`.worktrees/<current-branch>-<suffix>/`) — **не** в основному дереві й **не** паралельно. Перший крок такого скіла (блок `n-cursor:worktree:start` у його `SKILL.md`) — **preflight**: якщо `git rev-parse --show-toplevel` не вказує під `.worktrees/`, **STOP** і не питай користувача про назву гілки; створи worktree від поточної гілки готовим snippet з `SKILL.md` за конвенцією `<current-branch>-<suffix>` і без shell expansion (без command substitution, variable expansion чи backticks). Чисте робоче дерево — **не** привід пропустити preflight.',
|
|
655
655
|
''
|
|
656
656
|
]
|
|
657
657
|
}
|
package/package.json
CHANGED
|
@@ -4009,10 +4009,35 @@ function appLabelFromSpecSelector(spec) {
|
|
|
4009
4009
|
return typeof app === 'string' && app.trim() !== '' ? app : null
|
|
4010
4010
|
}
|
|
4011
4011
|
|
|
4012
|
+
/**
|
|
4013
|
+
* Витягує мітку `app` з `spec.template.metadata.labels.app` (pod-template labels).
|
|
4014
|
+
*
|
|
4015
|
+
* Джерело для Job і CronJob: у Job/CronJob **не можна** задавати ручний `spec.selector`
|
|
4016
|
+
* без `manualSelector: true` — apiserver сам генерує селектор з `controller-uid`, тож
|
|
4017
|
+
* `spec.selector.matchLabels.app` там невалідне джерело. Pod-template labels — валідне
|
|
4018
|
+
* й завжди присутнє поле.
|
|
4019
|
+
* @param {Record<string, unknown>} spec об'єкт `spec` (Job — `spec`, CronJob — `jobTemplate.spec`)
|
|
4020
|
+
* @returns {string | null} непорожнє значення `app` або null
|
|
4021
|
+
*/
|
|
4022
|
+
function appLabelFromPodTemplate(spec) {
|
|
4023
|
+
const template = getNestedObject(spec, 'template')
|
|
4024
|
+
if (template === null) return null
|
|
4025
|
+
const metadata = getNestedObject(template, 'metadata')
|
|
4026
|
+
if (metadata === null) return null
|
|
4027
|
+
const labels = getNestedObject(metadata, 'labels')
|
|
4028
|
+
if (labels === null) return null
|
|
4029
|
+
const app = labels.app
|
|
4030
|
+
return typeof app === 'string' && app.trim() !== '' ? app : null
|
|
4031
|
+
}
|
|
4032
|
+
|
|
4012
4033
|
/**
|
|
4013
4034
|
* Витягує мітку `app` для workload, для якого потрібен NetworkPolicy.
|
|
4014
|
-
* Deployment / StatefulSet / DaemonSet
|
|
4015
|
-
*
|
|
4035
|
+
* Deployment / StatefulSet / DaemonSet — `spec.selector.matchLabels.app`;
|
|
4036
|
+
* Job — `spec.template.metadata.labels.app`;
|
|
4037
|
+
* CronJob — `spec.jobTemplate.spec.template.metadata.labels.app`.
|
|
4038
|
+
*
|
|
4039
|
+
* Job і CronJob читаються з pod-template labels, бо ручний `spec.selector` у них невалідний
|
|
4040
|
+
* без `manualSelector: true` (селектор генерує контролер). Див. `appLabelFromPodTemplate`.
|
|
4016
4041
|
* @param {Record<string, unknown>} manifest AST workload
|
|
4017
4042
|
* @returns {string | null} непорожнє значення `app` або null
|
|
4018
4043
|
*/
|
|
@@ -4022,10 +4047,11 @@ export function workloadAppLabel(manifest) {
|
|
|
4022
4047
|
if (kind === 'CronJob') {
|
|
4023
4048
|
const jobTemplate = getNestedObject(getNestedObject(manifest, 'spec'), 'jobTemplate')
|
|
4024
4049
|
const jobSpec = jobTemplate === null ? null : getNestedObject(jobTemplate, 'spec')
|
|
4025
|
-
return jobSpec === null ? null :
|
|
4050
|
+
return jobSpec === null ? null : appLabelFromPodTemplate(jobSpec)
|
|
4026
4051
|
}
|
|
4027
4052
|
const spec = getNestedObject(manifest, 'spec')
|
|
4028
4053
|
if (spec === null) return null
|
|
4054
|
+
if (kind === 'Job') return appLabelFromPodTemplate(spec)
|
|
4029
4055
|
return appLabelFromSpecSelector(spec)
|
|
4030
4056
|
}
|
|
4031
4057
|
|
|
@@ -5631,7 +5657,7 @@ async function validateNetworkPoliciesForK8sWorkloads(root, yamlFilesAbs, fail,
|
|
|
5631
5657
|
}
|
|
5632
5658
|
if (appLabel === null) {
|
|
5633
5659
|
fail(
|
|
5634
|
-
`${deployRel}: ${workloadKind} '${workloadName}' без мітки app
|
|
5660
|
+
`${deployRel}: ${workloadKind} '${workloadName}' без мітки app (spec.selector.matchLabels.app; Job — spec.template.metadata.labels.app; CronJob — spec.jobTemplate.spec.template.metadata.labels.app) (k8s.mdc)`
|
|
5635
5661
|
)
|
|
5636
5662
|
continue
|
|
5637
5663
|
}
|
package/rules/k8s/k8s.mdc
CHANGED
|
@@ -385,7 +385,7 @@ images:
|
|
|
385
385
|
|
|
386
386
|
Для **кожного** `kind: Deployment` у каталозі **`…/k8s/…/base/`** (у будь-якому файлі `.yaml`, наприклад **`deploy.yaml`**, **`deployment.yaml`**) сам Deployment має канонічні **`spec.template.spec.topologySpreadConstraints`**, а **HPA і PDB** живуть у **sibling каталозі** **`…/k8s/…/components/`** (Kustomize Component, фіксована назва каталогу — `components`). У `base/` локальні `hpa.yaml` і `pdb.yaml` **заборонені** (file-existence error). У дереві base-kustomize HPA / PDB також **не дозволені** через `resources` / `components` / `bases`.
|
|
387
387
|
|
|
388
|
-
**NetworkPolicy** — інша історія: оскільки обмеження мережі мають діяти **і на dev**, NP лежить **у `base/`** (а не в `components/`). Для **кожного** з **`Deployment`**, **`StatefulSet`**, **`DaemonSet`**, **`Job`**, **`CronJob`** під `k8s` обов'язковий **NetworkPolicy**: у **`…/k8s/…/base/`** — у **`base/networkpolicy.yaml`** поруч з workload-маніфестом (multi-doc, якщо workload-ів кілька); у **не-base** — **`networkpolicy.yaml`** поруч із маніфестом workload у тому ж каталозі (overlay-specific override). `metadata.name` NetworkPolicy **= `metadata.name`** workload; `spec.podSelector.matchLabels.app` **= мітка `app`** з `spec.selector.matchLabels` (для **CronJob** — з `spec.jobTemplate.spec.
|
|
388
|
+
**NetworkPolicy** — інша історія: оскільки обмеження мережі мають діяти **і на dev**, NP лежить **у `base/`** (а не в `components/`). Для **кожного** з **`Deployment`**, **`StatefulSet`**, **`DaemonSet`**, **`Job`**, **`CronJob`** під `k8s` обов'язковий **NetworkPolicy**: у **`…/k8s/…/base/`** — у **`base/networkpolicy.yaml`** поруч з workload-маніфестом (multi-doc, якщо workload-ів кілька); у **не-base** — **`networkpolicy.yaml`** поруч із маніфестом workload у тому ж каталозі (overlay-specific override). `metadata.name` NetworkPolicy **= `metadata.name`** workload; `spec.podSelector.matchLabels.app` **= мітка `app`** з `spec.selector.matchLabels` (для **Job** — з `spec.template.metadata.labels.app`, для **CronJob** — з `spec.jobTemplate.spec.template.metadata.labels.app`; у Job/CronJob ручний `spec.selector` невалідний без `manualSelector: true`, тож джерело — pod-template labels). У `base/kustomization.yaml` `resources:` має бути `networkpolicy.yaml`. Відсутні документи **`check k8s`** створює автоматично і додає `networkpolicy.yaml` у `base/kustomization.yaml` `resources:`.
|
|
389
389
|
|
|
390
390
|
**Канонічна структура `<pkg>/k8s/components/`** (sibling до `base/`) — лише HPA і PDB:
|
|
391
391
|
|
|
@@ -114,14 +114,18 @@ function buildNoticeBody(suffix) {
|
|
|
114
114
|
**Крок 0 — preflight (обовʼязковий, перед будь-якими іншими діями).** Якщо перевірка падає — **STOP**: не питай користувача про назву гілки, а сам створи worktree від поточної гілки за конвенцією \`<current-branch>-${suffix}\`. Суфікс \`${suffix}\` — коротка (до 10 символів) транслітерація задачі. Не виконуй **жоден** наступний крок скіла, поки preflight не завершився успіхом.
|
|
115
115
|
|
|
116
116
|
\`\`\`bash
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
117
|
+
git rev-parse --show-toplevel
|
|
118
|
+
git branch --show-current
|
|
119
|
+
\`\`\`
|
|
120
|
+
|
|
121
|
+
Якщо перша команда показала, що ти **не** в \`.worktrees/\`, візьми вивід другої команди як \`<current-branch>\` і виконай **literal-команди без shell expansion** (без command substitution, variable expansion чи backticks). Наприклад, якщо поточна гілка \`feature/x\`:
|
|
122
|
+
|
|
123
|
+
\`\`\`bash
|
|
124
|
+
npx @nitra/cursor worktree add "feature/x-${suffix}" "n-${suffix}: worktree-only skill"
|
|
125
|
+
cd ".worktrees/feature-x-${suffix}"
|
|
126
|
+
\`\`\`
|
|
127
|
+
|
|
128
|
+
Тобто branch-argument лишає slash як у git-гілці, а шлях для \`cd\` бере sanitized форму: slash → \`-\`.`;
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
/**
|