@nitra/cursor 5.1.0 → 5.2.1
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 +18 -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/models.md +24 -17
- 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/js/docs/consistency.md +36 -383
- 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/feedback/docs/fix.md +21 -131
- package/rules/ga/docs/fix.md +14 -12
- package/rules/ga/js/docs/lint.md +12 -9
- package/rules/ga/js/docs/workflows.md +20 -19
- package/rules/ga/lint/docs/lint.md +6 -0
- package/rules/graphql/docs/fix.md +6 -0
- package/rules/graphql/js/docs/tooling.md +18 -253
- package/rules/graphql/lib/docs/graphql-gql-scan.md +6 -0
- package/rules/hasura/docs/fix.md +18 -111
- 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 +48 -13
- package/skills/{docgen → doc-files}/js/docgen-extract.mjs +39 -10
- package/skills/doc-files/js/docgen-files-batch.mjs +181 -0
- package/skills/doc-files/js/docgen-gen.mjs +336 -0
- package/skills/{docgen → doc-files}/js/docgen-prompts.mjs +65 -50
- 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/js/units-js.mjs +139 -0
- package/skills/doc-files/js/units.mjs +19 -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
|
@@ -8,7 +8,11 @@ export const STYLE = [
|
|
|
8
8
|
'Заборонено: сигнатури, типи, параметри функцій; перелік stdlib-модулів; опис regex чи внутрішніх приватних імен.'
|
|
9
9
|
].join(' ')
|
|
10
10
|
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Окремий блок інструкцій з анкорами — підставляється коли вони є.
|
|
13
|
+
* @param {object|null} anchors анкори файлу (або null)
|
|
14
|
+
* @returns {string} текстовий блок для system-промпта або порожній рядок
|
|
15
|
+
*/
|
|
12
16
|
function anchorsBlock(anchors) {
|
|
13
17
|
if (!anchors) return ''
|
|
14
18
|
const txt = anchorsToPrompt(anchors)
|
|
@@ -35,6 +39,12 @@ function factsSummary(facts) {
|
|
|
35
39
|
return lines.join('\n')
|
|
36
40
|
}
|
|
37
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Пара system+user messages для одного виклику.
|
|
44
|
+
* @param {string} system system-промпт
|
|
45
|
+
* @param {string} user user-промпт
|
|
46
|
+
* @returns {Array<{role:string, content:string}>} messages-масив
|
|
47
|
+
*/
|
|
38
48
|
const msgs = (system, user) => [
|
|
39
49
|
{ role: 'system', content: system },
|
|
40
50
|
{ role: 'user', content: user }
|
|
@@ -42,67 +52,83 @@ const msgs = (system, user) => [
|
|
|
42
52
|
|
|
43
53
|
/**
|
|
44
54
|
* Секційні набори messages з МІНІМАЛЬНИМ контекстом під кожну секцію.
|
|
45
|
-
* Код потрапляє лише в `behavior`;
|
|
55
|
+
* Код потрапляє лише в `behavior`; «Огляд» генерується окремо ОСТАННІМ
|
|
56
|
+
* (`overviewMessages`) з уже написаної Поведінки — тут його немає.
|
|
46
57
|
* @param {object} facts факт-лист про файл
|
|
47
58
|
* @param {string} src вміст файлу
|
|
48
|
-
* @
|
|
59
|
+
* @param {object|null} [anchors] анкори файлу для обовʼязкового включення
|
|
60
|
+
* @returns {Array<{key:string, messages:object[], numPredict:number}>} набір секційних промптів (behavior[, api])
|
|
49
61
|
*/
|
|
50
62
|
export function sectionMessages(facts, src, anchors = null) {
|
|
51
63
|
const factsTxt = factsSummary(facts)
|
|
52
64
|
const anch = anchorsBlock(anchors)
|
|
53
65
|
const multi = (facts.exports?.length || 0) > 1
|
|
54
|
-
const out = []
|
|
55
|
-
|
|
56
|
-
// Огляд — лише факти (без коду)
|
|
57
|
-
out.push({
|
|
58
|
-
key: 'overview',
|
|
59
|
-
numPredict: 220,
|
|
60
|
-
messages: msgs(
|
|
61
|
-
`${STYLE}\n\nВІДОМІ ФАКТИ:\n${factsTxt}${anch}`,
|
|
62
|
-
'Напиши вміст секції «Огляд»: 1-3 речення — що файл робить і навіщо існує (роль у системі). Без заголовка, без переліку функцій. Заборонені generic-фрази типу «забезпечує перевірку», «виконує валідацію» — пиши КОНКРЕТНО що саме і за яким контрактом.'
|
|
63
|
-
)
|
|
64
|
-
})
|
|
65
66
|
|
|
66
|
-
// Поведінка
|
|
67
|
-
|
|
67
|
+
// R6: Поведінка описує РІВНО експортовані імена, не службові помічники
|
|
68
|
+
const exportNames = (facts.exports ?? []).map(e => e.name)
|
|
69
|
+
const behaviorTask = multi
|
|
70
|
+
? 'для кожної публічної функції — один короткий пункт «що вона робить»'
|
|
71
|
+
: 'нумерований алгоритм у бізнес-термінах'
|
|
72
|
+
const onlyExports = exportNames.length
|
|
73
|
+
? ` Описуй РІВНО ці публічні імена і жодних інших: ${exportNames.join(', ')}.`
|
|
74
|
+
: ''
|
|
75
|
+
const noInternal = facts.internalSymbols?.length
|
|
76
|
+
? ` НЕ згадуй за іменами службові функції: ${facts.internalSymbols.join(', ')}.`
|
|
77
|
+
: ''
|
|
78
|
+
const behavior = {
|
|
68
79
|
key: 'behavior',
|
|
69
80
|
numPredict: 500,
|
|
70
81
|
messages: msgs(
|
|
71
82
|
`${STYLE}\n\nФАЙЛ ${facts.relPath}:\n\`\`\`\n${src}\n\`\`\`\n\nВІДОМІ ФАКТИ:\n${factsTxt}${anch}`,
|
|
72
|
-
`Напиши вміст секції «Поведінка»: ${
|
|
83
|
+
`Напиши вміст секції «Поведінка»: ${behaviorTask}.${onlyExports} Якщо у фактах є свідомі пропуски шляхів — згадай їх там, де доречно (не вигадуй інших «не перевіряє»). НЕ пиши аргументи функцій у дужках, без regex.${noInternal} Без заголовка, без додаткових ## чи # підзаголовків усередині секції.`
|
|
73
84
|
)
|
|
74
|
-
}
|
|
85
|
+
}
|
|
75
86
|
|
|
76
87
|
// API — лише список експортів (без коду)
|
|
77
|
-
if (multi
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
})
|
|
88
|
+
if (!multi && !facts.exports?.some(e => e.desc)) return [behavior]
|
|
89
|
+
const list = facts.exports.map(e => `- ${e.name}: ${e.desc || '(сформулюй стисло з наміру файлу)'}`).join('\n')
|
|
90
|
+
const api = {
|
|
91
|
+
key: 'api',
|
|
92
|
+
numPredict: 320,
|
|
93
|
+
messages: msgs(
|
|
94
|
+
`${STYLE}${anch}`,
|
|
95
|
+
`Перепиши цей список як стислі маркери «назва — що робить», СВОЇМИ словами (не копіюй дослівно), без типів і сигнатур. Використовуй РІВНО ці назви, не додавай і не прибирай:\n${list}\nБез заголовка. Без generic-фраз «застосовує логіку», «перевіряє коректність» — пиши конкретно ЩО саме застосовує/перевіряє.`
|
|
96
|
+
)
|
|
87
97
|
}
|
|
98
|
+
return [behavior, api]
|
|
99
|
+
}
|
|
88
100
|
|
|
89
|
-
|
|
101
|
+
/**
|
|
102
|
+
* R3 — «Огляд» ОСТАННІМ: узагальнення вже написаної Поведінки, а не здогад із
|
|
103
|
+
* голого факт-листа. Лікує generic/хибний Огляд на складних файлах.
|
|
104
|
+
* @param {object} facts факт-лист про файл
|
|
105
|
+
* @param {string} behaviorText готовий текст секції «Поведінка»
|
|
106
|
+
* @param {object|null} [anchors] анкори файлу
|
|
107
|
+
* @returns {Array<{role:string,content:string}>} messages-масив для Огляду
|
|
108
|
+
*/
|
|
109
|
+
export function overviewMessages(facts, behaviorText, anchors = null) {
|
|
110
|
+
const factsTxt = factsSummary(facts)
|
|
111
|
+
const anch = anchorsBlock(anchors)
|
|
112
|
+
return msgs(
|
|
113
|
+
`${STYLE}\n\nВІДОМІ ФАКТИ:\n${factsTxt}${anch}`,
|
|
114
|
+
`На основі вже написаної секції «Поведінка» (нижче) напиши «Огляд»: 1-3 речення — що файл робить і навіщо існує (роль у системі). Узагальнюй САМЕ описану поведінку, не додавай нових фактів. Без заголовка, без переліку функцій. Заборонені абстрактні формули без конкретики («перевірка/валідація/обробка даних», «відповідність контракту», «застосовує логіку») — пиши, ЩО саме і за яким контрактом.\n\nПОВЕДІНКА:\n${behaviorText}`
|
|
115
|
+
)
|
|
90
116
|
}
|
|
91
117
|
|
|
92
118
|
/**
|
|
93
119
|
* E2-step 1 — критик. Перевіряє чорнетку секції на конкретні дефекти.
|
|
94
120
|
* Повертає messages для LLM-запиту: вихід має бути СПИСКОМ issues або словом NONE.
|
|
95
|
-
* @param {'overview'|'behavior'|'api'} sectionKey
|
|
121
|
+
* @param {'overview'|'behavior'|'api'} sectionKey ключ секції
|
|
96
122
|
* @param {string} draft вже згенерована чорнетка секції
|
|
97
123
|
* @param {object} facts факт-лист
|
|
98
|
-
* @param {ReturnType<import('./docgen-extract-anchors.mjs').extractAnchors>} anchors
|
|
99
|
-
* @returns {Array<{role:string,content:string}>}
|
|
124
|
+
* @param {ReturnType<import('./docgen-extract-anchors.mjs').extractAnchors>} anchors анкори файлу
|
|
125
|
+
* @returns {Array<{role:string,content:string}>} messages-масив для критика
|
|
100
126
|
*/
|
|
101
127
|
export function criticMessages(sectionKey, draft, facts, anchors) {
|
|
102
128
|
const anch = anchorsBlock(anchors)
|
|
103
129
|
const criteria = [
|
|
104
130
|
'generic-фрази без конкретики («забезпечує перевірку», «виконує валідацію», «застосовує логіку»)',
|
|
105
|
-
|
|
131
|
+
"пропущені обов'язкові АНКОРИ з контексту (URLs, magic-string constants, error-маркери, конфіги, code-приклади)",
|
|
106
132
|
'граматичні помилки українською («перед їх застосування», «моделіне», англіцизми як «applys», «moduleline»)',
|
|
107
133
|
'h1/h2/h3 підзаголовки всередині секції — їх не повинно бути',
|
|
108
134
|
'дослівна копія JSDoc-сигнатури або параметрів у дужках',
|
|
@@ -122,12 +148,12 @@ export function criticMessages(sectionKey, draft, facts, anchors) {
|
|
|
122
148
|
|
|
123
149
|
/**
|
|
124
150
|
* E2-step 2 — refine. Переписує чорнетку, виправляючи перелічені issues.
|
|
125
|
-
* @param {'overview'|'behavior'|'api'} sectionKey
|
|
126
|
-
* @param {string} draft
|
|
151
|
+
* @param {'overview'|'behavior'|'api'} sectionKey ключ секції
|
|
152
|
+
* @param {string} draft чорнетка секції
|
|
127
153
|
* @param {string} issues список issues від critic
|
|
128
|
-
* @param {object} facts
|
|
129
|
-
* @param {ReturnType<import('./docgen-extract-anchors.mjs').extractAnchors>} anchors
|
|
130
|
-
* @returns {Array<{role:string,content:string}>}
|
|
154
|
+
* @param {object} facts факт-лист
|
|
155
|
+
* @param {ReturnType<import('./docgen-extract-anchors.mjs').extractAnchors>} anchors анкори файлу
|
|
156
|
+
* @returns {Array<{role:string,content:string}>} messages-масив для переписування
|
|
131
157
|
*/
|
|
132
158
|
export function refineMessages(sectionKey, draft, issues, facts, anchors) {
|
|
133
159
|
const anch = anchorsBlock(anchors)
|
|
@@ -146,7 +172,7 @@ export function refineMessages(sectionKey, draft, issues, facts, anchors) {
|
|
|
146
172
|
/**
|
|
147
173
|
* E3 — детермінований шаблон секції «Гарантії поведінки» з facts.markers.
|
|
148
174
|
* НЕ використовує LLM: 0 запитів, 0 галюцинацій, 0 generic-фраз.
|
|
149
|
-
* @param {object} facts
|
|
175
|
+
* @param {object} facts факт-лист
|
|
150
176
|
* @returns {string} текст секції (без `## Гарантії` — це додає assemble())
|
|
151
177
|
*/
|
|
152
178
|
export function guaranteesFromMarkers(facts) {
|
|
@@ -177,14 +203,3 @@ export function oneShotMessages(facts, src) {
|
|
|
177
203
|
`Напиши документацію для файлу. Секції: ## Огляд (1-3 речення), ## Поведінка (нумерований/маркований алгоритм), ${multi ? '## Публічний API (назва + що робить), ' : ''}## Гарантії поведінки.\n\nФАЙЛ ${facts.relPath}:\n\`\`\`\n${src}\n\`\`\``
|
|
178
204
|
)
|
|
179
205
|
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Лише текст user-промпту для one-shot (для хмарного fallback через Anthropic SDK).
|
|
183
|
-
* @param {object} facts факт-лист про файл
|
|
184
|
-
* @param {string} src вміст файлу
|
|
185
|
-
* @returns {string} plain-text user-prompt
|
|
186
|
-
*/
|
|
187
|
-
export function oneShotPromptText(facts, src) {
|
|
188
|
-
const multi = (facts.exports?.length || 0) > 1
|
|
189
|
-
return `Напиши документацію для файлу. Секції: ## Огляд (1-3 речення), ## Поведінка (нумерований/маркований алгоритм), ${multi ? '## Публічний API (назва + що робить), ' : ''}## Гарантії поведінки.\n\nФАЙЛ ${facts.relPath}:\n\`\`\`\n${src}\n\`\`\``
|
|
190
|
-
}
|
|
@@ -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.
|