@nitra/cursor 12.15.0 → 12.16.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 (77) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/bin/n-cursor.js +2 -11
  3. package/lib/docs/index.md +9 -6
  4. package/lib/docs/pi-agent-fix.md +28 -0
  5. package/lib/docs/pi-agent-skill.md +36 -0
  6. package/lib/docs/pi-model-tiers.md +46 -0
  7. package/lib/docs/pi-one-shot.md +34 -0
  8. package/lib/docs/pi-telemetry-store.md +33 -0
  9. package/lib/docs/pi-trace.md +27 -0
  10. package/lib/docs/pi-write-guard.md +32 -0
  11. package/lib/pi-agent-fix.mjs +253 -0
  12. package/lib/pi-agent-skill.mjs +181 -0
  13. package/lib/pi-model-tiers.mjs +109 -0
  14. package/lib/pi-one-shot.mjs +129 -0
  15. package/lib/pi-telemetry-store.mjs +0 -0
  16. package/lib/pi-trace.mjs +40 -0
  17. package/lib/pi-write-guard.mjs +147 -0
  18. package/package.json +5 -1
  19. package/rules/bun/docs/main.md +7 -6
  20. package/rules/doc-files/js/docgen-files-batch.mjs +20 -5
  21. package/rules/doc-files/js/docgen-gen.mjs +42 -25
  22. package/rules/doc-files/js/docgen-judge-measure.mjs +16 -13
  23. package/rules/doc-files/js/docgen-judge.mjs +11 -9
  24. package/rules/doc-files/js/docs/docgen-files-batch.md +3 -20
  25. package/rules/doc-files/js/docs/docgen-gen.md +3 -20
  26. package/rules/doc-files/js/docs/docgen-judge-measure.md +3 -18
  27. package/rules/doc-files/js/docs/docgen-judge.md +3 -22
  28. package/rules/npm-module/js/docs/skill_meta.md +22 -15
  29. package/rules/npm-module/js/skill_meta.mjs +5 -1
  30. package/rules/python/docs/main.md +11 -11
  31. package/rules/rust/docs/main.md +5 -5
  32. package/rules/text/js/cspell-fix.mjs +15 -16
  33. package/rules/text/js/docs/cspell-fix.md +16 -9
  34. package/rules/text/main.mjs +4 -4
  35. package/schemas/skill-meta.json +8 -0
  36. package/scripts/docs/skills-cli.md +21 -25
  37. package/scripts/docs/update-blue-oak.md +8 -8
  38. package/scripts/lib/adr/docs/normalize-cli.md +3 -20
  39. package/scripts/lib/adr/docs/normalize-pipeline.md +3 -33
  40. package/scripts/lib/adr/normalize-cli.mjs +2 -2
  41. package/scripts/lib/adr/normalize-pipeline.mjs +78 -44
  42. package/scripts/lib/docs/discover-checkable-rules.md +6 -6
  43. package/scripts/lib/docs/inline-template-links.md +8 -6
  44. package/scripts/lib/docs/list-project-rules-mdc.md +5 -3
  45. package/scripts/lib/docs/root-notice.md +13 -16
  46. package/scripts/lib/docs/run-lint.md +10 -8
  47. package/scripts/lib/docs/skill-meta.md +29 -10
  48. package/scripts/lib/fix/docs/discover-t0-patterns.md +10 -13
  49. package/scripts/lib/fix/docs/escalation-log.md +10 -9
  50. package/scripts/lib/fix/docs/index.md +0 -1
  51. package/scripts/lib/fix/docs/orchestrator.md +15 -13
  52. package/scripts/lib/fix/escalation-log.mjs +1 -1
  53. package/scripts/lib/fix/orchestrator.mjs +67 -32
  54. package/scripts/lib/run-lint.mjs +2 -10
  55. package/scripts/lib/skill-meta.mjs +22 -0
  56. package/scripts/skills-cli.mjs +52 -14
  57. package/scripts/utils/ast-extract.mjs +105 -0
  58. package/scripts/utils/docs/ast-extract.md +30 -0
  59. package/scripts/utils/docs/walkDir.md +17 -20
  60. package/lib/docs/llm.md +0 -33
  61. package/lib/docs/models.md +0 -48
  62. package/lib/docs/omlx-trace.md +0 -49
  63. package/lib/docs/omlx.md +0 -41
  64. package/lib/llm.mjs +0 -215
  65. package/lib/models.mjs +0 -75
  66. package/lib/omlx-trace.mjs +0 -158
  67. package/lib/omlx.mjs +0 -220
  68. package/scripts/lib/fix/analyze-escalation.mjs +0 -353
  69. package/scripts/lib/fix/docs/analyze-escalation.md +0 -44
  70. package/scripts/lib/fix/docs/llm-fix-apply.md +0 -31
  71. package/scripts/lib/fix/docs/llm-lint-fix.md +0 -31
  72. package/scripts/lib/fix/docs/llm-worker.md +0 -33
  73. package/scripts/lib/fix/docs/verbose-block.md +0 -27
  74. package/scripts/lib/fix/llm-fix-apply.mjs +0 -113
  75. package/scripts/lib/fix/llm-lint-fix.mjs +0 -82
  76. package/scripts/lib/fix/llm-worker.mjs +0 -332
  77. package/scripts/lib/fix/verbose-block.mjs +0 -82
@@ -24,8 +24,8 @@
24
24
  * Повертає той самий operations[]-контракт, що й single-shot — apply-логіка спільна.
25
25
  */
26
26
  import { z } from 'zod'
27
- import { callLlm, classifyOmlxError } from '../../../lib/llm.mjs'
28
- import { CLOUD_MIN, resolveModel } from '../../../lib/models.mjs'
27
+ import { runOneShot } from '../../../lib/pi-one-shot.mjs'
28
+ import { CLOUD_MIN, resolveModel } from '../../../lib/pi-model-tiers.mjs'
29
29
 
30
30
  // ─────────────────────────── Stage 0: retrieval (JS) ───────────────────────────
31
31
 
@@ -168,36 +168,44 @@ const LOCAL = () => resolveModel('min')
168
168
  * @param {Array<{role:string,content:string}>} messages чат-повідомлення для LLM
169
169
  * @param {(raw:string)=>any} parse валідатор (кидає на невалідному)
170
170
  * @param {{label:string, allowCloud:boolean, attempts?:number, stats:object, maxTokens?:number}} cfg конфіг каскаду (мітка, дозвіл на хмару, спроби, лічильники, ліміт токенів)
171
- * @returns {any} результат parse
171
+ * @returns {Promise<any>} результат parse
172
172
  * @throws {Error} якщо всі спроби провалені
173
173
  */
174
- function callWithCascade(messages, parse, cfg) {
174
+ async function callWithCascade(messages, parse, cfg) {
175
175
  const attempts = cfg.attempts ?? 2
176
- const temps = [0.1, 0.4, 0.7]
177
176
  let lastErr = null
178
177
  for (let a = 0; a < attempts; a++) {
179
- try {
180
- cfg.stats.localCalls++
181
- const raw = callLlm(messages, LOCAL(), {
182
- timeoutMs: 120_000,
183
- temperature: temps[a] ?? 0.2,
184
- maxTokens: cfg.maxTokens ?? 4096,
185
- caller: `adr-pipe:${cfg.label}`
186
- })
187
- return parse(raw)
188
- } catch (error) {
189
- lastErr = error
190
- if (classifyOmlxError(error.message) === 'infra') break
178
+ cfg.stats.localCalls++
179
+ const res = await runOneShot({ messages, modelSpec: LOCAL(), timeoutMs: 120_000, caller: `adr-pipe:${cfg.label}` })
180
+ if (!res.error) {
181
+ try {
182
+ return parse(res.content)
183
+ } catch (error) {
184
+ lastErr = error // невалідний вихід → наступна спроба
185
+ continue
186
+ }
191
187
  }
188
+ lastErr = new Error(res.error)
189
+ // infra (registry/session/модель недоступна) → ретрай локально марний.
190
+ if (/registry:|session:|не знайдена/i.test(res.error)) break
192
191
  }
193
192
  if (cfg.allowCloud && CLOUD_MIN) {
194
- try {
195
- cfg.stats.cloudCalls++
196
- cfg.stats.escalations++
197
- const raw = callLlm(messages, CLOUD_MIN, { timeoutMs: 120_000, temperature: 0.2, maxTokens: cfg.maxTokens ?? 4096, caller: `adr-pipe:${cfg.label}:cloud` })
198
- return parse(raw)
199
- } catch (error) {
200
- lastErr = error
193
+ cfg.stats.cloudCalls++
194
+ cfg.stats.escalations++
195
+ const res = await runOneShot({
196
+ messages,
197
+ modelSpec: CLOUD_MIN,
198
+ timeoutMs: 120_000,
199
+ caller: `adr-pipe:${cfg.label}:cloud`
200
+ })
201
+ if (!res.error) {
202
+ try {
203
+ return parse(res.content)
204
+ } catch (error) {
205
+ lastErr = error
206
+ }
207
+ } else {
208
+ lastErr = new Error(res.error)
201
209
  }
202
210
  }
203
211
  cfg.stats.failures++
@@ -244,20 +252,27 @@ same=true ЛИШЕ якщо це по суті одне рішення (дубл
244
252
  * @param {{votes?:number, minConf?:number}} [vote] override голосів і порога на тип ребра
245
253
  * @returns {{same:boolean, votes:object[]}} підтвердження same та сирі голоси
246
254
  */
247
- function judgeEdge(aTitle, aBody, bTitle, bBody, cfg, vote = {}) {
255
+ async function judgeEdge(aTitle, aBody, bTitle, bBody, cfg, vote = {}) {
248
256
  const nVotes = vote.votes ?? cfg.votes ?? 2
249
257
  const minConf = vote.minConf ?? 0.5
250
258
  const user = `Запис A — "${aTitle}":\n${aBody.slice(0, 1500)}\n\n---\n\nЗапис B — "${bTitle}":\n${bBody.slice(0, 1500)}\n\nЦе одне й те саме рішення?`
251
- const parse = (raw) => EdgeSchema.parse(extractJson(raw))
259
+ const parse = raw => EdgeSchema.parse(extractJson(raw))
252
260
  const votes = []
253
261
  for (let v = 0; v < nVotes; v++) {
254
262
  try {
255
- votes.push(callWithCascade([{ role: 'system', content: EDGE_SYS }, { role: 'user', content: user }], parse, { label: 'edge', allowCloud: cfg.allowCloud, stats: cfg.stats, maxTokens: 300 }))
263
+ votes.push(
264
+ await callWithCascade([{ role: 'system', content: EDGE_SYS }, { role: 'user', content: user }], parse, {
265
+ label: 'edge',
266
+ allowCloud: cfg.allowCloud,
267
+ stats: cfg.stats,
268
+ maxTokens: 300
269
+ })
270
+ )
256
271
  } catch {
257
272
  votes.push({ same: false, confidence: 0, reason: 'judge failed → conservative different' })
258
273
  }
259
274
  }
260
- const sameCount = votes.filter((v) => v.same && v.confidence >= minConf).length
275
+ const sameCount = votes.filter(v => v.same && v.confidence >= minConf).length
261
276
  return { same: sameCount === votes.length, votes }
262
277
  }
263
278
 
@@ -275,11 +290,16 @@ const KIND_SYS = `Ти оцінюєш чернетку архітектурно
275
290
  Поверни ЛИШЕ JSON: { "kind": "standalone"|"trivial", "reason": "<коротко українською>" }
276
291
  Якщо сумніваєшся — "standalone" (краще зберегти).`
277
292
 
278
- function judgeKind(title, body, cfg) {
293
+ async function judgeKind(title, body, cfg) {
279
294
  const user = `Чернетка — "${title}":\n${body.slice(0, 2500)}\n\nstandalone чи trivial?`
280
- const parse = (raw) => KindSchema.parse(extractJson(raw))
295
+ const parse = raw => KindSchema.parse(extractJson(raw))
281
296
  try {
282
- return callWithCascade([{ role: 'system', content: KIND_SYS }, { role: 'user', content: user }], parse, { label: 'kind', allowCloud: cfg.allowCloud, stats: cfg.stats, maxTokens: 200 })
297
+ return await callWithCascade([{ role: 'system', content: KIND_SYS }, { role: 'user', content: user }], parse, {
298
+ label: 'kind',
299
+ allowCloud: cfg.allowCloud,
300
+ stats: cfg.stats,
301
+ maxTokens: 200
302
+ })
283
303
  } catch {
284
304
  return { kind: 'standalone', reason: 'judge failed → conservative standalone' }
285
305
  }
@@ -424,20 +444,28 @@ export function assembleMadr({ title, date, sections: s }) {
424
444
  ].join('\n')
425
445
  }
426
446
 
427
- export function genMadr(title, body, captured, cfg, file = '') {
447
+ export async function genMadr(title, body, captured, cfg, file = '') {
428
448
  const date = madrDate(captured, file)
429
449
  const slug = slugify(title)
430
450
  const user = `Чернетка "${title}":\n\n${body.slice(0, 4000)}\n\nВитягни зміст рішення у JSON.`
431
- const parse = (raw) => {
451
+ const parse = raw => {
432
452
  const sections = normalizeSections(extractJson(raw))
433
- if (!sections.context && !sections.chosen && !sections.rationale) throw new Error('empty extraction (no context/decision)')
453
+ if (!sections.context && !sections.chosen && !sections.rationale) {
454
+ throw new Error('empty extraction (no context/decision)')
455
+ }
434
456
  const content = assembleMadr({ title, date, sections })
435
457
  const v = validateMadr(content)
436
458
  if (!v.ok) throw new Error(`MADR invalid: ${v.errors.join('; ')}`)
437
459
  return content
438
460
  }
439
461
  try {
440
- const content = callWithCascade([{ role: 'system', content: GEN_SYS }, { role: 'user', content: user }], parse, { label: 'gen', allowCloud: cfg.allowCloud, stats: cfg.stats, attempts: 3, maxTokens: 2048 })
462
+ const content = await callWithCascade([{ role: 'system', content: GEN_SYS }, { role: 'user', content: user }], parse, {
463
+ label: 'gen',
464
+ allowCloud: cfg.allowCloud,
465
+ stats: cfg.stats,
466
+ attempts: 3,
467
+ maxTokens: 2048
468
+ })
441
469
  return { content, slug, valid: true }
442
470
  } catch (error) {
443
471
  cfg.stats.madrInvalid++
@@ -451,7 +479,7 @@ export function genMadr(title, body, captured, cfg, file = '') {
451
479
  // новий зміст-прозу; заголовок із детермінованою датою додає genMerge.
452
480
  const MERGE_SYS = `Ти готуєш короткий додаток до існуючого ADR. Напиши ЛИШЕ новий зміст (проза/bullets), якого ще НЕМА в цільовому ADR — уточнення/виправлення/продовження. Стисло, українською, без заголовків, без code-fence, без передмови.`
453
481
 
454
- function genMerge(title, body, captured, targetTitle, cfg, file = '') {
482
+ async function genMerge(title, body, captured, targetTitle, cfg, file = '') {
455
483
  const date = madrDate(captured, file)
456
484
  const user = `Цільовий ADR: "${targetTitle}".\nЧернетка-доповнення "${title}" (${date}):\n${body.slice(0, 2500)}\n\nЛише новий зміст, без заголовка.`
457
485
  const head = `## Update ${date}`
@@ -464,7 +492,13 @@ function genMerge(title, body, captured, targetTitle, cfg, file = '') {
464
492
  return `${head}\n\n${cleaned}`
465
493
  }
466
494
  try {
467
- return callWithCascade([{ role: 'system', content: MERGE_SYS }, { role: 'user', content: user }], parse, { label: 'merge', allowCloud: cfg.allowCloud, stats: cfg.stats, attempts: 2, maxTokens: 1500 })
495
+ return await callWithCascade([{ role: 'system', content: MERGE_SYS }, { role: 'user', content: user }], parse, {
496
+ label: 'merge',
497
+ allowCloud: cfg.allowCloud,
498
+ stats: cfg.stats,
499
+ attempts: 2,
500
+ maxTokens: 1500
501
+ })
468
502
  } catch {
469
503
  return `${head}\n\n(доповнення з чернетки "${title}")`
470
504
  }
@@ -498,7 +532,7 @@ const noop = () => {
498
532
  * @param {{allowCloud?:boolean, votes?:number, onProgress?:(m:string)=>void}} [opts] хмарна ескалація, кількість голосів і колбек прогресу
499
533
  * @returns {{operations:object[], stats:object, trace:object}} операції apply-ops, лічильники та діагностичний trace
500
534
  */
501
- export function normalizePipeline(drafts, cleanList, opts = {}) {
535
+ export async function normalizePipeline(drafts, cleanList, opts = {}) {
502
536
  const allowCloud = opts.allowCloud ?? false
503
537
  const log = opts.onProgress ?? noop
504
538
  const stats = { localCalls: 0, cloudCalls: 0, escalations: 0, failures: 0, madrInvalid: 0 }
@@ -522,7 +556,7 @@ export function normalizePipeline(drafts, cleanList, opts = {}) {
522
556
  const dsu = makeDSU(drafts.length)
523
557
  const confirmedDD = []
524
558
  for (const [i, j] of dd) {
525
- const r = judgeEdge(titles[i], drafts[i].body, titles[j], drafts[j].body, cfg, { votes: 3, minConf: 0.6 })
559
+ const r = await judgeEdge(titles[i], drafts[i].body, titles[j], drafts[j].body, cfg, { votes: 3, minConf: 0.6 })
526
560
  if (r.same) { dsu.union(i, j); confirmedDD.push([i, j]) }
527
561
  }
528
562
  log(`edge-judge: ${confirmedDD.length}/${dd.length} draft-draft ребер підтверджено`)
@@ -534,7 +568,7 @@ export function normalizePipeline(drafts, cleanList, opts = {}) {
534
568
  for (const [i, cands] of dcByDraft) {
535
569
  for (const c of cands) {
536
570
  const cTitle = stripAdrName(c)
537
- const r = judgeEdge(titles[i], drafts[i].body, cTitle, cTitle, cfg)
571
+ const r = await judgeEdge(titles[i], drafts[i].body, cTitle, cTitle, cfg)
538
572
  if (r.same) { cleanTarget[i] = c; break }
539
573
  }
540
574
  }
@@ -578,7 +612,7 @@ export function normalizePipeline(drafts, cleanList, opts = {}) {
578
612
  // одинаки без clean-target → kind-judge
579
613
  for (let i = 0; i < drafts.length; i++) {
580
614
  if (decision[i].op === 'kind') {
581
- const k = judgeKind(titles[i], drafts[i].body, cfg)
615
+ const k = await judgeKind(titles[i], drafts[i].body, cfg)
582
616
  decision[i] = k.kind === 'trivial' ? { op: 'delete', reason: k.reason } : { op: 'rewrite' }
583
617
  }
584
618
  }
@@ -587,7 +621,7 @@ export function normalizePipeline(drafts, cleanList, opts = {}) {
587
621
  const slugByIdx = Array.from({ length: drafts.length }).fill(null)
588
622
  for (let i = 0; i < drafts.length; i++) {
589
623
  if (decision[i].op !== 'rewrite') continue
590
- const g = genMadr(titles[i], drafts[i].body, captured[i], cfg, drafts[i].file)
624
+ const g = await genMadr(titles[i], drafts[i].body, captured[i], cfg, drafts[i].file)
591
625
  slugByIdx[i] = g.slug
592
626
  if (g.valid) {
593
627
  operations.push({ op: 'rewrite', file: drafts[i].file, slug: g.slug, content: g.content })
@@ -603,11 +637,11 @@ export function normalizePipeline(drafts, cleanList, opts = {}) {
603
637
  if (d.op === 'merge-anchor') {
604
638
  const slug = slugByIdx[d.anchorIdx]
605
639
  if (!slug) { log(`merge-anchor ${drafts[i].file}: anchor gen failed → skip`); continue }
606
- const add = genMerge(titles[i], drafts[i].body, captured[i], titles[d.anchorIdx], cfg, drafts[i].file)
640
+ const add = await genMerge(titles[i], drafts[i].body, captured[i], titles[d.anchorIdx], cfg, drafts[i].file)
607
641
  operations.push({ op: 'merge-into', file: drafts[i].file, target: `${slug}.md`, additions: add })
608
642
  } else if (d.op === 'merge-existing') {
609
643
  const cTitle = stripAdrName(d.target)
610
- const add = genMerge(titles[i], drafts[i].body, captured[i], cTitle, cfg, drafts[i].file)
644
+ const add = await genMerge(titles[i], drafts[i].body, captured[i], cTitle, cfg, drafts[i].file)
611
645
  operations.push({ op: 'merge-into', file: drafts[i].file, target: d.target, additions: add })
612
646
  } else if (d.op === 'delete') {
613
647
  operations.push({ op: 'delete', file: drafts[i].file, reason: d.reason })
@@ -3,24 +3,24 @@ type: JS Module
3
3
  title: discover-checkable-rules.mjs
4
4
  resource: npm/scripts/lib/discover-checkable-rules.mjs
5
5
  docgen:
6
- crc: 403ab2ee
6
+ crc: d06cd969
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
11
  ## Огляд
12
12
 
13
- Шукає правила, для яких є щось «прогонне» у визначених структурах. Виявляє JS-концерни за шляхами `rules/<id>/js/<concern>.mjs` та Policy-концерни, які мають пару з `<concern>.rego` (зв'язану з конфігурацією `target.json`). Ігнорує файли з префіксом `_` та `*.test.mjs`, а також каталоги `_lib/`. Виконання є швидким скануванням структури (шляхи та назви) без парсингу вмісту.
13
+ Визначає наявність JS-концернів та policy-концернів у заданому каталозі правила. Сканує всі каталоги правил, шукаючи JS-концерни у файлах `rules/<id>/js/<concern>.mjs` та policy-концерни, що асоціюються з парою `<concern>.rego` та `target.json`. Повертає список правил, які містять такі прогонні частини. Файли з префіксом `_` або `*.test.mjs` ігноруються.
14
14
 
15
15
  ## Поведінка
16
16
 
17
- discoverOneRule описує набір JS-концернів та policy-концернів для одного каталогу правила, використовуючи шлях до цього каталогу та його ідентифікатор.
18
- discoverCheckableRules сканує каталог з усіма правилами та повертає список правил, які мають JS-концерни або policy-концерни, фільтруючи ті, що не містять прогонного контенту.
17
+ discoverOneRule описує правило, виявляючи JS-концерни та policy-концерни в заданому каталозі правила.
18
+ discoverCheckableRules сканує каталог правил і повертає список правил, які мають JS-концерни або policy-концерни, фільтруючи ті, що не мають прогонних частин.
19
19
 
20
20
  ## Публічний API
21
21
 
22
- discoverOneRule — створює об'єкт перевірки для одного каталогу правила.
23
- discoverCheckableRules — знаходити правила у каталозі `rules/`, які мають відповідні скрипти у `js/` або політики у `policy/`.
22
+ - discoverOneRule — Створює об'єкт для одного правила, що перевіряє конкретний каталог.
23
+ - discoverCheckableRules — Знаходить правила у каталозі `rules/`, які мають відповідні скрипти (у `js/`) або політики (у `policy/`). Ігнорує правила, що містять лише документацію.
24
24
 
25
25
  ## Гарантії поведінки
26
26
 
@@ -3,22 +3,24 @@ type: JS Module
3
3
  title: inline-template-links.mjs
4
4
  resource: npm/scripts/lib/inline-template-links.mjs
5
5
  docgen:
6
- crc: e1bed533
6
+ crc: 252fb1e1
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
- Модуль інтегрує зовнішній контент у текстовий вивід, використовуючи конфігурації з `package.json.snippet.json` та `package.json`. Він замінює посилання на шаблони в тексті на їхній вміст за допомогою `inlineTemplateLinks` та доповнює текст вмістом усіх знайдених файлів `.mdc` з директорій `js/` та `policy/<concern>/` за допомогою `appendDiscoveredMdcFiles`.
11
+ ## Огляд
12
+
13
+ Модуль збагачує текстовий контент, використовуючи конфігурації з package.json.snippet.json та package.json. Функція inlineTemplateLinks замінює текстові посилання на шаблони вбудованими блоками, якщо відповідні файли знаходяться у директорії правил. Функція appendDiscoveredMdcFiles доповнює текст вмістом усіх знайдених файлів `.mdc` з піддиректорій `js/` та `policy/` у директорії правил.
12
14
 
13
15
  ## Поведінка
14
16
 
15
- inlineTemplateLinks замінює посилання на шаблони в тексті на вбудовані блоки з вмістом відповідних файлів.
16
- appendDiscoveredMdcFiles додає вміст усіх знайдених файлів \*.mdc з директорій js/ та policy/<concern>/ до наданого тексту.
17
+ inlineTemplateLinks замінює посилання на шаблони в тексті на вбудовані блоки з вмістом файлу, якщо ці файли існують у вказаній директорії правил.
18
+ appendDiscoveredMdcFiles додає до кінця тексту вміст усіх знайдених файлів `.mdc` з піддиректорій `js/` та `policy/` у директорії правил.
17
19
 
18
20
  ## Публічний API
19
21
 
20
- inlineTemplateLinks — Замінює посилання у Markdown, що містять `/template/`, на вбудовані блоки, зчитуючи вміст з вказаного файлу. Викидає помилку, якщо цільове посилання не знайдено.
21
- appendDiscoveredMdcFiles — Додає всі знайдені файли з розширенням `.mdc` з підкаталогів `js/` та `policy/<concern>/`. Спочатку додаються файли з `js/` алфавітному порядку), а потім файли з підкаталогів `policy/` (у алфавітному порядку за назвою `concern`, а потім за назвою файлу).
22
+ inlineTemplateLinks — Замінює посилання на шаблони в Markdown на вбудовані блоки, якщо шлях містить `/template/`. Помилка виникає, якщо цільовий файл посилання відсутній.
23
+ appendDiscoveredMdcFiles — Додає всі знайдені файли `.mdc` з папок `js/` та `policy/<concern>/`. Файли з `js/` йдуть першими, а потім файли з підпапок `policy/<concern>/` (у алфавітному порядку за `concern`, а потім за назвою файлу).
22
24
 
23
25
  ## Гарантії поведінки
24
26
 
@@ -3,17 +3,19 @@ type: JS Module
3
3
  title: list-project-rules-mdc.mjs
4
4
  resource: npm/scripts/lib/list-project-rules-mdc.mjs
5
5
  docgen:
6
- crc: 60cd4e83
6
+ crc: 59fe0e6e
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
- Цей модуль визначає шлях до `.mdc`-файлів правил у проєкті-споживачі, що зберігаються у директорії, вказаній константою CURSOR_RULES_DIR. Він надає відсортований список імен цих файлів. Це винесення логіки з `bin/n-cursor.js` забезпечує розділення відповідальності між інструментом командного рядка та функцією перевірки конформності.
11
+ ## Огляд
12
+
13
+ Цей модуль визначає шлях до правил у проєкті-споживачі, що константою експортується як CURSOR_RULES_DIR=".cursor/rules". Він надає відсортований список імен файлів `.mdc` з каталогу правил. Це дозволяє розділити логіку диспетчеризації командного інтерфейсу та логіку перевірки конформності. Функція зчитує та повертає список файлів, якщо каталог правил існує.
12
14
 
13
15
  ## Поведінка
14
16
 
15
17
  CURSOR_RULES_DIR — Вказує на каталог правил у проєкті-споживачі.
16
- listProjectRulesMdcFiles — Повертає відсортований список імен файлів з розширенням `.mdc` у каталозі правил проєкту, або порожній масив, якщо каталог не існує.
18
+ listProjectRulesMdcFiles — Повертає відсортований список імен файлів `.mdc` з каталогу правил проєкту-споживача, якщо цей каталог існує.
17
19
 
18
20
  ## Публічний API
19
21
 
@@ -3,30 +3,27 @@ type: JS Module
3
3
  title: root-notice.mjs
4
4
  resource: npm/scripts/lib/root-notice.mjs
5
5
  docgen:
6
- crc: 5f3ca2bb
6
+ crc: 65db7d81
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
7
9
  ---
8
10
 
9
- Цей файл вставляє root-guard preflight для скілів, які змінюють проєкт у поточному каталозі. Він забезпечує, що скіл виконується in-place, без ізоляції worktree, і використовується для випадків, коли не потрібна ізоляція worktree. Це гарантує коректне виконання скілу від імені проєкту.
11
+ ## Огляд
12
+
13
+ Цей модуль вшиває інструкцію для агента у файл `SKILL.md`. Інструкція вказує, що скіл мутує проєкт у поточному каталозі, але виконується in-place (без worktree-ізоляції), що відповідає конфігурації `meta.json` $\to$ `requireRoot: true` і `worktree: false`. Це застосовується до скілів, які змінюють `meta.json` або `package.json` безпосередньо, на відміну від worktree-скілів, які мають свій `root-assert` у `worktree-notice.mjs`. Блок є інструкцією агенту, що читає `SKILL.md`, і є ре-синк ідемпотентним: наявний блок замінюється, при `false` — видаляється. Програмний аналог для CLI-команд — `assertCwdIsProjectRoot`. Інструкція позначається маркерами `ROOT_START` ("<!-- n-cursor:root:start -->") та `ROOT_END` ("<!-- n-cursor:root:end -->").
10
14
 
11
15
  ## Поведінка
12
16
 
13
- ROOT_START: вставляє маркер початку root-блоку.
14
- ROOT_END: вставляє маркер кінця root-блоку.
15
- injectRootNotice: вставляє, оновлює або видаляє root-guard блок у вмісті `SKILL.md` на основі параметра `enabled`.
17
+ ROOT_START: Позначає початок блоку інструкції щодо перевірки кореня репозиторію.
18
+ ROOT_END: Позначає кінець блоку інструкції щодо перевірки кореня репозиторію.
19
+ injectRootNotice: Вставляє, оновлює або видаляє блок інструкції щодо перевірки кореня репозиторію у вмісті файлу `SKILL.md` на основі булевого прапорця.
16
20
 
17
21
  ## Публічний API
18
22
 
19
- - ROOT_START — Позначає початок блоку root.
20
- - ROOT_END — Позначає кінець блоку root.
21
- - injectRootNotice — Змінює вміст `SKILL.md` щодо root-guard блоку.
23
+ ROOT_START — маркер початку основного блоку документації.
24
+ ROOT_END — маркер завершення основного блоку документації.
25
+ injectRootNotice — додає, змінює або видаляє захисний блок у файлі `SKILL.md`.
22
26
 
23
27
  ## Гарантії поведінки
24
28
 
25
- - Якщо `requireRoot: true` у `meta.json`, то поточний робочий каталог є коренем проєкту.
26
- - Якщо `requireRoot: false`, то блок не вставляється.
27
- - Вставка відбувається лише між маркерами.
28
- - Вставка ре-синк ідемпотентно: наявний блок замінюється.
29
- - `ROOT_START` викликається перед вставкою блоку.
30
- - `ROOT_END` викликається після вставки блоку.
31
- - `injectRootNotice` викликається під час вставки блоку.
32
- - Немає кешування.
29
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -3,26 +3,28 @@ type: JS Module
3
3
  title: run-lint.mjs
4
4
  resource: npm/scripts/lib/run-lint.mjs
5
5
  docgen:
6
- crc: f574f097
6
+ crc: 3c7deca0
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
- Модуль керує процесом лінтування коду. Він визначає набір активних правил лінтування, використовуючи конфігурації з `meta.json` та `.n-cursor.json`. Модуль надає можливість вибрати ці правила за допомогою `selectLintRules` та ініціювати запуск перевірки коду за допомогою `runLint`.
11
+ ## Огляд
12
+
13
+ Модуль визначає набір правил лінтування, спираючись на конфігурації `meta.json` та `.n-cursor.json`. Він використовує `selectLintRules` для вибору та сортування цих правил, а потім ініціалізує процес перевірки конформності та форматування за допомогою `runLint` у різних режимах (scoped, hook, delta, full).
12
14
 
13
15
  ## Поведінка
14
16
 
15
- selectLintRules визначає, які правила лінтуються на основі їхньої конфігурації та активності в `.n-cursor.json`, повертаючи відсортований список ID.
16
- runLint запускає лінтер-оркестрацію залежно від наданих опцій: виконує прогін для конкретних правил, перевіряє лише змінені файли, виконує повний прогін репозиторію або форматування.
17
+ selectLintRules вибирає і алфавітно сортує ідентифікатори правил для лінтування на основі їхньої метаінформації та стану активації в `.n-cursor.json`.
18
+ runLint запускає лінт-оркестрацію, виконуючи різні режими залежно від наданих опцій: scoped (за назвами правил), hook (за явним списком файлів), delta (зміна відносно origin) або full (весь репозиторій), включаючи конформність та форматування у fix-режимі.
17
19
 
18
20
  ## Публічний API
19
21
 
20
- selectLintRules — вибирає ідентифікатори правил для контексту в алфавітному порядку.
22
+ selectLintRules — обирає ідентифікатори правил для контексту, у алфавітному порядку.
21
23
  runLint — ініціює процес лінтування.
22
24
  full — аналізує весь репозиторій, порівнюючи поточний стан із початковим.
23
- readOnly — лише виявляє проблеми, не вносячи змін.
24
- rules — виконує повний прогін лише для заданого набору правил у вказаному контексті.
25
- files — виконує перевірку лише для перелічених файлів у режимі хука.
25
+ readOnly — лише виявляє проблеми без внесення змін.
26
+ rules — виконує повний прогін лише для заданого набору правил.
27
+ files — застосовує правила до конкретного переліку файлів у режимі хука.
26
28
 
27
29
  ## Гарантії поведінки
28
30
 
@@ -3,26 +3,45 @@ type: JS Module
3
3
  title: skill-meta.mjs
4
4
  resource: npm/scripts/lib/skill-meta.mjs
5
5
  docgen:
6
- crc: c1757867
6
+ crc: fd8085f3
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
+ issues: judge:inaccurate:0.99
10
+ judgeModel: openai-codex/gpt-5.4-mini
9
11
  ---
10
12
 
11
- Спільний парсер метаданих скіла, що зчитує інформацію з `main.json`. Цей файл є єдиним джерелом правди для скіла. Модуль дозволяє визначати умови автоактивації скіла (через поле `auto`), вказувати, чи виконувати скіл в окремому git-worktree (`worktree`), та чи вимагає він запуску з кореня репозиторію (`requireRoot`). Надає константу `SKILL_ALWAYS` для безумовної активації. Хелпер використовується іншими компонентами для уникнення дублювання парсингу. Функції забезпечують перехоплення помилок (fail-safe), повертаючи порожнє значення замість винятків.
13
+ ## Огляд
14
+
15
+ Спільний парсер метаданих скіла з `npm/skills/<id>/main.json`. Цей механізм зчитує та інтерпретує конфігурацію скіла, використовуючи `main.json` як єдине джерело правди. Він визначає умову автоактивації (`auto`, де `SKILL_ALWAYS` означає безумовну активацію), необхідність роботи з кореневою директорією (`requireRoot`), та визначає, чи повинен скіл виконуватися ізольовано в окремому git-worktree (`worktree`).
16
+
17
+ Поведінка
18
+ SKILL_ALWAYS — Константа-рядок, яка позначає, що скіл має бути активований завжди.
19
+ SKILL_TIERS — Список допустимих рівнів моделі для виконання скіла.
20
+ DEFAULT_SKILL_TIER — Константа-рядок, яка встановлює рівень моделі за замовчуванням, якщо він не вказаний у метаданих.
21
+ parseSkillAutoSpec — Визначає умови, за яких скіл може бути автоматично активований, ґрунтуючись на конфігурації `main.json`.
22
+ skillRequiresRoot — Визначає, чи вимагає скіл запуску з кореневої директорії репозиторію. Зверніть увагу, що скіли з `worktree:true` не вимагають цього поля явно, оскільки коренем є сам worktree.
23
+ skillTier — Визначає рівень моделі, який буде використаний для виконання скіла, враховуючи конфігурацію `main.json` або повертаючи `DEFAULT_SKILL_TIER`.
24
+ readSkillMetaRaw — Зчитує та парсить сирі метадані скіла з файлу `main.json`. При виявленні помилок парсингу або в IO-операціях, функція не генерує винятки, а повертає `null` (fail-safe).
12
25
 
13
26
  ## Поведінка
14
27
 
15
- SKILL_ALWAYS — надає літерал для безумовної автоактивації скіла.
16
- parseSkillAutoSpecперетворює значення поля `auto` з `main.json` у специфікацію автоактивації.
17
- skillRequiresRootвизначає, чи вимагає скіл запуску з кореня репозиторію, базуючись на метаданих.
18
- readSkillMetaRawчитає та парсить файл `main.json` для заданого каталогу скіла.
28
+ SKILL_ALWAYS — Константа, що позначає безумовну активацію скіла.
29
+ SKILL_TIERSСписок допустимих рівнів моделі для виконання скіла.
30
+ DEFAULT_SKILL_TIERТир моделі, який застосовується за замовчуванням, якщо рівень не вказаний у метаданих.
31
+ parseSkillAutoSpecВизначає, як скіл може бути автоматично активований на основі конфігурації `main.json`.
32
+ skillRequiresRoot — Визначає, чи повинен скіл виконуватися з кореневої директорії репозиторію, базуючись на налаштуваннях `main.json`.
33
+ skillTier — Визначає рівень моделі, який буде використано для виконання скіла, приймаючи в до уваги значення з `main.json` або повертаючи `DEFAULT_SKILL_TIER`.
34
+ readSkillMetaRaw — Зчитує та парсить метадані скіла з файлу `main.json` за вказаним шляхом, обробляючи можливі помилки формату.
19
35
 
20
36
  ## Публічний API
21
37
 
22
- SKILL_ALWAYS — позначка для безумовної активації.
23
- parseSkillAutoSpecвитягує налаштування автоматичного запуску з `main.json` у структуру `SkillAutoSpec`.
24
- skillRequiresRootвизначає, чи потрібен запуск скіла з кореня репозиторію (активує захист від запуску з інших місць).
25
- readSkillMetaRawзчитує та розбирає метадані одного скіла з `main.json`.
38
+ **SKILL_ALWAYS**позначка, що робить скіл автоматично активним у будь-якій ситуації.
39
+ **SKILL_TIERS**список допустимих рівнів (тирів) моделей, які можуть виконувати скіл через `pi`-runner.
40
+ **DEFAULT_SKILL_TIER**рівень моделі, який використовується за замовчуванням для скілів, якщо `main.json` цього скіла не вказує конкретний рівень.
41
+ **parseSkillAutoSpec**визначає, чи має скіл автоматично запускатися на основі конфігурації в `main.json`.
42
+ **skillRequiresRoot** — позначає, чи повинен скіл виконуватися з кореневої директорії репозиторію; це захист від несанкціонованого запуску.
43
+ **skillTier** — визначає необхідний рівень моделі для виконання скіла, використовуючи значення з `main.json` або встановлюючи `DEFAULT_SKILL_TIER`.
44
+ **readSkillMetaRaw** — зчитує та аналізує метадані конкретного скіла з файлу `main.json`.
26
45
 
27
46
  ## Гарантії поведінки
28
47
 
@@ -3,28 +3,25 @@ type: JS Module
3
3
  title: discover-t0-patterns.mjs
4
4
  resource: npm/scripts/lib/fix/discover-t0-patterns.mjs
5
5
  docgen:
6
- crc: f2a262bb
6
+ crc: 3f3b47e0
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
11
  ## Огляд
12
12
 
13
- Модуль сканує директорію правил для пошуку файлів `fix-*.mjs`, що містять визначення T0-паттернів. Він динамічно імпортує ці файли з папок `js` та `policy/{concern}` для кожного правила. Ініціалізація результату відбувається за допомогою _top-level await_ у `t0.mjs`. Функція збирає масив об'єктів, що описують усі знайдені паттерни.
13
+ Сканує директорії `npm/rules/{rule}/js/fix-*.mjs` та `npm/rules/{rule}/policy/{concern}/fix-*.mjs` для всіх правил. Динамічно імпортує кожен знайдений файл `fix-*.mjs` та збирає масиви `patterns`. Результат ініціалізується через top-level await у `t0.mjs` і представляє унікальний список усіх виявлених T0-паттернів. Функція не виконує операцій запису та перехоплює помилки, повертаючи `null` у випадку збоїв замість викидання винятків.
14
14
 
15
15
  ## Поведінка
16
16
 
17
- 1. Перевіряє існування директорії, вказаної як `rulesDir`. Якщо директорія не існує, повертає порожній масив.
18
- 2. Зчитує список усіх елементів у директорії `rulesDir`.
19
- 3. Для кожного елемента, який є директорією та не починається з крапки, виконується наступне:
20
- а. Визначається повний шлях до директорії правила.
21
- б. Знаходяться всі файли `fix-*.mjs` у піддиректорії `js` цього правила.
22
- в. Знаходяться всі файли `fix-*.mjs` у піддиректоріях `policy/{concern}` цього правила.
23
- г. Для кожного знайденого файлу `fix-*.mjs` виконується динамічний імпорт.
24
- д. Якщо імпорт успішний, збирається масив `patterns` з імпортованого модуля та додається до загального списку паттернів.
25
- е. У разі помилки імпорту, помилка виводиться в консоль, але процес продовжується.
26
- 4. Після обробки всіх правил, виконується дедуплікація загального списку паттернів за їхніми ідентифікаторами.
27
- 5. Повертається фінальний, унікальний масив T0-паттернів.
17
+ 1. Перевіряє існування шляху `rulesDir`. Якщо шлях не існує, повертає порожній масив.
18
+ 2. Знаходить усі файли з шаблонами у шляхах `*/js/fix-*.mjs` та `*/policy/*/fix-*.mjs` у межах `rulesDir`.
19
+ 3. Для кожного знайденого файлу:
20
+ а. Намагається динамічно імпортувати вміст файлу.
21
+ б. Якщо імпорт успішний і вміст містить масив `patterns`, додає ці паттерни до загального списку.
22
+ в. Якщо імпорт не вдається, реєструє помилку, але продовжує роботу.
23
+ 4. Фільтрує загальний список паттернів, щоб уникнути дублікатів, зберігаючи лише перше входження кожного паттерна за його ідентифікатором.
24
+ 5. Повертає відфільтрований масив унікальних T0-паттернів.
28
25
 
29
26
  ## Публічний API
30
27
 
@@ -3,25 +3,26 @@ type: JS Module
3
3
  title: escalation-log.mjs
4
4
  resource: npm/scripts/lib/fix/escalation-log.mjs
5
5
  docgen:
6
- crc: 07ae959f
6
+ crc: 91898427
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
- Append-only JSONL-лог драбини ескалації конформність-фіксу. Один запис на рунг драбини: модель, чи виклик удався, чи правило стало зеленим після рунга («чи допомогло»), залишковий violation і само-аналіз моделі (`diagnosis`). Доповнює always-on wire-trace — той знає вміст викликів, але не результат re-check; join — за полем `caller` (`fix:<rule>:<rung>`).
11
+ ## Огляд
12
12
 
13
- ## Поведінка
13
+ Файл створює append-only JSONL-лог ескалації конформність-фіксу (спека 2026-06-19-fix-escalation-cascade-design). Він фіксує кожен запис на рунг драбини, що включає деталі використаної моделі (`model`), подання зворотного зв'язку (`withFeedback`), результат виклику (`callOk`/`callError`), оцінку, чи допомогло це усунути порушення (`recheckOk`), залишковий `violation` та самоаналіз моделі (`diagnosis`). Цей лог доповнює always-on wire-trace (`lib/pi-trace.mjs`) і формує логіку для join за полем `caller` (`fix:<rule>:<rung>`). Запис відбувається за певним шільною: або через kill-switch `N_CURSOR_FIX_ESCALATION_LOG`, або за явним шляхом, дефолтно у `<cwd>/.n-cursor/fix-escalation.jsonl`.
14
14
 
15
- `escalationLogPath` резолвить шлях: `N_CURSOR_FIX_ESCALATION_LOG` як kill-switch (`0|false|off|no` → лог вимкнено, повертає `null`) або як явний шлях; інакше дефолт `<cwd>/.n-cursor/fix-escalation.jsonl`.
15
+ ## Поведінка
16
16
 
17
- `logEscalation` дописує один JSONL-рядок (no-op, якщо лог вимкнено). Поля `remainingViolation` і `diagnosis` обрізаються до межі; `recheckOk` обнуляє `remainingViolation`. Помилки запису ковтаються — лог діагностичний і не має валити сам фікс.
17
+ Поведінка:
18
+ escalationLogPath визначає шлях до файлу журналу ескалації конформності. Якщо встановлено змінну середовища N_CURSOR_FIX_ESCALATION_LOG, використовується цей шлях; інакше, за замовчуванням, це .n-cursor/fix-escalation.jsonl у поточній робочій директорії.
19
+ logEscalation записує один запис про рунг у JSONL-лог, якщо шлях до логу визначено. Запис містить метадані про спробу фіксування, результати виклику та аналіз. Помилки під час запису логу ігноруються.
18
20
 
19
21
  ## Публічний API
20
22
 
21
- - `escalationLogPath()`шлях активного логу або `null`, якщо вимкнено.
22
- - `logEscalation(rec)`дописує запис рунга у JSONL.
23
+ escalationLogPath — вказує на місце зберігання логу ескалацій, якщо функція не вимкнена.
24
+ logEscalation — записує один подію виконання в спеціальний лог у форматі JSONL, ігноруючи внутрішні помилки запису.
23
25
 
24
26
  ## Гарантії поведінки
25
27
 
26
- - Не звертається до мережі.
27
- - Перехоплює помилки запису і не пропускає винятків назовні (fail-safe).
28
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
@@ -8,7 +8,6 @@ resource: npm/scripts/lib/fix/
8
8
 
9
9
  | Файл | Тип |
10
10
  | ----------------------------------------------------- | --------- |
11
- | [analyze-escalation.mjs](analyze-escalation.md) | JS Module |
12
11
  | [discover-t0-patterns.mjs](discover-t0-patterns.md) | JS Module |
13
12
  | [escalation-log.mjs](escalation-log.md) | JS Module |
14
13
  | [llm-fix-apply.mjs](llm-fix-apply.md) | JS Module |