@nitra/cursor 12.16.1 → 12.16.2

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [12.16.2] - 2026-06-27
4
+
5
+ ### Changed
6
+
7
+ - feat(orchestrator): skip non-actionable violation (не годувати агента tool-crash); feat(pi-agent-fix): логувати вхід LLM у trace (violation + розмір промпта)
8
+
3
9
  ## [12.16.1] - 2026-06-26
4
10
 
5
11
  ### Changed
@@ -213,12 +213,11 @@ export async function runPiAgentFix(ruleId, violation, cwd, opts = {}) {
213
213
  }
214
214
  })
215
215
 
216
+ const fixPrompt = buildFixPrompt({ ruleId, violation, ruleText, feedback })
216
217
  const startedAt = clock()
217
218
  let error = null
218
219
  try {
219
- await withTimeout(session.prompt(buildFixPrompt({ ruleId, violation, ruleText, feedback })), timeoutMs, () =>
220
- session.abort?.()
221
- )
220
+ await withTimeout(session.prompt(fixPrompt), timeoutMs, () => session.abort?.())
222
221
  } catch (e) {
223
222
  error = e.message
224
223
  }
@@ -244,9 +243,17 @@ export async function runPiAgentFix(ruleId, violation, cwd, opts = {}) {
244
243
  rung: tier,
245
244
  model: modelSpec,
246
245
  cwd,
246
+ // ВХІД LLM (щоб «що подали» було видно у trace, без розкопок escalation-log):
247
+ // violation — обрізаний (може бути великим); promptChars — повний розмір промпта.
248
+ violation: typeof violation === 'string' ? violation.slice(0, 4000) : null,
249
+ violationChars: typeof violation === 'string' ? violation.length : 0,
250
+ promptChars: fixPrompt.length,
251
+ // вихід:
247
252
  turnCount,
248
253
  toolCallCount,
254
+ touchedFiles,
249
255
  backstopHit,
256
+ wallMs: clock() - startedAt,
250
257
  error
251
258
  })
252
259
  return { applied: touchedFiles.length > 0, touchedFiles, telemetry, error, rollback: guard.rollback }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "12.16.1",
3
+ "version": "12.16.2",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -51,6 +51,19 @@ export function classifyFixError(error) {
51
51
  return 'quality'
52
52
  }
53
53
 
54
+ /**
55
+ * Чи violation придатний для LLM-фіксу: містить хоч одне actionable `❌`-порушення
56
+ * (формат конформ-чека `❌ <file>: <інструкція>`). Без жодного ❌ — це не список
57
+ * фіксабельних порушень, а шум/збій тула (Usage, «лок взято», порожньо). Годувати
58
+ * таким агента = марні turns/timeout (вимір 7n-test: doc-files surface-ив Usage
59
+ * downstream-тула → агент флоундерив 4 рунги). Тоді LLM-фікс пропускаємо.
60
+ * @param {string|null|undefined} output violation-вивід правила
61
+ * @returns {boolean} true — є що фіксити
62
+ */
63
+ export function hasActionableViolation(output) {
64
+ return /❌/u.test(output ?? '')
65
+ }
66
+
54
67
  /**
55
68
  * Будує драбину ескалації за наявними тирами (спека 2026-06-19-fix-escalation-cascade):
56
69
  * 1. `local-min` — `N_LOCAL_MIN_MODEL`, перший прохід;
@@ -124,6 +137,27 @@ export async function escalateRule(rule, cwd, deps) {
124
137
  const skipModels = new Set()
125
138
  let avgUsed = 0
126
139
 
140
+ // §2-профілактика: violation без actionable ❌ (tool-crash/Usage/шум) → не годуємо
141
+ // агента (інакше флоундерить рунги до timeout). Рапортуємо як non-actionable, не фіксимо.
142
+ if (!hasActionableViolation(rule.output)) {
143
+ record({
144
+ rung: -1,
145
+ tier: 'skip',
146
+ model: '',
147
+ withFeedback: false,
148
+ callOk: false,
149
+ callError: 'non-actionable violation (нема ❌ — ймовірно check-error/tool-crash)',
150
+ recheckOk: false,
151
+ remainingViolation: rule.output,
152
+ diagnosis: null,
153
+ ms: 0
154
+ })
155
+ log(
156
+ ` ⏭️ ${rule.ruleId}: LLM-фікс пропущено — у violation немає ❌-порушень (check-error/tool-crash, не фіксабельне)`
157
+ )
158
+ return { resolved: false, avgUsed: 0 }
159
+ }
160
+
127
161
  for (const [idx, rung] of ladder.entries()) {
128
162
  if (skipModels.has(rung.model)) continue
129
163