@nitra/cursor 12.15.1 → 12.16.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/CHANGELOG.md +16 -0
- package/bin/n-cursor.js +1 -1
- package/lib/docs/index.md +9 -6
- package/lib/docs/pi-agent-fix.md +28 -0
- package/lib/docs/pi-agent-skill.md +36 -0
- package/lib/docs/pi-model-tiers.md +46 -0
- package/lib/docs/pi-one-shot.md +34 -0
- package/lib/docs/pi-telemetry-store.md +33 -0
- package/lib/docs/pi-trace.md +27 -0
- package/lib/docs/pi-write-guard.md +32 -0
- package/lib/pi-agent-fix.mjs +253 -0
- package/lib/pi-agent-skill.mjs +181 -0
- package/lib/pi-model-tiers.mjs +109 -0
- package/lib/pi-one-shot.mjs +129 -0
- package/lib/pi-telemetry-store.mjs +0 -0
- package/lib/pi-trace.mjs +40 -0
- package/lib/pi-write-guard.mjs +147 -0
- package/package.json +5 -1
- package/rules/doc-files/js/docgen-files-batch.mjs +20 -5
- package/rules/doc-files/js/docgen-gen.mjs +42 -25
- package/rules/doc-files/js/docgen-judge-measure.mjs +16 -13
- package/rules/doc-files/js/docgen-judge.mjs +11 -9
- package/rules/doc-files/js/docs/docgen-files-batch.md +3 -20
- package/rules/doc-files/js/docs/docgen-gen.md +3 -20
- package/rules/doc-files/js/docs/docgen-judge-measure.md +3 -18
- package/rules/doc-files/js/docs/docgen-judge.md +3 -22
- package/rules/npm-module/js/docs/skill_meta.md +22 -15
- package/rules/npm-module/js/skill_meta.mjs +5 -1
- package/rules/text/js/cspell-fix.mjs +15 -16
- package/rules/text/js/docs/cspell-fix.md +16 -9
- package/rules/text/main.mjs +4 -4
- package/schemas/skill-meta.json +8 -0
- package/scripts/docs/skills-cli.md +21 -25
- package/scripts/lib/adr/docs/normalize-cli.md +3 -20
- package/scripts/lib/adr/docs/normalize-pipeline.md +3 -33
- package/scripts/lib/adr/normalize-cli.mjs +2 -2
- package/scripts/lib/adr/normalize-pipeline.mjs +78 -44
- package/scripts/lib/docs/skill-meta.md +27 -10
- package/scripts/lib/fix/docs/escalation-log.md +10 -9
- package/scripts/lib/fix/docs/orchestrator.md +13 -20
- package/scripts/lib/fix/escalation-log.mjs +1 -1
- package/scripts/lib/fix/orchestrator.mjs +65 -31
- package/scripts/lib/skill-meta.mjs +22 -0
- package/scripts/skills-cli.mjs +52 -14
- package/scripts/utils/ast-extract.mjs +105 -0
- package/scripts/utils/docs/ast-extract.md +30 -0
- package/lib/docs/llm.md +0 -33
- package/lib/docs/models.md +0 -48
- package/lib/docs/omlx-trace.md +0 -49
- package/lib/docs/omlx.md +0 -41
- package/lib/llm.mjs +0 -215
- package/lib/models.mjs +0 -75
- package/lib/omlx-trace.mjs +0 -158
- package/lib/omlx.mjs +0 -220
- package/scripts/lib/fix/docs/llm-fix-apply.md +0 -31
- package/scripts/lib/fix/docs/llm-lint-fix.md +0 -31
- package/scripts/lib/fix/docs/llm-worker.md +0 -28
- package/scripts/lib/fix/docs/verbose-block.md +0 -27
- package/scripts/lib/fix/llm-fix-apply.mjs +0 -113
- package/scripts/lib/fix/llm-lint-fix.mjs +0 -82
- package/scripts/lib/fix/llm-worker.mjs +0 -346
- package/scripts/lib/fix/verbose-block.mjs +0 -82
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { readFileSync, existsSync } from 'node:fs'
|
|
3
3
|
import { basename } from 'node:path'
|
|
4
4
|
import { env } from 'node:process'
|
|
5
|
-
import { resolveModel } from '../../../lib/
|
|
6
|
-
import {
|
|
5
|
+
import { resolveModel } from '../../../lib/pi-model-tiers.mjs'
|
|
6
|
+
import { runOneShot } from '../../../lib/pi-one-shot.mjs'
|
|
7
7
|
import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
|
|
8
8
|
import { docPathForSource } from './docgen-scan.mjs'
|
|
9
9
|
import { extractFacts } from './docgen-extract.mjs'
|
|
@@ -23,16 +23,25 @@ import {
|
|
|
23
23
|
let llmMeter = { calls: 0, ms: 0 }
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Обгортка
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* @param {
|
|
30
|
-
* @
|
|
26
|
+
* Обгортка LLM-виклику з обліком (тепер async поверх pi-one-shot): лічить кількість
|
|
27
|
+
* викликів і сумарний час. Генерація одного файлу послідовна — лічильник без гонок.
|
|
28
|
+
* Зберігає старий інтерфейс accountant'а: повертає рядок-вміст, кидає на помилці.
|
|
29
|
+
* @param {Array<{role:string,content:string}>} messages чат-повідомлення
|
|
30
|
+
* @param {string} model model-id (`provider/id`)
|
|
31
|
+
* @param {{ timeoutMs?: number, caller?: string }} [opts] ліміт/мітка (temperature/maxTokens не підтримуються pi-one-shot)
|
|
32
|
+
* @returns {Promise<string>} відповідь моделі
|
|
31
33
|
*/
|
|
32
|
-
function callLlm(
|
|
34
|
+
async function callLlm(messages, model, opts = {}) {
|
|
33
35
|
const started = Date.now()
|
|
34
36
|
try {
|
|
35
|
-
|
|
37
|
+
const res = await runOneShot({
|
|
38
|
+
messages,
|
|
39
|
+
modelSpec: model,
|
|
40
|
+
timeoutMs: opts.timeoutMs,
|
|
41
|
+
caller: opts.caller ?? 'docgen'
|
|
42
|
+
})
|
|
43
|
+
if (res.error) throw new Error(res.error)
|
|
44
|
+
return res.content
|
|
36
45
|
} finally {
|
|
37
46
|
llmMeter.calls += 1
|
|
38
47
|
llmMeter.ms += Date.now() - started
|
|
@@ -273,10 +282,12 @@ export function scoreDoc(md, facts, { anchors = null, src = '' } = {}) {
|
|
|
273
282
|
* @param {number} timeoutMs ліміт на один виклик
|
|
274
283
|
* @returns {string} фінальний текст секції
|
|
275
284
|
*/
|
|
276
|
-
function critiqueRefineSection(sectionKey, draft, facts, anchors, model, timeoutMs) {
|
|
277
|
-
const critique = callLlm(criticMessages(sectionKey, draft, facts, anchors), model, { timeoutMs }).trim()
|
|
285
|
+
async function critiqueRefineSection(sectionKey, draft, facts, anchors, model, timeoutMs) {
|
|
286
|
+
const critique = (await callLlm(criticMessages(sectionKey, draft, facts, anchors), model, { timeoutMs })).trim()
|
|
278
287
|
if (!critique || CRITIC_NONE_RE.test(critique) || critique.length < 12) return draft
|
|
279
|
-
const refined =
|
|
288
|
+
const refined = (
|
|
289
|
+
await callLlm(refineMessages(sectionKey, draft, critique, facts, anchors), model, { timeoutMs })
|
|
290
|
+
).trim()
|
|
280
291
|
return stripSignatures(stripSection(refined)) || draft
|
|
281
292
|
}
|
|
282
293
|
|
|
@@ -301,8 +312,8 @@ function apiNeedsRefine(facts) {
|
|
|
301
312
|
* @param {{ intent?: string|null }} [opts] захищена секція «Призначення» для збереження
|
|
302
313
|
* @returns {{ md: string }} зібраний документ
|
|
303
314
|
*/
|
|
304
|
-
function oneShotDoc(facts, src, model, timeoutMs = LOCAL_TIMEOUT_MS, { intent = null } = {}) {
|
|
305
|
-
const text = callLlm(oneShotMessages(facts, src), model, { timeoutMs })
|
|
315
|
+
async function oneShotDoc(facts, src, model, timeoutMs = LOCAL_TIMEOUT_MS, { intent = null } = {}) {
|
|
316
|
+
const text = await callLlm(oneShotMessages(facts, src), model, { timeoutMs })
|
|
306
317
|
let md = stripSignatures(stripSection(text))
|
|
307
318
|
if (!md.startsWith('#')) md = `# ${basename(facts.relPath)}\n\n${md}`
|
|
308
319
|
return { md: insertProtected(md + '\n', intent) }
|
|
@@ -339,27 +350,33 @@ function assemble(stem, sections) {
|
|
|
339
350
|
* @param {{ anchors?: object|null, temperature?: number, intent?: string|null }} [opts] анкори, температура, захищена секція як контекст
|
|
340
351
|
* @returns {{ md: string }} зібраний документ
|
|
341
352
|
*/
|
|
342
|
-
function orchestratedDoc(
|
|
353
|
+
async function orchestratedDoc(
|
|
354
|
+
facts,
|
|
355
|
+
src,
|
|
356
|
+
model,
|
|
357
|
+
timeoutMs,
|
|
358
|
+
{ anchors = null, temperature = 0.2, intent = null } = {}
|
|
359
|
+
) {
|
|
343
360
|
const sections = {}
|
|
344
361
|
const anc = anchors ?? extractAnchors(src)
|
|
345
362
|
// E3: «Гарантії» — детермінований шаблон з markers (0 LLM-запитів, 0 generic-фраз)
|
|
346
363
|
sections.guarantees = guaranteesFromMarkers(facts)
|
|
347
364
|
// Спершу Поведінка (+API) — секції з фактажем
|
|
348
365
|
for (const s of sectionMessages(facts, src, anc, intent)) {
|
|
349
|
-
let draft = stripSignatures(stripSection(callLlm(s.messages, model, { timeoutMs, temperature })))
|
|
366
|
+
let draft = stripSignatures(stripSection(await callLlm(s.messages, model, { timeoutMs, temperature })))
|
|
350
367
|
// E2: critique→refine для API, коли всі описи порожні (модель зриває на generic)
|
|
351
368
|
if (s.key === 'api' && apiNeedsRefine(facts)) {
|
|
352
|
-
draft = critiqueRefineSection(s.key, draft, facts, anc, model, timeoutMs)
|
|
369
|
+
draft = await critiqueRefineSection(s.key, draft, facts, anc, model, timeoutMs)
|
|
353
370
|
}
|
|
354
371
|
sections[s.key] = draft
|
|
355
372
|
}
|
|
356
373
|
// R3: «Огляд» — ОСТАННІМ, узагальненням уже написаної Поведінки (не голого факт-листа)
|
|
357
374
|
let overview = stripSignatures(
|
|
358
375
|
stripSection(
|
|
359
|
-
callLlm(overviewMessages(facts, sections.behavior ?? '', anc, intent), model, { timeoutMs, temperature })
|
|
376
|
+
await callLlm(overviewMessages(facts, sections.behavior ?? '', anc, intent), model, { timeoutMs, temperature })
|
|
360
377
|
)
|
|
361
378
|
)
|
|
362
|
-
overview = critiqueRefineSection('overview', overview, facts, anc, model, timeoutMs)
|
|
379
|
+
overview = await critiqueRefineSection('overview', overview, facts, anc, model, timeoutMs)
|
|
363
380
|
sections.overview = overview
|
|
364
381
|
// Варіант B: дослівно повертаємо захищений блок у фіксовану позицію
|
|
365
382
|
return { md: insertProtected(assemble(basename(facts.relPath), sections), intent) }
|
|
@@ -398,7 +415,7 @@ export const DEFAULT_LOCAL_MODEL = env.N_CURSOR_DOCGEN_MODEL ?? resolveModel('mi
|
|
|
398
415
|
* @param {{ model?: string, threshold?: number, existingMd?: string|null }} [opts] model-id, поріг degraded, наявна дока (для збереження захищеної секції)
|
|
399
416
|
* @returns {{ md: string, ms: number, llmMs: number, llmCalls: number, score: number|null, issues: string[], degraded: boolean, model: string }} документ і метадані генерації (ms — увесь файл; llmMs/llmCalls — лише LLM; решта ms — оркестрація)
|
|
400
417
|
*/
|
|
401
|
-
export function generateDoc(
|
|
418
|
+
export async function generateDoc(
|
|
402
419
|
file,
|
|
403
420
|
{ model = DEFAULT_LOCAL_MODEL, threshold = QUALITY_THRESHOLD, existingMd = null } = {}
|
|
404
421
|
) {
|
|
@@ -421,8 +438,8 @@ export function generateDoc(
|
|
|
421
438
|
const intent = existingMd ? splitProtected(existingMd).body : null
|
|
422
439
|
const anchors = facts.unsupported ? null : extractAnchors(src)
|
|
423
440
|
let r = facts.unsupported
|
|
424
|
-
? oneShotDoc(facts, src, model, LOCAL_TIMEOUT_MS, { intent })
|
|
425
|
-
: orchestratedDoc(facts, src, model, LOCAL_TIMEOUT_MS, { anchors, intent })
|
|
441
|
+
? await oneShotDoc(facts, src, model, LOCAL_TIMEOUT_MS, { intent })
|
|
442
|
+
: await orchestratedDoc(facts, src, model, LOCAL_TIMEOUT_MS, { anchors, intent })
|
|
426
443
|
|
|
427
444
|
// unsupported (vue/py до юніт-шару): скорер не застосовний — score=null, не degraded
|
|
428
445
|
if (facts.unsupported) {
|
|
@@ -444,7 +461,7 @@ export function generateDoc(
|
|
|
444
461
|
// E4: best-of-2 — один retry з вищою температурою, det-вибір кращого
|
|
445
462
|
if (score < threshold && env.N_CURSOR_DOCGEN_BEST_OF !== '0') {
|
|
446
463
|
try {
|
|
447
|
-
const r2 = orchestratedDoc(facts, src, model, LOCAL_TIMEOUT_MS, { anchors, temperature: 0.5, intent })
|
|
464
|
+
const r2 = await orchestratedDoc(facts, src, model, LOCAL_TIMEOUT_MS, { anchors, temperature: 0.5, intent })
|
|
448
465
|
const s2 = scoreDoc(r2.md, facts, { anchors, src })
|
|
449
466
|
if (s2.score > score) {
|
|
450
467
|
r = r2
|
|
@@ -463,7 +480,7 @@ export function generateDoc(
|
|
|
463
480
|
let judge = null
|
|
464
481
|
if (JUDGE_ENABLED && score >= threshold) {
|
|
465
482
|
try {
|
|
466
|
-
judge = { ...judgeDoc(src, r.md), model: JUDGE_MODEL }
|
|
483
|
+
judge = { ...(await judgeDoc(src, r.md)), model: JUDGE_MODEL }
|
|
467
484
|
if (judgeFailsDoc(judge)) issues = [...issues, `judge:inaccurate:${judge.confidence}`]
|
|
468
485
|
} catch (error) {
|
|
469
486
|
issues = [...issues, `judge:error: ${error.message.slice(0, 80)}`]
|
|
@@ -495,7 +512,7 @@ if (isRunAsCli(import.meta.url)) {
|
|
|
495
512
|
// Зберегти захищену секцію «Призначення», якщо дока вже існує
|
|
496
513
|
const docPath = docPathForSource(file)
|
|
497
514
|
const existingMd = existsSync(docPath) ? readFileSync(docPath, 'utf8') : null
|
|
498
|
-
const r = generateDoc(file, { model, existingMd })
|
|
515
|
+
const r = await generateDoc(file, { model, existingMd })
|
|
499
516
|
const issuesTxt = r.issues?.length ? ` issues=${r.issues.join(',')}` : ''
|
|
500
517
|
process.stderr.write(
|
|
501
518
|
`[local ${r.model}] ${r.ms}ms (llm ${r.llmMs}ms/${r.llmCalls} calls, orch ${r.ms - r.llmMs}ms) / score=${r.score}${r.degraded ? ' DEGRADED' : ''}${issuesTxt}\n`
|
|
@@ -3,7 +3,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs'
|
|
|
3
3
|
import { createHash } from 'node:crypto'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
import { generateDoc } from './docgen-gen.mjs'
|
|
6
|
-
import {
|
|
6
|
+
import { runOneShot } from '../../../lib/pi-one-shot.mjs'
|
|
7
7
|
import { QUALITY_THRESHOLD } from './docgen-crc.mjs'
|
|
8
8
|
|
|
9
9
|
const env = process.env
|
|
@@ -43,11 +43,11 @@ function cacheSet(key, val) {
|
|
|
43
43
|
* @param {string} src вміст файлу
|
|
44
44
|
* @returns {{md: string, score: number|null, issues: string[], degraded: boolean, cached: boolean}} результат генерації
|
|
45
45
|
*/
|
|
46
|
-
function genCached(file, src) {
|
|
46
|
+
async function genCached(file, src) {
|
|
47
47
|
const key = 'gen-' + sha(GEN_MODEL + '\0' + src)
|
|
48
48
|
const hit = cacheGet(key)
|
|
49
49
|
if (hit) return { ...hit, cached: true }
|
|
50
|
-
const r = generateDoc(file, { model: GEN_MODEL })
|
|
50
|
+
const r = await generateDoc(file, { model: GEN_MODEL })
|
|
51
51
|
const out = { md: r.md, score: r.score, issues: r.issues, degraded: r.degraded }
|
|
52
52
|
cacheSet(key, out)
|
|
53
53
|
return { ...out, cached: false }
|
|
@@ -59,19 +59,22 @@ function genCached(file, src) {
|
|
|
59
59
|
* @param {string} doc згенерована документація
|
|
60
60
|
* @returns {{verdict: string, confidence: number, reason: string, offending?: string[], cached: boolean}} verdict судді
|
|
61
61
|
*/
|
|
62
|
-
function judgeCached(src, doc) {
|
|
62
|
+
async function judgeCached(src, doc) {
|
|
63
63
|
const key = 'judge-' + sha(JUDGE_MODEL + '\0' + src + '\0' + doc)
|
|
64
64
|
const hit = cacheGet(key)
|
|
65
65
|
if (hit) return { ...hit, cached: true }
|
|
66
66
|
const user = `SOURCE FILE:\n\`\`\`\n${src.slice(0, 12000)}\n\`\`\`\n\nGENERATED DOC:\n\`\`\`md\n${doc.slice(0, 8000)}\n\`\`\`\n\nReturn the JSON verdict.`
|
|
67
|
-
const
|
|
68
|
-
[
|
|
67
|
+
const res = await runOneShot({
|
|
68
|
+
messages: [
|
|
69
69
|
{ role: 'system', content: SYSTEM },
|
|
70
70
|
{ role: 'user', content: user }
|
|
71
71
|
],
|
|
72
|
-
JUDGE_MODEL,
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
modelSpec: JUDGE_MODEL,
|
|
73
|
+
timeoutMs: JUDGE_TIMEOUT,
|
|
74
|
+
caller: 'docgen-measure'
|
|
75
|
+
})
|
|
76
|
+
if (res.error) throw new Error(res.error)
|
|
77
|
+
const raw = res.content
|
|
75
78
|
const a = raw.indexOf('{'),
|
|
76
79
|
b = raw.lastIndexOf('}')
|
|
77
80
|
if (a === -1 || b === -1) throw new Error('no JSON in judge reply: ' + raw.slice(0, 160))
|
|
@@ -83,7 +86,7 @@ function judgeCached(src, doc) {
|
|
|
83
86
|
/**
|
|
84
87
|
*
|
|
85
88
|
*/
|
|
86
|
-
function main() {
|
|
89
|
+
async function main() {
|
|
87
90
|
const files = process.argv.slice(2).filter(f => !f.startsWith('--'))
|
|
88
91
|
if (!files.length) {
|
|
89
92
|
console.error('Usage: node docgen-judge-measure.mjs <file1> <file2> ...')
|
|
@@ -106,7 +109,7 @@ function main() {
|
|
|
106
109
|
|
|
107
110
|
let gen
|
|
108
111
|
try {
|
|
109
|
-
gen = genCached(file, src)
|
|
112
|
+
gen = await genCached(file, src)
|
|
110
113
|
} catch (error) {
|
|
111
114
|
console.error(`[gen-err] ${tag}: ${error.message.slice(0, 120)}`)
|
|
112
115
|
rows.push({ file, error: 'gen', detail: error.message.slice(0, 200) })
|
|
@@ -124,7 +127,7 @@ function main() {
|
|
|
124
127
|
|
|
125
128
|
if (passed) {
|
|
126
129
|
try {
|
|
127
|
-
const v = judgeCached(src, gen.md)
|
|
130
|
+
const v = await judgeCached(src, gen.md)
|
|
128
131
|
row.verdict = v.verdict
|
|
129
132
|
row.confidence = v.confidence
|
|
130
133
|
row.reason = v.reason
|
|
@@ -195,4 +198,4 @@ function main() {
|
|
|
195
198
|
console.log(`report: ${out}`)
|
|
196
199
|
}
|
|
197
200
|
|
|
198
|
-
main()
|
|
201
|
+
await main()
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* `N_CLOUD_MIN_MODEL` → 0 змін поведінки. Патерн дзеркалить `scripts/coverage-classify`.
|
|
13
13
|
*/
|
|
14
14
|
import { env } from 'node:process'
|
|
15
|
-
import {
|
|
16
|
-
import { CLOUD_MIN } from '../../../lib/
|
|
15
|
+
import { runOneShot } from '../../../lib/pi-one-shot.mjs'
|
|
16
|
+
import { CLOUD_MIN } from '../../../lib/pi-model-tiers.mjs'
|
|
17
17
|
|
|
18
18
|
/** Модель-суддя = `N_CLOUD_MIN_MODEL` (хмарний cloud-min tier). */
|
|
19
19
|
export const JUDGE_MODEL = CLOUD_MIN
|
|
@@ -56,17 +56,19 @@ export function parseDocVerdict(rawText) {
|
|
|
56
56
|
* @param {{model?: string, timeoutMs?: number}} [opts] override моделі/таймауту
|
|
57
57
|
* @returns {{verdict: string, confidence: number, reason: string}} verdict судді
|
|
58
58
|
*/
|
|
59
|
-
export function judgeDoc(src, doc, { model = JUDGE_MODEL, timeoutMs = 120_000 } = {}) {
|
|
59
|
+
export async function judgeDoc(src, doc, { model = JUDGE_MODEL, timeoutMs = 120_000 } = {}) {
|
|
60
60
|
const user = `SOURCE FILE:\n\`\`\`\n${src.slice(0, 12_000)}\n\`\`\`\n\nGENERATED DOC:\n\`\`\`md\n${doc.slice(0, 8000)}\n\`\`\`\n\nReturn the JSON verdict.`
|
|
61
|
-
const
|
|
62
|
-
[
|
|
61
|
+
const res = await runOneShot({
|
|
62
|
+
messages: [
|
|
63
63
|
{ role: 'system', content: JUDGE_SYSTEM },
|
|
64
64
|
{ role: 'user', content: user }
|
|
65
65
|
],
|
|
66
|
-
model,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
modelSpec: model,
|
|
67
|
+
timeoutMs,
|
|
68
|
+
caller: 'docgen-judge'
|
|
69
|
+
})
|
|
70
|
+
if (res.error) throw new Error(res.error)
|
|
71
|
+
return parseDocVerdict(res.content)
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
/**
|
|
@@ -3,29 +3,12 @@ type: JS Module
|
|
|
3
3
|
title: docgen-files-batch.mjs
|
|
4
4
|
resource: npm/rules/doc-files/js/docgen-files-batch.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 26217123
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 55
|
|
9
|
+
issues: no-overview,short-behavior,best-of-2:retry-lost
|
|
9
10
|
---
|
|
10
11
|
|
|
11
|
-
Модуль керує життєвим циклом документації. Він вибирає цілі для оновлення за допомогою `selectTargets`, очищає від неіснуючих джерел за допомогою `purgeOrphanedDocs`, запускає генерацію файлів через `runDocFilesGenCli` та `runDocFilesStampCli`, а також виконує пакетну генерацію за допомогою `runGenerationBatch`. Усі операції виконуються з механізмом перехоплення помилок, що запобігає виникненню винятків назовні.
|
|
12
|
-
|
|
13
|
-
## Поведінка
|
|
14
|
-
|
|
15
|
-
selectTargets відфільтровує документи для генерації, вибираючи застарілі або ті, які мають низьку якість і не були спробовані раніше.
|
|
16
|
-
purgeOrphanedDocs видаляє документи, для яких не існує відповідного джерела, і оновлює індекси директорій.
|
|
17
|
-
runDocFilesGenCli запускає генерацію документації для застарілих або низькоякісних файлів, після попереднього очищення сирітських документів.
|
|
18
|
-
runGenerationBatch виконує послідовну генерацію документації для заданого набору цілей, керуючи процесом через механізм виходу з ладу.
|
|
19
|
-
runDocFilesStampCli детерміновано оновлює метадані (frontmatter) існуючих документів, додаючи CRC та зберігаючи дані про якість.
|
|
20
|
-
|
|
21
|
-
## Публічний API
|
|
22
|
-
|
|
23
|
-
selectTargets — Визначає цілі для генерації документації: застарілі або деградовані документи, або всі документи при використанні прапора перезапису.
|
|
24
|
-
purgeOrphanedDocs — Видаляє документи, для яких відсутній відповідний вихідний файл, та оновлює файл індексу.
|
|
25
|
-
runDocFilesGenCli — Генерує документацію для застарілих або відсутніх документів.
|
|
26
|
-
runGenerationBatch — Виконує повний цикл генерації: перевіряє стан локального бекенду, послідовно генерує документи з обробкою збоїв, та створює фінальний звіт.
|
|
27
|
-
runDocFilesStampCli — Додає або оновлює метадані джерела та контрольної суми до існуючих документів без використання великих мовних моделей.
|
|
28
|
-
|
|
29
12
|
## Гарантії поведінки
|
|
30
13
|
|
|
31
14
|
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
@@ -3,29 +3,12 @@ type: JS Module
|
|
|
3
3
|
title: docgen-gen.mjs
|
|
4
4
|
resource: npm/rules/doc-files/js/docgen-gen.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 2d3245b9
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 50
|
|
9
|
+
issues: no-overview,short-behavior,anchor-miss:(abie.mdc),best-of-2:retry-lost
|
|
9
10
|
---
|
|
10
11
|
|
|
11
|
-
Модуль забезпечує повний цикл роботи з документами: відокремлює захищений блок за допомогою `splitProtected`, вставляє його у визначене місце за допомогою `insertProtected`, генерує повний Markdown-документ через `generateDoc` (з використанням `DEFAULT_LOCAL_MODEL`), а також оцінює якість документа за допомогою `scoreDoc`. Кешування застосовується протягом прогону. (abie.mdc)
|
|
12
|
-
|
|
13
|
-
## Поведінка
|
|
14
|
-
|
|
15
|
-
splitProtected відокремлює захищений блок Призначення від основного документа.
|
|
16
|
-
insertProtected вставляє захищений блок Призначення у фіксовану позицію після заголовка документа.
|
|
17
|
-
scoreDoc оцінює якість згенерованого документа за заданими критеріями.
|
|
18
|
-
DEFAULT_LOCAL_MODEL визначає модель, яка використовується для генерації документа.
|
|
19
|
-
generateDoc генерує повний Markdown-документ з опису файлу, застосовуючи оцінку та можливий ретрай.
|
|
20
|
-
|
|
21
|
-
## Публічний API
|
|
22
|
-
|
|
23
|
-
splitProtected — Розділяє захищений розділ `## Призначення` (Варіант B), використовуючи наступний заголовок рівня H2 або H3 як межу.
|
|
24
|
-
insertProtected — Розміщує захищений блок `## Призначення` безпосередньо після основного заголовка (H1).
|
|
25
|
-
scoreDoc — Виконує детерміновану оцінку (Stage 2.5), порівнюючи вихідні дані з фактичною інформацією.
|
|
26
|
-
DEFAULT_LOCAL_MODEL — Визначає модель для генерації документа, використовуючи мінімальну локальну модель. Якщо модель не задана, процес зупиняється з помилкою.
|
|
27
|
-
generateDoc — Основна функція, яка перетворює вхідний файл у Markdown-документ з оцінкою достовірності.
|
|
28
|
-
|
|
29
12
|
## Гарантії поведінки
|
|
30
13
|
|
|
31
14
|
- Кешує результати в межах одного прогону.
|
|
@@ -3,27 +3,12 @@ type: JS Module
|
|
|
3
3
|
title: docgen-judge-measure.mjs
|
|
4
4
|
resource: npm/rules/doc-files/js/docgen-judge-measure.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 4362c310
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 50
|
|
9
|
+
issues: no-overview,short-behavior,anchor-miss:report.json,best-of-2:retry-lost
|
|
9
10
|
---
|
|
10
11
|
|
|
11
|
-
Файл аналізує надані файли коду, створюючи документацію та оцінюючи її якість відповідно до конфігурації, що міститься у report.json. Процес збирає результати для кожного файлу, використовуючи кешування у межах прогону на основі вмісту джерела та вже згенерованої документації. У кінці процес агрегує дані та зберігає повний звіт у report.json, а також виводить його у консоль.
|
|
12
|
-
|
|
13
|
-
## Поведінка
|
|
14
|
-
|
|
15
|
-
1. Зчитує список файлів для аналізу з аргументів командного рядка.
|
|
16
|
-
2. Для кожного файлу зчитує його вміст.
|
|
17
|
-
3. Генерує документацію для файлу, використовуючи кешування за вмістом джерела.
|
|
18
|
-
4. Якщо генерація документації успішна і отриманий бал перевищує встановлений поріг, переходить до етапу оцінки.
|
|
19
|
-
5. Якщо бал не перевищує порогу, файл вважається "degraded" (зниженою якістю) і не підлягає подальшій оцінці.
|
|
20
|
-
6. Якщо бал перевищує поріг, документація передається для оцінки потужній моделі, використовуючи кешування за вмістом джерела та згенерованою документацією.
|
|
21
|
-
7. Оцінка повертає вердикт ("accurate", "generic" або "inaccurate"), впевненість та причину.
|
|
22
|
-
8. Збираються результати для кожного файлу.
|
|
23
|
-
9. Після обробки всіх файлів агрегуються результати: підраховуються загальні показники (кількість файлів, помилки генерації, кількість пройшлих тестів, розподіл вердиктів).
|
|
24
|
-
10. Зберігається звіт у файл `report.json` у каталозі кешу.
|
|
25
|
-
11. Виводиться консольний звіт про результати вимірювання.
|
|
26
|
-
|
|
27
12
|
## Гарантії поведінки
|
|
28
13
|
|
|
29
14
|
- Кешує результати в межах одного прогону.
|
|
@@ -3,31 +3,12 @@ type: JS Module
|
|
|
3
3
|
title: docgen-judge.mjs
|
|
4
4
|
resource: npm/rules/doc-files/js/docgen-judge.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: d20a0bc7
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 55
|
|
9
|
+
issues: no-overview,short-behavior,best-of-2:retry-lost
|
|
9
10
|
---
|
|
10
11
|
|
|
11
|
-
Модуль реалізує механізм оцінки якості документації. Він визначає модель для оцінки за допомогою `JUDGE_MODEL`, перевіряє статус активності гейту через `JUDGE_ENABLED` та встановлює поріг впевненості за допомогою `JUDGE_CONFIDENCE`. Модуль отримує, парсить та визначає фінальний статус документації, викликаючи `judgeDoc` для отримання висновків, а також може використовувати `judgeFailsDoc` для визначення провалу.
|
|
12
|
-
|
|
13
|
-
## Поведінка
|
|
14
|
-
|
|
15
|
-
JUDGE_MODEL — Вказує модель, яку використовує суддя для оцінки документації.
|
|
16
|
-
JUDGE_ENABLED — Позначає, чи активний семантичний гейт судді.
|
|
17
|
-
JUDGE_CONFIDENCE — Визначає мінімальну впевненість, необхідну для позначення документації як деградованої.
|
|
18
|
-
parseDocVerdict — Витягує та валідує об'єкт оцінки з сирого текстового виводу судді.
|
|
19
|
-
judgeDoc — Зіставляє вміст вихідного файлу та згенеровану документацію, щоб отримати оцінку від судді.
|
|
20
|
-
judgeFailsDoc — Визначає, чи слід вважати документацію деградованою на основі оцінки судді.
|
|
21
|
-
|
|
22
|
-
## Публічний API
|
|
23
|
-
|
|
24
|
-
JUDGE_MODEL — Визначає модель-суддю як `N_CLOUD_MIN_MODEL` (хмарний мінімальний рівень).
|
|
25
|
-
JUDGE_ENABLED — Автоматично вмикає механізм суддівства, якщо обрано `N_CLOUD_MIN_MODEL`.
|
|
26
|
-
JUDGE_CONFIDENCE — Встановлює мінімальний рівень впевненості для позначення документа як погіршеного (degraded) через неточність.
|
|
27
|
-
parseDocVerdict — Витягує та перевіряє JSON-вердикт із відповіді великої мовної моделі.
|
|
28
|
-
judgeDoc — Оцінює згенерований документ потужною моделлю порівняно з оригінальним джерелом.
|
|
29
|
-
judgeFailsDoc — Визначає, чи повинен документ бути позначений як погіршений, якщо вердикт є `inaccurate` і впевненість достатня.
|
|
30
|
-
|
|
31
12
|
## Гарантії поведінки
|
|
32
13
|
|
|
33
14
|
- Read-only: не виконує операцій запису (ФС/БД).
|
|
@@ -3,31 +3,38 @@ type: JS Module
|
|
|
3
3
|
title: skill_meta.mjs
|
|
4
4
|
resource: npm/rules/npm-module/js/skill_meta.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: a134317c
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
|
+
issues: judge:inaccurate:0.98
|
|
10
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
9
11
|
---
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## Огляд
|
|
14
|
+
|
|
15
|
+
Скрипт проводить валідацію конфігурацій усіх каталогів у `npm/skills`, перевіряючи відповідність кожного скіла встановленому контракту. Аналіз здійснюється з урахуванням конфігурацій `meta.json` та `main.json`, що контролюють надійність полів, таких як `worktree`, `auto` та `requireRoot`. У разі успіху повідомляється про відповідність конфігурацій скілів визначеним правилам; у випадку виявлення помилок інформується про них.
|
|
12
16
|
|
|
13
17
|
## Поведінка
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
Поведінка
|
|
20
|
+
|
|
21
|
+
1. Викликає check для перевірки всіх каталогів у `npm/skills`.
|
|
22
|
+
2. Якщо каталог `npm/skills` відсутній, повідомляє про це як успішний результат, оскільки скілів для валідації немає.
|
|
23
|
+
3. Для кожного каталогу скіла виконує валідацію.
|
|
24
|
+
4. Перевіряє, чи існує файл `auto.md` у каталозі скіла; якщо так, повідомляє про його залишковий статус, оскільки метадані тепер знаходяться у `main.json`.
|
|
25
|
+
5. Зчитує вміст `main.json` скіла. Якщо файл відсутній або невалідний, повідомляє про це і припиняє перевірку для цього скіла.
|
|
26
|
+
6. Якщо `main.json` знайдено, викликає внутрішню перевірку полів для валідації його структури.
|
|
27
|
+
7. Перевіряє, чи `worktree` є булевим значенням. Якщо ні, повідомляє про помилку.
|
|
28
|
+
8. Перевіряє поле `auto`; якщо воно визначене, викликає внутрішній аналіз для визначення, чи є його значення коректним ("завжди" або непорожній масив правил). Якщо ні, повідомляє про нерозпізнаність.
|
|
29
|
+
9. Перевіряє поле `requireRoot`; якщо воно визначене, перевіряє, чи є воно булевим значенням. Якщо ні, повідомляє про помилку.
|
|
30
|
+
10. Перевіряє на суперечливість: якщо `worktree` встановлено як `true`, а `requireRoot` як `false`, повідомляє про конфігураційну помилку, оскільки `worktree` вже вимагає кореня.
|
|
31
|
+
11. Перевіряє поле `tier`; якщо воно визначене, перевіряє, чи є воно рядком і чи належить до визначених категорій скілів. Якщо ні, повідомляє про допустимі значення.
|
|
32
|
+
12. Якщо всі поля `main.json` валідні, повідомляє про успішну валідацію скіла.
|
|
33
|
+
13. Повертає загальний код завершення, що відображає всі зафіксовані порушення в каталогах скілів.
|
|
27
34
|
|
|
28
35
|
## Публічний API
|
|
29
36
|
|
|
30
|
-
check — перевіряє відповідність
|
|
37
|
+
check — перевіряє відповідність структури всіх файлів конфігурації `npm/skills/<id>/meta.json` вимогам.
|
|
31
38
|
|
|
32
39
|
## Гарантії поведінки
|
|
33
40
|
|
|
@@ -3,7 +3,7 @@ import { existsSync, readdirSync } from 'node:fs'
|
|
|
3
3
|
import { join } from 'node:path'
|
|
4
4
|
|
|
5
5
|
import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
6
|
-
import { parseSkillAutoSpec, readSkillMetaRaw } from '../../../scripts/lib/skill-meta.mjs'
|
|
6
|
+
import { SKILL_TIERS, parseSkillAutoSpec, readSkillMetaRaw } from '../../../scripts/lib/skill-meta.mjs'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Перевіряє поля сирого meta.json одного скіла (без auto.md / відсутності файлу).
|
|
@@ -32,6 +32,10 @@ function checkSkillFields(id, raw, reporter) {
|
|
|
32
32
|
)
|
|
33
33
|
ok = false
|
|
34
34
|
}
|
|
35
|
+
if (raw.tier !== undefined && !(typeof raw.tier === 'string' && SKILL_TIERS.includes(raw.tier))) {
|
|
36
|
+
reporter.fail(`skills/${id}: main.json.tier має бути ${SKILL_TIERS.map(t => `"${t}"`).join(' | ')}`)
|
|
37
|
+
ok = false
|
|
38
|
+
}
|
|
35
39
|
return ok
|
|
36
40
|
}
|
|
37
41
|
|
|
@@ -20,7 +20,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
|
20
20
|
import { join } from 'node:path'
|
|
21
21
|
|
|
22
22
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
23
|
-
import {
|
|
23
|
+
import { runOneShot } from '../../../lib/pi-one-shot.mjs'
|
|
24
24
|
|
|
25
25
|
/** Слово у рядку cspell: `<file>:<line>:<col> - Unknown word (xxx)`. */
|
|
26
26
|
const UNKNOWN_WORD_RE = /Unknown word \(([^)]+)\)/u
|
|
@@ -114,10 +114,10 @@ export function appendWordsToDict(cwd, words) {
|
|
|
114
114
|
* cspell-крок lint-text: класифікація → словник (нова схема).
|
|
115
115
|
* @param {string} [cwd] корінь
|
|
116
116
|
* @param {boolean} [readOnly] true → лише детект (нуль мутацій)
|
|
117
|
-
* @param {boolean} [llmFix] opt-in
|
|
118
|
-
* @returns {number} 0 — чисто; 1 — лишились знахідки / помилка середовища
|
|
117
|
+
* @param {boolean} [llmFix] opt-in LLM-класифікація (з `meta.json: llmFix:true`); без нього — лише детект
|
|
118
|
+
* @returns {Promise<number>} 0 — чисто; 1 — лишились знахідки / помилка середовища
|
|
119
119
|
*/
|
|
120
|
-
export function runCspellText(cwd = process.cwd(), readOnly = false, llmFix = false) {
|
|
120
|
+
export async function runCspellText(cwd = process.cwd(), readOnly = false, llmFix = false) {
|
|
121
121
|
const bin = resolveCmd('npx')
|
|
122
122
|
if (!bin) {
|
|
123
123
|
process.stderr.write('❌ npx не знайдено в PATH (cspell).\n')
|
|
@@ -133,9 +133,8 @@ export function runCspellText(cwd = process.cwd(), readOnly = false, llmFix = fa
|
|
|
133
133
|
|
|
134
134
|
// Fix-режим: класифікація знахідок (bounded JSON-вихід), валідні → у словник.
|
|
135
135
|
const model = fixModel()
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
process.stdout.write(`⚠️ cspell: класифікацію пропущено (${problem})\n`)
|
|
136
|
+
if (!model) {
|
|
137
|
+
process.stdout.write('⚠️ cspell: класифікацію пропущено (локальну модель не задано)\n')
|
|
139
138
|
process.stdout.write(first.out)
|
|
140
139
|
return first.code
|
|
141
140
|
}
|
|
@@ -148,19 +147,19 @@ export function runCspellText(cwd = process.cwd(), readOnly = false, llmFix = fa
|
|
|
148
147
|
)
|
|
149
148
|
}
|
|
150
149
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
process.stdout.write(`⚠️ cspell:
|
|
150
|
+
const res = await runOneShot({
|
|
151
|
+
messages: [{ role: 'user', content: classifyPrompt(batch) }],
|
|
152
|
+
modelSpec: model,
|
|
153
|
+
caller: 'cspell-classify',
|
|
154
|
+
cwd
|
|
155
|
+
})
|
|
156
|
+
if (res.error) {
|
|
157
|
+
process.stdout.write(`⚠️ cspell: LLM-класифікація впала (${res.error}) — без авто-словника\n`)
|
|
159
158
|
process.stdout.write(first.out)
|
|
160
159
|
return first.code
|
|
161
160
|
}
|
|
162
161
|
|
|
163
|
-
const parsed = parseClassify(
|
|
162
|
+
const parsed = parseClassify(res.content)
|
|
164
163
|
if (!parsed) {
|
|
165
164
|
process.stdout.write('⚠️ cspell: не вдалося розпарсити класифікацію — без авто-словника\n')
|
|
166
165
|
process.stdout.write(first.out)
|
|
@@ -3,24 +3,31 @@ type: JS Module
|
|
|
3
3
|
title: cspell-fix.mjs
|
|
4
4
|
resource: npm/rules/text/js/cspell-fix.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 3469ac34
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 95
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Огляд: Цей модуль інтегрує `cspell` у ланцюжок `lint-text` для забезпечення термінологічної коректності у тексті, використовуючи схему `omlx-класифікації`. Він ідентифікує неозначені слова, класифікує їх за допомогою LLM та автоматично доповнює словник `.cspell.json` валідними термінами, залишаючи потенційні помилки для ручного рев'ю.
|
|
14
|
+
|
|
15
|
+
Поведінка:
|
|
16
|
+
unknownWords витягує унікальні неозначені слова з виводу інструмента `cspell`.
|
|
17
|
+
appendWordsToDict додає визначені як валідні слова до конфігураційного файлу `.cspell.json`, оновлюючи його в алфавітному порядку.
|
|
18
|
+
runCspellText запускає процес класифікації неозначених слів, спочатку визначаючи їх, а потім, за умови увімкненого LLM-класифікатора, відправляючи їх на перевірку та додаючи валідні слова до словника, після чого повторно перевіряє код.
|
|
12
19
|
|
|
13
20
|
## Поведінка
|
|
14
21
|
|
|
15
|
-
unknownWords витягує унікальні
|
|
16
|
-
appendWordsToDict
|
|
17
|
-
runCspellText запускає
|
|
22
|
+
unknownWords витягує унікальні неозначені слова з виводу інструмента `cspell`.
|
|
23
|
+
appendWordsToDict додає визначені як валідні слова до конфігураційного файлу `.cspell.json`, оновлюючи його в алфавітному порядку.
|
|
24
|
+
runCspellText запускає процес класифікації неозначених слів, спочатку визначаючи їх, а потім, за умови увімкненого LLM-класифікатора, відправляючи їх на перевірку та додаючи валідні слова до словника, після чого повторно перевіряє код.
|
|
18
25
|
|
|
19
26
|
## Публічний API
|
|
20
27
|
|
|
21
|
-
unknownWords — Збирає унікальні слова, які
|
|
22
|
-
appendWordsToDict — Додає зібрані слова до словника `.cspell.json
|
|
23
|
-
runCspellText — Виконує
|
|
28
|
+
unknownWords — Збирає унікальні слова, які `cspell` виявив як незнайомі у виводі.
|
|
29
|
+
appendWordsToDict — Додає зібрані слова до словника `.cspell.json#words`, забезпечуючи унікальність та сортування.
|
|
30
|
+
runCspellText — Виконує лінтування тексту з `cspell`: класифікує слова та оновлює словник з новою схемою.
|
|
24
31
|
|
|
25
32
|
## Гарантії поведінки
|
|
26
33
|
|