@nitra/cursor 11.2.0 → 11.3.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 +6 -0
- package/bin/n-cursor.js +2 -1
- package/package.json +1 -1
- package/rules/doc-files/js/lint.mjs +4 -3
- package/rules/lint/js/orchestrate.mjs +10 -2
- package/rules/text/js/lint.mjs +3 -2
- package/rules/text/lint/cspell-fix.mjs +3 -2
- package/rules/text/lint/lint.mjs +7 -5
- package/rules/text/meta.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [11.3.0] - 2026-06-15
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- lint: opt-in `meta.json: llmFix:true` тепер реально дротується (раніше прапор був декоративний — opportunistic LLM-fix біг просто на `!readOnly`). `runLint` читає `llmFix` з meta правила й передає в `lint(files, cwd, { readOnly, llmFix })`; правило без прапора лишається detect-only. Це й забезпечує safety-тріаж зі спеки (логічні лінтери не вмикають LLM-fix випадково). doc-files і text позначені `llmFix:true`; cspell-класифікація гейтиться через `llmFix` (проведено `runLintTextCli`/`runLintTextSteps`/`runCspellText`), standalone `lint-text` передає `llmFix:true`. Принагідно: justified `no-unsanitized/method`-disable на package-internal динамічний import у `runLint` (pre-existing).
|
|
8
|
+
|
|
3
9
|
## [11.2.0] - 2026-06-15
|
|
4
10
|
|
|
5
11
|
### Changed
|
package/bin/n-cursor.js
CHANGED
|
@@ -1568,7 +1568,8 @@ try {
|
|
|
1568
1568
|
case 'lint-text': {
|
|
1569
1569
|
// Канонічний lint-text: cspell → shellcheck → dotenv → markdownlint → v8r (text.mdc).
|
|
1570
1570
|
// `--read-only` (CI): без авто-фіксу (markdownlint/shellcheck/dotenv) — нуль мутацій.
|
|
1571
|
-
|
|
1571
|
+
// `llmFix:true` — text llmFix-capable, тож standalone lint-text робить omlx-класифікацію cspell.
|
|
1572
|
+
process.exitCode = await runLintTextCli({ readOnly: args.includes('--read-only'), llmFix: true })
|
|
1572
1573
|
|
|
1573
1574
|
break
|
|
1574
1575
|
}
|
package/package.json
CHANGED
|
@@ -98,13 +98,14 @@ function collectStale(files, cwd) {
|
|
|
98
98
|
* Крок агрегатора lint для doc-files (opportunistic LLM-fix tier).
|
|
99
99
|
* @param {string[] | undefined} files quick: лише ці файли; undefined: весь репозиторій
|
|
100
100
|
* @param {string} [cwd] корінь репо
|
|
101
|
-
* @param {{ readOnly?: boolean }} [opts] readOnly: лише детект (CI/hook)
|
|
101
|
+
* @param {{ readOnly?: boolean, llmFix?: boolean }} [opts] readOnly: лише детект (CI/hook);
|
|
102
|
+
* llmFix: opt-in opportunistic-генерація (з `meta.json: llmFix:true`) — без нього detect-only
|
|
102
103
|
* @returns {Promise<number>} 0 — доки свіжі; 1 — є застарілі (детект, fix пропущено чи помилка генерації)
|
|
103
104
|
*/
|
|
104
|
-
export async function lint(files, cwd = process.cwd(), { readOnly = false } = {}) {
|
|
105
|
+
export async function lint(files, cwd = process.cwd(), { readOnly = false, llmFix = false } = {}) {
|
|
105
106
|
const stale = collectStale(files, cwd)
|
|
106
107
|
if (stale.length === 0) return 0
|
|
107
|
-
if (readOnly) return reportStale(stale)
|
|
108
|
+
if (readOnly || !llmFix) return reportStale(stale)
|
|
108
109
|
|
|
109
110
|
// fix-by-default: opportunistic-генерація через спільне ядро (preflight omlx →
|
|
110
111
|
// батч із circuit-breaker'ом). omlx недоступний → runGenerationBatch друкує причину
|
|
@@ -108,15 +108,23 @@ export async function runLint(opts = {}) {
|
|
|
108
108
|
return 0
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
const
|
|
111
|
+
const metaById = readAllMeta(rulesDir)
|
|
112
|
+
const ids = selectLintRules(metaById, full)
|
|
112
113
|
for (const id of ids) {
|
|
113
114
|
const lintPath = join(rulesDir, id, 'js', 'lint.mjs')
|
|
114
115
|
if (!existsSync(lintPath)) {
|
|
115
116
|
log(`⚠️ lint: правило ${id} має lint-фазу, але немає js/lint.mjs — пропускаю.\n`)
|
|
116
117
|
continue
|
|
117
118
|
}
|
|
119
|
+
// lintPath = join(rulesDir, id, …) — суто package-internal (rulesDir пакета + id зі
|
|
120
|
+
// selectLintRules за власним meta), не зовнішній вхід → ін'єкції немає.
|
|
121
|
+
// eslint-disable-next-line no-unsanitized/method
|
|
118
122
|
const mod = await import(lintPath)
|
|
119
|
-
|
|
123
|
+
// `llmFix` (opt-in opportunistic LLM-fix, спека 2026-06-15): лише правила з
|
|
124
|
+
// `meta.json: llmFix:true` отримують fix-сходинку; решта — detect-only. Це й
|
|
125
|
+
// забезпечує safety-тріаж (логічні лінтери не вмикають LLM-fix випадково).
|
|
126
|
+
const llmFix = metaById[id]?.llmFix === true
|
|
127
|
+
const code = await mod.lint(changed, cwd, { readOnly, llmFix })
|
|
120
128
|
if (code !== 0) return code
|
|
121
129
|
}
|
|
122
130
|
|
package/rules/text/js/lint.mjs
CHANGED
|
@@ -6,9 +6,10 @@ import { runLintTextCli } from '../lint/lint.mjs'
|
|
|
6
6
|
/**
|
|
7
7
|
* @param {string[] | undefined} _files ігнорується (whole-repo аналіз)
|
|
8
8
|
* @param {string} [_cwd] корінь (ігнорується — CLI працює від process.cwd())
|
|
9
|
-
* @param {{ readOnly?: boolean }} [opts] readOnly → детект без авто-фіксу (нуль мутацій)
|
|
9
|
+
* @param {{ readOnly?: boolean, llmFix?: boolean }} [opts] readOnly → детект без авто-фіксу (нуль мутацій);
|
|
10
|
+
* llmFix → opt-in omlx-класифікація cspell (з `meta.json: llmFix:true`)
|
|
10
11
|
* @returns {Promise<number>} exit code
|
|
11
12
|
*/
|
|
12
13
|
export function lint(_files, _cwd, opts = {}) {
|
|
13
|
-
return runLintTextCli({ readOnly: opts.readOnly === true })
|
|
14
|
+
return runLintTextCli({ readOnly: opts.readOnly === true, llmFix: opts.llmFix === true })
|
|
14
15
|
}
|
|
@@ -113,9 +113,10 @@ export function appendWordsToDict(cwd, words) {
|
|
|
113
113
|
* cspell-крок lint-text: класифікація → словник (нова схема).
|
|
114
114
|
* @param {string} [cwd] корінь
|
|
115
115
|
* @param {boolean} [readOnly] true → лише детект (нуль мутацій)
|
|
116
|
+
* @param {boolean} [llmFix] opt-in omlx-класифікація (з `meta.json: llmFix:true`); без нього — лише детект
|
|
116
117
|
* @returns {number} 0 — чисто; 1 — лишились знахідки / помилка середовища
|
|
117
118
|
*/
|
|
118
|
-
export function runCspellText(cwd = process.cwd(), readOnly = false) {
|
|
119
|
+
export function runCspellText(cwd = process.cwd(), readOnly = false, llmFix = false) {
|
|
119
120
|
const bin = resolveCmd('npx')
|
|
120
121
|
if (!bin) {
|
|
121
122
|
process.stderr.write('❌ npx не знайдено в PATH (cspell).\n')
|
|
@@ -124,7 +125,7 @@ export function runCspellText(cwd = process.cwd(), readOnly = false) {
|
|
|
124
125
|
|
|
125
126
|
const first = detectCspell(cwd, bin)
|
|
126
127
|
if (first.code === 0) return 0
|
|
127
|
-
if (readOnly) {
|
|
128
|
+
if (readOnly || !llmFix) {
|
|
128
129
|
process.stdout.write(first.out)
|
|
129
130
|
return first.code
|
|
130
131
|
}
|
package/rules/text/lint/lint.mjs
CHANGED
|
@@ -97,9 +97,10 @@ function preflight(dep) {
|
|
|
97
97
|
/**
|
|
98
98
|
* Внутрішні кроки `lint-text` без локу.
|
|
99
99
|
* @param {boolean} [readOnly] true → лише детект без авто-фіксу (нуль мутацій — CI/pre-commit)
|
|
100
|
+
* @param {boolean} [llmFix] opt-in omlx-класифікація cspell (інші кроки фіксяться детерміновано за readOnly)
|
|
100
101
|
* @returns {number} 0 — все OK, інакше — код першого кроку, що впав
|
|
101
102
|
*/
|
|
102
|
-
function runLintTextSteps(readOnly = false) {
|
|
103
|
+
function runLintTextSteps(readOnly = false, llmFix = false) {
|
|
103
104
|
// Auto-install: throws on failure → propagates as exit 1 from runStandardLint
|
|
104
105
|
ensureTool('shellcheck')
|
|
105
106
|
ensureTool('dotenv-linter')
|
|
@@ -107,8 +108,8 @@ function runLintTextSteps(readOnly = false) {
|
|
|
107
108
|
// patch потрібен лише для авто-фіксу shellcheck; у read-only пропускаємо preflight.
|
|
108
109
|
if (!readOnly && !preflight(PATCH_PREFLIGHT)) return 1
|
|
109
110
|
|
|
110
|
-
console.log(`\n▶ cspell (${readOnly
|
|
111
|
-
const cspellCode = runCspellText(process.cwd(), readOnly)
|
|
111
|
+
console.log(`\n▶ cspell (${!readOnly && llmFix ? 'omlx-класифікація + словник + перевірка' : 'перевірка'})`)
|
|
112
|
+
const cspellCode = runCspellText(process.cwd(), readOnly, llmFix)
|
|
112
113
|
if (cspellCode !== 0) return cspellCode
|
|
113
114
|
|
|
114
115
|
console.log(`\n▶ shellcheck (${readOnly ? 'перевірка' : 'авто-фікс + фінальна перевірка'} *.sh)`)
|
|
@@ -129,8 +130,9 @@ function runLintTextSteps(readOnly = false) {
|
|
|
129
130
|
|
|
130
131
|
/**
|
|
131
132
|
* Публічна CLI-форма: серіалізує через `withLock('lint-text')` + дедуп за станом git-дерева.
|
|
132
|
-
* @param {{ readOnly?: boolean }} [opts] readOnly → детект без
|
|
133
|
+
* @param {{ readOnly?: boolean, llmFix?: boolean }} [opts] readOnly → детект без авто-фіксу;
|
|
134
|
+
* llmFix → omlx-класифікація cspell (opt-in із `meta.json: llmFix:true`)
|
|
133
135
|
* @returns {Promise<number>} код виходу
|
|
134
136
|
*/
|
|
135
137
|
export const runLintTextCli = (opts = {}) =>
|
|
136
|
-
runStandardLint(import.meta.dirname, () => runLintTextSteps(opts.readOnly === true))
|
|
138
|
+
runStandardLint(import.meta.dirname, () => runLintTextSteps(opts.readOnly === true, opts.llmFix === true))
|
package/rules/text/meta.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "auto": "завжди", "lint": "per-file" }
|
|
1
|
+
{ "auto": "завжди", "lint": "per-file", "llmFix": true }
|