@7n/rules 1.21.0 → 1.23.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 +16 -0
- package/package.json +1 -1
- package/rules/doc-files/check/docs/main.md +1 -1
- package/rules/doc-files/check/main.mjs +1 -1
- package/rules/doc-files/docgen-crc/docs/main.md +1 -1
- package/rules/doc-files/docgen-crc/main.mjs +7 -6
- package/rules/doc-files/docgen-extract/docs/main.md +1 -1
- package/rules/doc-files/docgen-extract/main.mjs +0 -196
- package/rules/doc-files/docgen-gen/docs/main.md +1 -1
- package/rules/doc-files/docgen-gen/main.mjs +6 -1
- package/rules/doc-files/docgen-scan/docs/index.md +10 -0
- package/rules/doc-files/docgen-scan/docs/lang-extensions.md +34 -0
- package/rules/doc-files/docgen-scan/docs/main.md +1 -1
- package/rules/doc-files/docgen-scan/lang-extensions.mjs +89 -0
- package/rules/doc-files/docgen-scan/main.mjs +16 -7
- package/rules/doc-files/units/docs/main.md +1 -2
- package/rules/doc-files/units/main.mjs +2 -3
- package/scripts/lib/docs/resolve-plugins.md +1 -1
- package/scripts/lib/resolve-plugins.mjs +30 -4
- package/skills/doc-files/SKILL.md +1 -1
- package/skills/taze/js/docs/orchestrate.md +1 -1
- package/skills/taze/js/orchestrate.mjs +175 -64
- package/rules/doc-files/units-rs/concern.json +0 -3
- package/rules/doc-files/units-rs/docs/main.md +0 -36
- package/rules/doc-files/units-rs/main.mjs +0 -311
|
@@ -56,7 +56,7 @@ function hasLangSignal(projectRoot, signal, maxDepth) {
|
|
|
56
56
|
let level = [projectRoot]
|
|
57
57
|
for (let depth = 0; depth <= maxDepth && level.length > 0; depth++) {
|
|
58
58
|
if (level.some(dir => existsSync(join(dir, signal)))) return true
|
|
59
|
-
if (depth < maxDepth) level = level.flatMap(listScannableSubdirs)
|
|
59
|
+
if (depth < maxDepth) level = level.flatMap(dir => listScannableSubdirs(dir))
|
|
60
60
|
}
|
|
61
61
|
return false
|
|
62
62
|
}
|
|
@@ -192,7 +192,7 @@ export function ensurePluginInstalled(projectRoot, packageName) {
|
|
|
192
192
|
* @property {string} name npm-ім'я пакета (`@7n/rules` для ядра)
|
|
193
193
|
* @property {string} packageRoot абсолютний корінь пакета
|
|
194
194
|
* @property {string} rulesDir абсолютний шлях до `rules/` пакета
|
|
195
|
-
* @property {{ capabilities: string[], contributes: { rules?: boolean, handlers?: Record<string, string> } }} manifest нормалізований блок `n-rules` з package.json плагіна
|
|
195
|
+
* @property {{ capabilities: string[], contributes: { rules?: boolean, handlers?: Record<string, string>, docFilesExtensions?: Record<string, string> } }} manifest нормалізований блок `n-rules` з package.json плагіна
|
|
196
196
|
*/
|
|
197
197
|
|
|
198
198
|
/**
|
|
@@ -202,7 +202,7 @@ export function ensurePluginInstalled(projectRoot, packageName) {
|
|
|
202
202
|
*/
|
|
203
203
|
function readPluginManifest(packageRoot) {
|
|
204
204
|
/** @type {ResolvedPlugin['manifest']} */
|
|
205
|
-
const fallback = { capabilities: [], contributes: { rules: true, handlers: {} } }
|
|
205
|
+
const fallback = { capabilities: [], contributes: { rules: true, handlers: {}, docFilesExtensions: {} } }
|
|
206
206
|
try {
|
|
207
207
|
const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))
|
|
208
208
|
const raw = pkg?.['n-rules']
|
|
@@ -213,7 +213,17 @@ function readPluginManifest(packageRoot) {
|
|
|
213
213
|
contributes.handlers && typeof contributes.handlers === 'object' && !Array.isArray(contributes.handlers)
|
|
214
214
|
? Object.fromEntries(Object.entries(contributes.handlers).filter(([, v]) => typeof v === 'string'))
|
|
215
215
|
: {}
|
|
216
|
-
|
|
216
|
+
// Декларативні doc-files-розширення (`docFiles.extensions`: '.rs' → 'Rust Module') —
|
|
217
|
+
// саме в маніфесті, а не в handler-модулі, щоб hot-path (hook per-file) читав їх
|
|
218
|
+
// синхронно без динамічного import.
|
|
219
|
+
const rawDocFiles = contributes.docFiles && typeof contributes.docFiles === 'object' ? contributes.docFiles : {}
|
|
220
|
+
const docFilesExtensions =
|
|
221
|
+
rawDocFiles.extensions && typeof rawDocFiles.extensions === 'object' && !Array.isArray(rawDocFiles.extensions)
|
|
222
|
+
? Object.fromEntries(
|
|
223
|
+
Object.entries(rawDocFiles.extensions).filter(([k, v]) => k.startsWith('.') && typeof v === 'string')
|
|
224
|
+
)
|
|
225
|
+
: {}
|
|
226
|
+
return { capabilities, contributes: { rules: contributes.rules !== false, handlers, docFilesExtensions } }
|
|
217
227
|
} catch {
|
|
218
228
|
return fallback
|
|
219
229
|
}
|
|
@@ -295,6 +305,22 @@ export function getActiveCapabilities(projectRoot, config, options = {}) {
|
|
|
295
305
|
return caps
|
|
296
306
|
}
|
|
297
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Агреговані doc-files-розширення активних плагінів: '.rs' → 'Rust Module' тощо.
|
|
310
|
+
* Синхронно і без установки (hot-path hook) — лише вже встановлені плагіни.
|
|
311
|
+
* @param {string} projectRoot корінь репозиторію
|
|
312
|
+
* @param {{ plugins?: unknown } | null | undefined} config розпарсений `.n-rules.json`
|
|
313
|
+
* @returns {Record<string, string>} мапа розширення → тип-мітка доки
|
|
314
|
+
*/
|
|
315
|
+
export function getDocFilesExtensions(projectRoot, config) {
|
|
316
|
+
/** @type {Record<string, string>} */
|
|
317
|
+
const out = {}
|
|
318
|
+
for (const p of resolvePlugins(projectRoot, config, { allowInstall: false, quiet: true })) {
|
|
319
|
+
Object.assign(out, p.manifest.contributes.docFilesExtensions)
|
|
320
|
+
}
|
|
321
|
+
return out
|
|
322
|
+
}
|
|
323
|
+
|
|
298
324
|
/**
|
|
299
325
|
* Handlers для extension-point правила ядра (v1 — лише API; перший споживач — v2).
|
|
300
326
|
* @param {string} projectRoot корінь репозиторію
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: doc-files
|
|
3
3
|
description: >-
|
|
4
|
-
Обовʼязковий крок задачі (як lint): для кожного зміненого/нового кодового файлу (js/mjs/ts/vue/py) JS-оркестрована генерація лаконічної поведінкової української md-документації у теку docs/ поряд із кодом, зі звіркою застарілості за CRC у frontmatter
|
|
4
|
+
Обовʼязковий крок задачі (як lint): для кожного зміненого/нового кодового файлу (js/mjs/ts/vue вбудовано; rs/py — через lang-плагіни) JS-оркестрована генерація лаконічної поведінкової української md-документації у теку docs/ поряд із кодом, зі звіркою застарілості за CRC у frontmatter
|
|
5
5
|
version: '1.0'
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** @see ./docs/orchestrate.md */
|
|
2
2
|
import { spawnSync } from 'node:child_process'
|
|
3
3
|
import { existsSync } from 'node:fs'
|
|
4
|
-
import { copyFile, rm } from 'node:fs/promises'
|
|
5
|
-
import { join } from 'node:path'
|
|
4
|
+
import { copyFile, mkdir, rm } from 'node:fs/promises'
|
|
5
|
+
import { dirname, join } from 'node:path'
|
|
6
6
|
import { pathToFileURL } from 'node:url'
|
|
7
7
|
|
|
8
8
|
import { assertEcosystemProvider } from '../../../scripts/lib/plugin-api.mjs'
|
|
@@ -81,28 +81,122 @@ export async function callRunner(runner, prompt, cwd, deps = {}) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
|
-
*
|
|
85
|
-
* той самий контракт, що й для інших
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* не
|
|
91
|
-
*
|
|
84
|
+
* Гарантує, що подальші кроки оркестратора виконуються в ізольованому
|
|
85
|
+
* worktree (`main.json.worktree: true`, той самий контракт, що й для інших
|
|
86
|
+
* worktree-only скілів). Оркестратор не годує SKILL.md жодному агенту, тож
|
|
87
|
+
* замість покладатись на агента, що читає SKILL.md-preflight, сам детерміновано
|
|
88
|
+
* створює worktree конвенцією `<current-branch>-taze` (`npx \@7n/mt worktree
|
|
89
|
+
* create`) і ставить залежності — щоб `bunx taze -w -r latest`/`bun install`
|
|
90
|
+
* ніколи не виконались прямо в основному дереві виклику. `autoCreated: true`
|
|
91
|
+
* сигналить викликачеві, що після завершення прогону варто перенести зміни
|
|
92
|
+
* назад (`bringChangesBackToOriginal`) і прибрати worktree
|
|
93
|
+
* (`removeAutoCreatedWorktree`) — а не лишати сирітське дерево.
|
|
92
94
|
* @param {string} cwd каталог для перевірки
|
|
93
95
|
* @param {typeof spawnSync} spawnFn інжект для тестів
|
|
94
|
-
* @
|
|
96
|
+
* @param {(line: string) => void} log колбек прогресу
|
|
97
|
+
* @returns {{ cwd: string, autoCreated: boolean, branchArg: string|null }} `autoCreated: false` — `cwd` без змін
|
|
98
|
+
* (вже worktree); `autoCreated: true` — `cwd` щойно створеного worktree і `branchArg`, з яким його створено
|
|
95
99
|
*/
|
|
96
|
-
function
|
|
97
|
-
const
|
|
98
|
-
const toplevel =
|
|
100
|
+
function ensureRunningInWorktree(cwd, spawnFn, log) {
|
|
101
|
+
const toplevelResult = spawnFn('git', ['rev-parse', '--show-toplevel'], { cwd, encoding: 'utf8' })
|
|
102
|
+
const toplevel = toplevelResult.status === 0 ? toplevelResult.stdout.trim() : ''
|
|
99
103
|
const segments = new Set(toplevel.replaceAll('\\', '/').split('/'))
|
|
100
|
-
if (
|
|
104
|
+
if (segments.has('.worktrees')) return { cwd, autoCreated: false, branchArg: null }
|
|
105
|
+
|
|
106
|
+
const branchResult = spawnFn('git', ['branch', '--show-current'], { cwd, encoding: 'utf8' })
|
|
107
|
+
const currentBranch = branchResult.status === 0 ? branchResult.stdout.trim() : ''
|
|
108
|
+
if (!currentBranch) {
|
|
101
109
|
throw new Error(
|
|
102
|
-
`taze: "${cwd}" не в ізольованому worktree (git toplevel: "${toplevel || '?'}")
|
|
103
|
-
'
|
|
110
|
+
`taze: "${cwd}" не в ізольованому worktree (git toplevel: "${toplevel || '?'}"), і поточну гілку визначити не вдалось ` +
|
|
111
|
+
'(detached HEAD?) — автоматичне створення worktree за конвенцією `<current-branch>-taze` неможливе. Перейди на гілку вручну.'
|
|
104
112
|
)
|
|
105
113
|
}
|
|
114
|
+
|
|
115
|
+
const branchArg = `${currentBranch}-taze`
|
|
116
|
+
const pathSegment = branchArg.replaceAll('/', '-')
|
|
117
|
+
log(`⚠️ "${cwd}" не в ізольованому worktree — створюю ".worktrees/${pathSegment}"...`)
|
|
118
|
+
runCommand('npx', ['@7n/mt', 'worktree', 'create', branchArg, 'n-taze: worktree-only skill'], cwd, spawnFn)
|
|
119
|
+
|
|
120
|
+
const newCwd = join(cwd, '.worktrees', pathSegment)
|
|
121
|
+
log('📥 bun install (bootstrap нового дерева)...')
|
|
122
|
+
runCommand('bun', ['install'], newCwd, spawnFn)
|
|
123
|
+
return { cwd: newCwd, autoCreated: true, branchArg }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Переносить зміни з автоствореного worktree назад у вихідне дерево як
|
|
128
|
+
* **untracked/незакомічені** правки (просте копіювання файлів, без git
|
|
129
|
+
* merge/cherry-pick) — так само, як їх бачив би користувач, якби сам
|
|
130
|
+
* працював у вихідному дереві. Джерело істини — `git status --porcelain`
|
|
131
|
+
* у worktree: для кожного шляху копіює файл, якщо він існує (модифікація/
|
|
132
|
+
* додавання), або видаляє його у вихідному дереві, якщо existsSync каже,
|
|
133
|
+
* що в worktree його вже нема (видалення). Перейменування (`old -> new` у
|
|
134
|
+
* porcelain) переносять лише нову назву — стара лишається як була, це
|
|
135
|
+
* прийнятний компроміс: taze не перейменовує файли сам, ефект можливий
|
|
136
|
+
* лише як побічний результат LLM-рефакторингу.
|
|
137
|
+
* @param {string} worktreeCwd автостворений worktree, з якого переносимо
|
|
138
|
+
* @param {string} originalCwd вихідне дерево, куди переносимо
|
|
139
|
+
* @param {typeof spawnSync} spawnFn інжект для тестів
|
|
140
|
+
* @param {(line: string) => void} log колбек прогресу
|
|
141
|
+
* @param {{ copyFile?: (src: string, dest: string) => Promise<void>, rm?: (path: string, opts?: object) => Promise<void>, mkdir?: (path: string, opts?: object) => Promise<void> }} [deps] інжекти для тестів
|
|
142
|
+
* @returns {Promise<string[]>} відносні шляхи перенесених файлів
|
|
143
|
+
*/
|
|
144
|
+
export async function bringChangesBackToOriginal(worktreeCwd, originalCwd, spawnFn, log, deps = {}) {
|
|
145
|
+
const copy = deps.copyFile ?? copyFile
|
|
146
|
+
const removeFile = deps.rm ?? rm
|
|
147
|
+
const makeDir = deps.mkdir ?? mkdir
|
|
148
|
+
|
|
149
|
+
const statusResult = spawnFn('git', ['status', '--porcelain'], { cwd: worktreeCwd, encoding: 'utf8' })
|
|
150
|
+
if (statusResult.status !== 0) {
|
|
151
|
+
log(
|
|
152
|
+
`⚠️ Не вдалось прочитати git status у "${worktreeCwd}" — зміни НЕ перенесені назад, worktree лишиться для ручного розбору.`
|
|
153
|
+
)
|
|
154
|
+
return []
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const lines = statusResult.stdout.split('\n').filter(Boolean)
|
|
158
|
+
if (lines.length === 0) {
|
|
159
|
+
log('ℹ️ Worktree без змін — нічого переносити назад.')
|
|
160
|
+
return []
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const brought = []
|
|
164
|
+
for (const line of lines) {
|
|
165
|
+
const rest = line.slice(3)
|
|
166
|
+
const relPath = rest.includes(' -> ') ? rest.split(' -> ', 2)[1] : rest
|
|
167
|
+
const srcPath = join(worktreeCwd, relPath)
|
|
168
|
+
const destPath = join(originalCwd, relPath)
|
|
169
|
+
if (existsSync(srcPath)) {
|
|
170
|
+
await makeDir(dirname(destPath), { recursive: true })
|
|
171
|
+
await copy(srcPath, destPath)
|
|
172
|
+
} else {
|
|
173
|
+
await removeFile(destPath, { force: true })
|
|
174
|
+
}
|
|
175
|
+
brought.push(relPath)
|
|
176
|
+
}
|
|
177
|
+
log(`📤 Перенесено назад у "${originalCwd}" як untracked: ${brought.join(', ')}`)
|
|
178
|
+
return brought
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Прибирає автостворений worktree разом з його ефемерною git-гілкою
|
|
183
|
+
* (`npx \@7n/mt worktree remove <branch>`) — викликати лише ПІСЛЯ
|
|
184
|
+
* `bringChangesBackToOriginal`, інакше зміни згорять разом з деревом.
|
|
185
|
+
* Не кидає при провалі — це прибирання, а не крок, від якого залежить
|
|
186
|
+
* результат прогону; провал лише логується, worktree лишається для
|
|
187
|
+
* ручного розбору.
|
|
188
|
+
* @param {string} branchArg гілка, з якою worktree був створений (з `ensureRunningInWorktree`)
|
|
189
|
+
* @param {string} originalCwd вихідне дерево, звідки виконати `npx \@7n/mt worktree remove`
|
|
190
|
+
* @param {typeof spawnSync} spawnFn інжект для тестів
|
|
191
|
+
* @param {(line: string) => void} log колбек прогресу
|
|
192
|
+
* @returns {void}
|
|
193
|
+
*/
|
|
194
|
+
export function removeAutoCreatedWorktree(branchArg, originalCwd, spawnFn, log) {
|
|
195
|
+
log(`🧹 Прибираю автостворений worktree "${branchArg}"...`)
|
|
196
|
+
const result = spawnFn('npx', ['@7n/mt', 'worktree', 'remove', branchArg], { cwd: originalCwd, encoding: 'utf8' })
|
|
197
|
+
if (result.status !== 0) {
|
|
198
|
+
log(`⚠️ Не вдалось прибрати worktree "${branchArg}" — приберіть вручну (${result.stderr || result.stdout})`)
|
|
199
|
+
}
|
|
106
200
|
}
|
|
107
201
|
|
|
108
202
|
/**
|
|
@@ -329,62 +423,79 @@ export function formatReport({ minorPatch, totalChanged, results, ecosystems = [
|
|
|
329
423
|
* @returns {Promise<{ ok: boolean, report: string, results: Array<object>, ecosystems: Array<object> }>} результат
|
|
330
424
|
*/
|
|
331
425
|
export async function runTazeOrchestrator(options = {}) {
|
|
332
|
-
const cwd = options.cwd ?? process.cwd()
|
|
333
426
|
const runner = options.runner ?? 'pi'
|
|
334
427
|
const log = options.log ?? (line => console.log(line))
|
|
335
428
|
const deps = options.deps ?? {}
|
|
336
429
|
const spawnFn = deps.spawnFn ?? spawnSync
|
|
337
430
|
const call = deps.callRunner ?? callRunner
|
|
338
431
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// репо `bun install` падає з exit 1, і без цього гейта весь прогін гинув би
|
|
343
|
-
// до екосистемних провайдерів. Той самий принцип «тиші», що й для мовних
|
|
344
|
-
// екосистем: немає сигналу — немає ані кроків, ані згадки у звіті.
|
|
345
|
-
const npmPresent = existsSync(join(cwd, 'package.json'))
|
|
346
|
-
let diff = { major: [], minorPatch: 0, totalChanged: 0 }
|
|
347
|
-
const results = []
|
|
348
|
-
if (npmPresent) {
|
|
349
|
-
log('📦 Бекап package.json...')
|
|
350
|
-
const backedUpWorkspaces = await backupWorkspacePackageFiles(cwd, deps)
|
|
351
|
-
|
|
352
|
-
log('⬆️ bunx taze -w -r latest...')
|
|
353
|
-
runCommand('bunx', ['taze', '-w', '-r', 'latest'], cwd, spawnFn)
|
|
354
|
-
log('📥 bun install...')
|
|
355
|
-
runCommand('bun', ['install'], cwd, spawnFn)
|
|
356
|
-
|
|
357
|
-
const collectDiff = deps.collectTazeDiff ?? collectTazeDiff
|
|
358
|
-
diff = await collectDiff(cwd)
|
|
359
|
-
log(`🔍 diff: ${diff.major.length} major, ${diff.minorPatch} minor/patch`)
|
|
432
|
+
const originalCwd = options.cwd ?? process.cwd()
|
|
433
|
+
const worktree = ensureRunningInWorktree(originalCwd, spawnFn, log)
|
|
434
|
+
const cwd = worktree.cwd
|
|
360
435
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
436
|
+
try {
|
|
437
|
+
// npm/bun-гілка активна лише за кореневим package.json — на чисто-Python/Rust
|
|
438
|
+
// репо `bun install` падає з exit 1, і без цього гейта весь прогін гинув би
|
|
439
|
+
// до екосистемних провайдерів. Той самий принцип «тиші», що й для мовних
|
|
440
|
+
// екосистем: немає сигналу — немає ані кроків, ані згадки у звіті.
|
|
441
|
+
const npmPresent = existsSync(join(cwd, 'package.json'))
|
|
442
|
+
let diff = { major: [], minorPatch: 0, totalChanged: 0 }
|
|
443
|
+
const results = []
|
|
444
|
+
if (npmPresent) {
|
|
445
|
+
log('📦 Бекап package.json...')
|
|
446
|
+
const backedUpWorkspaces = await backupWorkspacePackageFiles(cwd, deps)
|
|
447
|
+
|
|
448
|
+
log('⬆️ bunx taze -w -r latest...')
|
|
449
|
+
runCommand('bunx', ['taze', '-w', '-r', 'latest'], cwd, spawnFn)
|
|
450
|
+
log('📥 bun install...')
|
|
451
|
+
runCommand('bun', ['install'], cwd, spawnFn)
|
|
452
|
+
|
|
453
|
+
const collectDiff = deps.collectTazeDiff ?? collectTazeDiff
|
|
454
|
+
diff = await collectDiff(cwd)
|
|
455
|
+
log(`🔍 diff: ${diff.major.length} major, ${diff.minorPatch} minor/patch`)
|
|
456
|
+
|
|
457
|
+
for (const entry of diff.major) {
|
|
458
|
+
log(`🔧 ${entry.pkg} (${entry.workspace}): ${entry.from} → ${entry.to}...`)
|
|
459
|
+
const outcome = await call(runner, buildDependencyPrompt(entry), cwd, deps)
|
|
460
|
+
results.push({ ...entry, ...outcome })
|
|
461
|
+
log(outcome.ok ? ` ✅ ${entry.pkg}` : ` ❌ ${entry.pkg}: ${outcome.error}`)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
await cleanupBackups(cwd, backedUpWorkspaces, deps)
|
|
465
|
+
} else {
|
|
466
|
+
log('⏭ npm/bun: кореневого package.json немає — гілка пропущена')
|
|
366
467
|
}
|
|
367
468
|
|
|
368
|
-
await
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
469
|
+
const providers = deps.ecosystemProviders ?? (await loadPluginTazeProviders(cwd, log, deps))
|
|
470
|
+
const ecosystems = []
|
|
471
|
+
for (const provider of providers) {
|
|
472
|
+
ecosystems.push(await runEcosystem(provider, { cwd, runner, log, deps, spawnFn, call }))
|
|
473
|
+
}
|
|
372
474
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
475
|
+
const report = formatReport({
|
|
476
|
+
minorPatch: diff.minorPatch,
|
|
477
|
+
totalChanged: diff.totalChanged,
|
|
478
|
+
results,
|
|
479
|
+
ecosystems,
|
|
480
|
+
npmPresent
|
|
481
|
+
})
|
|
482
|
+
log(report)
|
|
483
|
+
|
|
484
|
+
const ecosystemsOk = ecosystems.every(eco => eco.error === null && eco.results.every(r => r.ok))
|
|
485
|
+
return { ok: results.every(r => r.ok) && ecosystemsOk, report, results, ecosystems }
|
|
486
|
+
} finally {
|
|
487
|
+
// Лише для АВТОстворених worktree — якщо викликач уже сидів у своєму
|
|
488
|
+
// worktree (worktree.autoCreated === false), це не наш worktree і не
|
|
489
|
+
// нам його чіпати/прибирати. `finally` — щоб зміни поверталися і
|
|
490
|
+
// сирітський worktree прибирався навіть при кинутому винятку всередині
|
|
491
|
+
// try (падіння bunx/diff/провайдера), а не лише при успішному прогоні.
|
|
492
|
+
if (worktree.autoCreated) {
|
|
493
|
+
try {
|
|
494
|
+
await bringChangesBackToOriginal(cwd, originalCwd, spawnFn, log, deps)
|
|
495
|
+
} catch (error) {
|
|
496
|
+
log(`⚠️ Перенесення змін назад провалилось: ${error instanceof Error ? error.message : String(error)}`)
|
|
497
|
+
}
|
|
498
|
+
removeAutoCreatedWorktree(worktree.branchArg, originalCwd, spawnFn, log)
|
|
499
|
+
}
|
|
377
500
|
}
|
|
378
|
-
|
|
379
|
-
const report = formatReport({
|
|
380
|
-
minorPatch: diff.minorPatch,
|
|
381
|
-
totalChanged: diff.totalChanged,
|
|
382
|
-
results,
|
|
383
|
-
ecosystems,
|
|
384
|
-
npmPresent
|
|
385
|
-
})
|
|
386
|
-
log(report)
|
|
387
|
-
|
|
388
|
-
const ecosystemsOk = ecosystems.every(eco => eco.error === null && eco.results.every(r => r.ok))
|
|
389
|
-
return { ok: results.every(r => r.ok) && ecosystemsOk, report, results, ecosystems }
|
|
390
501
|
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: JS Module
|
|
3
|
-
title: main.mjs
|
|
4
|
-
resource: npm/rules/doc-files/units-rs/main.mjs
|
|
5
|
-
docgen:
|
|
6
|
-
crc: 45adb1a4
|
|
7
|
-
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score: 100
|
|
9
|
-
issues: judge:inaccurate:0.98
|
|
10
|
-
judgeModel: openai-codex/gpt-5.4-mini
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Огляд
|
|
14
|
-
|
|
15
|
-
Огляд:
|
|
16
|
-
Цей файл відповідає за аналіз коду з метою вилучення інформації про публічно доступні елементи, зокрема, про структуру та методи. Він ідентифікує відображені елементи на підставі їхніх атрибутів або публічності, збираючи повні описи та тіла їхніх декларацій. Зокрема, функція `extractUnitsRs` забезпечує можливість автоматично генерувати технічну документацію, детально описуючи призначення кожного публічного елемента.
|
|
17
|
-
|
|
18
|
-
## Поведінка
|
|
19
|
-
|
|
20
|
-
Поведінка:
|
|
21
|
-
|
|
22
|
-
1. Функція `extractUnitsRs` аналізує вміст файлу з кодом.
|
|
23
|
-
2. Функція визначає всі визначені верхнерівневі та методи в блоках `impl`.
|
|
24
|
-
3. Визначення відображеності (exported) елементів відбувається на основі атрибутів, таких як `tauri::command`, або якщо функція звичайного виклику є вказана як публічна.
|
|
25
|
-
4. Для кожного знайденого елемента видобувається повний текстовий опис, що йде безпосередньо перед його декларацією.
|
|
26
|
-
5. Для функції, структури, перерахування, трейту або типу видобувається повний вміст тіла декларації.
|
|
27
|
-
6. Визначається, до якого блоку `impl` належить елемент, якщо він не є верхнерівневим.
|
|
28
|
-
7. Для вилучених елементів ідентифікуються внутрішні виклики до інших юнітів у межах того ж файлу.
|
|
29
|
-
|
|
30
|
-
## Публічний API
|
|
31
|
-
|
|
32
|
-
extractUnitsRs — витягує основні та реалізовані методи з файлів Rust.
|
|
33
|
-
|
|
34
|
-
## Гарантії поведінки
|
|
35
|
-
|
|
36
|
-
- Read-only: не виконує операцій запису (ФС/БД).
|