@nitra/cursor 3.26.0 → 3.28.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/CHANGELOG.md +18 -0
- package/bin/n-cursor.js +29 -9
- package/package.json +1 -1
- package/rules/abie/js/applies.mjs +1 -5
- package/rules/abie/js/env_dns.mjs +1 -9
- package/rules/abie/js/firebase_hosting.mjs +1 -5
- package/rules/abie/js/hc_pairing.mjs +1 -8
- package/rules/abie/js/ua_http_route.mjs +1 -10
- package/rules/abie/js/ua_node_selector.mjs +1 -8
- package/rules/adr/js/hooks.mjs +1 -20
- package/rules/bun/js/layout.mjs +1 -19
- package/rules/capacitor/js/platforms.mjs +1 -23
- package/rules/changelog/js/consistency.mjs +1 -29
- package/rules/ci4/js/marksman_config.mjs +1 -19
- package/rules/docker/js/lint.mjs +1 -34
- package/rules/ga/docs/fix.md +16 -149
- package/rules/ga/js/docs/lint.md +12 -93
- package/rules/ga/js/docs/workflows.md +28 -213
- package/rules/ga/js/workflows.mjs +1 -16
- package/rules/ga/lint/docs/lint.md +24 -206
- package/rules/graphql/js/tooling.mjs +1 -9
- package/rules/hasura/js/internal_urls.mjs +1 -24
- package/rules/image-avif/js/avif_generation.mjs +1 -27
- package/rules/image-compress/js/package_setup.mjs +1 -18
- package/rules/js-bun-db/js/safety.mjs +1 -31
- package/rules/js-bun-redis/js/imports.mjs +1 -12
- package/rules/js-lint/js/docs/lint-findings.md +30 -0
- package/rules/js-lint/js/lint-findings.mjs +1 -7
- package/rules/js-lint/js/lint.mjs +1 -10
- package/rules/js-lint/js/tooling.mjs +1 -13
- package/rules/js-lint/js/utils_imports.mjs +1 -18
- package/rules/js-lint-ci/js/lint.mjs +1 -6
- package/rules/js-mssql/js/deps.mjs +1 -10
- package/rules/js-run/js/runtime.mjs +1 -37
- package/rules/js-run/lib/docs/temporal-scan.md +25 -0
- package/rules/k8s/js/manifests.mjs +1 -137
- package/rules/nginx-default-tpl/js/template.mjs +1 -18
- package/rules/npm-module/js/docs/header_doc_pointer.md +25 -0
- package/rules/npm-module/js/header_doc_pointer.mjs +82 -0
- package/rules/npm-module/js/package_structure.mjs +1 -28
- package/rules/npm-module/js/rule_meta.mjs +1 -10
- package/rules/npm-module/js/skill_meta.mjs +1 -13
- package/rules/php/js/tooling.mjs +1 -11
- package/rules/python/js/applies.mjs +1 -8
- package/rules/python/js/tooling.mjs +1 -21
- package/rules/rego/js/applies.mjs +1 -11
- package/rules/rust/js/applies.mjs +1 -7
- package/rules/security/js/sample_secret.mjs +1 -28
- package/rules/security/js/trufflehog.mjs +1 -8
- package/rules/style-lint/js/lint.mjs +1 -5
- package/rules/style-lint/js/tooling.mjs +1 -19
- package/rules/tauri/js/cargo_mutants_config.mjs +1 -20
- package/rules/tauri/js/tooling.mjs +1 -21
- package/rules/test/js/cargo_mutants_config.mjs +1 -12
- package/rules/test/js/location.mjs +1 -9
- package/rules/test/js/no-process-chdir.mjs +1 -21
- package/rules/test/js/no-relative-fs-path.mjs +1 -23
- package/rules/test/js/stryker_config.mjs +4 -25
- package/rules/test/js/vitest-config-pool-forks.mjs +1 -17
- package/rules/text/js/forbidden-prettier.mjs +1 -10
- package/rules/text/js/formatting.mjs +1 -31
- package/rules/vue/js/packages.mjs +1 -24
- package/scripts/docs/coverage-fix-extract.md +32 -0
- package/scripts/docs/lint-cli.md +25 -0
- package/scripts/docs/post-tool-use-fix.md +27 -0
- package/scripts/docs/rename-yaml-extensions.md +36 -0
- package/scripts/docs/skills-cli.md +35 -0
- package/scripts/docs/sync-claude-config.md +52 -0
- package/scripts/docs/sync-setup-bun-deps-action.md +26 -0
- package/scripts/docs/upgrade-nitra-cursor-and-install.md +29 -0
- package/scripts/docs/worktree-cli.md +46 -0
- package/scripts/lib/docs/assert-project-root.md +28 -0
- package/scripts/lib/docs/diff-added-lines.md +34 -0
- package/scripts/lib/docs/read-n-cursor-config-lite.md +28 -0
- package/scripts/lib/docs/resolve-target-files.md +34 -0
- package/scripts/lib/docs/root-notice.md +28 -0
- package/scripts/lib/docs/rule-meta-helpers.md +34 -0
- package/scripts/lib/docs/rule-meta.md +34 -0
- package/scripts/lib/docs/rule-predicates.md +30 -0
- package/scripts/lib/docs/run-conftest-batch.md +26 -0
- package/scripts/lib/docs/run-lint-step.md +25 -0
- package/scripts/lib/docs/run-rule-cli.md +27 -0
- package/scripts/lib/docs/run-rule.md +32 -0
- package/scripts/lib/docs/run-standard-lint.md +22 -0
- package/scripts/lib/docs/run-standard-rule.md +24 -0
- package/scripts/lib/docs/skill-meta.md +31 -0
- package/scripts/lib/docs/sync-gitignore-worktree.md +31 -0
- package/scripts/lib/docs/template.md +40 -0
- package/scripts/lib/docs/timing-summary.md +24 -0
- package/scripts/lib/docs/workspaces.md +30 -0
- package/scripts/lib/docs/worktree-notice.md +27 -0
- package/scripts/lib/docs/worktree.md +38 -0
- package/scripts/utils/docs/ast-scan-utils.md +50 -0
- package/scripts/utils/docs/ensure-gitignore-entries.md +28 -0
- package/scripts/utils/docs/find-package-json-paths.md +26 -0
- package/scripts/utils/docs/lock-cache-dir.md +25 -0
- package/scripts/utils/docs/pass.md +25 -0
- package/scripts/utils/docs/resolve-cargo-manifest.md +23 -0
- package/scripts/utils/docs/resolve-cmd.md +29 -0
- package/scripts/utils/docs/resolve-js-root.md +25 -0
- package/scripts/utils/docs/test-helpers.md +36 -0
- package/scripts/utils/docs/walk-cache.md +27 -0
- package/scripts/utils/docs/walkDir.md +32 -0
- package/scripts/utils/docs/with-lock.md +25 -0
- package/scripts/utils/docs/worktree-fingerprint.md +27 -0
- package/skills/docgen/js/docgen-batch.mjs +95 -0
- package/skills/docgen/js/docgen-extract.mjs +33 -18
- package/skills/docgen/js/docgen-gen.mjs +258 -30
- package/skills/docgen/js/docgen-ignore.mjs +4 -7
- package/skills/docgen/js/docgen-prompts.mjs +40 -23
- package/skills/docgen/js/docgen-scan.mjs +1 -8
- package/skills/docgen/js/docs/docgen-extract.md +28 -0
- package/skills/docgen/js/docs/docgen-gen.md +41 -0
- package/skills/docgen/js/docs/docgen-ignore.md +24 -0
- package/skills/docgen/js/docs/docgen-prompts.md +24 -0
- package/skills/docgen/js/docs/docgen-scan.md +48 -0
- package/skills/fix/SKILL.md +5 -31
- package/skills/fix/js/docs/llm-worker.md +27 -0
- package/skills/fix/js/docs/orchestrator.md +32 -0
- package/skills/fix/js/docs/t0.md +29 -0
- package/skills/fix/js/llm-worker.mjs +216 -0
- package/skills/fix/js/orchestrator.mjs +119 -0
- package/skills/fix/js/t0.mjs +213 -0
- package/skills/fix/meta.json +1 -1
- package/skills/start-check/js/check.mjs +1 -16
- package/skills/start-check/js/docs/check.md +34 -0
- package/skills/taze/js/diff.mjs +1 -15
- package/skills/taze/js/docs/diff.md +33 -0
|
@@ -1,31 +1,37 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* docgen-конвеєр (входна точка): код файлу → .md-документація.
|
|
3
|
-
*
|
|
4
|
-
* Інверсія керування: веде цей JS, а локальна модель — лише сервіс перефразування.
|
|
5
|
-
* Stage 0 extractFacts — факти з коду (0 токенів)
|
|
6
|
-
* Stage 1 sectionInstructions — точкові промпти на кожну секцію (спільний KV-cached префікс)
|
|
7
|
-
* Stage 3 assemble — фіксовані заголовки/порядок + зрізання fence
|
|
8
|
-
* Режим `--oneshot` — база для порівняння (один промпт на весь документ).
|
|
9
|
-
*/
|
|
1
|
+
/** @see ./docs/docgen-gen.md */
|
|
10
2
|
import { readFileSync } from 'node:fs'
|
|
11
3
|
import { basename } from 'node:path'
|
|
12
4
|
import { request } from 'node:http'
|
|
5
|
+
import { env } from 'node:process'
|
|
6
|
+
import Anthropic from '@anthropic-ai/sdk'
|
|
13
7
|
import { extractFacts } from './docgen-extract.mjs'
|
|
14
|
-
import { sectionMessages, oneShotMessages } from './docgen-prompts.mjs'
|
|
8
|
+
import { sectionMessages, oneShotMessages, STYLE, oneShotPromptText } from './docgen-prompts.mjs'
|
|
9
|
+
|
|
10
|
+
const QUALITY_THRESHOLD = 70
|
|
15
11
|
|
|
16
12
|
/** Один виклик чату до ollama зі streaming (токени стримуються → socket активний, жодного timeout). */
|
|
17
13
|
async function ollamaChat(messages, { model, numPredict = 600 }) {
|
|
18
14
|
const body = JSON.stringify({
|
|
19
|
-
model,
|
|
15
|
+
model,
|
|
16
|
+
messages,
|
|
17
|
+
stream: true,
|
|
18
|
+
think: false,
|
|
20
19
|
options: { num_ctx: 8192, temperature: 0.2, num_predict: numPredict },
|
|
21
20
|
keep_alive: '15m'
|
|
22
21
|
})
|
|
23
22
|
return new Promise((resolve, reject) => {
|
|
24
23
|
const req = request(
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
{
|
|
25
|
+
hostname: 'localhost',
|
|
26
|
+
port: 11434,
|
|
27
|
+
path: '/api/chat',
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }
|
|
30
|
+
},
|
|
31
|
+
res => {
|
|
32
|
+
let text = '',
|
|
33
|
+
genTok = 0,
|
|
34
|
+
buf = ''
|
|
29
35
|
res.on('data', chunk => {
|
|
30
36
|
buf += chunk.toString()
|
|
31
37
|
const lines = buf.split('\n')
|
|
@@ -36,7 +42,9 @@ async function ollamaChat(messages, { model, numPredict = 600 }) {
|
|
|
36
42
|
const j = JSON.parse(line)
|
|
37
43
|
text += j.message?.content ?? ''
|
|
38
44
|
if (j.done) genTok = j.eval_count ?? 0
|
|
39
|
-
} catch {
|
|
45
|
+
} catch {
|
|
46
|
+
/* partial line */
|
|
47
|
+
}
|
|
40
48
|
}
|
|
41
49
|
})
|
|
42
50
|
res.on('end', () => resolve({ text, genTok }))
|
|
@@ -53,7 +61,10 @@ async function ollamaChat(messages, { model, numPredict = 600 }) {
|
|
|
53
61
|
function stripSection(text) {
|
|
54
62
|
let t = text.trim()
|
|
55
63
|
if (t.startsWith('```')) {
|
|
56
|
-
t = t
|
|
64
|
+
t = t
|
|
65
|
+
.replace(/^```[a-z]*\n?/, '')
|
|
66
|
+
.replace(/\n?```\s*$/, '')
|
|
67
|
+
.trim()
|
|
57
68
|
}
|
|
58
69
|
t = t.replace(/^#{1,6}\s+.*\n+/, '') // зрізати випадковий заголовок
|
|
59
70
|
return t.trim()
|
|
@@ -70,6 +81,131 @@ function stripSignatures(text) {
|
|
|
70
81
|
return t
|
|
71
82
|
}
|
|
72
83
|
|
|
84
|
+
/** Розбиває md на секції за ## заголовками → { огляд, поведінка, api, гарантіїповедінки, … } */
|
|
85
|
+
function parseSections(md) {
|
|
86
|
+
const result = {}
|
|
87
|
+
let cur = null
|
|
88
|
+
for (const line of md.split('\n')) {
|
|
89
|
+
const m = line.match(/^##\s+(.+)/)
|
|
90
|
+
if (m) {
|
|
91
|
+
cur = m[1].toLowerCase().replace(/[^а-яіїєґa-z0-9]/gi, '')
|
|
92
|
+
result[cur] = ''
|
|
93
|
+
} else if (cur) result[cur] += line + '\n'
|
|
94
|
+
}
|
|
95
|
+
return result
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Stage 2.5 — детермінований скоринг (0 токенів): перевіряє вихід проти фактів.
|
|
100
|
+
* @returns {{ score: number, issues: string[] }}
|
|
101
|
+
*/
|
|
102
|
+
function scoreDoc(md, facts) {
|
|
103
|
+
const s = parseSections(md)
|
|
104
|
+
let score = 100
|
|
105
|
+
const issues = []
|
|
106
|
+
|
|
107
|
+
if (!s['огляд']) {
|
|
108
|
+
score -= 25
|
|
109
|
+
issues.push('no-overview')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const behavior = s['поведінка'] ?? ''
|
|
113
|
+
if (behavior.length < 60) {
|
|
114
|
+
score -= 20
|
|
115
|
+
issues.push('short-behavior')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const guarantees = s['гарантіїповедінки'] ?? ''
|
|
119
|
+
// Будь-яка згадка "кеш" у Гарантіях коли файл не кешує — галюцинація
|
|
120
|
+
// Негація: "не кешує", "не має кешування", "без кешування", "немає кешу"
|
|
121
|
+
const cacheHit = /кеш/i.test(guarantees) && !/(?:не|без)\s+(?:\S+\s+)?кеш|немає\s+кеш/i.test(guarantees)
|
|
122
|
+
if (!facts.markers?.caches && cacheHit) {
|
|
123
|
+
score -= 20
|
|
124
|
+
issues.push('cache-hallucination')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Перевіряємо лише бектік-обгорнуті імена (`sym`) — уникаємо substring false positives
|
|
128
|
+
const hasName = (text, sym) => text.includes('`' + sym + '`')
|
|
129
|
+
for (const sym of facts.internalSymbols ?? []) {
|
|
130
|
+
const inDoc = hasName(guarantees, sym) || hasName(s['огляд'] ?? '', sym) || hasName(s['поведінка'] ?? '', sym)
|
|
131
|
+
if (inDoc) {
|
|
132
|
+
score -= 10
|
|
133
|
+
issues.push(`internal-name:${sym}`)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { score: Math.max(0, score), issues }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const SCORE_RUBRIC = `Оціни якість документації для JavaScript-модуля за 4 критеріями (1-3 кожен):
|
|
141
|
+
|
|
142
|
+
- огляд: 3=описує роль модуля в системі (ЩО і НАВІЩО); 2=частково розмитий; 1=відсутній або перераховує функції
|
|
143
|
+
- поведінка: 3=бізнес-терміни, без деталей реалізації; 2=деякі impl-деталі; 1=переважно реалізація або відсутня
|
|
144
|
+
- гарантії: 3=лише реальні інваріанти підтверджені кодом, без галюцинацій; 2=частково правильні; 1=вигадані або відсутні
|
|
145
|
+
- стиль: 3=без сигнатур/internal-імен, правильна markdown-структура; 2=дрібні порушення; 1=сигнатури/internal-імена/відсутні заголовки
|
|
146
|
+
|
|
147
|
+
Відповідай ТІЛЬКИ JSON без пояснень:
|
|
148
|
+
{"огляд":N,"поведінка":N,"гарантії":N,"стиль":N,"issues":["коротко про кожен мінус 1-5 слів"]}`
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Stage 2.5 cloud: Claude Haiku оцінює якість доку проти коду + фактів.
|
|
152
|
+
* Використовує найдешевшу хмарну модель — haiku — для мінімальної вартості судді.
|
|
153
|
+
* @returns {{ score: number, scores: object, issues: string[], tok: number }}
|
|
154
|
+
*/
|
|
155
|
+
async function cloudScoreDoc(md, facts, src, model = 'claude-haiku-4-5-20251001') {
|
|
156
|
+
const client = new Anthropic()
|
|
157
|
+
const factsTxt = [
|
|
158
|
+
facts.exports?.length ? `Публічні функції: ${facts.exports.map(e => e.name).join(', ')}` : '',
|
|
159
|
+
facts.internalSymbols?.length ? `Внутрішні (не публічні): ${facts.internalSymbols.join(', ')}` : '',
|
|
160
|
+
facts.markers?.caches ? 'Кешування: є' : 'Кешування: немає',
|
|
161
|
+
facts.markers?.network ? 'Мережа: є' : 'Мережа: немає',
|
|
162
|
+
facts.markers?.readOnly ? 'Read-only (не змінює файли/стан)' : ''
|
|
163
|
+
]
|
|
164
|
+
.filter(Boolean)
|
|
165
|
+
.join('\n')
|
|
166
|
+
|
|
167
|
+
const msg = await client.messages.create({
|
|
168
|
+
model,
|
|
169
|
+
max_tokens: 256,
|
|
170
|
+
system: SCORE_RUBRIC,
|
|
171
|
+
messages: [
|
|
172
|
+
{
|
|
173
|
+
role: 'user',
|
|
174
|
+
content: [
|
|
175
|
+
{ type: 'text', text: `ФАКТИ:\n${factsTxt}`, cache_control: { type: 'ephemeral' } },
|
|
176
|
+
{ type: 'text', text: `КОД:\n\`\`\`\n${src.slice(0, 4000)}\n\`\`\``, cache_control: { type: 'ephemeral' } },
|
|
177
|
+
{ type: 'text', text: `ДОКУМЕНТАЦІЯ:\n${md}` }
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
})
|
|
182
|
+
const tok = (msg.usage?.input_tokens ?? 0) + (msg.usage?.output_tokens ?? 0)
|
|
183
|
+
try {
|
|
184
|
+
const j = JSON.parse(msg.content[0]?.text ?? '{}')
|
|
185
|
+
const total = (((j.огляд ?? 0) + (j.поведінка ?? 0) + (j.гарантії ?? 0) + (j.стиль ?? 0)) / 12) * 100
|
|
186
|
+
return { score: Math.round(total), scores: j, issues: j.issues ?? [], tok }
|
|
187
|
+
} catch {
|
|
188
|
+
return { score: 50, scores: {}, issues: ['parse-error'], tok }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Tier 2: хмарний fallback через Claude коли local-score < QUALITY_THRESHOLD. */
|
|
193
|
+
async function claudeOneShot(facts, src, model = 'claude-sonnet-4-6') {
|
|
194
|
+
const client = new Anthropic()
|
|
195
|
+
const prompt = oneShotPromptText(facts, src)
|
|
196
|
+
const msg = await client.messages.create({
|
|
197
|
+
model,
|
|
198
|
+
max_tokens: 1500,
|
|
199
|
+
system: STYLE,
|
|
200
|
+
messages: [{ role: 'user', content: prompt }]
|
|
201
|
+
})
|
|
202
|
+
const text = msg.content[0]?.text ?? ''
|
|
203
|
+
const genTok = msg.usage?.output_tokens ?? 0
|
|
204
|
+
let md = stripSignatures(stripSection(text))
|
|
205
|
+
if (!md.startsWith('#')) md = `# ${basename(facts.relPath)}\n\n${md}`
|
|
206
|
+
return { md: md + '\n', genTok }
|
|
207
|
+
}
|
|
208
|
+
|
|
73
209
|
/** Stage 3: фіксовані заголовки у фіксованому порядку. */
|
|
74
210
|
function assemble(stem, sections) {
|
|
75
211
|
const order = [
|
|
@@ -106,28 +242,120 @@ async function generateOneShot(facts, src, model) {
|
|
|
106
242
|
return { md: md + '\n', genTok }
|
|
107
243
|
}
|
|
108
244
|
|
|
109
|
-
/**
|
|
110
|
-
|
|
245
|
+
/** Файли з sym ≥ цього значення одразу йдуть у Tier 2 (без локального проходу). */
|
|
246
|
+
const DEFAULT_SYM_THRESHOLD = 4
|
|
247
|
+
/** Максимальний час локальної генерації на один файл перед ескалацією у Tier 2. */
|
|
248
|
+
const LOCAL_TIMEOUT_MS = 5 * 60 * 1000
|
|
249
|
+
|
|
250
|
+
/** Повертає promise, що відхиляється через `ms` мс з повідомленням про timeout. */
|
|
251
|
+
function withTimeout(promise, ms) {
|
|
252
|
+
return Promise.race([
|
|
253
|
+
promise,
|
|
254
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`local timeout after ${ms / 1000}s`)), ms))
|
|
255
|
+
])
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Головний API: файл → { md, genTok, ms, score, issues, tier }.
|
|
260
|
+
*
|
|
261
|
+
* Routing (sym-threshold):
|
|
262
|
+
* sym < symThreshold → Tier 1 local (timeout: LOCAL_TIMEOUT_MS) + det-scorer
|
|
263
|
+
* → timeout або det-score < threshold → Tier 2
|
|
264
|
+
* sym >= symThreshold → Pre-routing одразу Tier 2
|
|
265
|
+
*
|
|
266
|
+
* @param {string} cloudModel — модель для Tier 2 генерації (Sonnet за замовч.)
|
|
267
|
+
*/
|
|
268
|
+
export async function generateDoc(
|
|
269
|
+
file,
|
|
270
|
+
{
|
|
271
|
+
model = 'gemma3:4b',
|
|
272
|
+
mode = 'orchestrated',
|
|
273
|
+
cloudModel = 'claude-sonnet-4-6',
|
|
274
|
+
threshold = QUALITY_THRESHOLD,
|
|
275
|
+
symThreshold = DEFAULT_SYM_THRESHOLD
|
|
276
|
+
} = {}
|
|
277
|
+
) {
|
|
111
278
|
const src = readFileSync(file, 'utf8')
|
|
112
279
|
const facts = extractFacts(src, file)
|
|
113
280
|
const t0 = Date.now()
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
281
|
+
|
|
282
|
+
// Pre-routing: складні файли (sym ≥ symThreshold) → одразу Tier 2, не витрачаємо local-час
|
|
283
|
+
const complexity = facts.internalSymbols?.length ?? 0
|
|
284
|
+
if (complexity >= symThreshold && env.ANTHROPIC_API_KEY) {
|
|
285
|
+
const r2 = await claudeOneShot(facts, src, cloudModel)
|
|
286
|
+
return {
|
|
287
|
+
...r2,
|
|
288
|
+
ms: Date.now() - t0,
|
|
289
|
+
score: null,
|
|
290
|
+
issues: [`pre-routed:sym=${complexity}`],
|
|
291
|
+
tier: 2,
|
|
292
|
+
model: cloudModel
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Tier 1: локальна генерація з timeout 5 хв — при перевищенні одразу Tier 2
|
|
297
|
+
let r
|
|
298
|
+
try {
|
|
299
|
+
const localPromise =
|
|
300
|
+
facts.unsupported || mode === 'oneshot'
|
|
301
|
+
? generateOneShot(facts, src, model)
|
|
302
|
+
: generateOrchestrated(facts, src, model)
|
|
303
|
+
r = await withTimeout(localPromise, LOCAL_TIMEOUT_MS)
|
|
304
|
+
} catch (e) {
|
|
305
|
+
if (env.ANTHROPIC_API_KEY) {
|
|
306
|
+
const r2 = await claudeOneShot(facts, src, cloudModel)
|
|
307
|
+
return {
|
|
308
|
+
...r2,
|
|
309
|
+
ms: Date.now() - t0,
|
|
310
|
+
score: null,
|
|
311
|
+
issues: [`local-timeout: ${e.message}`],
|
|
312
|
+
tier: 2,
|
|
313
|
+
model: cloudModel
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
throw e
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Stage 2.5: детермінований скоринг (0 токенів) — gate перед Tier 2
|
|
320
|
+
const { score: detScore, issues: detIssues } = scoreDoc(r.md, facts)
|
|
321
|
+
|
|
322
|
+
if (detScore < threshold && env.ANTHROPIC_API_KEY) {
|
|
323
|
+
const r2 = await claudeOneShot(facts, src, cloudModel)
|
|
324
|
+
return { ...r2, ms: Date.now() - t0, score: detScore, issues: detIssues, tier: 2, model: cloudModel }
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return { ...r, ms: Date.now() - t0, score: detScore, issues: detIssues, tier: 1, model }
|
|
120
328
|
}
|
|
121
329
|
|
|
122
|
-
// CLI: node docgen-gen.mjs <file> [--oneshot] [--model <m>]
|
|
330
|
+
// CLI: node docgen-gen.mjs <file> [--oneshot] [--model <m>] [--sym-threshold N] [--tier-only]
|
|
123
331
|
import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
|
|
124
332
|
if (isRunAsCli(import.meta.url)) {
|
|
125
333
|
const args = process.argv.slice(2)
|
|
126
334
|
const file = args.find(a => !a.startsWith('--'))
|
|
127
335
|
const mode = args.includes('--oneshot') ? 'oneshot' : 'orchestrated'
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
336
|
+
const tierOnly = args.includes('--tier-only')
|
|
337
|
+
const mi = args.indexOf('--model')
|
|
338
|
+
const model = mi >= 0 ? args[mi + 1] : 'gemma3:4b'
|
|
339
|
+
const si = args.indexOf('--sym-threshold')
|
|
340
|
+
const symThreshold = si >= 0 ? Number(args[si + 1]) : DEFAULT_SYM_THRESHOLD
|
|
341
|
+
if (!file) {
|
|
342
|
+
console.error('Usage: node docgen-gen.mjs <file> [--oneshot] [--model <m>] [--sym-threshold N] [--tier-only]')
|
|
343
|
+
process.exit(1)
|
|
344
|
+
}
|
|
345
|
+
if (tierOnly) {
|
|
346
|
+
const src = readFileSync(file, 'utf8')
|
|
347
|
+
const facts = extractFacts(src, file)
|
|
348
|
+
const sym = facts.internalSymbols?.length ?? 0
|
|
349
|
+
const icon = sym >= symThreshold ? '☁️ ' : '💻'
|
|
350
|
+
const label =
|
|
351
|
+
sym >= symThreshold
|
|
352
|
+
? `Tier 2 cloud (sym=${sym} ≥ ${symThreshold}, pre-routed)`
|
|
353
|
+
: `Tier 1 local (sym=${sym} < ${symThreshold})`
|
|
354
|
+
process.stdout.write(`${icon} ${label} | ${file}\n`)
|
|
355
|
+
process.exit(0)
|
|
356
|
+
}
|
|
357
|
+
const r = await generateDoc(file, { model, mode, symThreshold })
|
|
358
|
+
const issuesTxt = r.issues?.length ? ` issues=${r.issues.join(',')}` : ''
|
|
359
|
+
process.stderr.write(`[tier${r.tier} ${mode}] ${r.ms}ms / ${r.genTok} tok / score=${r.score}${issuesTxt}\n`)
|
|
132
360
|
process.stdout.write(r.md)
|
|
133
361
|
}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Глоби, які `docgen` завжди ігнорує.
|
|
3
|
-
*
|
|
4
|
-
* Це окремий snippet-модуль: список правиться тут, scanner лише читає його
|
|
5
|
-
* через predicate. Патерни пишуться в posix-формі відносно кореня проєкту.
|
|
6
|
-
*/
|
|
1
|
+
/** @see ./docs/docgen-ignore.md */
|
|
7
2
|
import picomatch from 'picomatch'
|
|
8
3
|
|
|
9
4
|
/** Базовий список glob-ів для `docgen` ignore. */
|
|
@@ -20,7 +15,9 @@ export const DOCGEN_IGNORE_GLOBS = Object.freeze([
|
|
|
20
15
|
'.worktrees/**',
|
|
21
16
|
'**/benchmarks/**',
|
|
22
17
|
'**/demo/**',
|
|
23
|
-
'**/docs/**'
|
|
18
|
+
'**/docs/**',
|
|
19
|
+
'npm/reports/**',
|
|
20
|
+
'npm/bin/**'
|
|
24
21
|
])
|
|
25
22
|
|
|
26
23
|
const IGNORE_MATCHERS = DOCGEN_IGNORE_GLOBS.map(glob => picomatch(glob, { dot: true }))
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stage 1 docgen-конвеєра: факт-лист + код → точкові промпти на кожну секцію.
|
|
3
|
-
*
|
|
4
|
-
* v2 — СЕКЦІЙНО-МІНІМАЛЬНИЙ контекст: код іде ЛИШЕ у `Поведінку`. `Огляд` бере тільки
|
|
5
|
-
* header, `API` — лише список експортів, `Гарантії` — лише markers. Так інгест коду
|
|
6
|
-
* оплачується один раз (а не на кожну секцію), і оркестрація перестає програвати в часі.
|
|
7
|
-
*/
|
|
1
|
+
/** @see ./docs/docgen-prompts.md */
|
|
8
2
|
|
|
9
|
-
const STYLE = [
|
|
3
|
+
export const STYLE = [
|
|
10
4
|
'Ти технічний письменник. Пишеш лаконічну ПОВЕДІНКОВУ документацію до коду українською, чистим Markdown.',
|
|
11
5
|
'Пиши ЩО і НАВІЩО, не ЯК. Без вступів і висновків. Не обгортай у ```-блок.',
|
|
12
6
|
'Заборонено: сигнатури, типи, параметри функцій; перелік stdlib-модулів; опис regex чи внутрішніх приватних імен.'
|
|
@@ -28,7 +22,10 @@ function factsSummary(facts) {
|
|
|
28
22
|
return lines.join('\n')
|
|
29
23
|
}
|
|
30
24
|
|
|
31
|
-
const msgs = (system, user) => [
|
|
25
|
+
const msgs = (system, user) => [
|
|
26
|
+
{ role: 'system', content: system },
|
|
27
|
+
{ role: 'user', content: user }
|
|
28
|
+
]
|
|
32
29
|
|
|
33
30
|
/**
|
|
34
31
|
* Секційні набори messages з МІНІМАЛЬНИМ контекстом під кожну секцію.
|
|
@@ -42,33 +39,45 @@ export function sectionMessages(facts, src) {
|
|
|
42
39
|
|
|
43
40
|
// Огляд — лише факти (без коду)
|
|
44
41
|
out.push({
|
|
45
|
-
key: 'overview',
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
key: 'overview',
|
|
43
|
+
numPredict: 220,
|
|
44
|
+
messages: msgs(
|
|
45
|
+
`${STYLE}\n\nВІДОМІ ФАКТИ:\n${factsTxt}`,
|
|
46
|
+
'Напиши вміст секції «Огляд»: 1-3 речення — що файл робить і навіщо існує (роль у системі). Без заголовка, без переліку функцій.'
|
|
47
|
+
)
|
|
48
48
|
})
|
|
49
49
|
|
|
50
50
|
// Поведінка — ЄДИНА секція, якій потрібен код
|
|
51
51
|
out.push({
|
|
52
|
-
key: 'behavior',
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
key: 'behavior',
|
|
53
|
+
numPredict: 500,
|
|
54
|
+
messages: msgs(
|
|
55
|
+
`${STYLE}\n\nФАЙЛ ${facts.relPath}:\n\`\`\`\n${src}\n\`\`\`\n\nВІДОМІ ФАКТИ:\n${factsTxt}`,
|
|
56
|
+
`Напиши вміст секції «Поведінка»: ${multi ? 'для кожної публічної функції — один короткий пункт «що вона робить»' : 'нумерований алгоритм у бізнес-термінах'}. Якщо у фактах є свідомі пропуски шляхів — згадай їх там, де доречно (не вигадуй інших «не перевіряє»). НЕ пиши аргументи функцій у дужках, без regex.${facts.internalSymbols?.length ? ` НЕ згадуй за іменами службові функції: ${facts.internalSymbols.join(', ')}.` : ''} Без заголовка.`
|
|
57
|
+
)
|
|
55
58
|
})
|
|
56
59
|
|
|
57
60
|
// API — лише список експортів (без коду)
|
|
58
61
|
if (multi || facts.exports?.some(e => e.desc)) {
|
|
59
62
|
const list = facts.exports.map(e => `- ${e.name}: ${e.desc || '(сформулюй стисло з наміру файлу)'}`).join('\n')
|
|
60
63
|
out.push({
|
|
61
|
-
key: 'api',
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
key: 'api',
|
|
65
|
+
numPredict: 320,
|
|
66
|
+
messages: msgs(
|
|
67
|
+
STYLE,
|
|
68
|
+
`Перепиши цей список як стислі маркери «назва — що робить», СВОЇМИ словами (не копіюй дослівно), без типів і сигнатур. Використовуй РІВНО ці назви, не додавай і не прибирай:\n${list}\nБез заголовка.`
|
|
69
|
+
)
|
|
64
70
|
})
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
// Гарантії — лише markers (без коду)
|
|
68
74
|
out.push({
|
|
69
|
-
key: 'guarantees',
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
key: 'guarantees',
|
|
76
|
+
numPredict: 300,
|
|
77
|
+
messages: msgs(
|
|
78
|
+
`${STYLE}\n\nВІДОМІ ФАКТИ:\n${factsTxt}`,
|
|
79
|
+
'Напиши вміст секції «Гарантії поведінки» як маркери-інваріанти СУВОРО на основі ВІДОМИХ ФАКТІВ (read-only, fail-safe, пропуски). Згадуй кеш ЛИШЕ якщо у фактах прямо є «Кешує». Без сигнатур у дужках і без імен внутрішніх структур/Map-ів/кешів. Не вигадуй гарантій, яких немає у фактах. Без заголовка.'
|
|
80
|
+
)
|
|
72
81
|
})
|
|
73
82
|
|
|
74
83
|
return out
|
|
@@ -77,6 +86,14 @@ export function sectionMessages(facts, src) {
|
|
|
77
86
|
/** One-shot messages (база для порівняння). */
|
|
78
87
|
export function oneShotMessages(facts, src) {
|
|
79
88
|
const multi = (facts.exports?.length || 0) > 1
|
|
80
|
-
return msgs(
|
|
81
|
-
|
|
89
|
+
return msgs(
|
|
90
|
+
STYLE,
|
|
91
|
+
`Напиши документацію для файлу. Секції: ## Огляд (1-3 речення), ## Поведінка (нумерований/маркований алгоритм), ${multi ? '## Публічний API (назва + що робить), ' : ''}## Гарантії поведінки.\n\nФАЙЛ ${facts.relPath}:\n\`\`\`\n${src}\n\`\`\``
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Лише текст user-промпту для one-shot (для хмарного fallback через Anthropic SDK). */
|
|
96
|
+
export function oneShotPromptText(facts, src) {
|
|
97
|
+
const multi = (facts.exports?.length || 0) > 1
|
|
98
|
+
return `Напиши документацію для файлу. Секції: ## Огляд (1-3 речення), ## Поведінка (нумерований/маркований алгоритм), ${multi ? '## Публічний API (назва + що робить), ' : ''}## Гарантії поведінки.\n\nФАЙЛ ${facts.relPath}:\n\`\`\`\n${src}\n\`\`\``
|
|
82
99
|
}
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* docgen scanner — детермінований обхід проєкту для скілу `docgen`.
|
|
3
|
-
*
|
|
4
|
-
* Друкує JSON-список кодових файлів із відносними `sourcePath`/`docPath`
|
|
5
|
-
* (тека `docs/` поряд із джерелом). Рішення про overwrite/skip приймає скіл —
|
|
6
|
-
* scanner лише лістить і ставить прапор `exists`. LLM/мережі тут немає: уся
|
|
7
|
-
* генерація доки — у субагентах скілу.
|
|
8
|
-
*/
|
|
1
|
+
/** @see ./docs/docgen-scan.md */
|
|
9
2
|
// eslint-disable-next-line unicorn/import-style
|
|
10
3
|
import path from 'node:path'
|
|
11
4
|
import { existsSync, readdirSync, statSync } from 'node:fs'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# docgen-extract.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл `extractFacts` витягує інформацію про поведінку коду, формуючи об'єкт "факт-лист". Цей факт-лист використовується Stage 1 docgen-конвеєра для створення точкових промптів. Функція є ключовою частиною детермінованого процесу генерації документації, виключаючи використання великих мовних моделей.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
1. **Обробка даних:** Система аналізує код, витягуючи інформацію про експортовані функції, імпорти та їхні описи.
|
|
10
|
+
2. **Ідентифікація відхилень:** Система визначає, чи використовується код для виконання операцій, які можуть призвести до помилок, наприклад, запис у файли, створення директорій, видалення файлів, обробка мережевих запитів або використання try/catch блоків.
|
|
11
|
+
3. **Виявлення пропущених шляхів:** Система виявляє, чи використовується код для обробки певних шляхів файлів, таких як `.github`, `.git`, `node_modules` або `base/`, `ua/`, `.firebase`.
|
|
12
|
+
4. **Визначення кешування:** Система визначає, чи використовується код для кешування даних, наприклад, за допомогою Map або Cache.
|
|
13
|
+
5. **Визначення відсутності обробки помилок:** Система визначає, чи використовується код для обробки помилок, наприклад, за допомогою try/catch блоків.
|
|
14
|
+
6. **Визначення мережевих операцій:** Система визначає, чи використовується код для виконання мережевих операцій, наприклад, за допомогою fetch або axios.
|
|
15
|
+
7. **Вихідні дані:** Система надає вихідні дані у вигляді об'єкта, що містить витягнуту інформацію про код, а також виявлені відхилення та пропущені шляхи.
|
|
16
|
+
|
|
17
|
+
## Публічний API
|
|
18
|
+
|
|
19
|
+
extractFacts — Витягує факти з коду файлу, представляючи їх у вигляді списку.
|
|
20
|
+
|
|
21
|
+
## Гарантії поведінки
|
|
22
|
+
|
|
23
|
+
- Екстрагує з коду список фактів.
|
|
24
|
+
- Повертає об'єкт з витягнутими фактами.
|
|
25
|
+
- Не викликає винятків при невдачі.
|
|
26
|
+
- Кешує результати для одного прогону.
|
|
27
|
+
- Не використовує мережу.
|
|
28
|
+
- Не обробляє файли/каталоги .github, .git, node_modules, base/, ua/, .firebase.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# docgen-gen.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Цей файл генерує документацію на основі коду, використовуючи конвеєр обробки. Він автоматично створює Markdown-файли, що містять опис коду, використовуючи декілька етапів, включаючи вилучення фактів та створення текстових описів. Ця функція є ключовою частиною системи документування, забезпечуючи автоматизований та послідовний процес створення документації.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
1. Модуль визначає основну функцію `generateDoc`, яка приймає код джерела та налаштовує параметри для генерації документації.
|
|
10
|
+
2. Вхідний код обробляється для вилучення ключових фактів про функціональність модуля, включаючи назви функцій та їх призначення.
|
|
11
|
+
3. Вилучені факти використовуються для оцінки якості документації, зокрема, перевіряється, чи опис відповідає реальній поведінці модуля.
|
|
12
|
+
4. Оцінка якості здійснюється за допомогою декількох критеріїв: опис має бути чітким та описовим, а поведінка – узгодженою з кодом.
|
|
13
|
+
5. Якщо оцінка якості низька, використовується хмарний сервіс Claude для перевірки документації та надання рекомендацій щодо покращення.
|
|
14
|
+
6. У випадку, коли оцінка якості висока, генерується документ, який містить огляд модуля, його поведінку, API та гарантії поведінки.
|
|
15
|
+
7. Документація форматується у Markdown, з використанням фіксованого набору заголовків та розділів.
|
|
16
|
+
8. Під час генерації документації враховуються можливі пропуски в інформації про шляхи, що може вплинути на оцінку якості.
|
|
17
|
+
9. Використовується хмарний сервіс Claude для оцінки якості документації та надання рекомендацій щодо покращення.
|
|
18
|
+
10. Генерується документ, який містить огляд модуля, його поведінку, API та гарантії поведінки.
|
|
19
|
+
|
|
20
|
+
## Публічний API
|
|
21
|
+
|
|
22
|
+
- generateDoc — Генерує документацію з файлу, включаючи метадані, токени, оцінки, проблеми та рівень.
|
|
23
|
+
- Routing (sym-threshold) — Розподіляє обробку на основі значення символу, визначаючи рівень (Tier) та тип рефері (Haiku або без хмарного рефері).
|
|
24
|
+
- sym < BORDERLINE_SYM_LOW — Tier 1 без хмарного рефері.
|
|
25
|
+
- sym ∈ [BORDERLINE_SYM_LOW, symThreshold) — Tier 1 з хмарним рефері (Haiku).
|
|
26
|
+
- sym >= symThreshold — Відразу Tier 2.
|
|
27
|
+
- scoreCloud=true — Автоматично запускає хмарний рефері для всіх Tier 1.
|
|
28
|
+
|
|
29
|
+
## Гарантії поведінки
|
|
30
|
+
|
|
31
|
+
- Конвеєр завжди генерує .md-документацію на основі вхідного коду.
|
|
32
|
+
- Конвеєр використовує інверсію керування, де JS-код визначає весь процес.
|
|
33
|
+
- Stage 0 (extractFacts) не впливає на вихідну документацію.
|
|
34
|
+
- Stage 1 (sectionInstructions) генерує точкові промпти для кожної секції коду.
|
|
35
|
+
- Stage 2 (stripSignatures) завжди видаляє інформацію про сигнатури.
|
|
36
|
+
- Stage 2.5 (scoreDoc) оцінює документацію за фактами.
|
|
37
|
+
- Stage 3 (assemble) збирає документацію з фіксованими заголовками та порядком.
|
|
38
|
+
- При низькому балі скорингу (claudeOneShot) використовується хмарний сервіс для перефразування.
|
|
39
|
+
- При сим-значенні нижче BORDERLINE_SYM_LOW, документація генерується локально без використання хмарного рефері.
|
|
40
|
+
- При сим-значенні між BORDERLINE_SYM_LOW та DEFAULT_SYM_THRESHOLD, документація генерується локально та оцінюється за допомогою cloudScoreDoc (Haiku).
|
|
41
|
+
- При сим-значенні, що перевищує DEFAULT_SYM_THRESHOLD, документація генерується безпосередньо, без локальної оцінки
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# docgen-ignore.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Цей файл містить список глобальних файлів, які `docgen` ігнорує під час генерації документації. Він використовується для визначення, які файли не потрібно аналізувати, забезпечуючи ефективність процесу генерації та запобігаючи включенню нерелевантної інформації. Цей список є основою для predicate, який scanner використовує для визначення, чи потрібно читати певний файл.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
DOCGEN_IGNORE_GLOBS: визначає список глобів, які `docgen` ігнорує.
|
|
10
|
+
isDocgenIgnored: перевіряє, чи шлях має бути пропущений `docgen`, використовуючи глоби.
|
|
11
|
+
|
|
12
|
+
## Публічний API
|
|
13
|
+
|
|
14
|
+
- DOCGEN_IGNORE_GLOBS — Список glob-ів, які ігнорує `docgen`.
|
|
15
|
+
- isDocgenIgnored — Перевіряє, чи потрібно пропустити шлях під час генерації документації.
|
|
16
|
+
|
|
17
|
+
## Гарантії поведінки
|
|
18
|
+
|
|
19
|
+
- `DOCGEN_IGNORE_GLOBS` завжди ігнорує певний набір шляхів.
|
|
20
|
+
- `isDocgenIgnored` повертає `true` якщо шлях ігнорується.
|
|
21
|
+
- Шляхи `.git` та `node_modules` завжди ігноруються.
|
|
22
|
+
- Файл є read-only.
|
|
23
|
+
- У разі невдачі повертає `false` або `null`.
|
|
24
|
+
- Результат прогону кешується для подальшого використання.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# docgen-prompts.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Цей файл містить опис коду для конвеєра docgen. Він визначає точкові промпти для кожної секції коду, що генерується. Це дозволяє ефективно генерувати документацію на основі коду.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
Напиши вміст секції «Поведінка»: для кожної публічної функції — один короткий пункт «що вона робить». Якщо у фактах є свідомі пропуски шляхів — згадай їх там, де доречно (не вигадуй інших «не перевіряє»). НЕ пиши аргументи функцій у дужках, без regex. Без заголовка.
|
|
10
|
+
|
|
11
|
+
## Публічний API
|
|
12
|
+
|
|
13
|
+
- STYLE — Підготовка документації на першому етапі генерації: створення факт-листа та коду, а потім генерація точкових промптів для кожної секції.
|
|
14
|
+
- v2 — Мінімальний контекст секції: код розміщується лише в `Поведінку`. `Огляд` містить лише заголовок, `API` – список експортів, `Гарантії` – маркери. Це дозволяє один раз обробити інсульт коду, а не секцію за секцією.
|
|
15
|
+
- sectionMessages — Набори повідомлень для кожної секції з мінімальним контекстом. Код розміщується лише в `Поведінку`, решта – на факт-листи.
|
|
16
|
+
- oneShotMessages — База повідомлень для порівняння.
|
|
17
|
+
- oneShotPromptText — Текст user-промпту для one-shot (для резервного копіювання в хмарі через Anthropic SDK).
|
|
18
|
+
|
|
19
|
+
## Гарантії поведінки
|
|
20
|
+
|
|
21
|
+
- `docgen-конвеєр` кешує результати генерації промптів для кожної секції.
|
|
22
|
+
- `Огляд` містить лише заголовок секції.
|
|
23
|
+
- `API` містить лише список експортів секції.
|
|
24
|
+
- `Гарантії` містять лише маркери.
|