@nitra/cursor 5.1.0 → 5.2.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/.claude-template/settings.template.json +22 -0
- package/.pi-template/extensions/n-cursor-adr/docs/index.md +15 -9
- package/CHANGELOG.md +12 -1
- package/bin/n-cursor.js +73 -16
- package/docs/stryker.config.md +6 -0
- package/docs/vitest.config.md +6 -0
- package/lib/docs/llm.md +29 -0
- package/lib/docs/omlx.md +32 -0
- package/lib/llm.mjs +137 -0
- package/lib/omlx.mjs +49 -4
- package/package.json +1 -1
- package/rules/abie/docs/fix.md +6 -0
- package/rules/abie/js/docs/applies.md +6 -0
- package/rules/abie/js/docs/env_dns.md +25 -22
- package/rules/abie/js/docs/firebase_hosting.md +6 -0
- package/rules/abie/js/docs/hc_pairing.md +21 -25
- package/rules/abie/js/docs/ua_http_route.md +27 -19
- package/rules/abie/js/docs/ua_node_selector.md +24 -19
- package/rules/abie/lib/docs/enabled.md +13 -7
- package/rules/abie/lib/docs/env-dns.md +9 -3
- package/rules/abie/lib/docs/hc-yaml.md +6 -0
- package/rules/abie/lib/docs/http-route.md +6 -0
- package/rules/abie/lib/docs/k8s-tree.md +6 -0
- package/rules/abie/lib/docs/kustomization-patches.md +6 -0
- package/rules/abie/lib/docs/overlay-paths.md +6 -0
- package/rules/abie/lib/docs/yaml.md +6 -0
- package/rules/adr/docs/fix.md +6 -0
- package/rules/adr/js/docs/hooks.md +29 -244
- package/rules/bun/docs/fix.md +6 -0
- package/rules/bun/js/docs/layout.md +37 -375
- package/rules/capacitor/docs/fix.md +22 -108
- package/rules/capacitor/js/docs/platforms.md +62 -268
- package/rules/changelog/docs/fix.md +6 -0
- package/rules/changelog/lib/docs/package-manifest.md +6 -0
- package/rules/ci4/docs/fix.md +23 -165
- package/rules/ci4/js/docs/marksman_config.md +9 -1
- package/rules/docker/docs/fix.md +6 -0
- package/rules/docker/js/docs/lint.md +55 -239
- package/rules/docker/lib/docs/docker-hadolint.md +6 -0
- package/rules/docker/lib/docs/docker-mirror.md +6 -0
- package/rules/docker/lib/docs/docker-native-addon.md +6 -0
- package/rules/docker/lib/docs/docker-nginx-user.md +6 -0
- package/rules/docker/lint/docs/lint.md +9 -1
- package/rules/efes/docs/fix.md +6 -0
- package/rules/ga/lint/docs/lint.md +6 -0
- package/rules/graphql/docs/fix.md +6 -0
- package/rules/graphql/lib/docs/graphql-gql-scan.md +6 -0
- package/rules/image-avif/docs/fix.md +6 -0
- package/rules/image-avif/js/docs/avif_generation.md +6 -0
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +9 -3
- package/rules/js-bun-redis/lib/docs/redis-imports.md +6 -0
- package/rules/js-lint/js/docs/utils_imports.md +6 -0
- package/rules/js-lint-ci/docs/fix.md +7 -1
- package/rules/js-mssql/docs/fix.md +6 -0
- package/rules/js-mssql/lib/docs/mssql-pool-scan.md +6 -0
- package/rules/js-run/docs/fix.md +6 -0
- package/rules/js-run/lib/docs/bunyan-imports.md +6 -0
- package/rules/js-run/lib/docs/check-env-scan.md +6 -0
- package/rules/js-run/lib/docs/conn-file-rules.md +6 -0
- package/rules/js-run/lib/docs/conn-imports-scan.md +6 -0
- package/rules/js-run/lib/docs/promise-settimeout-scan.md +6 -0
- package/rules/js-run/lib/docs/temporal-scan.md +6 -0
- package/rules/k8s/docs/fix.md +6 -0
- package/rules/k8s/lint/docs/lint.md +6 -0
- package/rules/nginx-default-tpl/docs/fix.md +6 -0
- package/rules/npm-module/js/docs/header_doc_pointer.md +7 -0
- package/rules/npm-module/js/header_doc_pointer.mjs +2 -8
- package/rules/php/docs/fix.md +6 -0
- package/rules/php/lint/docs/lint.md +6 -0
- package/rules/python/docs/fix.md +6 -0
- package/rules/python/lint/docs/lint.md +6 -0
- package/rules/rego/lint/docs/lint.md +6 -0
- package/rules/release/docs/change.md +6 -0
- package/rules/release/docs/fix.md +6 -0
- package/rules/release/docs/release.md +6 -0
- package/rules/release/lib/docs/aggregate.md +6 -0
- package/rules/release/lib/docs/change-file.md +6 -0
- package/rules/release/lib/docs/fallback.md +6 -0
- package/rules/rust/lib/docs/has-cargo-toml.md +6 -0
- package/rules/security/docs/fix.md +7 -1
- package/rules/security/js/docs/lint.md +6 -0
- package/rules/style-lint/docs/fix.md +6 -0
- package/rules/tauri/docs/fix.md +6 -0
- package/rules/test/docs/fix.md +6 -0
- package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +6 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +6 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +6 -0
- package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +6 -0
- package/rules/text/docs/fix.md +6 -0
- package/rules/text/lint/docs/lint.md +6 -0
- package/rules/text/lint/docs/run-dotenv-linter.md +6 -0
- package/rules/text/lint/docs/run-shellcheck.md +6 -0
- package/rules/text/lint/docs/run-v8r.md +6 -0
- package/rules/vue/lib/docs/vue-forbidden-imports.md +6 -0
- package/scripts/coverage-classify/cache.mjs +1 -1
- package/scripts/coverage-classify/docs/apply.md +6 -0
- package/scripts/coverage-classify/docs/cache.md +6 -0
- package/scripts/coverage-classify/docs/prompt.md +6 -0
- package/scripts/coverage-classify/docs/verdict-schema.md +6 -0
- package/scripts/coverage-classify/prompt.mjs +1 -1
- package/scripts/coverage-fix-extract.mjs +1 -1
- package/scripts/coverage-fix.mjs +2 -1
- package/scripts/docs/auto-skills.md +6 -0
- package/scripts/docs/build-agents-commands.md +7 -1
- package/scripts/docs/cli-entry.md +6 -0
- package/scripts/docs/coverage-fix-extract.md +6 -0
- package/scripts/docs/coverage-fix.md +6 -0
- package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +6 -0
- package/scripts/docs/lint-cli.md +6 -0
- package/scripts/docs/post-tool-use-fix.md +6 -0
- package/scripts/docs/rename-yaml-extensions.md +6 -0
- package/scripts/docs/skills-cli.md +6 -0
- package/scripts/docs/sync-setup-bun-deps-action.md +6 -0
- package/scripts/docs/upgrade-nitra-cursor-and-install.md +6 -0
- package/scripts/docs/worktree-cli.md +6 -0
- package/scripts/lib/docs/assert-project-root.md +6 -0
- package/scripts/lib/docs/check-mdc-template-refs.md +6 -0
- package/scripts/lib/docs/check-reporter.md +6 -0
- package/scripts/lib/docs/diff-added-lines.md +6 -0
- package/scripts/lib/docs/discover-check-rules-from-cursor.md +6 -0
- package/scripts/lib/docs/discover-checkable-rules.md +6 -0
- package/scripts/lib/docs/ensure-tool.md +6 -0
- package/scripts/lib/docs/generated-markdown.md +6 -0
- package/scripts/lib/docs/gha-workflow.md +6 -0
- package/scripts/lib/docs/inline-template-links.md +6 -0
- package/scripts/lib/docs/list-rule-ids.md +6 -0
- package/scripts/lib/docs/load-cursor-config.md +6 -0
- package/scripts/lib/docs/mirror-parity.md +6 -0
- package/scripts/lib/docs/read-n-cursor-config-lite.md +6 -0
- package/scripts/lib/docs/resolve-target-files.md +6 -0
- package/scripts/lib/docs/root-notice.md +6 -0
- package/scripts/lib/docs/rule-meta-helpers.md +6 -0
- package/scripts/lib/docs/rule-meta.md +6 -0
- package/scripts/lib/docs/run-conftest-batch.md +6 -0
- package/scripts/lib/docs/run-lint-step.md +6 -0
- package/scripts/lib/docs/run-rule-cli.md +6 -0
- package/scripts/lib/docs/run-rule.md +6 -0
- package/scripts/lib/docs/run-standard-lint.md +6 -0
- package/scripts/lib/docs/run-standard-rule.md +6 -0
- package/scripts/lib/docs/skill-meta.md +6 -0
- package/scripts/lib/docs/template.md +6 -0
- package/scripts/lib/docs/timing-summary.md +6 -0
- package/scripts/lib/docs/workspaces.md +6 -0
- package/scripts/lib/docs/worktree-notice.md +6 -0
- package/scripts/lib/docs/worktree.md +6 -0
- package/scripts/lib/mirror-parity.mjs +1 -1
- package/scripts/lib/root-notice.mjs +1 -1
- package/scripts/lib/worktree-notice.mjs +5 -5
- package/scripts/lib/worktree.mjs +1 -1
- package/scripts/sync-claude-config.mjs +3 -0
- package/scripts/utils/docs/ast-scan-utils.md +6 -0
- package/scripts/utils/docs/ensure-gitignore-entries.md +6 -0
- package/scripts/utils/docs/find-package-json-paths.md +6 -0
- package/scripts/utils/docs/lock-cache-dir.md +6 -0
- package/scripts/utils/docs/pass.md +6 -0
- package/scripts/utils/docs/resolve-cargo-manifest.md +6 -0
- package/scripts/utils/docs/resolve-cmd.md +6 -0
- package/scripts/utils/docs/resolve-js-root.md +6 -0
- package/scripts/utils/docs/test-helpers.md +6 -0
- package/scripts/utils/docs/walk-cache.md +6 -0
- package/scripts/utils/docs/walkDir.md +6 -0
- package/scripts/utils/docs/worktree-fingerprint.md +6 -0
- package/scripts/utils/resolve-js-root.mjs +1 -1
- package/skills/doc-aggregate/SKILL.md +129 -0
- package/skills/doc-aggregate/js/docgen-ignore.mjs +9 -0
- package/skills/{docgen → doc-aggregate}/js/docgen-scan.mjs +22 -67
- package/skills/doc-aggregate/js/docs/docgen-ignore.md +21 -0
- package/skills/doc-files/SKILL.md +100 -0
- package/skills/doc-files/js/docgen-crc.mjs +164 -0
- package/skills/{docgen → doc-files}/js/docgen-extract-anchors.mjs +20 -11
- package/skills/{docgen → doc-files}/js/docgen-extract.mjs +15 -9
- package/skills/doc-files/js/docgen-files-batch.mjs +181 -0
- package/skills/doc-files/js/docgen-gen.mjs +291 -0
- package/skills/{docgen → doc-files}/js/docgen-prompts.mjs +43 -40
- package/skills/doc-files/js/docgen-scan.mjs +298 -0
- package/skills/doc-files/js/docs/docgen-crc.md +32 -0
- package/skills/doc-files/js/docs/docgen-extract-anchors.md +27 -0
- package/skills/doc-files/js/docs/docgen-extract.md +29 -0
- package/skills/doc-files/js/docs/docgen-files-batch.md +25 -0
- package/skills/doc-files/js/docs/docgen-gen.md +30 -0
- package/skills/doc-files/js/docs/docgen-prompts.md +32 -0
- package/skills/doc-files/js/docs/docgen-scan.md +25 -0
- package/skills/doc-files/meta.json +1 -0
- package/skills/fix/js/docs/llm-worker.md +6 -0
- package/skills/fix/js/docs/orchestrator.md +6 -0
- package/skills/fix/js/llm-worker.mjs +3 -3
- package/skills/fix/js/orchestrator.mjs +1 -1
- package/skills/start-check/js/check.mjs +5 -3
- package/skills/start-check/js/docs/check.md +6 -0
- package/skills/docgen/SKILL.md +0 -224
- package/skills/docgen/bench/etalon/firebase_hosting.md +0 -19
- package/skills/docgen/bench/etalon/k8s-tree.md +0 -24
- package/skills/docgen/bench/etalon/overlay-paths.md +0 -24
- package/skills/docgen/js/docgen-batch-omlx.mjs +0 -82
- package/skills/docgen/js/docgen-batch.mjs +0 -95
- package/skills/docgen/js/docgen-compare-pi-vs-direct.mjs +0 -95
- package/skills/docgen/js/docgen-gen.mjs +0 -306
- package/skills/docgen/js/docs/docgen-extract.md +0 -28
- package/skills/docgen/js/docs/docgen-gen.md +0 -41
- package/skills/docgen/js/docs/docgen-ignore.md +0 -24
- package/skills/docgen/js/docs/docgen-prompts.md +0 -24
- package/skills/docgen/js/docs/docgen-scan.md +0 -48
- /package/skills/{docgen → doc-aggregate}/meta.json +0 -0
- /package/skills/{docgen → doc-files}/js/docgen-ignore.mjs +0 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/** @see ./docs/docgen-scan.md */
|
|
2
|
+
// eslint-disable-next-line unicorn/import-style
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { existsSync, readdirSync, statSync } from 'node:fs'
|
|
5
|
+
import { execFileSync } from 'node:child_process'
|
|
6
|
+
import { once } from 'node:events'
|
|
7
|
+
import { env } from 'node:process'
|
|
8
|
+
|
|
9
|
+
import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
|
|
10
|
+
import { isDocgenIgnored } from './docgen-ignore.mjs'
|
|
11
|
+
import { QUALITY_THRESHOLD, readDocQuality, staleness } from './docgen-crc.mjs'
|
|
12
|
+
|
|
13
|
+
/** Кодові розширення, для яких генеруємо документацію. */
|
|
14
|
+
const SOURCE_EXTENSIONS = new Set(['.js', '.mjs', '.ts', '.vue', '.py'])
|
|
15
|
+
|
|
16
|
+
/** `*.test.*`, `*.spec.*` — тести, документувати не треба. */
|
|
17
|
+
const TEST_FILE_RE = /\.(?:test|spec)\.[^.]+$/u
|
|
18
|
+
|
|
19
|
+
/** Поріг великого прогону для Stop-гейта: більше stale-файлів — не блокуємо. */
|
|
20
|
+
const DEFAULT_GATE_MAX = Number(env.N_CURSOR_DOC_FILES_GATE_MAX ?? 50) || 50
|
|
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
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Чи є файл кодовим джерелом для документування.
|
|
35
|
+
* @param {string} fileName базове ім'я файлу
|
|
36
|
+
* @returns {boolean} true — документуємо; false — пропускаємо
|
|
37
|
+
*/
|
|
38
|
+
export function isSourceFile(fileName) {
|
|
39
|
+
if (fileName.endsWith('.d.ts')) return false
|
|
40
|
+
if (TEST_FILE_RE.test(fileName)) return false
|
|
41
|
+
return SOURCE_EXTENSIONS.has(path.extname(fileName))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Обчислює шлях md-документа для кодового файлу: тека `docs/` поряд із джерелом.
|
|
46
|
+
* Якщо `sourcePath` відносний, `docPath` теж відносний; якщо абсолютний — абсолютний.
|
|
47
|
+
* @param {string} sourcePath шлях до джерела (відносний або абсолютний)
|
|
48
|
+
* @returns {string} шлях до `<dir>/docs/<stem>.md` у тому ж просторі шляхів
|
|
49
|
+
*/
|
|
50
|
+
export function docPathForSource(sourcePath) {
|
|
51
|
+
const dir = path.dirname(sourcePath)
|
|
52
|
+
const stem = path.basename(sourcePath, path.extname(sourcePath))
|
|
53
|
+
return path.join(dir, 'docs', `${stem}.md`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Чи кодовий файл `relPath` (posix, від кореня) підлягає документуванню:
|
|
58
|
+
* правильне розширення, не тест, не в ignore-дереві, не кореневий system-wide docs.
|
|
59
|
+
* @param {string} root абсолютний корінь
|
|
60
|
+
* @param {string} relPath posix-шлях файлу від кореня
|
|
61
|
+
* @returns {boolean} true — кандидат на доку
|
|
62
|
+
*/
|
|
63
|
+
export function isDocCandidate(root, relPath) {
|
|
64
|
+
const fileName = path.posix.basename(relPath)
|
|
65
|
+
if (!isSourceFile(fileName)) return false
|
|
66
|
+
if (isSystemWideDocsRoot(root) && path.posix.dirname(relPath) === '.') return false
|
|
67
|
+
return !isDocgenIgnored(relPath)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Описує один кодовий файл: шлях джерела, шлях доки, стан застарілості за CRC.
|
|
72
|
+
* @param {string} root абсолютний корінь
|
|
73
|
+
* @param {string} sourcePath posix-шлях джерела від кореня
|
|
74
|
+
* @returns {{sourcePath:string, docPath:string, stale:boolean, reason:'missing'|'crc-mismatch'|null}} опис файлу
|
|
75
|
+
*/
|
|
76
|
+
export function describeFile(root, sourcePath) {
|
|
77
|
+
const docPath = docPathForSource(sourcePath)
|
|
78
|
+
const { stale, reason } = staleness(path.join(root, sourcePath), path.join(root, docPath))
|
|
79
|
+
return { sourcePath, docPath, stale, reason }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Рекурсивно обходить дерево від `root`, повертає кодові файли зі станом застарілості.
|
|
84
|
+
* Синхронний `readdirSync` — детермінований порядок без гонок; обсяг дерева це дозволяє.
|
|
85
|
+
* @param {string} root абсолютний корінь обходу
|
|
86
|
+
* @returns {Array<{sourcePath:string, docPath:string, stale:boolean, reason:'missing'|'crc-mismatch'|null}>} кандидати з відносними шляхами
|
|
87
|
+
*/
|
|
88
|
+
export function scanForDocFiles(root) {
|
|
89
|
+
const results = []
|
|
90
|
+
|
|
91
|
+
/** @param {string} dir поточний каталог обходу */
|
|
92
|
+
function walk(dir) {
|
|
93
|
+
let entries
|
|
94
|
+
try {
|
|
95
|
+
entries = readdirSync(dir, { withFileTypes: true })
|
|
96
|
+
} catch {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
const fullPath = path.join(dir, entry.name)
|
|
101
|
+
const relPath = path.relative(root, fullPath)
|
|
102
|
+
if (entry.isDirectory()) {
|
|
103
|
+
if (isDocgenIgnored(relPath, 'dir')) continue
|
|
104
|
+
walk(fullPath)
|
|
105
|
+
} else if (entry.isFile() && isSourceFile(entry.name)) {
|
|
106
|
+
if (isSystemWideDocsRoot(root) && path.dirname(relPath) === '.') continue
|
|
107
|
+
const sourcePath = relPath.split(path.sep).join('/')
|
|
108
|
+
if (isDocgenIgnored(sourcePath)) continue
|
|
109
|
+
results.push(describeFile(root, sourcePath))
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
walk(root)
|
|
115
|
+
return results
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Парсить `--root <dir>` з argv; default — cwd.
|
|
120
|
+
* @param {string[]} argv аргументи після підкоманди
|
|
121
|
+
* @returns {string} абсолютний корінь
|
|
122
|
+
*/
|
|
123
|
+
export function resolveRoot(argv) {
|
|
124
|
+
const i = argv.indexOf('--root')
|
|
125
|
+
return i !== -1 && argv[i + 1] ? path.resolve(argv[i + 1]) : process.cwd()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Сканує дерево і друкує JSON-масив усіх кодових файлів зі станом застарілості.
|
|
130
|
+
* Рішення «генерувати лише stale чи всі» приймає скіл, фільтруючи поле `stale`.
|
|
131
|
+
* @param {string[]} argv аргументи після назви субкоманди
|
|
132
|
+
* @returns {number} exit-код: 0 — успіх, 1 — корінь не існує
|
|
133
|
+
*/
|
|
134
|
+
export function runDocFilesScanCli(argv) {
|
|
135
|
+
const root = resolveRoot(argv)
|
|
136
|
+
if (!existsSync(root) || !statSync(root).isDirectory()) {
|
|
137
|
+
console.error(`doc-files scan: корінь не існує або не є директорією: ${root}`)
|
|
138
|
+
return 1
|
|
139
|
+
}
|
|
140
|
+
console.log(JSON.stringify(scanForDocFiles(root), null, 2))
|
|
141
|
+
return 0
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Зчитує stdin до EOF як utf8 рядок. На TTY — повертає `''` одразу.
|
|
146
|
+
* @returns {Promise<string>} вміст stdin
|
|
147
|
+
*/
|
|
148
|
+
async function readStdin() {
|
|
149
|
+
if (process.stdin.isTTY) return ''
|
|
150
|
+
process.stdin.setEncoding('utf8')
|
|
151
|
+
const chunks = []
|
|
152
|
+
process.stdin.on('data', chunk => chunks.push(chunk))
|
|
153
|
+
try {
|
|
154
|
+
await once(process.stdin, 'end')
|
|
155
|
+
} catch {
|
|
156
|
+
// 'error' на stdin — повертаємо те, що встигли зібрати
|
|
157
|
+
}
|
|
158
|
+
return chunks.join('')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Дістає `tool_input.file_path` зі stdin JSON Claude Code PostToolUse hook.
|
|
163
|
+
* @param {string} stdinJson сирий вміст stdin
|
|
164
|
+
* @returns {string|null} відносний шлях або null
|
|
165
|
+
*/
|
|
166
|
+
function extractHookFilePath(stdinJson) {
|
|
167
|
+
if (!stdinJson) return null
|
|
168
|
+
try {
|
|
169
|
+
const fp = JSON.parse(stdinJson)?.tool_input?.file_path
|
|
170
|
+
return typeof fp === 'string' && fp !== '' ? fp : null
|
|
171
|
+
} catch {
|
|
172
|
+
return null
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Список змінених у задачі джерел — найшвидший спосіб: `git diff --name-only HEAD`
|
|
178
|
+
* (working-tree проти HEAD). Допускаємо неповне покриття (закомічене в межах задачі
|
|
179
|
+
* випадає) — це свідомий компроміс; CRC лишається джерелом правди про застарілість.
|
|
180
|
+
* @param {string} root абсолютний корінь
|
|
181
|
+
* @returns {string[]} posix-шляхи кодових файлів-кандидатів, що існують
|
|
182
|
+
*/
|
|
183
|
+
function gitChangedSources(root) {
|
|
184
|
+
let out
|
|
185
|
+
try {
|
|
186
|
+
out = execFileSync('git', ['diff', '--name-only', 'HEAD'], { cwd: root, encoding: 'utf8' })
|
|
187
|
+
} catch {
|
|
188
|
+
return []
|
|
189
|
+
}
|
|
190
|
+
return out
|
|
191
|
+
.split('\n')
|
|
192
|
+
.map(s => s.trim())
|
|
193
|
+
.filter(rel => rel && isDocCandidate(root, rel) && existsSync(path.join(root, rel)))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Нормалізує абсолютний/відносний шлях до posix-шляху від кореня (або null поза деревом).
|
|
198
|
+
* @param {string} root абсолютний корінь
|
|
199
|
+
* @param {string} candidate шлях-кандидат
|
|
200
|
+
* @returns {string|null} posix-шлях від кореня
|
|
201
|
+
*/
|
|
202
|
+
function toRelSource(root, candidate) {
|
|
203
|
+
const rel = path.relative(root, path.resolve(root, candidate))
|
|
204
|
+
if (rel.startsWith('..') || path.isAbsolute(rel)) return null
|
|
205
|
+
return rel.split(path.sep).join('/')
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* `doc-files check --degraded` — інформаційний список свіжих за CRC док зі
|
|
210
|
+
* `score < QUALITY_THRESHOLD` (локальний конвеєр не дотягнув; ADR 260610-2228).
|
|
211
|
+
* Не блокує (exit 0): degraded — борг для `gen --retry-degraded`, а не гейт.
|
|
212
|
+
* @param {string} root абсолютний корінь
|
|
213
|
+
* @returns {number} exit-код: завжди 0
|
|
214
|
+
*/
|
|
215
|
+
function runDegradedReport(root) {
|
|
216
|
+
const degraded = []
|
|
217
|
+
for (const f of scanForDocFiles(root)) {
|
|
218
|
+
if (f.stale) continue
|
|
219
|
+
const { score, issues } = readDocQuality(path.join(root, f.docPath))
|
|
220
|
+
if (score !== null && score < QUALITY_THRESHOLD) degraded.push({ ...f, score, issues })
|
|
221
|
+
}
|
|
222
|
+
if (degraded.length === 0) {
|
|
223
|
+
console.log(`✓ doc-files: degraded-док немає (поріг ${QUALITY_THRESHOLD}).`)
|
|
224
|
+
return 0
|
|
225
|
+
}
|
|
226
|
+
const list = degraded
|
|
227
|
+
.map(f => {
|
|
228
|
+
const issuesTxt = f.issues.length ? ': ' + f.issues.join(',') : ''
|
|
229
|
+
return ` - ${f.sourcePath} (score=${f.score}${issuesTxt})`
|
|
230
|
+
})
|
|
231
|
+
.join('\n')
|
|
232
|
+
console.log(
|
|
233
|
+
`⚠ doc-files: degraded-док ${degraded.length} (score < ${QUALITY_THRESHOLD}):\n${list}\n→ перегенеруй: npx @nitra/cursor doc-files gen --retry-degraded`
|
|
234
|
+
)
|
|
235
|
+
return 0
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* `doc-files check` — детермінований детектор застарілості для hook'ів і CLI.
|
|
240
|
+
*
|
|
241
|
+
* Режими:
|
|
242
|
+
* - `--hook` — PostToolUse: бере `file_path` зі stdin JSON, перевіряє один файл.
|
|
243
|
+
* - `--git` — Stop-гейт: перевіряє `git diff --name-only HEAD`. Поріг `--max N`
|
|
244
|
+
* (default 50): якщо stale більше — не блокуємо (exit 0 + попередження).
|
|
245
|
+
* - `--degraded` — інформаційний звіт по доках зі score нижче порогу (exit 0).
|
|
246
|
+
* - `<paths…>` — явні шляхи-джерела.
|
|
247
|
+
*
|
|
248
|
+
* Exit 2 (стале знайдено) — для hook'а це блок/нагадування Claude; exit 0 — все свіже
|
|
249
|
+
* або великий прогін понад поріг.
|
|
250
|
+
* @param {string[]} argv аргументи після назви субкоманди
|
|
251
|
+
* @returns {Promise<number>} exit-код (0 / 2)
|
|
252
|
+
*/
|
|
253
|
+
export async function runDocFilesCheckCli(argv) {
|
|
254
|
+
const root = resolveRoot(argv)
|
|
255
|
+
if (argv.includes('--degraded')) return runDegradedReport(root)
|
|
256
|
+
const hookMode = argv.includes('--hook')
|
|
257
|
+
const gitMode = argv.includes('--git')
|
|
258
|
+
const maxIdx = argv.indexOf('--max')
|
|
259
|
+
const gateMax = maxIdx !== -1 && argv[maxIdx + 1] ? Number(argv[maxIdx + 1]) || DEFAULT_GATE_MAX : DEFAULT_GATE_MAX
|
|
260
|
+
|
|
261
|
+
let sources
|
|
262
|
+
if (hookMode) {
|
|
263
|
+
const fp = extractHookFilePath(await readStdin())
|
|
264
|
+
const rel = fp ? toRelSource(root, fp) : null
|
|
265
|
+
sources = rel && isDocCandidate(root, rel) && existsSync(path.join(root, rel)) ? [rel] : []
|
|
266
|
+
} else if (gitMode) {
|
|
267
|
+
sources = gitChangedSources(root)
|
|
268
|
+
} else {
|
|
269
|
+
sources = argv
|
|
270
|
+
.filter(a => !a.startsWith('--') && a !== argv[maxIdx + 1])
|
|
271
|
+
.map(a => toRelSource(root, a))
|
|
272
|
+
.filter(rel => rel && isDocCandidate(root, rel) && existsSync(path.join(root, rel)))
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const stale = sources.map(src => describeFile(root, src)).filter(f => f.stale)
|
|
276
|
+
if (stale.length === 0) return 0
|
|
277
|
+
|
|
278
|
+
// Великий прогін: Stop-гейт не блокує, лише попереджає (захист від нескінченного блоку).
|
|
279
|
+
if (gitMode && stale.length > gateMax) {
|
|
280
|
+
console.error(
|
|
281
|
+
`⚠ doc-files: застарілих док ${stale.length} (> ${gateMax}) — гейт не блокує. Запусти масовий прогін:\n npx @nitra/cursor doc-files gen`
|
|
282
|
+
)
|
|
283
|
+
return 0
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const list = stale.map(f => ` - ${f.sourcePath} (${f.reason})`).join('\n')
|
|
287
|
+
console.error(
|
|
288
|
+
`✗ doc-files: документація застаріла/відсутня для ${stale.length} файл(ів):\n${list}\n→ перегенеруй: /doc-files`
|
|
289
|
+
)
|
|
290
|
+
return 2
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (isRunAsCli(import.meta.url)) {
|
|
294
|
+
// Прямий запуск: `node skills/doc-files/js/docgen-scan.mjs [scan|check] [args]`
|
|
295
|
+
const [sub, ...rest] = process.argv.slice(2)
|
|
296
|
+
const argv = sub === 'scan' || sub === 'check' ? rest : process.argv.slice(2)
|
|
297
|
+
process.exitCode = sub === 'check' ? await runDocFilesCheckCli(argv) : runDocFilesScanCli(argv)
|
|
298
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/skills/doc-files/js/docgen-crc.mjs
|
|
4
|
+
crc: 54e8e12b
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# docgen-crc
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
Детермінований маркер актуальності файлових док: контрольна сума джерела у frontmatter плюс опційний degraded-маркер якості. Єдине джерело правди про «дока свіжа/застаріла/неякісна» для генерації, перевірок і хуків.
|
|
12
|
+
|
|
13
|
+
## Поведінка
|
|
14
|
+
|
|
15
|
+
1. Контрольна сума обчислюється з байтів джерела і записується у машинний frontmatter доки разом зі шляхом джерела; розбіжність суми з поточним джерелом (або відсутність доки) означає застарілість.
|
|
16
|
+
2. Якщо генерація не дотягнула до порогу якості, frontmatter додатково несе оцінку (`score`) і коди проблем (`issues`); коди нормалізуються до YAML-безпечних (без пробілів, обмежена кількість), а старі доки без цих полів лишаються валідними.
|
|
17
|
+
3. Поріг degraded — `70`, override через `N_CURSOR_DOC_FILES_THRESHOLD`.
|
|
18
|
+
4. Перештампування знімає наявний frontmatter і ставить свіжий, не торкаючись тіла документа; якість при цьому передається явно — без неї поля якості зникають.
|
|
19
|
+
|
|
20
|
+
## Публічний API
|
|
21
|
+
|
|
22
|
+
- `crc32` — контрольна сума вмісту в hex.
|
|
23
|
+
- `staleness` — стан доки відносно джерела: `missing` / `crc-mismatch` / свіжа.
|
|
24
|
+
- `parseDocFrontmatter` / `buildDocFrontmatter` / `stampDoc` — читання і (пере)штампування машинного блока.
|
|
25
|
+
- `readDocCrc` / `readDocQuality` — точкове читання суми та якості з доки.
|
|
26
|
+
- `QUALITY_THRESHOLD` — чинний поріг degraded.
|
|
27
|
+
|
|
28
|
+
## Гарантії поведінки
|
|
29
|
+
|
|
30
|
+
- Сума не залежить від git-стану: rebase, гілки й незакомічені зміни на неї не впливають.
|
|
31
|
+
- Frontmatter — єдиний машинний виняток із правила «чистий Markdown»; тіло доки модуль не редагує.
|
|
32
|
+
- Відсутні поля якості читаються як «не оцінено» (`score: null`), а не як нуль.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/skills/doc-files/js/docgen-extract-anchors.mjs
|
|
4
|
+
crc: e80e0827
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# docgen-extract-anchors
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
Детермінований витяг «анкорів» — конкретних фрагментів коду, які модель зобов'язана згадати в документації, щоб не зісковзнути на generic-фрази. Анкори підставляються у промпти окремим блоком обов'язкового включення, а їх покриття перевіряється скорером.
|
|
12
|
+
|
|
13
|
+
## Поведінка
|
|
14
|
+
|
|
15
|
+
1. З тексту джерела збираються п'ять категорій анкорів: усі URL; експортовані константи-рядки з непорожнім значенням; маркери повідомлень про помилки виду `(rule.mdc)`; посилання на json-конфіги проєкту; code-block-приклади з провідного коментаря файлу (де автор зазвичай показує контракт).
|
|
16
|
+
2. Кожна категорія дедуплікується зі збереженням порядку появи.
|
|
17
|
+
3. Для промпта анкори форматуються в компактний текстовий блок з інструкціями, де саме їх згадати; якщо анкорів немає взагалі — блок не додається, щоб не вводити модель в оману «обов'язковими» полями.
|
|
18
|
+
|
|
19
|
+
## Публічний API
|
|
20
|
+
|
|
21
|
+
- `extractAnchors` — текст джерела → категоризовані анкори.
|
|
22
|
+
- `anchorsToPrompt` — анкори → текстовий блок для system-промпта або порожній рядок.
|
|
23
|
+
|
|
24
|
+
## Гарантії поведінки
|
|
25
|
+
|
|
26
|
+
- Повністю детермінований і read-only; жодних LLM-викликів і мережі.
|
|
27
|
+
- Працює по сирому тексту без AST: дешево і свідомо толерує надлишок (зайвий анкор — менша проблема, ніж пропущений).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/skills/doc-files/js/docgen-extract.mjs
|
|
4
|
+
crc: 26bb2901
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# docgen-extract
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
Детермінований екстрактор фактів про кодовий файл (нуль токенів): з тексту джерела збирається факт-лист, на якому конвеєр будує промпти, маркери поведінки й список заборонених до згадки внутрішніх імен.
|
|
12
|
+
|
|
13
|
+
## Поведінка
|
|
14
|
+
|
|
15
|
+
1. Провідний блок-коментар файлу (до першого коду) стає «наміром файлу»; з документувальних коментарів перед кожним експортом витягуються опис, параметри та опис повернення, відкидаючи беззмістовні заглушки.
|
|
16
|
+
2. Збираються всі експортовані оголошення з безпосередньо передуючими їм коментарями.
|
|
17
|
+
3. Імпорти класифікуються на stdlib / npm / внутрішні; імена символів, імпортованих із внутрішніх модулів, складають список internalSymbols — модель не має згадувати їх у доці, а скорер штрафує за витік.
|
|
18
|
+
4. Маркери поведінки визначаються евристиками по тексту: read-only (немає запису у файлову систему), перехоплення помилок, повернення false/null при невдачі, звертання до мережі, кешування, свідомі пропуски шляхів.
|
|
19
|
+
5. Для розширень поза js/mjs/ts повертається позначка unsupported — конвеєр переходить на one-shot-шлях.
|
|
20
|
+
|
|
21
|
+
## Публічний API
|
|
22
|
+
|
|
23
|
+
- `extractFacts` — головна точка: текст джерела + шлях → факт-лист `{header, exports, imports, internalSymbols, markers}` або `{unsupported: true}`.
|
|
24
|
+
|
|
25
|
+
## Гарантії поведінки
|
|
26
|
+
|
|
27
|
+
- Повністю детермінований: однаковий вхід → однаковий факт-лист; жодних LLM-викликів і мережі.
|
|
28
|
+
- Read-only: файл не виконує операцій запису у файлову систему.
|
|
29
|
+
- Евристики свідомо толерують надлишок (зайвий маркер — менша проблема, ніж пропущений).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/skills/doc-files/js/docgen-files-batch.mjs
|
|
4
|
+
crc: 20a14675
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# docgen-files-batch
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
CLI-оркестратор масової генерації файлових док (`doc-files gen` / `doc-files stamp`): черга, вибір цілей, preflight локального сервера, запис док зі свіжою контрольною сумою і degraded-маркером. Уся важка робота живе тут, а не в контексті агента.
|
|
12
|
+
|
|
13
|
+
## Поведінка
|
|
14
|
+
|
|
15
|
+
1. Дерево проєкту сканується, цілі обираються за режимом: за замовчуванням — застарілі доки; `--overwrite` — усі; `--retry-degraded` — свіжі за сумою, але з оцінкою нижче порогу. Зріз великого прогону — `--from N --limit M`.
|
|
16
|
+
2. Перед генерацією — preflight локального сервера: «сервер лежить», «модель не влазить у пам'ять зайнятої машини» чи «потрібен API-ключ» зупиняють прогін одним зрозумілим повідомленням замість лавини помилок по файлах.
|
|
17
|
+
3. Кожна ціль генерується локальним конвеєром; дока пишеться поряд із джерелом у `docs/` зі свіжою сумою. Якщо оцінка нижча за поріг — у frontmatter додаються оцінка й коди проблем, файл рахується як degraded.
|
|
18
|
+
4. Підсумок: кількість успішних, degraded і помилкових файлів; за наявності degraded — підказка про `--retry-degraded`. Помилка хоча б одного файлу → exit-код `1`.
|
|
19
|
+
5. `stamp` детерміновано перештамповує frontmatter у наявних доках без LLM (міграція док без суми), зберігаючи наявні поля якості.
|
|
20
|
+
|
|
21
|
+
## Гарантії поведінки
|
|
22
|
+
|
|
23
|
+
- Жодних хмарних викликів: збій локальної генерації стає помилкою чи degraded-маркером, а не ескалацією.
|
|
24
|
+
- Доки пишуться атомарно по файлу: успішні цілі не відкочуються через подальші збої.
|
|
25
|
+
- Прогін ніколи не комітить — рішення про фіксацію приймає користувач.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/skills/doc-files/js/docgen-gen.mjs
|
|
4
|
+
crc: 2d6e5f79
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# docgen-gen
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
Генератор однієї файлової доки local-only конвеєра: файл → українська поведінкова md-документація локальною моделлю, з детермінованим скорингом і позначкою degraded замість будь-яких хмарних ескалацій.
|
|
12
|
+
|
|
13
|
+
## Поведінка
|
|
14
|
+
|
|
15
|
+
1. З джерела детерміновано витягуються факти (намір файлу, експорти, маркери поведінки) та анкори (URL, константи, маркери помилок, конфіги, приклади з header-коментаря) — без жодного токена.
|
|
16
|
+
2. Для підтримуваних структур документ збирається посекційно окремими викликами моделі: «Огляд» — лише з фактів, «Поведінка» — єдина секція з кодом у вікні, «Публічний API» — зі списку експортів; «Гарантії поведінки» — детермінований шаблон із маркерів без LLM. Для непідтримуваних структур (`vue`/`py` до появи юніт-шару) — один виклик на весь документ без скорингу.
|
|
17
|
+
3. Чорнетки секцій проходять детермінований пост-лінт (зрізання сигнатур, обгорток, випадкових заголовків); найвразливіші секції — один цикл критика-редактора.
|
|
18
|
+
4. Зібраний документ оцінюється детермінованим скорером (наявність огляду, змістовність поведінки, галюцинація кешу, витік внутрішніх імен). Якщо оцінка нижча за поріг — один повторний прогін з вищою температурою, перемагає кращий за оцінкою (вимикається `N_CURSOR_DOCGEN_BEST_OF=0`).
|
|
19
|
+
5. Результат повертається з оцінкою, списком проблем і прапором degraded; рішення про повторну генерацію приймає batch-обгортка або користувач — конвеєр ніколи не звертається до хмари.
|
|
20
|
+
|
|
21
|
+
## Публічний API
|
|
22
|
+
|
|
23
|
+
- `generateDoc` — головна точка: шлях файлу → `{ md, ms, score, issues, degraded, model }`.
|
|
24
|
+
- `DEFAULT_LOCAL_MODEL` — модель конвеєра: `N_CURSOR_DOCGEN_MODEL` → каскад локальних тирів → локальний omlx-дефолт напряму.
|
|
25
|
+
|
|
26
|
+
## Гарантії поведінки
|
|
27
|
+
|
|
28
|
+
- Local-only: жодних хмарних викликів і pre-route за складністю — будь-який файл генерується локальною моделлю.
|
|
29
|
+
- Скоринг і пост-лінт детерміновані: однаковий вхід → однакова оцінка.
|
|
30
|
+
- Помилка транспорту прокидається назовні — не маскується під degraded.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/skills/doc-files/js/docgen-prompts.mjs
|
|
4
|
+
crc: c454d2a6
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# docgen-prompts
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
Промптовий шар local-only конвеєра файлових док: будує messages-набори для секційної генерації, критики й переписування, а секцію «Гарантії поведінки» формує детерміновано без LLM. Весь стиль документа (українська, поведінковість, заборона сигнатур) зашитий у спільний system-блок.
|
|
12
|
+
|
|
13
|
+
## Поведінка
|
|
14
|
+
|
|
15
|
+
1. Для кожної секції збирається мінімальний контекст: «Огляд» — лише людиночитний витяг фактів (без коду), «Поведінка» — єдина секція з повним кодом файлу у вікні, «Публічний API» — лише список експортів з їхніми описами. Анкори (URL, константи, маркери, приклади) додаються окремим блоком, коли вони є.
|
|
16
|
+
2. Факт-лист перекладається в негативні й позитивні твердження для моделі: свідомі пропуски шляхів, read-only, перехоплення помилок, явне «Кешування: НЕМАЄ — не згадуй кеш» — щоб відсікти типові галюцинації ще в промпті.
|
|
17
|
+
3. Критик отримує чорнетку секції і закритий список критеріїв дефектів (generic-фрази, пропущені анкори, граматика, підзаголовки, скопійовані сигнатури, вигадані факти); відповідь — список проблем або `NONE`. Переписувач отримує чорнетку разом зі списком проблем і повертає лише оновлений текст.
|
|
18
|
+
4. «Гарантії поведінки» складаються шаблоном з маркерів факт-листа (read-only, fail-safe, кешування, пропуски шляхів, відсутність мережі) — нуль запитів і нуль generic-фраз; за відсутності маркерів — твердження про детермінованість.
|
|
19
|
+
|
|
20
|
+
## Публічний API
|
|
21
|
+
|
|
22
|
+
- `sectionMessages` — секційні набори messages з мінімальним контекстом під кожну секцію.
|
|
23
|
+
- `criticMessages` / `refineMessages` — пара критика й переписувача для одного циклу уточнення секції.
|
|
24
|
+
- `guaranteesFromMarkers` — детермінований текст секції гарантій з маркерів.
|
|
25
|
+
- `oneShotMessages` — один запит на весь документ для нестандартних структур файлів.
|
|
26
|
+
- `STYLE` — спільний system-блок стилю.
|
|
27
|
+
|
|
28
|
+
## Гарантії поведінки
|
|
29
|
+
|
|
30
|
+
- Код файлу потрапляє лише у промпт секції «Поведінка» й у one-shot — решта секцій працюють на факт-листі.
|
|
31
|
+
- Формування промптів детерміноване: однакові факти й код → однакові messages.
|
|
32
|
+
- Не звертається до мережі й не виконує LLM-викликів — лише будує тексти.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/skills/doc-files/js/docgen-scan.mjs
|
|
4
|
+
crc: b517ab25
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# docgen-scan
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
Сканер кодових файлів і детектор стану файлових док: які джерела підлягають документуванню, де лежить їхня дока, що застаріло, що degraded. Працює і як бібліотека для batch-генерації, і як CLI (`doc-files scan` / `doc-files check`) для хуків.
|
|
12
|
+
|
|
13
|
+
## Поведінка
|
|
14
|
+
|
|
15
|
+
1. Рекурсивний обхід від кореня збирає кодові файли (`js`/`mjs`/`ts`/`vue`/`py`), пропускаючи тести, оголошення типів, ignore-дерева і теки `docs/`; для кореня з system-wide docs-layout файлові доки на верхньому рівні не плануються.
|
|
16
|
+
2. Для кожного джерела обчислюється шлях доки (`<dir>/docs/<stem>.md`) і стан застарілості за контрольною сумою.
|
|
17
|
+
3. `check` працює в режимах: `--hook` — один файл зі stdin-payload редакторського хука; `--git` — змінені відносно HEAD джерела як Stop-гейт із порогом великого прогону (`--max`, дефолт `50` або `N_CURSOR_DOC_FILES_GATE_MAX`: більше застарілих — попередження без блоку); явні шляхи — точкова перевірка.
|
|
18
|
+
4. Застарілі доки → exit-код `2` зі списком і підказкою перегенерації; усе свіже або великий прогін — `0`.
|
|
19
|
+
5. `check --degraded` — інформаційний звіт (exit `0`): свіжі за сумою доки з оцінкою нижче порогу, з кодами проблем і підказкою про `gen --retry-degraded`. Degraded — видимий борг, не гейт.
|
|
20
|
+
|
|
21
|
+
## Гарантії поведінки
|
|
22
|
+
|
|
23
|
+
- Сканер read-only: жодних записів у дерево.
|
|
24
|
+
- Рішення «застаріла/свіжа» детерміноване і залежить лише від вмісту джерела й frontmatter доки.
|
|
25
|
+
- Stop-гейт ніколи не блокує масовий перший прогін понад поріг — захист від нескінченного блокування задач.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "auto": "завжди", "worktree": false, "requireRoot": true }
|
|
@@ -12,7 +12,7 @@ import { callOmlx, isOmlxModel } from '../../../lib/omlx.mjs'
|
|
|
12
12
|
export const MODEL = env.N_CURSOR_FIX_MODEL ?? resolveModel('min')
|
|
13
13
|
export const MODEL_HEAVY = env.N_CURSOR_FIX_MODEL_HEAVY ?? resolveModel('avg')
|
|
14
14
|
|
|
15
|
-
const JSON_CODE_BLOCK_RE = /```(?:json)
|
|
15
|
+
const JSON_CODE_BLOCK_RE = /```(?:json)?[ \t]{0,8}\n?([\s\S]*?)```/
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Витягує відносні шляхи файлів із violation output.
|
|
@@ -35,7 +35,7 @@ function extractFilePaths(output) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// Патерн без workspace: просто path/to/file.ext або ./file.ext
|
|
38
|
-
const re = /(?:^|\s)(
|
|
38
|
+
const re = /(?:^|\s)(\.?\w[\w./-]*\.(?:json|js|mjs|ts|vue|yml|yaml|toml|mdc|md|sh|py))(?::\d+)?/gm
|
|
39
39
|
for (const m of output.matchAll(re)) {
|
|
40
40
|
const p = m[1]
|
|
41
41
|
if (!seen.has(p)) {
|
|
@@ -116,7 +116,7 @@ function callModel(prompt, model) {
|
|
|
116
116
|
error: [
|
|
117
117
|
`pi: немає ключа для ${provider}.`,
|
|
118
118
|
`Встановіть N_CLOUD_MIN_MODEL=provider/model-id`,
|
|
119
|
-
`(напр.: openai/gpt-5.4-mini, google/gemini-2.5-flash, ollama/gemma3:4b)
|
|
119
|
+
`(напр.: openai/gpt-5.4-mini, google/gemini-2.5-flash, ollama/gemma3:4b)`
|
|
120
120
|
].join(' ')
|
|
121
121
|
}
|
|
122
122
|
}
|
|
@@ -20,7 +20,7 @@ export async function runOrchestratorCli(args, cwd) {
|
|
|
20
20
|
|
|
21
21
|
const maxIterIdx = args.indexOf('--max-iter')
|
|
22
22
|
const maxIter =
|
|
23
|
-
maxIterIdx === -1 ? DEFAULT_MAX_ITER :
|
|
23
|
+
maxIterIdx === -1 ? DEFAULT_MAX_ITER : Number(args[maxIterIdx + 1] ?? DEFAULT_MAX_ITER) || DEFAULT_MAX_ITER
|
|
24
24
|
const skipIdxs = new Set(maxIterIdx === -1 ? [] : [maxIterIdx, maxIterIdx + 1])
|
|
25
25
|
const ruleFilter = args.filter((a, i) => !a.startsWith('-') && !skipIdxs.has(i))
|
|
26
26
|
|
|
@@ -67,8 +67,8 @@ export async function scanStartWorkspaces(cwd) {
|
|
|
67
67
|
* @param {string} log обʼєднаний stdout+stderr
|
|
68
68
|
* @returns {{ready:boolean, firstError:string|null, logTail:string}} витяг
|
|
69
69
|
*/
|
|
70
|
-
export function parseStartLog(log) {
|
|
71
|
-
const text = log
|
|
70
|
+
export function parseStartLog(log = '') {
|
|
71
|
+
const text = log
|
|
72
72
|
const lines = text.split('\n')
|
|
73
73
|
const firstError = lines.find(l => ERROR_RE.test(l))?.trim() ?? null
|
|
74
74
|
const logTail = lines
|
|
@@ -137,7 +137,9 @@ export async function runWorkspaceStart(cwd, workspace, opts = {}) {
|
|
|
137
137
|
|
|
138
138
|
// server: успіх = дожив до кінця grace (timedOut) або встиг віддати рядок готовності.
|
|
139
139
|
// cli: успіх = чистий вихід 0 у межах grace.
|
|
140
|
-
|
|
140
|
+
let status
|
|
141
|
+
if (type === 'server') status = timedOut || ready ? 'OK' : 'FAIL'
|
|
142
|
+
else status = exitCode === 0 ? 'OK' : 'FAIL'
|
|
141
143
|
|
|
142
144
|
return {
|
|
143
145
|
workspace,
|