@nitra/cursor 12.11.0 → 12.11.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 +12 -0
- package/bin/n-cursor.js +9 -27
- package/package.json +1 -1
- package/rules/adr/js/docs/hooks.md +0 -2
- package/rules/bun/js/docs/fix-layout.md +25 -0
- package/rules/bun/js/fix-layout.mjs +55 -0
- package/rules/changelog/js/docs/consistency.md +11 -13
- package/rules/changelog/js/docs/fix-consistency.md +27 -0
- package/rules/changelog/js/docs/index.md +2 -2
- package/rules/changelog/js/fix-consistency.mjs +50 -0
- package/rules/ci4/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/ci4/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/ga/policy/vscode_extensions/docs/fix-vscode_extensions.md +22 -0
- package/rules/ga/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/ga/policy/workflow_common/workflow_common.rego +15 -0
- package/rules/graphql/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/graphql/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/js/js/docs/dep-policy.md +12 -10
- package/rules/js/policy/vscode_extensions/docs/fix-vscode_extensions.md +22 -0
- package/rules/js/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/js-run/js/docs/fix-runtime.md +25 -0
- package/rules/js-run/js/fix-runtime.mjs +41 -0
- package/rules/k8s/policy/lint_k8s_yml/lint_k8s_yml.rego +57 -0
- package/rules/k8s/policy/lint_k8s_yml/target.json +4 -0
- package/rules/k8s/policy/lint_k8s_yml/template/lint-k8s.yml.snippet.yml +43 -0
- package/rules/nginx-default-tpl/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/nginx-default-tpl/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/rego/policy/vscode_extensions/docs/fix-vscode_extensions.md +22 -0
- package/rules/rego/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/rust/policy/vscode_extensions/docs/fix-vscode_extensions.md +22 -0
- package/rules/rust/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/style/js/docs/fix-tooling.md +29 -0
- package/rules/style/js/fix-tooling.mjs +46 -0
- package/rules/style/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/style/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/tauri/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/tauri/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/text/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/text/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/vue/js/docs/packages.md +0 -2
- package/scripts/docs/index.md +0 -2
- package/scripts/lib/discover-checkable-rules.mjs +1 -0
- package/scripts/lib/docs/discover-checkable-rules.md +13 -155
- package/scripts/lib/fix/discover-t0-patterns.mjs +83 -0
- package/scripts/lib/fix/docs/discover-t0-patterns.md +37 -0
- package/scripts/lib/fix/docs/llm-fix-apply.md +12 -10
- package/scripts/lib/fix/docs/llm-worker.md +6 -14
- package/scripts/lib/fix/docs/orchestrator.md +0 -2
- package/scripts/lib/fix/docs/t0.md +11 -10
- package/scripts/lib/fix/docs/vscode-ext-add.md +29 -0
- package/scripts/lib/fix/llm-fix-apply.mjs +34 -3
- package/scripts/lib/fix/llm-worker.mjs +24 -15
- package/scripts/lib/fix/t0.mjs +8 -119
- package/scripts/lib/fix/vscode-ext-add.mjs +45 -0
- package/rules/test/coverage/coverage.mjs +0 -317
- package/scripts/coverage-classify/apply.mjs +0 -67
- package/scripts/coverage-classify/cache.mjs +0 -77
- package/scripts/coverage-classify/docs/apply.md +0 -206
- package/scripts/coverage-classify/docs/cache.md +0 -207
- package/scripts/coverage-classify/docs/index.md +0 -14
- package/scripts/coverage-classify/docs/prompt.md +0 -136
- package/scripts/coverage-classify/docs/verdict-schema.md +0 -28
- package/scripts/coverage-classify/index.mjs +0 -114
- package/scripts/coverage-classify/prompt.mjs +0 -126
- package/scripts/coverage-classify/verdict-schema.mjs +0 -35
- package/scripts/coverage-fix-extract.mjs +0 -122
- package/scripts/coverage-fix.mjs +0 -119
- package/scripts/docs/coverage-fix-extract.md +0 -36
- package/scripts/docs/coverage-fix.md +0 -181
- package/skills/coverage-fix/SKILL.md +0 -131
- package/skills/coverage-fix/main.json +0 -1
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zod-схема для verdict-відповіді LLM-класифікатора (coverage-classify).
|
|
3
|
-
* parseVerdict — витяг JSON з raw-text LLM-відповіді + validate.
|
|
4
|
-
*
|
|
5
|
-
* Категорії:
|
|
6
|
-
* - worth-testing: pure logic, real branches — пиши тест
|
|
7
|
-
* - equivalent: мутант поведінково еквівалентний (не killable)
|
|
8
|
-
* - defensive: гілка для impossible state (не killable)
|
|
9
|
-
* - glue: CLI entry / runStandardRule wrapper (integration covers)
|
|
10
|
-
* - wrapper: тонкий spawn/fetch wrapper (integration covers)
|
|
11
|
-
*/
|
|
12
|
-
import { z } from 'zod'
|
|
13
|
-
|
|
14
|
-
export const VerdictSchema = z.object({
|
|
15
|
-
verdict: z.enum(['worth-testing', 'equivalent', 'defensive', 'glue', 'wrapper']),
|
|
16
|
-
confidence: z.number().min(0).max(1),
|
|
17
|
-
reason: z.string().min(20).max(500),
|
|
18
|
-
suggestedTest: z.string().max(300).optional()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Витягує JSON-об'єкт з raw-text LLM-відповіді і валідує через VerdictSchema.
|
|
23
|
-
* @param {string} rawText raw-text відповідь LLM
|
|
24
|
-
* @returns {{verdict: string, confidence: number, reason: string, suggestedTest?: string}} verdict
|
|
25
|
-
* @throws {Error} якщо JSON не знайдено, не парситься, або не відповідає схемі
|
|
26
|
-
*/
|
|
27
|
-
export function parseVerdict(rawText) {
|
|
28
|
-
const jsonStart = rawText.indexOf('{')
|
|
29
|
-
const jsonEnd = rawText.lastIndexOf('}')
|
|
30
|
-
if (jsonStart === -1 || jsonEnd === -1) {
|
|
31
|
-
throw new Error('No JSON object found in LLM response')
|
|
32
|
-
}
|
|
33
|
-
const json = JSON.parse(rawText.slice(jsonStart, jsonEnd + 1))
|
|
34
|
-
return VerdictSchema.parse(json)
|
|
35
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `n-cursor coverage-fix index|slice` — read-only витяг вцілілих мутантів із
|
|
3
|
-
* `COVERAGE.md` для скілу `n-coverage-fix`.
|
|
4
|
-
*
|
|
5
|
-
* Мотивація: `COVERAGE.md` може важити мегабайти (секція `## Вцілілі мутанти`
|
|
6
|
-
* з JSON-блоком на сотні файлів). Якщо цей файл читає LLM-оркестратор, він
|
|
7
|
-
* спалює сотні тисяч токенів лише на парсинг. Натомість важкий парсинг несе цей
|
|
8
|
-
* скрипт (для JS — мілісекунди, 0 токенів), а агенту віддається рівно потрібна
|
|
9
|
-
* порція:
|
|
10
|
-
* - `index` — крихітний `[{file, mutants}]` для рішення про фан-аут;
|
|
11
|
-
* - `slice --file <path>` — промпт лише для одного файлу (контекст ±3 рядки),
|
|
12
|
-
* рівно під когнітивне навантаження одного субагента.
|
|
13
|
-
*
|
|
14
|
-
* Команда read-only: лише парсить наявний `COVERAGE.md`, нічого не мутує і не
|
|
15
|
-
* перезапускає Stryker (тож не входить у root-guard).
|
|
16
|
-
*/
|
|
17
|
-
import { readFile } from 'node:fs/promises'
|
|
18
|
-
import { join } from 'node:path'
|
|
19
|
-
|
|
20
|
-
import { buildFixPrompt } from './coverage-fix.mjs'
|
|
21
|
-
|
|
22
|
-
/** Заголовок секції вцілілих мутантів у COVERAGE.md (контракт із renderMarkdown). */
|
|
23
|
-
const SURVIVED_SECTION = '## Вцілілі мутанти'
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Огорожа json-блоку: ≥3 бектики, далі `json` і решта рядка до `\n`. Довжина
|
|
27
|
-
* захоплюється в групу 1 — renderMarkdown пише 3, але oxfmt підвищує до 4+, коли
|
|
28
|
-
* сам JSON-вміст містить ``` (типово для original/replacement мутантів).
|
|
29
|
-
*/
|
|
30
|
-
const FENCE_OPEN_RE = /(`{3,8})json[^\n]{0,200}\n/
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Витягує JSON-масив вцілілих мутантів із тексту COVERAGE.md: знаходить секцію
|
|
34
|
-
* `## Вцілілі мутанти`, перший огороджений ` ```json ` блок під нею і парсить.
|
|
35
|
-
* @param {string} md повний текст COVERAGE.md
|
|
36
|
-
* @returns {import('./coverage-fix.mjs').SurvivedFileGroup[]} групи вцілілих по файлах (порожньо, якщо секції/блоку немає або JSON невалідний)
|
|
37
|
-
*/
|
|
38
|
-
export function parseSurvivedBlock(md) {
|
|
39
|
-
const sectionAt = md.indexOf(SURVIVED_SECTION)
|
|
40
|
-
if (sectionAt === -1) return []
|
|
41
|
-
const after = md.slice(sectionAt)
|
|
42
|
-
const open = after.match(FENCE_OPEN_RE)
|
|
43
|
-
if (!open) return []
|
|
44
|
-
const fence = open[1]
|
|
45
|
-
const bodyStart = open.index + open[0].length
|
|
46
|
-
const rest = after.slice(bodyStart)
|
|
47
|
-
// Закриття — рядок із тих самих бектиків. Усередині JSON реальних переводів
|
|
48
|
-
// рядка немає (JSON.stringify екранує їх як `\n`), тож `\n<fence>` унікально
|
|
49
|
-
// позначає кінець блоку навіть якщо значення містять бектики.
|
|
50
|
-
const closeAt = rest.indexOf(`\n${fence}`)
|
|
51
|
-
const json = closeAt === -1 ? rest : rest.slice(0, closeAt)
|
|
52
|
-
try {
|
|
53
|
-
const parsed = JSON.parse(json)
|
|
54
|
-
return Array.isArray(parsed) ? parsed : []
|
|
55
|
-
} catch {
|
|
56
|
-
return []
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Читає `COVERAGE.md` із кореня проєкту і повертає структуровані групи вцілілих.
|
|
62
|
-
* @param {string} cwd корінь проєкту
|
|
63
|
-
* @returns {Promise<import('./coverage-fix.mjs').SurvivedFileGroup[]>} групи вцілілих по файлах
|
|
64
|
-
*/
|
|
65
|
-
export async function readSurvived(cwd) {
|
|
66
|
-
let md
|
|
67
|
-
try {
|
|
68
|
-
md = await readFile(join(cwd, 'COVERAGE.md'), 'utf8')
|
|
69
|
-
} catch {
|
|
70
|
-
return []
|
|
71
|
-
}
|
|
72
|
-
return parseSurvivedBlock(md)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Згортає групи вцілілих у компактний index `[{file, mutants}]`.
|
|
77
|
-
* @param {import('./coverage-fix.mjs').SurvivedFileGroup[]} survived групи вцілілих
|
|
78
|
-
* @returns {Array<{file:string, mutants:number}>} файл → кількість вцілілих мутантів
|
|
79
|
-
*/
|
|
80
|
-
export function buildIndex(survived) {
|
|
81
|
-
return survived
|
|
82
|
-
.filter(group => group && typeof group.file === 'string' && Array.isArray(group.mutants))
|
|
83
|
-
.map(group => ({ file: group.file, mutants: group.mutants.length }))
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const USAGE = 'Usage: n-cursor coverage-fix <index | slice --file <path>>'
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* CLI: `index` друкує компактний JSON-масив, `slice --file <path>` — промпт для
|
|
90
|
-
* одного файлу. Обидва read-only (читають лише COVERAGE.md).
|
|
91
|
-
* @param {string[]} args аргументи після `coverage-fix`
|
|
92
|
-
* @param {string} [cwd] корінь проєкту (ін'єкція для тестів)
|
|
93
|
-
* @returns {Promise<number>} exit code
|
|
94
|
-
*/
|
|
95
|
-
export async function runCoverageFixCli(args, cwd = process.cwd()) {
|
|
96
|
-
const sub = args[0]
|
|
97
|
-
const survived = await readSurvived(cwd)
|
|
98
|
-
|
|
99
|
-
if (sub === 'index') {
|
|
100
|
-
process.stdout.write(`${JSON.stringify(buildIndex(survived))}\n`)
|
|
101
|
-
return 0
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (sub === 'slice') {
|
|
105
|
-
const flagAt = args.indexOf('--file')
|
|
106
|
-
const file = flagAt === -1 ? undefined : args[flagAt + 1]
|
|
107
|
-
if (!file) {
|
|
108
|
-
console.error(USAGE)
|
|
109
|
-
return 1
|
|
110
|
-
}
|
|
111
|
-
const group = survived.find(g => g && g.file === file)
|
|
112
|
-
if (!group) {
|
|
113
|
-
console.error(`✗ Файл не знайдено серед вцілілих мутантів: ${file}`)
|
|
114
|
-
return 1
|
|
115
|
-
}
|
|
116
|
-
process.stdout.write(`${await buildFixPrompt([group], cwd)}\n`)
|
|
117
|
-
return 0
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
console.error(USAGE)
|
|
121
|
-
return 1
|
|
122
|
-
}
|
package/scripts/coverage-fix.mjs
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `n-cursor coverage --fix`: запускає pi-агента для написання тестів
|
|
3
|
-
* по вцілілих мутантах Stryker. Агент отримує список мутантів з контекстом
|
|
4
|
-
* (file, line, оригінальний код, вцілілий варіант, тип мутації) і самостійно
|
|
5
|
-
* знаходить або створює відповідні test-файли.
|
|
6
|
-
*
|
|
7
|
-
* Модель: CLOUD_MAX (складна агентна задача) або N_CURSOR_COVERAGE_FIX_MODEL.
|
|
8
|
-
*/
|
|
9
|
-
import { readFile } from 'node:fs/promises'
|
|
10
|
-
import { join } from 'node:path'
|
|
11
|
-
import { spawnSync } from 'node:child_process'
|
|
12
|
-
import { env } from 'node:process'
|
|
13
|
-
|
|
14
|
-
import { resolveModel } from '../lib/models.mjs'
|
|
15
|
-
|
|
16
|
-
const MODEL = env.N_CURSOR_COVERAGE_FIX_MODEL ?? resolveModel('max')
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @typedef {{line:number, col:number, mutantType:string, original:string, replacement:string}} MutantDetail
|
|
20
|
-
* @typedef {{file:string, mutants:MutantDetail[], exampleTest:{testFile:string,code:string|null}|null, recommendationText:string|null}} SurvivedFileGroup
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Запускає pi-агента для написання тестів по вцілілих мутантах.
|
|
25
|
-
* @param {SurvivedFileGroup[]} survived вцілілі мутанти, згруповані по файлах
|
|
26
|
-
* @param {string} projectRoot абсолютний шлях до кореня проєкту
|
|
27
|
-
* @param {{ callPi?: (prompt: string, model: string, opts: { cwd: string }) => void }} [opts] ін'єкції для тестів
|
|
28
|
-
* @returns {Promise<void>}
|
|
29
|
-
*/
|
|
30
|
-
export async function fixSurvivedMutants(survived, projectRoot, opts = {}) {
|
|
31
|
-
const totalMutants = survived.reduce((s, g) => s + g.mutants.length, 0)
|
|
32
|
-
if (totalMutants === 0) {
|
|
33
|
-
console.log('✓ Всі мутанти вбиті — доповнення тестів не потрібне')
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const prompt = await buildFixPrompt(survived, projectRoot)
|
|
38
|
-
console.log(`\n🤖 coverage --fix: запускаю агента для ${totalMutants} вцілілих мутантів...\n`)
|
|
39
|
-
|
|
40
|
-
const callPiFn = opts.callPi ?? callPi
|
|
41
|
-
callPiFn(prompt, MODEL, { cwd: projectRoot })
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Викликає pi в агентному режимі з live-output до stdout.
|
|
46
|
-
* @param {string} prompt текст промпта
|
|
47
|
-
* @param {string} model provider/model-id або '' для pi-дефолту
|
|
48
|
-
* @param {{ cwd?: string }} [piOpts] опційні параметри (cwd)
|
|
49
|
-
*/
|
|
50
|
-
function callPi(prompt, model, { cwd } = {}) {
|
|
51
|
-
const modelArgs = model ? ['--model', model] : []
|
|
52
|
-
spawnSync('pi', ['-p', prompt, ...modelArgs, '--no-session'], {
|
|
53
|
-
cwd,
|
|
54
|
-
stdio: 'inherit',
|
|
55
|
-
timeout: 900_000
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Формує rich-промпт для агента: список вцілілих мутантів згрупований по файлах,
|
|
61
|
-
* з контекстом ±3 рядки навколо кожного мутанта з source-файлу.
|
|
62
|
-
* @param {SurvivedFileGroup[]} survived групи вцілілих мутантів по файлах
|
|
63
|
-
* @param {string} projectRoot корінь проєкту
|
|
64
|
-
* @returns {Promise<string>} текст rich-промпту
|
|
65
|
-
*/
|
|
66
|
-
export async function buildFixPrompt(survived, projectRoot) {
|
|
67
|
-
const sections = []
|
|
68
|
-
|
|
69
|
-
for (const { file, mutants, exampleTest } of survived) {
|
|
70
|
-
let srcLines = []
|
|
71
|
-
try {
|
|
72
|
-
const src = await readFile(join(projectRoot, file), 'utf8')
|
|
73
|
-
srcLines = src.split('\n')
|
|
74
|
-
} catch {
|
|
75
|
-
// файл може бути недоступним — пропускаємо контекст, але продовжуємо
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const mutantDescriptions = mutants
|
|
79
|
-
.map(m => {
|
|
80
|
-
const ctxStart = Math.max(0, m.line - 4)
|
|
81
|
-
const ctxEnd = Math.min(srcLines.length, m.line + 3)
|
|
82
|
-
const context = srcLines
|
|
83
|
-
.slice(ctxStart, ctxEnd)
|
|
84
|
-
.map((l, i) => `${ctxStart + i + 1}: ${l}`)
|
|
85
|
-
.join('\n')
|
|
86
|
-
return [
|
|
87
|
-
` - Рядок ${m.line}, колонка ${m.col}, тип мутації \`${m.mutantType}\``,
|
|
88
|
-
` Оригінал: \`${m.original}\``,
|
|
89
|
-
` Вижив варіант: \`${m.replacement}\``,
|
|
90
|
-
context ? ` Контекст:\n\`\`\`\n${context}\n\`\`\`` : ''
|
|
91
|
-
]
|
|
92
|
-
.filter(Boolean)
|
|
93
|
-
.join('\n')
|
|
94
|
-
})
|
|
95
|
-
.join('\n')
|
|
96
|
-
|
|
97
|
-
const exampleSection = exampleTest?.code
|
|
98
|
-
? `\n\nПриклад тесту з \`${exampleTest.testFile}\`:\n\`\`\`js\n${exampleTest.code}\n\`\`\``
|
|
99
|
-
: ''
|
|
100
|
-
|
|
101
|
-
sections.push(`### \`${file}\`${exampleSection}\n${mutantDescriptions}`)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return [
|
|
105
|
-
'Твоє завдання — написати unit-тести, що вбивають наступні вцілілі мутанти Stryker.',
|
|
106
|
-
'Для кожного мутанта: знайди або створи відповідний test-файл, додай тест-кейс,',
|
|
107
|
-
'що явно перевіряє цю гілку/умову і провалиться якщо код замінити на "вцілілий варіант".',
|
|
108
|
-
'',
|
|
109
|
-
'## Вцілілі мутанти',
|
|
110
|
-
'',
|
|
111
|
-
...sections,
|
|
112
|
-
'',
|
|
113
|
-
'## Правила',
|
|
114
|
-
'- Не змінюй source-файли — лише test-файли.',
|
|
115
|
-
'- Використовуй той самий test-фреймворк, що вже в проєкті.',
|
|
116
|
-
'- Запусти `bun test` (або відповідну команду) після кожного файлу — переконайся, що 0 fail.',
|
|
117
|
-
'- Якщо мутант охоплений іншим новим тестом — не дублюй.'
|
|
118
|
-
].join('\n')
|
|
119
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: coverage-fix-extract.mjs
|
|
4
|
-
resource: npm/scripts/coverage-fix-extract.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: a7f39f0e
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
Цей файл витягує вцілілі мутанти з файлу `COVERAGE.md` у форматі JSON. Витягнуті дані надаються агенту для вирішення проблем з покриттям, зменшуючи навантаження на LLM-оркестратор та забезпечуючи ефективний обмін інформацією. Це ключовий компонент процесу `n-coverage-fix`, що дозволяє агенту швидко реагувати на зміни в покритті.
|
|
10
|
-
|
|
11
|
-
## Поведінка
|
|
12
|
-
|
|
13
|
-
parseSurvivedBlock: Витягує JSON-масив вцілілих мутантів із тексту `COVERAGE.md`.
|
|
14
|
-
readSurvived: Читає `COVERAGE.md` із кореня проєкту і повертає групи вцілілих.
|
|
15
|
-
buildIndex: Згортає групи вцілілих у компактний index `[{file, mutants}]`.
|
|
16
|
-
runCoverageFixCli: CLI: `index` друкує компактний JSON-масив, `slice --file <path>` — промпт для одного файлу.
|
|
17
|
-
|
|
18
|
-
## Публічний API
|
|
19
|
-
|
|
20
|
-
- parseSurvivedBlock — Витягує дані про вцілілих мутантів з `COVERAGE.md` у форматі JSON.
|
|
21
|
-
- readSurvived — Зчитує `COVERAGE.md` та повертає структуровані дані про вцілілих.
|
|
22
|
-
- buildIndex — Створює компактний індекс у форматі JSON, що містить інформацію про файли та мутанти.
|
|
23
|
-
- runCoverageFixCli — CLI для друку та обрізки даних з `COVERAGE.md`.
|
|
24
|
-
|
|
25
|
-
## Гарантії поведінки
|
|
26
|
-
|
|
27
|
-
- Скрипт парсить файл `COVERAGE.md`.
|
|
28
|
-
- Результатом парсингу є об'єкт з інформацією про вцілілі мутанти.
|
|
29
|
-
- Скрипт повертає об'єкт, якщо файл `COVERAGE.md` існує та може бути успішно прочитаний.
|
|
30
|
-
- Якщо файл `COVERAGE.md` не існує, скрипт повертає порожній об'єкт.
|
|
31
|
-
- Скрипт не змінює вміст файлу `COVERAGE.md`.
|
|
32
|
-
- Скрипт не запускає Stryker.
|
|
33
|
-
- Скрипт не генерує винятків.
|
|
34
|
-
- Скрипт не використовує кешування.
|
|
35
|
-
- Використання команди `index` повертає мінімальний об'єкт, що містить інформацію про вцілілі мутанти.
|
|
36
|
-
- Використання команди `slice --file <path>` повертає промпт для одного файлу, що містить контекст ±3 рядки.
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: coverage-fix.mjs
|
|
4
|
-
resource: npm/scripts/coverage-fix.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: 7f0568dd
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
Файл `npm/scripts/coverage-fix.mjs` — це ESM-модуль, який реалізує fix-режим команди `n-cursor coverage --fix`. Його призначення — автоматично запустити агента Claude Code (через офіційний SDK `@anthropic-ai/claude-agent-sdk`) і доручити йому дописати unit-тести, які «вб'ють» вцілілих мутантів, знайдених попереднім прогоном Stryker.
|
|
10
|
-
|
|
11
|
-
Модуль не виконує власних мутацій коду й не аналізує покриття самостійно — він є тонким адаптером між інвентарем вцілілих мутантів (структури `SurvivedFileGroup[]`, отримані ззовні) і LLM-агентом: формує rich-промпт з контекстом ±3 рядки навколо кожного мутанта, опційно додає приклад уже існуючого тесту, після чого ітерує по стрімінгу повідомлень агента та виводить його текстові реплики у `stdout`.
|
|
12
|
-
|
|
13
|
-
Файл задумано як єдину публічну точку входу для CLI-флоу `coverage --fix`: викликається з оркестратора `n-cursor coverage`, який групує мутанти Stryker по source-файлах і передає сюди готову структуру разом з абсолютним шляхом до кореня проєкту.
|
|
14
|
-
|
|
15
|
-
## Експорти / API
|
|
16
|
-
|
|
17
|
-
| Експорт | Тип | Призначення |
|
|
18
|
-
| ------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
19
|
-
| `fixSurvivedMutants(survived, projectRoot)` | `async function` | Публічна функція. Запускає агента, проганяє промпт через `@anthropic-ai/claude-agent-sdk` і друкує текстові повідомлення в `stdout`. |
|
|
20
|
-
|
|
21
|
-
Внутрішні символи (не експортуються):
|
|
22
|
-
|
|
23
|
-
| Символ | Тип | Призначення |
|
|
24
|
-
| --------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
25
|
-
| `buildFixPrompt(survived, projectRoot)` | `async function` | Формує текстовий rich-промпт для агента: список мутантів по файлах, контекст з source, приклади тестів, правила. |
|
|
26
|
-
|
|
27
|
-
JSDoc-типи, оголошені у файлі:
|
|
28
|
-
|
|
29
|
-
- `MutantDetail` — `{ line: number, col: number, mutantType: string, original: string, replacement: string }` — опис одного вцілілого мутанта.
|
|
30
|
-
- `SurvivedFileGroup` — `{ file: string, mutants: MutantDetail[], exampleTest: { testFile: string, code: string | null } | null, recommendationText: string | null }` — група мутантів, об'єднаних спільним source-файлом, разом з опційним прикладом існуючого тесту й текстовою рекомендацією.
|
|
31
|
-
|
|
32
|
-
## Функції
|
|
33
|
-
|
|
34
|
-
### `fixSurvivedMutants(survived, projectRoot)`
|
|
35
|
-
|
|
36
|
-
**Сигнатура:** `async function fixSurvivedMutants(survived: SurvivedFileGroup[], projectRoot: string): Promise<void>`
|
|
37
|
-
|
|
38
|
-
**Параметри:**
|
|
39
|
-
|
|
40
|
-
- `survived` — масив груп вцілілих мутантів, згрупованих по source-файлу. Кожна група містить шлях до файлу, перелік мутантів, опційний приклад тесту й опційну текстову рекомендацію.
|
|
41
|
-
- `projectRoot` — абсолютний шлях до кореня проєкту. Використовується одночасно як `cwd` для агента і як база для читання source-файлів при підготовці контексту в промпті.
|
|
42
|
-
|
|
43
|
-
**Що повертає:** `Promise<void>`. Корисний результат — побічний ефект: створені/оновлені тести у файловій системі (їх пише агент через інструменти `Edit`/`Bash`), а також лог у `stdout`.
|
|
44
|
-
|
|
45
|
-
**Алгоритм:**
|
|
46
|
-
|
|
47
|
-
1. Обчислює загальну кількість мутантів: `survived.reduce((s, g) => s + g.mutants.length, 0)`.
|
|
48
|
-
2. Якщо загалом 0 мутантів — друкує `✓ Всі мутанти вбиті — доповнення тестів не потрібне` і повертається (без запуску агента, без імпорту SDK).
|
|
49
|
-
3. Викликає `buildFixPrompt(survived, projectRoot)` і отримує текст промпту.
|
|
50
|
-
4. Друкує заголовок `🤖 coverage --fix: запускаю агента для N вцілілих мутантів...`.
|
|
51
|
-
5. Робить **динамічний** `await import('@anthropic-ai/claude-agent-sdk')` (щоб не вимагати пакет у звичайному coverage-прогоні без `--fix`) і дістає функцію `query`.
|
|
52
|
-
6. Запускає агента викликом `query({ prompt, options: { cwd: projectRoot, maxTurns: 20, allowedTools: ['Read', 'Edit', 'Bash'] } })`.
|
|
53
|
-
7. Ітерує отриманий `AsyncIterable` через `for await (const msg of …)`. Для повідомлень з `msg.type === 'text'` друкує `msg.text` у `process.stdout` (без додаткового переносу рядка). Інші типи повідомлень (наприклад, tool_use, tool_result) — мовчазно ігноруються.
|
|
54
|
-
8. Після завершення ітерації друкує фінальний `\n`.
|
|
55
|
-
|
|
56
|
-
**Опції агента:**
|
|
57
|
-
|
|
58
|
-
- `cwd: projectRoot` — поточна тека агента, від якої він читає файли й запускає команди.
|
|
59
|
-
- `maxTurns: 20` — обмеження на кількість turns у діалозі з моделлю, щоб уникнути нескінченних циклів.
|
|
60
|
-
- `allowedTools: ['Read', 'Edit', 'Bash']` — агент має право читати файли, редагувати їх (фактично — тест-файли, бо source заборонено правилом у промпті) і виконувати shell-команди (зокрема `bun test`).
|
|
61
|
-
|
|
62
|
-
**Side effects:**
|
|
63
|
-
|
|
64
|
-
- Запис у `process.stdout` (`console.log` + `process.stdout.write`).
|
|
65
|
-
- Динамічний імпорт зовнішнього пакету `@anthropic-ai/claude-agent-sdk` — у разі його відсутності `await import(…)` кине помилку (її обробка делегована викликачу).
|
|
66
|
-
- Мережеві виклики до API Anthropic (виконує сам SDK).
|
|
67
|
-
- Зміни у файловій системі проєкту: створення/редагування тест-файлів та потенційне виконання тестових команд агентом через інструмент `Bash`.
|
|
68
|
-
|
|
69
|
-
**Граничні випадки:**
|
|
70
|
-
|
|
71
|
-
- `totalMutants === 0` — рання терміновість без імпорту SDK.
|
|
72
|
-
- Помилка `import('@anthropic-ai/claude-agent-sdk')` (пакет не встановлений) — пробрасується нагору; цей файл її не ловить.
|
|
73
|
-
- Помилка з боку стріму `query(...)` — пробрасується нагору; final `\n` не друкується.
|
|
74
|
-
|
|
75
|
-
### `buildFixPrompt(survived, projectRoot)` (internal)
|
|
76
|
-
|
|
77
|
-
**Сигнатура:** `async function buildFixPrompt(survived: SurvivedFileGroup[], projectRoot: string): Promise<string>`
|
|
78
|
-
|
|
79
|
-
**Параметри:** ті самі семантики, що у `fixSurvivedMutants`.
|
|
80
|
-
|
|
81
|
-
**Що повертає:** текст rich-промпту для агента.
|
|
82
|
-
|
|
83
|
-
**Алгоритм:**
|
|
84
|
-
|
|
85
|
-
1. Готує пустий масив `sections`.
|
|
86
|
-
2. Для кожної групи `{ file, mutants, exampleTest }`:
|
|
87
|
-
1. Намагається прочитати source-файл `join(projectRoot, file)` у `utf8` і розбити на рядки. У разі помилки (`catch {}`) — `srcLines` лишається `[]`, і контекст просто не додасться.
|
|
88
|
-
2. Для кожного мутанта в групі формує опис:
|
|
89
|
-
- `ctxStart = Math.max(0, m.line - 4)` — індекс початку контексту (0-based, бо рядки в редакторі 1-based; `m.line - 4` дає 3 рядки до мутанта).
|
|
90
|
-
- `ctxEnd = Math.min(srcLines.length, m.line + 3)` — індекс кінця (виключний), 3 рядки після мутанта.
|
|
91
|
-
- `context` — зріз `srcLines.slice(ctxStart, ctxEnd)`, де кожен рядок префіксується абсолютним номером `${ctxStart + i + 1}: ${l}` і з'єднується через `\n`.
|
|
92
|
-
- Опис мутанта — markdown-bullet з полями: рядок, колонка, тип мутації (`m.mutantType` у backticks), оригінал (`m.original`), вцілілий варіант (`m.replacement`), а нижче — fenced code block з контекстом (якщо контекст не порожній). Порожні елементи фільтруються через `.filter(Boolean)`.
|
|
93
|
-
3. Якщо є `exampleTest.code` — додає окрему секцію `Приклад тесту з \`${exampleTest.testFile}\``з fenced`js`-блоком. Інакше `exampleSection` — порожній рядок.
|
|
94
|
-
4. Заштовхує у `sections` рядок виду:
|
|
95
|
-
```
|
|
96
|
-
### `<file>`<exampleSection>
|
|
97
|
-
<mutantDescriptions>
|
|
98
|
-
```
|
|
99
|
-
3. Повертає одну склеєну рядкову відповідь, що складається з:
|
|
100
|
-
- Заголовка-завдання (4 рядки): «Твоє завдання — написати unit-тести…» з конкретною інструкцією: знайти/створити тест-файл, додати кейс, що провалиться при заміні коду на «вцілілий варіант».
|
|
101
|
-
- Розділу `## Вцілілі мутанти` зі склеєними `sections`.
|
|
102
|
-
- Розділу `## Правила`:
|
|
103
|
-
- «Не змінюй source-файли — лише test-файли.»
|
|
104
|
-
- «Використовуй той самий test-фреймворк, що вже в проєкті.»
|
|
105
|
-
- «Запусти `bun test` (або відповідну команду) після кожного файлу — переконайся, що 0 fail.»
|
|
106
|
-
- «Якщо мутант охоплений іншим новим тестом — не дублюй.»
|
|
107
|
-
|
|
108
|
-
**Side effects:** читання source-файлів через `fs/promises.readFile`. Помилки читання (відсутній файл, нечитабельний шлях) тихо проковтуються — функція гарантує, що повертає валідний промпт навіть для повністю відсутніх файлів (просто без контексту).
|
|
109
|
-
|
|
110
|
-
**Граничні випадки:**
|
|
111
|
-
|
|
112
|
-
- `srcLines.length === 0` — `context` стає порожнім рядком, fenced-блок з контекстом не додається до bullet.
|
|
113
|
-
- `m.line` поза межами файлу — `slice` поверне порожній зріз; жодного винятку.
|
|
114
|
-
- `exampleTest === null` або `exampleTest.code === null/undefined` — `exampleSection` залишається порожнім (через `?.code` optional chaining).
|
|
115
|
-
- `mutants.length === 0` у групі — група все одно потрапить у `sections` з заголовком і порожнім тілом (виклична сторона зазвичай не подає пусті групи, бо `fixSurvivedMutants` ще раніше відсіче випадок `totalMutants === 0`).
|
|
116
|
-
|
|
117
|
-
## Залежності
|
|
118
|
-
|
|
119
|
-
**Зовнішні (npm):**
|
|
120
|
-
|
|
121
|
-
- `@anthropic-ai/claude-agent-sdk` — офіційний SDK Claude Code, експортує функцію `query()` з async-generator-інтерфейсом для стрімінгу повідомлень агента. Завантажується через **dynamic import**, виключно в момент виклику `fixSurvivedMutants` з ненульовим списком мутантів. Декларується у `dependencies` файлу `npm/package.json` (як зазначено у JSDoc-шапці модуля).
|
|
122
|
-
|
|
123
|
-
**Node.js stdlib:**
|
|
124
|
-
|
|
125
|
-
- `node:fs/promises` — використовується `readFile` для читання source-файлів при формуванні контексту в промпті.
|
|
126
|
-
- `node:path` — використовується `join` для зчеплення `projectRoot` з відносним шляхом файлу мутанта.
|
|
127
|
-
|
|
128
|
-
**Глобальні API середовища:**
|
|
129
|
-
|
|
130
|
-
- `console.log` — друк інформаційних рядків (старт/пропуск).
|
|
131
|
-
- `process.stdout.write` — потоковий друк reply-чанків агента (без auto-newline).
|
|
132
|
-
|
|
133
|
-
**Внутрішні залежності проєкту:**
|
|
134
|
-
|
|
135
|
-
- Модуль не імпортує інших файлів з `npm/scripts/` напряму, але формат вхідної структури `SurvivedFileGroup` визначається сусідніми скриптами в каталозі `npm/scripts/` (там, де живе оркестратор `coverage`, який збирає й групує мутантів Stryker і передає сюди готовий масив).
|
|
136
|
-
|
|
137
|
-
## Потік виконання / Використання
|
|
138
|
-
|
|
139
|
-
Типовий ланцюжок:
|
|
140
|
-
|
|
141
|
-
1. CLI `n-cursor coverage --fix` (зовнішня обгортка) запускає Stryker і збирає список вцілілих мутантів.
|
|
142
|
-
2. Оркестратор групує мутантів по `file`, готує `exampleTest` (опційно) і викликає:
|
|
143
|
-
|
|
144
|
-
```js
|
|
145
|
-
import { fixSurvivedMutants } from './npm/scripts/coverage-fix.mjs'
|
|
146
|
-
|
|
147
|
-
await fixSurvivedMutants(survived, projectRoot)
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
3. `fixSurvivedMutants`:
|
|
151
|
-
- Перевіряє, що мутанти взагалі є; якщо ні — друкує success-повідомлення і виходить.
|
|
152
|
-
- Будує промпт через `buildFixPrompt` (читання source-файлів для контексту відбувається саме тут).
|
|
153
|
-
- Динамічно імпортує `@anthropic-ai/claude-agent-sdk`.
|
|
154
|
-
- Стрімить агента з обмеженнями: `cwd = projectRoot`, до 20 turns, інструменти `Read | Edit | Bash`.
|
|
155
|
-
- Друкує всі текстові реплики у `stdout` у реальному часі.
|
|
156
|
-
4. Агент за правилами промпту читає source-файли (лише для розуміння), знаходить/створює відповідні test-файли, дописує кейси, запускає `bun test` і повторює, доки `maxTurns` або модель не вирішить, що готово.
|
|
157
|
-
5. Після завершення стріму `fixSurvivedMutants` дописує фінальний `\n` і повертає `void`.
|
|
158
|
-
|
|
159
|
-
**Принципи дизайну, що випливають із коду:**
|
|
160
|
-
|
|
161
|
-
- **Lazy SDK loading.** Імпорт SDK — динамічний, щоб звичайний `coverage` (без `--fix`) не вимагав встановленого `@anthropic-ai/claude-agent-sdk` і не платив за його завантаження.
|
|
162
|
-
- **Ізоляція знань про Stryker.** Цей файл нічого не знає про сам Stryker — він працює з уже нормалізованою структурою `SurvivedFileGroup[]`; уся інтеграція зі Stryker лежить в іншому модулі-оркестраторі.
|
|
163
|
-
- **No source mutation by agent.** Жорстке правило в промпті («Не змінюй source-файли — лише test-файли») задає очікувану поведінку агента; додатково обмежений набір інструментів (`Read | Edit | Bash`) дає змогу йому редагувати тести й запускати тестову команду, але не лазити в систему по непотрібному.
|
|
164
|
-
- **Стрімінг тільки текстових повідомлень.** Інші типи (наприклад, tool_use / tool_result) свідомо не рендеряться, щоб лог у консолі лишався читабельним для людини.
|
|
165
|
-
|
|
166
|
-
**Приклад вхідної структури:**
|
|
167
|
-
|
|
168
|
-
```js
|
|
169
|
-
const survived = [
|
|
170
|
-
{
|
|
171
|
-
file: 'src/foo.js',
|
|
172
|
-
mutants: [{ line: 42, col: 10, mutantType: 'ConditionalExpression', original: 'a > b', replacement: 'a >= b' }],
|
|
173
|
-
exampleTest: { testFile: 'src/foo.test.js', code: "test('foo', () => { /* ... */ })" },
|
|
174
|
-
recommendationText: null
|
|
175
|
-
}
|
|
176
|
-
]
|
|
177
|
-
|
|
178
|
-
await fixSurvivedMutants(survived, '/abs/path/to/project')
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
> Примітка: поле `recommendationText` присутнє в JSDoc-типі `SurvivedFileGroup`, але в поточній реалізації `coverage-fix.mjs` воно **не** використовується при формуванні промпту — резервоване для майбутнього розширення (наприклад, додавання текстових порад поряд із прикладом тесту).
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: n-coverage-fix
|
|
3
|
-
description: >-
|
|
4
|
-
Автономна команда: запускає n-cursor coverage → читає вцілілих мутантів → ітеративно пише тести до конвергенції (max 3 ітерації)
|
|
5
|
-
version: '1.0'
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# n-coverage-fix — підвищення mutation score
|
|
9
|
-
|
|
10
|
-
## Мета
|
|
11
|
-
|
|
12
|
-
Автоматично підвищити mutation score: запускає coverage, знаходить survived mutants, пише тести, повторює до конвергенції.
|
|
13
|
-
|
|
14
|
-
## ⚠️ Не запускати паралельно
|
|
15
|
-
|
|
16
|
-
Цей скіл **не можна** запускати паралельно в різних агентах або Bash-задачах.
|
|
17
|
-
|
|
18
|
-
`n-cursor coverage` всередині серіалізований через `withLock('coverage')` — другий виклик чекатиме. Але Stryker пише `mutation.json` і `incremental.json` в одну директорію: паралельний запуск **зіпсує обидва файли**. Запускай тільки один `/n-coverage-fix` одночасно.
|
|
19
|
-
|
|
20
|
-
## Передумови
|
|
21
|
-
|
|
22
|
-
- Поточна директорія — корінь проєкту (де `.n-cursor.json` і `COVERAGE.md`)
|
|
23
|
-
- `n-cursor coverage` доступний (`npx @nitra/cursor coverage` або `bun run coverage`)
|
|
24
|
-
- Залежності встановлені (`bun i`)
|
|
25
|
-
|
|
26
|
-
## Workflow
|
|
27
|
-
|
|
28
|
-
### Крок 0: Визнач команди (з `package.json#scripts`)
|
|
29
|
-
|
|
30
|
-
Прочитай кореневий `package.json` і зафіксуй дві команди (перша що існує):
|
|
31
|
-
|
|
32
|
-
- **coverage-команда**: `scripts["coverage"]` → виклик `bun run coverage`; fallback `n-cursor coverage`
|
|
33
|
-
- **test-команда**: `scripts["test"]` → виклик `bun run test` (або те, що в скрипті); fallback `bun test`
|
|
34
|
-
|
|
35
|
-
Далі по тексту «coverage-команда» / «test-команда» = знайдені тут значення.
|
|
36
|
-
|
|
37
|
-
### Крок 1: Згенеруй або переви́користай `COVERAGE.md`
|
|
38
|
-
|
|
39
|
-
**Early-skip.** Якщо `COVERAGE.md` уже існує, свіжий (новіший за останню зміну source/тестів) і має секцію `## Вцілілі мутанти` — можеш одразу перейти до Кроку 2 без повторної генерації. Інакше — згенеруй звіт:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
n-cursor coverage # або coverage-команда з Кроку 0
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Ця команда генерує `COVERAGE.md`. Якщо є survived mutants — COVERAGE.md матиме секцію `## Вцілілі мутанти` з JSON-блоком.
|
|
46
|
-
|
|
47
|
-
### Крок 2: Прочитай компактний index вцілілих
|
|
48
|
-
|
|
49
|
-
> **Не читай `COVERAGE.md` сам.** Файл може важити мегабайти (секція `## Вцілілі
|
|
50
|
-
мутанти` на сотні файлів) — його читання спалило б сотні тисяч токенів. Важкий
|
|
51
|
-
> парсинг несе CLI; ти отримуєш лише крихітний index.
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
n-cursor coverage-fix index
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Друкує компактний JSON-масив `[{ "file": "<path>", "mutants": <N> }]` (кілобайти, не мегабайти). Якщо `[]` — зупинись:
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
✓ Жодних вцілілих мутантів — mutation score повний. Coverage завершено.
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Запам'ятай `prevCount = сума всіх mutants` (загальна кількість вцілілих мутантів).
|
|
64
|
-
|
|
65
|
-
### Крок 3: Для кожного файлу — slice + Agent
|
|
66
|
-
|
|
67
|
-
Для кожного запису `{ file, mutants }` з index:
|
|
68
|
-
|
|
69
|
-
**3a. Визнач test файл (завжди у `tests/` директорії):**
|
|
70
|
-
|
|
71
|
-
Цільовий: `<dir>/tests/<basename>.test.mjs`
|
|
72
|
-
(де `<dir>` — директорія source-файлу, `<basename>` — ім'я source без розширення)
|
|
73
|
-
|
|
74
|
-
1. Якщо `<dir>/tests/<basename>.test.mjs` існує → використай
|
|
75
|
-
2. Якщо `<dir>/<basename>.test.js` або `<dir>/<basename>.test.mjs` існує (co-located) →
|
|
76
|
-
- Перенеси до `<dir>/tests/<basename>.test.mjs`
|
|
77
|
-
- Оновити відносні imports (тепер `../` рівень вгору до source)
|
|
78
|
-
3. Жоден не знайдено → буде створено `<dir>/tests/<basename>.test.mjs`
|
|
79
|
-
|
|
80
|
-
**3b. Отримай готовий зріз-промпт лише для цього файлу:**
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
n-cursor coverage-fix slice --file <file>
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
CLI друкує самодостатній промпт **рівно для одного файлу**: список вцілілих мутантів цього файлу (рядок/колонка/`original → replacement`/тип) з контекстом ±3 рядки навколо кожного та фіксовані правила. Це і є «порція під когнітивне навантаження» одного субагента — нічого зайвого.
|
|
87
|
-
|
|
88
|
-
**3c. Запусти Agent** з цим зрізом як промптом, дописавши один рядок про цільовий test-файл із 3a (`<dir>/tests/<basename>.test.mjs`, з правильними відносними imports). Дочекайся завершення.
|
|
89
|
-
|
|
90
|
-
### Крок 4: Перевір що всі тести проходять
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
bun test # або test-команда з Кроку 0
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Якщо падають — поверни відповідний Agent з помилкою і попроси виправити.
|
|
97
|
-
|
|
98
|
-
### Крок 5: Запусти coverage і порівняй
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
n-cursor coverage # або coverage-команда з Кроку 0
|
|
102
|
-
n-cursor coverage-fix index
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
`newCount = сума mutants` зі свіжого index (знову — без читання `COVERAGE.md` вручну).
|
|
106
|
-
|
|
107
|
-
**Рішення:**
|
|
108
|
-
|
|
109
|
-
- `newCount < prevCount` AND iterations < 3 → повтор з Кроку 2 з оновленим index
|
|
110
|
-
- `newCount >= prevCount` → конвергенція:
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
✓ Конвергенція: mutation score більше не покращується.
|
|
114
|
-
Було вцілілих: <prevCount>, стало: <newCount>.
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
- iterations == 3 → зупинись:
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
⚠️ Досягнуто максимум ітерацій (3).
|
|
121
|
-
Вціліло: <newCount> мутантів. Деякі можуть бути стійкими (dead code, external state).
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
## Конвергенція — нормальний результат
|
|
125
|
-
|
|
126
|
-
Деякі мутанти неможливо вбити: захищений зовнішній стан, недетермінована логіка, еквівалентні мутації. Не намагайся виправити те що не змінилось після ітерації.
|
|
127
|
-
|
|
128
|
-
## Нотатки
|
|
129
|
-
|
|
130
|
-
- Stryker `incremental` (`incrementalFile`) зберігає прогрес між запусками — crash ≠ перезапуск з нуля
|
|
131
|
-
- Не комітити зміни автоматично — user вирішує коли комітити
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "auto": ["js"], "worktree": true }
|