@nitra/cursor 3.21.1 → 3.23.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/n-cursor-adr/docs/index.md +181 -0
- package/CHANGELOG.md +37 -3
- package/bin/docs/n-cursor.md +636 -0
- package/bin/docs/rename-yaml-extensions.md +207 -0
- package/bin/n-cursor.js +30 -3
- package/package.json +1 -1
- package/rules/abie/docs/fix.md +18 -0
- package/rules/abie/js/docs/applies.md +26 -0
- package/rules/abie/js/docs/env_dns.md +32 -0
- package/rules/abie/js/docs/firebase_hosting.md +23 -0
- package/rules/abie/js/docs/hc_pairing.md +35 -0
- package/rules/abie/js/docs/ua_http_route.md +28 -0
- package/rules/abie/js/docs/ua_node_selector.md +28 -0
- package/rules/abie/lib/docs/enabled.md +29 -0
- package/rules/abie/lib/docs/env-dns.md +35 -0
- package/rules/abie/lib/docs/hc-yaml.md +33 -0
- package/rules/abie/lib/docs/http-route.md +44 -0
- package/rules/abie/lib/docs/k8s-tree.md +40 -0
- package/rules/abie/lib/docs/kustomization-patches.md +47 -0
- package/rules/abie/lib/docs/overlay-paths.md +38 -0
- package/rules/abie/lib/docs/yaml.md +29 -0
- package/rules/adr/docs/fix.md +148 -0
- package/rules/adr/js/docs/hooks.md +259 -0
- package/rules/bun/docs/fix.md +156 -0
- package/rules/bun/js/docs/layout.md +393 -0
- package/rules/capacitor/docs/fix.md +121 -0
- package/rules/capacitor/js/docs/platforms.md +295 -0
- package/rules/changelog/changelog.mdc +2 -2
- package/rules/changelog/docs/fix.md +174 -0
- package/rules/changelog/js/consistency.mjs +114 -13
- package/rules/changelog/js/docs/consistency.md +387 -0
- package/rules/changelog/lib/docs/package-manifest.md +210 -0
- package/rules/ci4/docs/fix.md +179 -0
- package/rules/ci4/js/docs/marksman_config.md +128 -0
- package/rules/docker/docker.mdc +8 -3
- package/rules/docker/docs/fix.md +171 -0
- package/rules/docker/js/docs/lint.md +258 -0
- package/rules/docker/lib/docs/docker-hadolint.md +184 -0
- package/rules/docker/lib/docs/docker-mirror.md +247 -0
- package/rules/docker/lib/docs/docker-native-addon.md +170 -0
- package/rules/docker/lib/docs/docker-nginx-user.md +219 -0
- package/rules/docker/lint/docs/lint.md +193 -0
- package/rules/efes/docs/fix.md +203 -0
- package/rules/feedback/docs/fix.md +140 -0
- package/rules/flow/docs/fix.md +152 -0
- package/rules/ga/docs/fix.md +158 -0
- package/rules/ga/js/docs/lint.md +100 -0
- package/rules/ga/js/docs/workflows.md +217 -0
- package/rules/ga/lint/docs/lint.md +209 -0
- package/rules/ga/policy/clean_merged_branch/clean_merged_branch.rego +11 -2
- package/rules/ga/policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +3 -4
- package/rules/graphql/docs/fix.md +126 -0
- package/rules/graphql/js/docs/tooling.md +264 -0
- package/rules/graphql/lib/docs/graphql-gql-scan.md +219 -0
- package/rules/hasura/docs/fix.md +120 -0
- package/rules/hasura/hasura.mdc +14 -0
- package/rules/hasura/js/docs/internal_urls.md +326 -0
- package/rules/image-avif/docs/fix.md +132 -0
- package/rules/image-avif/js/docs/avif_generation.md +241 -0
- package/rules/image-compress/docs/fix.md +150 -0
- package/rules/image-compress/js/docs/package_setup.md +191 -0
- package/rules/js-bun-db/docs/fix.md +148 -0
- package/rules/js-bun-db/js/docs/safety.md +231 -0
- package/rules/js-bun-db/js-bun-db.mdc +42 -13
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +347 -0
- package/rules/js-bun-redis/docs/fix.md +123 -0
- package/rules/js-bun-redis/js/docs/imports.md +176 -0
- package/rules/js-bun-redis/lib/docs/redis-imports.md +223 -0
- package/rules/js-lint/docs/fix.md +117 -0
- package/rules/js-lint/js/docs/lint.md +250 -0
- package/rules/js-lint/js/docs/tooling.md +348 -0
- package/rules/js-lint/js/docs/utils_imports.md +207 -0
- package/rules/js-lint/js/lint-findings.mjs +110 -0
- package/rules/js-lint/js/lint.mjs +86 -15
- package/rules/js-lint-ci/docs/fix.md +154 -0
- package/rules/js-lint-ci/js/docs/lint.md +144 -0
- package/rules/js-mssql/docs/fix.md +128 -0
- package/rules/js-mssql/js/docs/deps.md +263 -0
- package/rules/js-mssql/lib/docs/mssql-pool-scan.md +367 -0
- package/rules/js-run/docs/fix.md +144 -0
- package/rules/js-run/js/docs/runtime.md +388 -0
- package/rules/js-run/lib/docs/bunyan-imports.md +117 -0
- package/rules/js-run/lib/docs/check-env-scan.md +433 -0
- package/rules/js-run/lib/docs/conn-file-rules.md +300 -0
- package/rules/js-run/lib/docs/conn-imports-scan.md +204 -0
- package/rules/js-run/lib/docs/promise-settimeout-scan.md +326 -0
- package/rules/k8s/docs/fix.md +129 -0
- package/rules/k8s/js/docs/manifests.md +344 -0
- package/rules/k8s/js/manifests.mjs +6 -2
- package/rules/k8s/k8s.mdc +4 -2
- package/rules/k8s/lint/docs/lint.md +411 -0
- package/rules/k8s/policy/network_policy/template/deployment.snippet.yaml +2 -0
- package/rules/k8s/policy/network_policy/template/stateful-set.snippet.yaml +2 -0
- package/rules/nginx-default-tpl/docs/fix.md +124 -0
- package/rules/nginx-default-tpl/js/docs/template.md +378 -0
- package/rules/npm-module/docs/fix.md +98 -0
- package/rules/npm-module/js/docs/package_structure.md +274 -0
- package/rules/npm-module/js/docs/rule_meta.md +137 -0
- package/rules/npm-module/js/docs/skill_meta.md +190 -0
- package/rules/php/docs/fix.md +107 -0
- package/rules/php/js/docs/tooling.md +152 -0
- package/rules/php/lint/docs/lint.md +215 -0
- package/rules/python/docs/fix.md +163 -0
- package/rules/python/js/docs/applies.md +108 -0
- package/rules/python/js/docs/tooling.md +153 -0
- package/rules/python/lint/docs/lint.md +322 -0
- package/rules/rego/docs/fix.md +121 -0
- package/rules/rego/js/docs/applies.md +174 -0
- package/rules/rego/js/docs/lint.md +118 -0
- package/rules/rego/lint/docs/lint.md +204 -0
- package/rules/release/docs/change.md +185 -0
- package/rules/release/docs/fix.md +119 -0
- package/rules/release/docs/release.md +222 -0
- package/rules/release/lib/docs/aggregate.md +246 -0
- package/rules/release/lib/docs/change-file.md +200 -0
- package/rules/release/lib/docs/fallback.md +203 -0
- package/rules/rust/docs/fix.md +129 -0
- package/rules/rust/js/docs/applies.md +140 -0
- package/rules/rust/lib/docs/has-cargo-toml.md +130 -0
- package/rules/security/docs/fix.md +86 -0
- package/rules/security/js/docs/lint.md +171 -0
- package/rules/security/js/docs/sample_secret.md +190 -0
- package/rules/security/js/docs/trufflehog.md +137 -0
- package/rules/security/js/lint.mjs +9 -1
- package/rules/style-lint/docs/fix.md +155 -0
- package/rules/style-lint/js/docs/lint.md +184 -0
- package/rules/style-lint/js/docs/tooling.md +194 -0
- package/rules/tauri/docs/fix.md +158 -0
- package/rules/tauri/js/docs/cargo_mutants_config.md +168 -0
- package/rules/tauri/js/docs/tooling.md +228 -0
- package/rules/test/coverage/coverage.mjs +15 -3
- package/rules/test/docs/fix.md +132 -0
- package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +138 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +134 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +160 -0
- package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +195 -0
- package/rules/test/js/docs/cargo_mutants_config.md +173 -0
- package/rules/test/js/docs/location.md +136 -0
- package/rules/test/js/docs/no-process-chdir.md +160 -0
- package/rules/test/js/docs/no-relative-fs-path.md +271 -0
- package/rules/test/js/docs/stryker_config.md +152 -0
- package/rules/test/js/docs/vitest-config-pool-forks.md +174 -0
- package/rules/text/docs/fix.md +118 -0
- package/rules/text/js/docs/forbidden-prettier.md +143 -0
- package/rules/text/js/docs/formatting.md +256 -0
- package/rules/text/js/docs/lint.md +122 -0
- package/rules/text/lint/docs/lint.md +220 -0
- package/rules/text/lint/docs/run-dotenv-linter.md +157 -0
- package/rules/text/lint/docs/run-shellcheck.md +212 -0
- package/rules/text/lint/docs/run-v8r.md +197 -0
- package/rules/vue/docs/fix.md +127 -0
- package/rules/vue/js/docs/packages.md +335 -0
- package/rules/vue/lib/docs/vue-forbidden-imports.md +261 -0
- package/rules/worktree/docs/fix.md +161 -0
- package/schemas/rule-meta.json +5 -1
- package/scripts/auto-rules.mjs +7 -4
- package/scripts/coverage-classify/docs/apply.md +202 -0
- package/scripts/coverage-classify/docs/cache.md +203 -0
- package/scripts/coverage-classify/docs/index.md +218 -0
- package/scripts/coverage-classify/docs/prompt.md +132 -0
- package/scripts/coverage-classify/docs/verdict-schema.md +169 -0
- package/scripts/coverage-fix-extract.mjs +122 -0
- package/scripts/coverage-fix.mjs +1 -1
- package/scripts/dispatcher/docs/graph.md +346 -0
- package/scripts/dispatcher/docs/index.md +236 -0
- package/scripts/dispatcher/docs/trace.md +296 -0
- package/scripts/dispatcher/index.mjs +1 -1
- package/scripts/dispatcher/lib/active.mjs +4 -8
- package/scripts/dispatcher/lib/commands.mjs +7 -11
- package/scripts/dispatcher/lib/docs/active.md +348 -0
- package/scripts/dispatcher/lib/docs/artifact.md +232 -0
- package/scripts/dispatcher/lib/docs/budget.md +167 -0
- package/scripts/dispatcher/lib/docs/capability.md +196 -0
- package/scripts/dispatcher/lib/docs/commands.md +210 -0
- package/scripts/dispatcher/lib/docs/events.md +182 -0
- package/scripts/dispatcher/lib/docs/executor.md +190 -0
- package/scripts/dispatcher/lib/docs/flow-lock.md +161 -0
- package/scripts/dispatcher/lib/docs/flow-resolve.md +267 -0
- package/scripts/dispatcher/lib/docs/gate.md +231 -0
- package/scripts/dispatcher/lib/docs/level.md +335 -0
- package/scripts/dispatcher/lib/docs/plan-panel.md +181 -0
- package/scripts/dispatcher/lib/docs/plan.md +200 -0
- package/scripts/dispatcher/lib/docs/planner.md +269 -0
- package/scripts/dispatcher/lib/docs/review.md +255 -0
- package/scripts/dispatcher/lib/docs/reviewer.md +240 -0
- package/scripts/dispatcher/lib/docs/snapshot.md +247 -0
- package/scripts/dispatcher/lib/docs/spec.md +203 -0
- package/scripts/dispatcher/lib/docs/state-store.md +303 -0
- package/scripts/dispatcher/lib/docs/subagent-runner.md +173 -0
- package/scripts/dispatcher/lib/executor.mjs +6 -1
- package/scripts/dispatcher/lib/flow-resolve.mjs +3 -1
- package/scripts/dispatcher/lib/level.mjs +29 -3
- package/scripts/dispatcher/lib/review.mjs +1 -1
- package/scripts/dispatcher/lib/subagent-runner.mjs +5 -3
- package/scripts/docs/auto-rules.md +376 -0
- package/scripts/docs/auto-skills.md +173 -0
- package/scripts/docs/build-agents-commands.md +183 -0
- package/scripts/docs/cli-entry.md +153 -0
- package/scripts/docs/coverage-fix.md +177 -0
- package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +189 -0
- package/scripts/lib/changed-files.mjs +4 -1
- package/scripts/lib/diff-added-lines.mjs +85 -0
- package/scripts/lib/docs/changed-files.md +149 -0
- package/scripts/lib/docs/check-mdc-template-refs.md +222 -0
- package/scripts/lib/docs/check-reporter.md +175 -0
- package/scripts/lib/docs/discover-check-rules-from-cursor.md +157 -0
- package/scripts/lib/docs/discover-checkable-rules.md +165 -0
- package/scripts/lib/docs/ensure-tool.md +254 -0
- package/scripts/lib/docs/generated-markdown.md +275 -0
- package/scripts/lib/docs/gha-workflow.md +326 -0
- package/scripts/lib/docs/inline-template-links.md +303 -0
- package/scripts/lib/docs/list-rule-ids.md +156 -0
- package/scripts/lib/docs/load-cursor-config.md +147 -0
- package/scripts/lib/docs/mirror-parity.md +167 -0
- package/scripts/lib/worktree.mjs +26 -0
- package/scripts/worktree-cli.mjs +12 -2
- package/skills/coverage-fix/SKILL.md +34 -45
- package/skills/docgen/SKILL.md +44 -23
- package/skills/docgen/bench/etalon/firebase_hosting.md +19 -0
- package/skills/docgen/bench/etalon/k8s-tree.md +24 -0
- package/skills/docgen/bench/etalon/overlay-paths.md +24 -0
- package/skills/docgen/js/docgen-ignore.mjs +54 -0
- package/skills/docgen/js/docgen-scan.mjs +37 -21
- package/skills/llm-patch/SKILL.md +23 -2
- package/skills/start-check/SKILL.md +26 -53
- package/skills/start-check/js/check.mjs +211 -0
- package/skills/taze/SKILL.md +9 -3
- package/skills/taze/js/diff.mjs +154 -0
- package/types/bin/n-cursor.d.ts +1 -1
- package/skills/fix-tests/SKILL.md +0 -119
- package/skills/fix-tests/meta.json +0 -1
|
@@ -1,27 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* docgen scanner — детермінований обхід проєкту для скілу `docgen`.
|
|
3
3
|
*
|
|
4
|
-
* Друкує JSON-список кодових файлів із
|
|
5
|
-
* джерелом). Рішення про overwrite/skip приймає скіл —
|
|
6
|
-
* прапор `exists`. LLM/мережі тут немає: уся
|
|
4
|
+
* Друкує JSON-список кодових файлів із відносними `sourcePath`/`docPath`
|
|
5
|
+
* (тека `docs/` поряд із джерелом). Рішення про overwrite/skip приймає скіл —
|
|
6
|
+
* scanner лише лістить і ставить прапор `exists`. LLM/мережі тут немає: уся
|
|
7
|
+
* генерація доки — у субагентах скілу.
|
|
7
8
|
*/
|
|
8
9
|
// eslint-disable-next-line unicorn/import-style
|
|
9
10
|
import path from 'node:path'
|
|
10
11
|
import { existsSync, readdirSync, statSync } from 'node:fs'
|
|
11
12
|
|
|
12
13
|
import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
|
|
14
|
+
import { isDocgenIgnored } from './docgen-ignore.mjs'
|
|
13
15
|
|
|
14
16
|
/** Кодові розширення, для яких генеруємо документацію. */
|
|
15
17
|
const SOURCE_EXTENSIONS = new Set(['.js', '.mjs', '.ts', '.vue', '.py'])
|
|
16
18
|
|
|
17
|
-
/** Теки, які scanner ніколи не заходить (включно з самими `docs/`). */
|
|
18
|
-
const IGNORED_DIRS = new Set([
|
|
19
|
-
'node_modules', 'dist', '.git', '__pycache__', 'coverage', '.cursor', '.claude', 'docs'
|
|
20
|
-
])
|
|
21
|
-
|
|
22
19
|
/** `*.test.*`, `*.spec.*` — тести, документувати не треба. */
|
|
23
20
|
const TEST_FILE_RE = /\.(?:test|spec)\.[^.]+$/u
|
|
24
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Чи корінь має system-wide docs layout.
|
|
24
|
+
* Такий корінь зарезервований під репозиторні docs/adr, docs/explanation тощо,
|
|
25
|
+
* тому file-level docs у нього не пишемо.
|
|
26
|
+
* @param {string} root абсолютний корінь обходу
|
|
27
|
+
* @returns {boolean} true — корінь system-wide docs
|
|
28
|
+
*/
|
|
29
|
+
function isSystemWideDocsRoot(root) {
|
|
30
|
+
return existsSync(path.join(root, 'docs', 'adr')) || existsSync(path.join(root, 'docs', 'explanation'))
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
/**
|
|
26
34
|
* Чи є файл кодовим джерелом для документування.
|
|
27
35
|
* @param {string} fileName базове ім'я файлу
|
|
@@ -35,8 +43,9 @@ export function isSourceFile(fileName) {
|
|
|
35
43
|
|
|
36
44
|
/**
|
|
37
45
|
* Обчислює шлях md-документа для кодового файлу: тека `docs/` поряд із джерелом.
|
|
46
|
+
* Якщо `sourcePath` відносний, `docPath` теж відносний; якщо абсолютний — абсолютний.
|
|
38
47
|
* @param {string} sourcePath шлях до джерела (відносний або абсолютний)
|
|
39
|
-
* @returns {string} шлях до `<dir>/docs/<stem>.md`
|
|
48
|
+
* @returns {string} шлях до `<dir>/docs/<stem>.md` у тому ж просторі шляхів
|
|
40
49
|
*/
|
|
41
50
|
export function docPathForSource(sourcePath) {
|
|
42
51
|
const dir = path.dirname(sourcePath)
|
|
@@ -49,7 +58,7 @@ export function docPathForSource(sourcePath) {
|
|
|
49
58
|
* Синхронний `readdirSync` — детермінований порядок і простий рекурсивний обхід без
|
|
50
59
|
* гонок; обсяг дерева проєкту це дозволяє.
|
|
51
60
|
* @param {string} root абсолютний корінь обходу
|
|
52
|
-
* @returns {Array<{sourcePath:string,
|
|
61
|
+
* @returns {Array<{sourcePath:string, docPath:string, exists:boolean}>} список кандидатів з відносними шляхами
|
|
53
62
|
*/
|
|
54
63
|
export function scanForDocgen(root) {
|
|
55
64
|
const results = []
|
|
@@ -66,16 +75,19 @@ export function scanForDocgen(root) {
|
|
|
66
75
|
}
|
|
67
76
|
for (const entry of entries) {
|
|
68
77
|
const fullPath = path.join(dir, entry.name)
|
|
78
|
+
const relPath = path.relative(root, fullPath)
|
|
69
79
|
if (entry.isDirectory()) {
|
|
70
|
-
if (
|
|
80
|
+
if (isDocgenIgnored(relPath, 'dir')) continue
|
|
71
81
|
walk(fullPath)
|
|
72
82
|
} else if (entry.isFile() && isSourceFile(entry.name)) {
|
|
73
|
-
|
|
83
|
+
if (isSystemWideDocsRoot(root) && path.dirname(relPath) === '.') continue
|
|
84
|
+
const sourcePath = relPath.split(path.sep).join('/')
|
|
85
|
+
if (isDocgenIgnored(sourcePath)) continue
|
|
86
|
+
const docPath = docPathForSource(sourcePath)
|
|
74
87
|
results.push({
|
|
75
|
-
sourcePath
|
|
76
|
-
relSource: path.relative(root, fullPath),
|
|
88
|
+
sourcePath,
|
|
77
89
|
docPath,
|
|
78
|
-
exists: existsSync(docPath)
|
|
90
|
+
exists: existsSync(path.join(root, docPath))
|
|
79
91
|
})
|
|
80
92
|
}
|
|
81
93
|
}
|
|
@@ -95,12 +107,15 @@ export function slugForModule(root, moduleRoot) {
|
|
|
95
107
|
const rel = path.relative(root, moduleRoot)
|
|
96
108
|
// корінь репо: фіксований sentinel 'root'
|
|
97
109
|
if (rel === '') return 'root'
|
|
98
|
-
return rel
|
|
110
|
+
return rel
|
|
111
|
+
.split(path.sep)
|
|
112
|
+
.join('-')
|
|
113
|
+
.replaceAll(/[^\w-]+/gu, '-')
|
|
99
114
|
}
|
|
100
115
|
|
|
101
116
|
/**
|
|
102
117
|
* Знаходить корені модулів — теки з `package.json` (корінь завжди модуль).
|
|
103
|
-
* Ті ж
|
|
118
|
+
* Ті ж ignore-glob правила, тож `package.json` у службових деревах не враховується.
|
|
104
119
|
* @param {string} root абсолютний корінь обходу
|
|
105
120
|
* @returns {string[]} абсолютні шляхи коренів модулів
|
|
106
121
|
*/
|
|
@@ -117,8 +132,9 @@ export function findModuleRoots(root) {
|
|
|
117
132
|
}
|
|
118
133
|
for (const entry of entries) {
|
|
119
134
|
const fullPath = path.join(dir, entry.name)
|
|
135
|
+
const relPath = path.relative(root, fullPath)
|
|
120
136
|
if (entry.isDirectory()) {
|
|
121
|
-
if (
|
|
137
|
+
if (isDocgenIgnored(relPath, 'dir')) continue
|
|
122
138
|
walk(fullPath)
|
|
123
139
|
} else if (entry.isFile() && entry.name === 'package.json' && dir !== root) {
|
|
124
140
|
roots.push(dir)
|
|
@@ -150,17 +166,17 @@ export function nearestModuleRoot(filePath, moduleRoots) {
|
|
|
150
166
|
* Лістить логічні модулі проєкту з членами-файлами і docPath module-summary.
|
|
151
167
|
* Модулі без кодових файлів пропускаються.
|
|
152
168
|
* @param {string} root абсолютний корінь обходу
|
|
153
|
-
* @returns {Array<{moduleRoot:string, relRoot:string, slug:string, docPath:string, members:string[], exists:boolean}>} модулі (members —
|
|
169
|
+
* @returns {Array<{moduleRoot:string, relRoot:string, slug:string, docPath:string, members:string[], exists:boolean}>} модулі (members — sourcePath-и, відносні від root)
|
|
154
170
|
*/
|
|
155
171
|
export function scanForModules(root) {
|
|
156
172
|
const files = scanForDocgen(root)
|
|
157
173
|
const moduleRoots = findModuleRoots(root)
|
|
158
174
|
const byRoot = new Map()
|
|
159
175
|
for (const file of files) {
|
|
160
|
-
const moduleRoot = nearestModuleRoot(file.sourcePath, moduleRoots)
|
|
176
|
+
const moduleRoot = nearestModuleRoot(path.join(root, file.sourcePath), moduleRoots)
|
|
161
177
|
if (moduleRoot === null) continue
|
|
162
178
|
if (!byRoot.has(moduleRoot)) byRoot.set(moduleRoot, [])
|
|
163
|
-
byRoot.get(moduleRoot).push(file.
|
|
179
|
+
byRoot.get(moduleRoot).push(file.sourcePath)
|
|
164
180
|
}
|
|
165
181
|
|
|
166
182
|
const results = []
|
|
@@ -71,6 +71,14 @@ description: >-
|
|
|
71
71
|
з одним рядком пояснення кожна.
|
|
72
72
|
- **Реальні обмеження,** які не виводяться з коду: версії в репо-споживачі,
|
|
73
73
|
inflight-міграції, breaking-change політика, зовнішні залежності.
|
|
74
|
+
- **Change-file flow,** якщо завдання змінює файли у пакетному workspace (код,
|
|
75
|
+
правила, скіли, конфіги, тести — не лише `docs/`): промпт має вимагати
|
|
76
|
+
`npx @nitra/cursor change --bump <major|minor|patch> --section <Added|Changed|Fixed|Removed> --message "<опис>" [--ws <шлях>]`
|
|
77
|
+
і `npx @nitra/cursor fix changelog`. **Ніколи** не інструктуй ручне редагування
|
|
78
|
+
`CHANGELOG.md` чи bump `version` — це робить release flow / CI (деталь —
|
|
79
|
+
`.cursor/rules/n-changelog.mdc`, не дублюй її текст). Якщо потрібна реліз-нота —
|
|
80
|
+
це change-файл `<ws>/.changes/<timestamp>-<rand>.md` з `bump:` і `section:`,
|
|
81
|
+
який створює саме `change` CLI, а не редагування файлу вручну.
|
|
74
82
|
- **Як перевірити** — конкретні команди й специфічні до завдання сигнали
|
|
75
83
|
успіху.
|
|
76
84
|
|
|
@@ -185,6 +193,11 @@ description: >-
|
|
|
185
193
|
агенту**, не людині. Використовуй "зроби", "онови", "додай".
|
|
186
194
|
- **Без секцій-пустушок:** якщо немає `Обмежень` чи `Симптому` — пропусти
|
|
187
195
|
секцію цілком.
|
|
196
|
+
- **Без ручного changelog/version у промпті:** не формулюй у виводі інструкції
|
|
197
|
+
на кшталт "додати запис у `CHANGELOG.md`", "bump `version` вручну" чи
|
|
198
|
+
"оновити `package.json#version`". Зміни у workspace фіксуються винятково
|
|
199
|
+
через change-file flow (`npx @nitra/cursor change …` → `npx @nitra/cursor fix changelog`);
|
|
200
|
+
`version`/`CHANGELOG.md` формує CI.
|
|
188
201
|
- **Не вмикай у промпт:** секрети, `.env`, `node_modules`, бінарні файли,
|
|
189
202
|
довгі логи, дампи `tree`, повні JSON конфігів, цитати існуючих
|
|
190
203
|
helpers/функцій з CWD.
|
|
@@ -219,17 +232,25 @@ description: >-
|
|
|
219
232
|
# Точки правки
|
|
220
233
|
|
|
221
234
|
- `package.json:18` — `engines.node`
|
|
222
|
-
|
|
235
|
+
|
|
236
|
+
# Що треба зробити
|
|
237
|
+
|
|
238
|
+
- Підняти `engines.node` до `>=25`; якщо peer `eslint ^9` несумісний —
|
|
239
|
+
підняти range.
|
|
240
|
+
- Зафіксувати зміну change-файлом (НЕ редагувати `CHANGELOG.md` чи `version`
|
|
241
|
+
вручну): `npx @nitra/cursor change --bump minor --section Changed --message "engines.node >=25"`.
|
|
223
242
|
|
|
224
243
|
# Обмеження
|
|
225
244
|
|
|
226
|
-
- Дотриматись `.cursor/rules/n-js-lint.mdc`.
|
|
245
|
+
- Дотриматись `.cursor/rules/n-js-lint.mdc` і `.cursor/rules/n-changelog.mdc`
|
|
246
|
+
(зміни у workspace = change-файл, не ручний CHANGELOG/version bump).
|
|
227
247
|
- Якщо `eslint ^9` офіційно не підтримує Node 25 — підняти peer range.
|
|
228
248
|
|
|
229
249
|
# Як перевірити
|
|
230
250
|
|
|
231
251
|
- `bun test` — зелений
|
|
232
252
|
- `node -p "require('./package.json').engines.node"` → `>=25`
|
|
253
|
+
- `npx @nitra/cursor fix changelog` → exit `0`
|
|
233
254
|
```
|
|
234
255
|
````
|
|
235
256
|
|
|
@@ -21,80 +21,53 @@ description: >-
|
|
|
21
21
|
|
|
22
22
|
### 1. Зібрати список воркспейсів
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
### 2. Відфільтрувати воркспейси зі `start`-скриптом
|
|
27
|
-
|
|
28
|
-
Воркспейс перевіряється, лише якщо в його `package.json` є `scripts.start`. Решту — пропусти й познач у звіті як `SKIP (немає start)`.
|
|
29
|
-
|
|
30
|
-
### 3. Класифікувати `start` і зафіксувати стан репо
|
|
31
|
-
|
|
32
|
-
Скіл прогоняє **кожен** воркспейс зі `start` — запуск не пропускається. Але `start` буває двох типів, і це впливає і на трактування коду виходу, і на ризик побічних ефектів:
|
|
33
|
-
|
|
34
|
-
- **Запуск сервера/застосунку** (`vite`, `next dev`, `nuxt dev`, `node`/`bun <server>`, `nodemon` тощо) — успіх визначається за живучістю процесу.
|
|
35
|
-
- **CLI/разова дія** (зокрема ті, що мутують репо — sync, генерація коду, міграції, розгортання, перезапис конфігів) — успіх визначається за кодом виходу; такий `start` може **виконати реальну роботу** й лишити зміни в репозиторії.
|
|
36
|
-
|
|
37
|
-
Щоб прогін мутаційного `start` не лишив сміття, зафіксуй стан робочого дерева **перед** запусками:
|
|
24
|
+
> **Не парси `package.json`/glob вручну.** Розгортання воркспейсів і класифікацію `start` несе CLI.
|
|
38
25
|
|
|
39
26
|
```bash
|
|
40
|
-
|
|
27
|
+
n-cursor start-check scan
|
|
41
28
|
```
|
|
42
29
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
### 4. Прогнати `start` послідовно
|
|
30
|
+
Друкує JSON `[{ workspace, name, hasStart, startCmd, type }]` для root + усіх воркспейсів. `type` — `server` (dev-сервер/демон) чи `cli` (разова дія). Воркспейси з `hasStart:false` — `SKIP (немає start)` у звіті; решту прогоняй послідовно (крок 2).
|
|
46
31
|
|
|
47
|
-
|
|
32
|
+
### 2. Прогнати `start` кожного воркспейсу — по черзі
|
|
48
33
|
|
|
49
|
-
`
|
|
34
|
+
> **Не запускай `perl alarm` і не інтерпретуй exit-коди вручну.** CLI запускає процес із grace-таймаутом (крос-платформно через `spawnSync`), класифікує OK/FAIL за типом і парсить лог.
|
|
50
35
|
|
|
51
|
-
|
|
52
|
-
- **Короткий процес** (CLI, разова дія): успіх = вихід із кодом `0`.
|
|
53
|
-
|
|
54
|
-
macOS не має `timeout` як стандартної утиліти. Обмеж час через `perl` + `alarm`: `alarm` зводить SIGALRM, а `exec` зберігає той самий PID, тож таймер спрацьовує на справжньому процесі й знімає його після grace-періоду:
|
|
36
|
+
Для кожного воркспейсу з `hasStart:true` (**по одному, НЕ паралельно** — dev-сервери конфліктують за портами):
|
|
55
37
|
|
|
56
38
|
```bash
|
|
57
|
-
|
|
58
|
-
perl -e 'alarm shift; exec @ARGV' 12 bun run start > /tmp/n-start-check.log 2>&1
|
|
59
|
-
CODE=$?
|
|
39
|
+
n-cursor start-check run <workspace> # напр. demo (--grace <ms>, дефолт 12000)
|
|
60
40
|
```
|
|
61
41
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- `142` — процес дожив до кінця grace-періоду й був знятий SIGALRM (`128 + 14`). Для **довгого процесу** (dev-сервер) це **успіх** — стартував і не впав.
|
|
65
|
-
- `0` — процес завершився сам без помилки. Для **короткого процесу** (CLI, разова дія) це **успіх**.
|
|
66
|
-
- будь-що інше — **FAIL**: крах або ненульовий вихід ще до кінця grace-періоду.
|
|
67
|
-
|
|
68
|
-
Звір також лог: для dev-сервера — рядок готовності (`ready`, `listening`, `Local:`); для CLI — відсутність трас помилок.
|
|
69
|
-
|
|
70
|
-
Альтернатива `cd` — `bun run --filter '<package-name>' start` (запуск за іменем пакета).
|
|
42
|
+
Друкує JSON:
|
|
71
43
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
44
|
+
```
|
|
45
|
+
{ workspace, type, exitCode, timedOut, status, ready, firstError, logTail, sideEffects }
|
|
46
|
+
```
|
|
75
47
|
|
|
76
|
-
|
|
48
|
+
- `status: "OK"` — server дожив до кінця grace (`timedOut`) або встиг віддати рядок готовності; cli вийшов із кодом `0`.
|
|
49
|
+
- `status: "FAIL"` — крах/ненульовий код до grace. `firstError` + `logTail` — для діагностики.
|
|
50
|
+
- `sideEffects: { newFiles, changedTracked }` — read-only git-diff проти стану до запуску (для кроку 3).
|
|
77
51
|
|
|
78
|
-
|
|
79
|
-
- відстежувані файли, що стали зміненими, а у знімку були чисті — `git checkout -- <path>`;
|
|
80
|
-
- усе, що було брудним **до** прогону, — НЕ чіпай: це зміни користувача, не пов'язані з прогоном.
|
|
52
|
+
Розрізняй **помилку коду** (синтаксис, відсутній імпорт, падіння на ініціалізації) і **середовищний збій** (немає `.env`, недоступна БД/порт зайнятий) — останній не означає, що проєкт зламаний.
|
|
81
53
|
|
|
82
|
-
|
|
54
|
+
### 3. Відкотити побічні ефекти прогону
|
|
83
55
|
|
|
84
|
-
|
|
56
|
+
CLI **не** мутує дерево сам (відкат — деструктивний, лишається явним). Якщо `run` повернув непорожній `sideEffects`, відкоти **лише те, що зʼявилося через прогін**, перш ніж запускати наступний воркспейс:
|
|
85
57
|
|
|
86
|
-
|
|
58
|
+
- `sideEffects.newFiles` — нові невідстежувані файли/директорії → видали (`rm`);
|
|
59
|
+
- `sideEffects.changedTracked` — відстежувані файли, що стали зміненими → `git checkout -- <path>`.
|
|
87
60
|
|
|
88
|
-
|
|
89
|
-
- `FAIL` — крах або ненульовий код. Додай витяг із логу: останні значущі рядки з помилкою.
|
|
90
|
-
- `SKIP` — у воркспейсі немає `start`-скрипта (крок 2). Сам запуск `start` не пропускається.
|
|
61
|
+
Усе, що було брудним **до** прогону, у `sideEffects` не потрапляє (CLI рахує лише різницю) — зміни користувача не чіпай. Gitignored-артефакти (кеші, `node_modules`) у git-diff не зʼявляються.
|
|
91
62
|
|
|
92
|
-
|
|
63
|
+
### 4. Звіт
|
|
93
64
|
|
|
94
|
-
|
|
65
|
+
Підсумкова таблиця: `воркспейс | статус | примітка`, з даних `scan` + `run`:
|
|
95
66
|
|
|
96
|
-
|
|
67
|
+
- `OK` — `status:"OK"` (живий dev-сервер або вихід `0`).
|
|
68
|
+
- `FAIL` — `status:"FAIL"`; додай `firstError` / `logTail`.
|
|
69
|
+
- `SKIP` — `hasStart:false`.
|
|
97
70
|
|
|
98
|
-
|
|
71
|
+
Якщо `run` повернув непорожній `sideEffects` — познач у примітці: проєкт стартує, але `start` не є чистим запуском (мутує репо). Якщо є хоч один `FAIL` — винеси його окремо з повною помилкою.
|
|
99
72
|
|
|
100
73
|
Скіл лише **діагностує** запуск. Виправлення коду — поза скоупом; якщо причина FAIL очевидна, познач її у звіті, але не правь, поки про це не попросять окремо.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `n-cursor start-check scan|run` — детермінована частина smoke-перевірки
|
|
3
|
+
* bun-монорепо для скілу `n-start-check`.
|
|
4
|
+
*
|
|
5
|
+
* Мотивація: скіл раніше казав LLM-агенту вручну розгортати glob-воркспейси,
|
|
6
|
+
* читати кожен `package.json`, класифікувати `start` (сервер vs CLI), запускати
|
|
7
|
+
* процес із таймаутом через `perl alarm`, інтерпретувати exit-коди й парсити лог
|
|
8
|
+
* на рядки готовності/помилки. Усе це детерміновано — скрипт робить це надійно й
|
|
9
|
+
* крос-платформно (`spawnSync` з `timeout`), а агент лишається тільки з
|
|
10
|
+
* діагностикою: **чому** саме воркспейс упав.
|
|
11
|
+
*
|
|
12
|
+
* `scan` — `[{workspace, name, hasStart, startCmd, type}]`.
|
|
13
|
+
* `run <ws>` — `{workspace, type, exitCode, timedOut, status, ready, firstError,
|
|
14
|
+
* logTail, sideEffects}` (sideEffects — read-only git-diff проти стану до запуску;
|
|
15
|
+
* власне відкат лишається явним кроком скіла).
|
|
16
|
+
*/
|
|
17
|
+
import { spawnSync } from 'node:child_process'
|
|
18
|
+
import { existsSync } from 'node:fs'
|
|
19
|
+
import { readFile } from 'node:fs/promises'
|
|
20
|
+
import { join } from 'node:path'
|
|
21
|
+
|
|
22
|
+
import { getMonorepoPackageRootDirs } from '../../../scripts/lib/workspaces.mjs'
|
|
23
|
+
|
|
24
|
+
/** Маркери довгого процесу (dev-сервер/демон) у команді `start`. */
|
|
25
|
+
const SERVER_CMD_RE = /\b(vite|next|nuxt|nodemon|serve|astro|remix|webpack-dev-server|http-server)\b|\bdev\b|--watch/
|
|
26
|
+
/** Рядки готовності dev-сервера в логу (`\b` лише де треба — щоб «already» не матчив «ready»). */
|
|
27
|
+
const READY_RE = /\bready\b|\blistening\b|\bstarted\b|\bcompiled\b|server running|local:/i
|
|
28
|
+
/** Сигнатури помилок у логу. */
|
|
29
|
+
const ERROR_RE = /(error|exception|fatal|cannot find|module not found|unhandled|panic|traceback)/i
|
|
30
|
+
/** Скільки останніх рядків логу повертати. */
|
|
31
|
+
const LOG_TAIL_LINES = 15
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Класифікує `start`-команду: довгий процес (сервер) чи разова дія (CLI).
|
|
35
|
+
* @param {string} startCmd значення `scripts.start`
|
|
36
|
+
* @returns {'server'|'cli'} тип процесу
|
|
37
|
+
*/
|
|
38
|
+
export function classifyStartType(startCmd) {
|
|
39
|
+
return typeof startCmd === 'string' && SERVER_CMD_RE.test(startCmd) ? 'server' : 'cli'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Зчитує `package.json` воркспейсу або повертає null.
|
|
44
|
+
* @param {string} dir абсолютний шлях до каталогу воркспейсу
|
|
45
|
+
* @returns {Promise<object|null>} розпарсений package.json або null
|
|
46
|
+
*/
|
|
47
|
+
async function readPkg(dir) {
|
|
48
|
+
const path = join(dir, 'package.json')
|
|
49
|
+
if (!existsSync(path)) return null
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(await readFile(path, 'utf8'))
|
|
52
|
+
} catch {
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Сканує монорепо: для кожного воркспейсу — чи є `start`, його команда і тип.
|
|
59
|
+
* @param {string} cwd корінь репозиторію
|
|
60
|
+
* @returns {Promise<Array<{workspace:string, name:string|null, hasStart:boolean, startCmd:string|null, type:('server'|'cli'|null)}>>} список воркспейсів
|
|
61
|
+
*/
|
|
62
|
+
export async function scanStartWorkspaces(cwd) {
|
|
63
|
+
const roots = await getMonorepoPackageRootDirs(cwd)
|
|
64
|
+
const out = []
|
|
65
|
+
for (const ws of roots) {
|
|
66
|
+
const pkg = await readPkg(join(cwd, ws))
|
|
67
|
+
const startCmd = pkg?.scripts?.start ?? null
|
|
68
|
+
const hasStart = typeof startCmd === 'string' && startCmd.length > 0
|
|
69
|
+
out.push({
|
|
70
|
+
workspace: ws,
|
|
71
|
+
name: pkg?.name ?? null,
|
|
72
|
+
hasStart,
|
|
73
|
+
startCmd: hasStart ? startCmd : null,
|
|
74
|
+
type: hasStart ? classifyStartType(startCmd) : null
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
return out
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Парсить лог процесу: готовність (сервер), перша помилка, хвіст.
|
|
82
|
+
* @param {string} log обʼєднаний stdout+stderr
|
|
83
|
+
* @returns {{ready:boolean, firstError:string|null, logTail:string}} витяг
|
|
84
|
+
*/
|
|
85
|
+
export function parseStartLog(log) {
|
|
86
|
+
const text = log ?? ''
|
|
87
|
+
const lines = text.split('\n')
|
|
88
|
+
const firstError = lines.find(l => ERROR_RE.test(l))?.trim() ?? null
|
|
89
|
+
const logTail = lines
|
|
90
|
+
.filter(l => l.trim() !== '')
|
|
91
|
+
.slice(-LOG_TAIL_LINES)
|
|
92
|
+
.join('\n')
|
|
93
|
+
return { ready: READY_RE.test(text), firstError, logTail }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Read-only знімок `git status --porcelain` як множина рядків.
|
|
98
|
+
* @param {string} cwd корінь репозиторію
|
|
99
|
+
* @returns {Set<string>} рядки porcelain (статус + шлях)
|
|
100
|
+
*/
|
|
101
|
+
function gitPorcelain(cwd) {
|
|
102
|
+
const res = spawnSync('git', ['status', '--porcelain'], { cwd, encoding: 'utf8' })
|
|
103
|
+
if (res.status !== 0 || typeof res.stdout !== 'string') return new Set()
|
|
104
|
+
return new Set(res.stdout.split('\n').filter(l => l.trim() !== ''))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Обчислює побічні ефекти прогону як різницю git-станів до/після.
|
|
109
|
+
* @param {Set<string>} before знімок до
|
|
110
|
+
* @param {Set<string>} after знімок після
|
|
111
|
+
* @returns {{newFiles:string[], changedTracked:string[]}} нові untracked і ново-змінені tracked шляхи
|
|
112
|
+
*/
|
|
113
|
+
function diffSideEffects(before, after) {
|
|
114
|
+
const newFiles = []
|
|
115
|
+
const changedTracked = []
|
|
116
|
+
for (const line of after) {
|
|
117
|
+
if (before.has(line)) continue
|
|
118
|
+
const path = line.slice(3)
|
|
119
|
+
if (line.startsWith('??')) newFiles.push(path)
|
|
120
|
+
else changedTracked.push(path)
|
|
121
|
+
}
|
|
122
|
+
return { newFiles, changedTracked }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Запускає `start` одного воркспейсу з grace-таймаутом і класифікує результат.
|
|
127
|
+
* @param {string} cwd корінь репозиторію
|
|
128
|
+
* @param {string} workspace відносний шлях воркспейсу
|
|
129
|
+
* @param {{graceMs?:number, type?:('server'|'cli'), spawnImpl?:Function}} [opts] grace-період, тип (інакше з package.json), інʼєкція spawn для тестів
|
|
130
|
+
* @returns {Promise<{workspace:string, type:string, exitCode:number|null, timedOut:boolean, status:('OK'|'FAIL'), ready:boolean, firstError:string|null, logTail:string, sideEffects:{newFiles:string[], changedTracked:string[]}}>} результат прогону
|
|
131
|
+
*/
|
|
132
|
+
export async function runWorkspaceStart(cwd, workspace, opts = {}) {
|
|
133
|
+
const { graceMs = 12_000, spawnImpl = spawnSync } = opts
|
|
134
|
+
const dir = join(cwd, workspace)
|
|
135
|
+
const pkg = await readPkg(dir)
|
|
136
|
+
const startCmd = pkg?.scripts?.start
|
|
137
|
+
if (typeof startCmd !== 'string' || startCmd.length === 0) {
|
|
138
|
+
throw new Error(`У воркспейсі ${workspace} немає scripts.start`)
|
|
139
|
+
}
|
|
140
|
+
const type = opts.type ?? classifyStartType(startCmd)
|
|
141
|
+
|
|
142
|
+
const before = gitPorcelain(cwd)
|
|
143
|
+
const res = spawnImpl('bun', ['run', 'start'], {
|
|
144
|
+
cwd: dir,
|
|
145
|
+
encoding: 'utf8',
|
|
146
|
+
timeout: graceMs,
|
|
147
|
+
killSignal: 'SIGTERM'
|
|
148
|
+
})
|
|
149
|
+
const timedOut = res.error?.code === 'ETIMEDOUT' || res.signal === 'SIGTERM'
|
|
150
|
+
const exitCode = typeof res.status === 'number' ? res.status : null
|
|
151
|
+
const { ready, firstError, logTail } = parseStartLog(`${res.stdout ?? ''}${res.stderr ?? ''}`)
|
|
152
|
+
|
|
153
|
+
// server: успіх = дожив до кінця grace (timedOut) або встиг віддати рядок готовності.
|
|
154
|
+
// cli: успіх = чистий вихід 0 у межах grace.
|
|
155
|
+
const status = type === 'server' ? (timedOut || ready ? 'OK' : 'FAIL') : exitCode === 0 ? 'OK' : 'FAIL'
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
workspace,
|
|
159
|
+
type,
|
|
160
|
+
exitCode,
|
|
161
|
+
timedOut,
|
|
162
|
+
status,
|
|
163
|
+
ready,
|
|
164
|
+
firstError,
|
|
165
|
+
logTail,
|
|
166
|
+
sideEffects: diffSideEffects(before, gitPorcelain(cwd))
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const USAGE = 'Usage: n-cursor start-check <scan | run <workspace> [--grace <ms>]>'
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* CLI: `scan` друкує список воркспейсів зі `start`; `run <ws>` запускає один і
|
|
174
|
+
* друкує класифікований результат. Обидва — JSON у stdout.
|
|
175
|
+
* @param {string[]} args аргументи після `start-check`
|
|
176
|
+
* @param {string} [cwd] корінь репозиторію (інʼєкція для тестів)
|
|
177
|
+
* @returns {Promise<number>} exit code
|
|
178
|
+
*/
|
|
179
|
+
export async function runStartCheckCli(args, cwd = process.cwd()) {
|
|
180
|
+
const sub = args[0]
|
|
181
|
+
|
|
182
|
+
if (sub === 'scan') {
|
|
183
|
+
process.stdout.write(`${JSON.stringify(await scanStartWorkspaces(cwd))}\n`)
|
|
184
|
+
return 0
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (sub === 'run') {
|
|
188
|
+
const workspace = args[1] && !args[1].startsWith('--') ? args[1] : undefined
|
|
189
|
+
if (!workspace) {
|
|
190
|
+
console.error(USAGE)
|
|
191
|
+
return 1
|
|
192
|
+
}
|
|
193
|
+
const graceAt = args.indexOf('--grace')
|
|
194
|
+
const graceMs = graceAt === -1 ? undefined : Number(args[graceAt + 1])
|
|
195
|
+
if (graceAt !== -1 && (!Number.isFinite(graceMs) || graceMs <= 0)) {
|
|
196
|
+
console.error('✗ --grace очікує додатнє число (мс)')
|
|
197
|
+
return 1
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const result = await runWorkspaceStart(cwd, workspace, graceMs ? { graceMs } : {})
|
|
201
|
+
process.stdout.write(`${JSON.stringify(result)}\n`)
|
|
202
|
+
return 0
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error(`✗ ${error.message}`)
|
|
205
|
+
return 1
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.error(USAGE)
|
|
210
|
+
return 1
|
|
211
|
+
}
|
package/skills/taze/SKILL.md
CHANGED
|
@@ -43,9 +43,15 @@ bun install
|
|
|
43
43
|
|
|
44
44
|
### 3. Виявити major-оновлення
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
> **Не порівнюй `package.json` вручну.** Класифікацію semver несе CLI — детерміновано, по всіх воркспейсах.
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
```bash
|
|
49
|
+
n-cursor taze diff
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Друкує компактний JSON: `{ "major": [{workspace, pkg, from, to}], "minorPatch": <N>, "totalChanged": <N> }`. `major` — список залежностей, у яких змінилась найлівіша ненульова компонента semver (`1.x→2.x`, `0.4.x→0.5.x`, `0.0.3→0.0.4`); саме він іде в кроки 4–6. `minorPatch` — лічба сумісних (для звіту в кроці 8).
|
|
53
|
+
|
|
54
|
+
Покриває **прямі** залежності з `package.json` (root + воркспейси). Транзитивні major-стрибки (`bun.lock`) — за потреби переглянь окремо; основний ризик breaking-змін — у прямих.
|
|
49
55
|
|
|
50
56
|
### 4. Зібрати breaking changes по кожному major-оновленню
|
|
51
57
|
|
|
@@ -94,7 +100,7 @@ rm package.json.taze-bak bun.lock.taze-bak
|
|
|
94
100
|
|
|
95
101
|
Коротко в одному повідомленні:
|
|
96
102
|
|
|
97
|
-
- **Оновлено (minor/patch):** кількість
|
|
103
|
+
- **Оновлено (minor/patch):** кількість пакетів (`minorPatch` із кроку 3), без деталей.
|
|
98
104
|
- **Major-оновлення:** список `<name>: <old> → <new>` з посиланням на release notes.
|
|
99
105
|
- **Зрефакторено автоматично:** список файлів і коротко що саме змінено.
|
|
100
106
|
- **Потребує ручного втручання:** список TODO з причиною (нетривіальна міграція / неоднозначність / падіння тестів).
|