@nitra/cursor 5.0.3 → 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.
Files changed (206) hide show
  1. package/.claude-template/settings.template.json +22 -0
  2. package/.pi-template/extensions/n-cursor-adr/docs/index.md +15 -9
  3. package/CHANGELOG.md +18 -1
  4. package/bin/n-cursor.js +73 -16
  5. package/docs/stryker.config.md +6 -0
  6. package/docs/vitest.config.md +6 -0
  7. package/lib/docs/llm.md +29 -0
  8. package/lib/docs/omlx.md +32 -0
  9. package/lib/llm.mjs +137 -0
  10. package/lib/models.mjs +9 -1
  11. package/lib/omlx.mjs +147 -0
  12. package/package.json +1 -1
  13. package/rules/abie/docs/fix.md +6 -0
  14. package/rules/abie/js/docs/applies.md +6 -0
  15. package/rules/abie/js/docs/env_dns.md +25 -22
  16. package/rules/abie/js/docs/firebase_hosting.md +6 -0
  17. package/rules/abie/js/docs/hc_pairing.md +21 -25
  18. package/rules/abie/js/docs/ua_http_route.md +27 -19
  19. package/rules/abie/js/docs/ua_node_selector.md +24 -19
  20. package/rules/abie/lib/docs/enabled.md +13 -7
  21. package/rules/abie/lib/docs/env-dns.md +9 -3
  22. package/rules/abie/lib/docs/hc-yaml.md +6 -0
  23. package/rules/abie/lib/docs/http-route.md +6 -0
  24. package/rules/abie/lib/docs/k8s-tree.md +6 -0
  25. package/rules/abie/lib/docs/kustomization-patches.md +6 -0
  26. package/rules/abie/lib/docs/overlay-paths.md +6 -0
  27. package/rules/abie/lib/docs/yaml.md +6 -0
  28. package/rules/adr/docs/fix.md +6 -0
  29. package/rules/adr/js/docs/hooks.md +29 -244
  30. package/rules/bun/docs/fix.md +6 -0
  31. package/rules/bun/js/docs/layout.md +37 -375
  32. package/rules/capacitor/docs/fix.md +22 -108
  33. package/rules/capacitor/js/docs/platforms.md +62 -268
  34. package/rules/changelog/docs/fix.md +6 -0
  35. package/rules/changelog/lib/docs/package-manifest.md +6 -0
  36. package/rules/ci4/docs/fix.md +23 -165
  37. package/rules/ci4/js/docs/marksman_config.md +9 -1
  38. package/rules/docker/docs/fix.md +6 -0
  39. package/rules/docker/js/docs/lint.md +55 -239
  40. package/rules/docker/lib/docs/docker-hadolint.md +6 -0
  41. package/rules/docker/lib/docs/docker-mirror.md +6 -0
  42. package/rules/docker/lib/docs/docker-native-addon.md +6 -0
  43. package/rules/docker/lib/docs/docker-nginx-user.md +6 -0
  44. package/rules/docker/lint/docs/lint.md +9 -1
  45. package/rules/efes/docs/fix.md +6 -0
  46. package/rules/ga/lint/docs/lint.md +6 -0
  47. package/rules/graphql/docs/fix.md +6 -0
  48. package/rules/graphql/lib/docs/graphql-gql-scan.md +6 -0
  49. package/rules/image-avif/docs/fix.md +6 -0
  50. package/rules/image-avif/js/docs/avif_generation.md +6 -0
  51. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +9 -3
  52. package/rules/js-bun-redis/lib/docs/redis-imports.md +6 -0
  53. package/rules/js-lint/js/docs/utils_imports.md +6 -0
  54. package/rules/js-lint-ci/docs/fix.md +7 -1
  55. package/rules/js-mssql/docs/fix.md +6 -0
  56. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +6 -0
  57. package/rules/js-run/docs/fix.md +6 -0
  58. package/rules/js-run/lib/docs/bunyan-imports.md +6 -0
  59. package/rules/js-run/lib/docs/check-env-scan.md +6 -0
  60. package/rules/js-run/lib/docs/conn-file-rules.md +6 -0
  61. package/rules/js-run/lib/docs/conn-imports-scan.md +6 -0
  62. package/rules/js-run/lib/docs/promise-settimeout-scan.md +6 -0
  63. package/rules/js-run/lib/docs/temporal-scan.md +6 -0
  64. package/rules/k8s/docs/fix.md +6 -0
  65. package/rules/k8s/lint/docs/lint.md +6 -0
  66. package/rules/nginx-default-tpl/docs/fix.md +6 -0
  67. package/rules/npm-module/js/docs/header_doc_pointer.md +7 -0
  68. package/rules/npm-module/js/header_doc_pointer.mjs +2 -8
  69. package/rules/php/docs/fix.md +6 -0
  70. package/rules/php/lint/docs/lint.md +6 -0
  71. package/rules/python/docs/fix.md +6 -0
  72. package/rules/python/lint/docs/lint.md +6 -0
  73. package/rules/rego/lint/docs/lint.md +6 -0
  74. package/rules/release/docs/change.md +6 -0
  75. package/rules/release/docs/fix.md +6 -0
  76. package/rules/release/docs/release.md +6 -0
  77. package/rules/release/lib/docs/aggregate.md +6 -0
  78. package/rules/release/lib/docs/change-file.md +6 -0
  79. package/rules/release/lib/docs/fallback.md +6 -0
  80. package/rules/rust/lib/docs/has-cargo-toml.md +6 -0
  81. package/rules/security/docs/fix.md +7 -1
  82. package/rules/security/js/docs/lint.md +6 -0
  83. package/rules/style-lint/docs/fix.md +6 -0
  84. package/rules/tauri/docs/fix.md +6 -0
  85. package/rules/test/docs/fix.md +6 -0
  86. package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +6 -0
  87. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +6 -0
  88. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +6 -0
  89. package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +6 -0
  90. package/rules/text/docs/fix.md +6 -0
  91. package/rules/text/lint/docs/lint.md +6 -0
  92. package/rules/text/lint/docs/run-dotenv-linter.md +6 -0
  93. package/rules/text/lint/docs/run-shellcheck.md +6 -0
  94. package/rules/text/lint/docs/run-v8r.md +6 -0
  95. package/rules/vue/lib/docs/vue-forbidden-imports.md +6 -0
  96. package/scripts/coverage-classify/cache.mjs +1 -1
  97. package/scripts/coverage-classify/docs/apply.md +6 -0
  98. package/scripts/coverage-classify/docs/cache.md +6 -0
  99. package/scripts/coverage-classify/docs/prompt.md +6 -0
  100. package/scripts/coverage-classify/docs/verdict-schema.md +6 -0
  101. package/scripts/coverage-classify/index.mjs +24 -15
  102. package/scripts/coverage-classify/prompt.mjs +1 -1
  103. package/scripts/coverage-fix-extract.mjs +1 -1
  104. package/scripts/coverage-fix.mjs +2 -1
  105. package/scripts/docs/auto-skills.md +6 -0
  106. package/scripts/docs/build-agents-commands.md +7 -1
  107. package/scripts/docs/cli-entry.md +6 -0
  108. package/scripts/docs/coverage-fix-extract.md +6 -0
  109. package/scripts/docs/coverage-fix.md +6 -0
  110. package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +6 -0
  111. package/scripts/docs/lint-cli.md +6 -0
  112. package/scripts/docs/post-tool-use-fix.md +6 -0
  113. package/scripts/docs/rename-yaml-extensions.md +6 -0
  114. package/scripts/docs/skills-cli.md +6 -0
  115. package/scripts/docs/sync-setup-bun-deps-action.md +6 -0
  116. package/scripts/docs/upgrade-nitra-cursor-and-install.md +6 -0
  117. package/scripts/docs/worktree-cli.md +6 -0
  118. package/scripts/lib/docs/assert-project-root.md +6 -0
  119. package/scripts/lib/docs/check-mdc-template-refs.md +6 -0
  120. package/scripts/lib/docs/check-reporter.md +6 -0
  121. package/scripts/lib/docs/diff-added-lines.md +6 -0
  122. package/scripts/lib/docs/discover-check-rules-from-cursor.md +6 -0
  123. package/scripts/lib/docs/discover-checkable-rules.md +6 -0
  124. package/scripts/lib/docs/ensure-tool.md +6 -0
  125. package/scripts/lib/docs/generated-markdown.md +6 -0
  126. package/scripts/lib/docs/gha-workflow.md +6 -0
  127. package/scripts/lib/docs/inline-template-links.md +6 -0
  128. package/scripts/lib/docs/list-rule-ids.md +6 -0
  129. package/scripts/lib/docs/load-cursor-config.md +6 -0
  130. package/scripts/lib/docs/mirror-parity.md +6 -0
  131. package/scripts/lib/docs/read-n-cursor-config-lite.md +6 -0
  132. package/scripts/lib/docs/resolve-target-files.md +6 -0
  133. package/scripts/lib/docs/root-notice.md +6 -0
  134. package/scripts/lib/docs/rule-meta-helpers.md +6 -0
  135. package/scripts/lib/docs/rule-meta.md +6 -0
  136. package/scripts/lib/docs/run-conftest-batch.md +6 -0
  137. package/scripts/lib/docs/run-lint-step.md +6 -0
  138. package/scripts/lib/docs/run-rule-cli.md +6 -0
  139. package/scripts/lib/docs/run-rule.md +6 -0
  140. package/scripts/lib/docs/run-standard-lint.md +6 -0
  141. package/scripts/lib/docs/run-standard-rule.md +6 -0
  142. package/scripts/lib/docs/skill-meta.md +6 -0
  143. package/scripts/lib/docs/template.md +6 -0
  144. package/scripts/lib/docs/timing-summary.md +6 -0
  145. package/scripts/lib/docs/workspaces.md +6 -0
  146. package/scripts/lib/docs/worktree-notice.md +6 -0
  147. package/scripts/lib/docs/worktree.md +6 -0
  148. package/scripts/lib/mirror-parity.mjs +1 -1
  149. package/scripts/lib/root-notice.mjs +1 -1
  150. package/scripts/lib/worktree-notice.mjs +5 -5
  151. package/scripts/lib/worktree.mjs +1 -1
  152. package/scripts/sync-claude-config.mjs +3 -0
  153. package/scripts/utils/docs/ast-scan-utils.md +6 -0
  154. package/scripts/utils/docs/ensure-gitignore-entries.md +6 -0
  155. package/scripts/utils/docs/find-package-json-paths.md +6 -0
  156. package/scripts/utils/docs/lock-cache-dir.md +6 -0
  157. package/scripts/utils/docs/pass.md +6 -0
  158. package/scripts/utils/docs/resolve-cargo-manifest.md +6 -0
  159. package/scripts/utils/docs/resolve-cmd.md +6 -0
  160. package/scripts/utils/docs/resolve-js-root.md +6 -0
  161. package/scripts/utils/docs/test-helpers.md +6 -0
  162. package/scripts/utils/docs/walk-cache.md +6 -0
  163. package/scripts/utils/docs/walkDir.md +6 -0
  164. package/scripts/utils/docs/worktree-fingerprint.md +6 -0
  165. package/scripts/utils/resolve-js-root.mjs +1 -1
  166. package/skills/doc-aggregate/SKILL.md +129 -0
  167. package/skills/doc-aggregate/js/docgen-ignore.mjs +9 -0
  168. package/skills/{docgen → doc-aggregate}/js/docgen-scan.mjs +22 -67
  169. package/skills/doc-aggregate/js/docs/docgen-ignore.md +21 -0
  170. package/skills/doc-files/SKILL.md +100 -0
  171. package/skills/doc-files/js/docgen-crc.mjs +164 -0
  172. package/skills/{docgen → doc-files}/js/docgen-extract-anchors.mjs +24 -15
  173. package/skills/{docgen → doc-files}/js/docgen-extract.mjs +15 -9
  174. package/skills/doc-files/js/docgen-files-batch.mjs +181 -0
  175. package/skills/doc-files/js/docgen-gen.mjs +291 -0
  176. package/skills/{docgen → doc-files}/js/docgen-prompts.mjs +43 -40
  177. package/skills/doc-files/js/docgen-scan.mjs +298 -0
  178. package/skills/doc-files/js/docs/docgen-crc.md +32 -0
  179. package/skills/doc-files/js/docs/docgen-extract-anchors.md +27 -0
  180. package/skills/doc-files/js/docs/docgen-extract.md +29 -0
  181. package/skills/doc-files/js/docs/docgen-files-batch.md +25 -0
  182. package/skills/doc-files/js/docs/docgen-gen.md +30 -0
  183. package/skills/doc-files/js/docs/docgen-prompts.md +32 -0
  184. package/skills/doc-files/js/docs/docgen-scan.md +25 -0
  185. package/skills/doc-files/meta.json +1 -0
  186. package/skills/fix/js/docs/llm-worker.md +6 -0
  187. package/skills/fix/js/docs/orchestrator.md +6 -0
  188. package/skills/fix/js/llm-worker.mjs +23 -14
  189. package/skills/fix/js/orchestrator.mjs +1 -1
  190. package/skills/start-check/js/check.mjs +5 -3
  191. package/skills/start-check/js/docs/check.md +6 -0
  192. package/skills/docgen/SKILL.md +0 -224
  193. package/skills/docgen/bench/etalon/firebase_hosting.md +0 -19
  194. package/skills/docgen/bench/etalon/k8s-tree.md +0 -24
  195. package/skills/docgen/bench/etalon/overlay-paths.md +0 -24
  196. package/skills/docgen/js/docgen-batch-omlx.mjs +0 -82
  197. package/skills/docgen/js/docgen-batch.mjs +0 -95
  198. package/skills/docgen/js/docgen-compare-pi-vs-direct.mjs +0 -95
  199. package/skills/docgen/js/docgen-gen.mjs +0 -339
  200. package/skills/docgen/js/docs/docgen-extract.md +0 -28
  201. package/skills/docgen/js/docs/docgen-gen.md +0 -41
  202. package/skills/docgen/js/docs/docgen-ignore.md +0 -24
  203. package/skills/docgen/js/docs/docgen-prompts.md +0 -24
  204. package/skills/docgen/js/docs/docgen-scan.md +0 -48
  205. /package/skills/{docgen → doc-aggregate}/meta.json +0 -0
  206. /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 }
@@ -45,64 +55,68 @@ const msgs = (system, user) => [
45
55
  * Код потрапляє лише в `behavior`; решта секцій — на факт-листі.
46
56
  * @param {object} facts факт-лист про файл
47
57
  * @param {string} src вміст файлу
58
+ * @param {object|null} [anchors] анкори файлу для обовʼязкового включення
48
59
  * @returns {Array<{key:string, messages:object[], numPredict:number}>} набір секційних промптів
49
60
  */
50
61
  export function sectionMessages(facts, src, anchors = null) {
51
62
  const factsTxt = factsSummary(facts)
52
63
  const anch = anchorsBlock(anchors)
53
64
  const multi = (facts.exports?.length || 0) > 1
54
- const out = []
55
65
 
56
66
  // Огляд — лише факти (без коду)
57
- out.push({
67
+ const overview = {
58
68
  key: 'overview',
59
69
  numPredict: 220,
60
70
  messages: msgs(
61
71
  `${STYLE}\n\nВІДОМІ ФАКТИ:\n${factsTxt}${anch}`,
62
72
  'Напиши вміст секції «Огляд»: 1-3 речення — що файл робить і навіщо існує (роль у системі). Без заголовка, без переліку функцій. Заборонені generic-фрази типу «забезпечує перевірку», «виконує валідацію» — пиши КОНКРЕТНО що саме і за яким контрактом.'
63
73
  )
64
- })
74
+ }
65
75
 
66
76
  // Поведінка — ЄДИНА секція, якій потрібен код
67
- out.push({
77
+ const behaviorTask = multi
78
+ ? 'для кожної публічної функції — один короткий пункт «що вона робить»'
79
+ : 'нумерований алгоритм у бізнес-термінах'
80
+ const noInternal = facts.internalSymbols?.length
81
+ ? ` НЕ згадуй за іменами службові функції: ${facts.internalSymbols.join(', ')}.`
82
+ : ''
83
+ const behavior = {
68
84
  key: 'behavior',
69
85
  numPredict: 500,
70
86
  messages: msgs(
71
87
  `${STYLE}\n\nФАЙЛ ${facts.relPath}:\n\`\`\`\n${src}\n\`\`\`\n\nВІДОМІ ФАКТИ:\n${factsTxt}${anch}`,
72
- `Напиши вміст секції «Поведінка»: ${multi ? 'для кожної публічної функції — один короткий пункт «що вона робить»' : 'нумерований алгоритм у бізнес-термінах'}. Якщо у фактах є свідомі пропуски шляхів — згадай їх там, де доречно (не вигадуй інших «не перевіряє»). НЕ пиши аргументи функцій у дужках, без regex.${facts.internalSymbols?.length ? ` НЕ згадуй за іменами службові функції: ${facts.internalSymbols.join(', ')}.` : ''} Без заголовка, без додаткових ## чи # підзаголовків усередині секції.`
88
+ `Напиши вміст секції «Поведінка»: ${behaviorTask}. Якщо у фактах є свідомі пропуски шляхів — згадай їх там, де доречно (не вигадуй інших «не перевіряє»). НЕ пиши аргументи функцій у дужках, без regex.${noInternal} Без заголовка, без додаткових ## чи # підзаголовків усередині секції.`
73
89
  )
74
- })
90
+ }
75
91
 
76
92
  // API — лише список експортів (без коду)
77
- if (multi || facts.exports?.some(e => e.desc)) {
78
- const list = facts.exports.map(e => `- ${e.name}: ${e.desc || '(сформулюй стисло з наміру файлу)'}`).join('\n')
79
- out.push({
80
- key: 'api',
81
- numPredict: 320,
82
- messages: msgs(
83
- `${STYLE}${anch}`,
84
- `Перепиши цей список як стислі маркери «назва — що робить», СВОЇМИ словами (не копіюй дослівно), без типів і сигнатур. Використовуй РІВНО ці назви, не додавай і не прибирай:\n${list}\nБез заголовка. Без generic-фраз «застосовує логіку», «перевіряє коректність» — пиши конкретно ЩО саме застосовує/перевіряє.`
85
- )
86
- })
93
+ if (!multi && !facts.exports?.some(e => e.desc)) return [overview, behavior]
94
+ const list = facts.exports.map(e => `- ${e.name}: ${e.desc || '(сформулюй стисло з наміру файлу)'}`).join('\n')
95
+ const api = {
96
+ key: 'api',
97
+ numPredict: 320,
98
+ messages: msgs(
99
+ `${STYLE}${anch}`,
100
+ `Перепиши цей список як стислі маркери «назва — що робить», СВОЇМИ словами (не копіюй дослівно), без типів і сигнатур. Використовуй РІВНО ці назви, не додавай і не прибирай:\n${list}\nБез заголовка. Без generic-фраз «застосовує логіку», «перевіряє коректність» — пиши конкретно ЩО саме застосовує/перевіряє.`
101
+ )
87
102
  }
88
-
89
- return out
103
+ return [overview, behavior, api]
90
104
  }
91
105
 
92
106
  /**
93
107
  * E2-step 1 — критик. Перевіряє чорнетку секції на конкретні дефекти.
94
108
  * Повертає messages для LLM-запиту: вихід має бути СПИСКОМ issues або словом NONE.
95
- * @param {'overview'|'behavior'|'api'} sectionKey
109
+ * @param {'overview'|'behavior'|'api'} sectionKey ключ секції
96
110
  * @param {string} draft вже згенерована чорнетка секції
97
111
  * @param {object} facts факт-лист
98
- * @param {ReturnType<import('./docgen-extract-anchors.mjs').extractAnchors>} anchors
99
- * @returns {Array<{role:string,content:string}>}
112
+ * @param {ReturnType<import('./docgen-extract-anchors.mjs').extractAnchors>} anchors анкори файлу
113
+ * @returns {Array<{role:string,content:string}>} messages-масив для критика
100
114
  */
101
115
  export function criticMessages(sectionKey, draft, facts, anchors) {
102
116
  const anch = anchorsBlock(anchors)
103
117
  const criteria = [
104
118
  'generic-фрази без конкретики («забезпечує перевірку», «виконує валідацію», «застосовує логіку»)',
105
- 'пропущені обов\'язкові АНКОРИ з контексту (URLs, magic-string constants, error-маркери, конфіги, code-приклади)',
119
+ "пропущені обов'язкові АНКОРИ з контексту (URLs, magic-string constants, error-маркери, конфіги, code-приклади)",
106
120
  'граматичні помилки українською («перед їх застосування», «моделіне», англіцизми як «applys», «moduleline»)',
107
121
  'h1/h2/h3 підзаголовки всередині секції — їх не повинно бути',
108
122
  'дослівна копія JSDoc-сигнатури або параметрів у дужках',
@@ -122,12 +136,12 @@ export function criticMessages(sectionKey, draft, facts, anchors) {
122
136
 
123
137
  /**
124
138
  * E2-step 2 — refine. Переписує чорнетку, виправляючи перелічені issues.
125
- * @param {'overview'|'behavior'|'api'} sectionKey
126
- * @param {string} draft
139
+ * @param {'overview'|'behavior'|'api'} sectionKey ключ секції
140
+ * @param {string} draft чорнетка секції
127
141
  * @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}>}
142
+ * @param {object} facts факт-лист
143
+ * @param {ReturnType<import('./docgen-extract-anchors.mjs').extractAnchors>} anchors анкори файлу
144
+ * @returns {Array<{role:string,content:string}>} messages-масив для переписування
131
145
  */
132
146
  export function refineMessages(sectionKey, draft, issues, facts, anchors) {
133
147
  const anch = anchorsBlock(anchors)
@@ -146,7 +160,7 @@ export function refineMessages(sectionKey, draft, issues, facts, anchors) {
146
160
  /**
147
161
  * E3 — детермінований шаблон секції «Гарантії поведінки» з facts.markers.
148
162
  * НЕ використовує LLM: 0 запитів, 0 галюцинацій, 0 generic-фраз.
149
- * @param {object} facts
163
+ * @param {object} facts факт-лист
150
164
  * @returns {string} текст секції (без `## Гарантії` — це додає assemble())
151
165
  */
152
166
  export function guaranteesFromMarkers(facts) {
@@ -177,14 +191,3 @@ export function oneShotMessages(facts, src) {
177
191
  `Напиши документацію для файлу. Секції: ## Огляд (1-3 речення), ## Поведінка (нумерований/маркований алгоритм), ${multi ? '## Публічний API (назва + що робить), ' : ''}## Гарантії поведінки.\n\nФАЙЛ ${facts.relPath}:\n\`\`\`\n${src}\n\`\`\``
178
192
  )
179
193
  }
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.
@@ -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-викликів — лише будує тексти.