@nitra/cursor 3.27.0 → 3.29.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 +17 -0
- package/package.json +1 -3
- package/rules/abie/js/applies.mjs +1 -5
- package/rules/abie/js/env_dns.mjs +1 -9
- package/rules/abie/js/firebase_hosting.mjs +1 -5
- package/rules/abie/js/hc_pairing.mjs +1 -8
- package/rules/abie/js/ua_http_route.mjs +1 -10
- package/rules/abie/js/ua_node_selector.mjs +1 -8
- package/rules/adr/js/hooks.mjs +1 -20
- package/rules/bun/js/layout.mjs +1 -19
- package/rules/capacitor/js/platforms.mjs +1 -23
- package/rules/changelog/js/consistency.mjs +1 -29
- package/rules/ci4/js/marksman_config.mjs +1 -19
- package/rules/docker/js/lint.mjs +1 -34
- package/rules/ga/docs/fix.md +4 -4
- package/rules/ga/js/docs/lint.md +3 -3
- package/rules/ga/js/docs/workflows.md +14 -14
- package/rules/ga/js/workflows.mjs +1 -16
- package/rules/ga/lint/docs/lint.md +9 -9
- package/rules/graphql/js/tooling.mjs +1 -9
- package/rules/hasura/js/internal_urls.mjs +1 -24
- package/rules/image-avif/js/avif_generation.mjs +1 -27
- package/rules/image-compress/js/package_setup.mjs +1 -18
- package/rules/js-bun-db/js/safety.mjs +1 -31
- package/rules/js-bun-redis/js/imports.mjs +1 -12
- package/rules/js-lint/js/docs/lint-findings.md +30 -0
- package/rules/js-lint/js/lint-findings.mjs +1 -7
- package/rules/js-lint/js/lint.mjs +1 -10
- package/rules/js-lint/js/tooling.mjs +1 -13
- package/rules/js-lint/js/utils_imports.mjs +1 -18
- package/rules/js-lint-ci/js/lint.mjs +1 -6
- package/rules/js-mssql/js/deps.mjs +1 -10
- package/rules/js-run/js/runtime.mjs +1 -37
- package/rules/js-run/lib/docs/temporal-scan.md +25 -0
- package/rules/k8s/js/manifests.mjs +1 -137
- package/rules/nginx-default-tpl/js/template.mjs +1 -18
- package/rules/npm-module/js/docs/header_doc_pointer.md +25 -0
- package/rules/npm-module/js/header_doc_pointer.mjs +82 -0
- package/rules/npm-module/js/package_structure.mjs +1 -28
- package/rules/npm-module/js/rule_meta.mjs +1 -10
- package/rules/npm-module/js/skill_meta.mjs +1 -13
- package/rules/php/js/tooling.mjs +1 -11
- package/rules/python/js/applies.mjs +1 -8
- package/rules/python/js/tooling.mjs +1 -21
- package/rules/rego/js/applies.mjs +1 -11
- package/rules/rust/js/applies.mjs +1 -7
- package/rules/security/js/sample_secret.mjs +1 -28
- package/rules/security/js/trufflehog.mjs +1 -8
- package/rules/style-lint/js/lint.mjs +1 -5
- package/rules/style-lint/js/tooling.mjs +1 -19
- package/rules/tauri/js/cargo_mutants_config.mjs +1 -20
- package/rules/tauri/js/tooling.mjs +1 -21
- package/rules/test/js/cargo_mutants_config.mjs +1 -12
- package/rules/test/js/location.mjs +1 -9
- package/rules/test/js/no-process-chdir.mjs +1 -21
- package/rules/test/js/no-relative-fs-path.mjs +1 -23
- package/rules/test/js/stryker_config.mjs +4 -25
- package/rules/test/js/vitest-config-pool-forks.mjs +1 -17
- package/rules/text/js/forbidden-prettier.mjs +1 -10
- package/rules/text/js/formatting.mjs +1 -31
- package/rules/vue/js/packages.mjs +1 -24
- package/scripts/coverage-classify/index.mjs +60 -72
- package/scripts/coverage-fix.mjs +26 -23
- package/scripts/dispatcher/lib/subagent-runner.mjs +33 -102
- package/scripts/docs/coverage-fix-extract.md +32 -0
- package/scripts/docs/lint-cli.md +25 -0
- package/scripts/docs/post-tool-use-fix.md +27 -0
- package/scripts/docs/rename-yaml-extensions.md +36 -0
- package/scripts/docs/skills-cli.md +35 -0
- package/scripts/docs/sync-claude-config.md +52 -0
- package/scripts/docs/sync-setup-bun-deps-action.md +26 -0
- package/scripts/docs/upgrade-nitra-cursor-and-install.md +29 -0
- package/scripts/docs/worktree-cli.md +46 -0
- package/scripts/lib/docs/assert-project-root.md +28 -0
- package/scripts/lib/docs/diff-added-lines.md +34 -0
- package/scripts/lib/docs/read-n-cursor-config-lite.md +28 -0
- package/scripts/lib/docs/resolve-target-files.md +34 -0
- package/scripts/lib/docs/root-notice.md +28 -0
- package/scripts/lib/docs/rule-meta-helpers.md +34 -0
- package/scripts/lib/docs/rule-meta.md +34 -0
- package/scripts/lib/docs/rule-predicates.md +30 -0
- package/scripts/lib/docs/run-conftest-batch.md +26 -0
- package/scripts/lib/docs/run-lint-step.md +25 -0
- package/scripts/lib/docs/run-rule-cli.md +27 -0
- package/scripts/lib/docs/run-rule.md +32 -0
- package/scripts/lib/docs/run-standard-lint.md +22 -0
- package/scripts/lib/docs/run-standard-rule.md +24 -0
- package/scripts/lib/docs/skill-meta.md +31 -0
- package/scripts/lib/docs/sync-gitignore-worktree.md +31 -0
- package/scripts/lib/docs/template.md +40 -0
- package/scripts/lib/docs/timing-summary.md +24 -0
- package/scripts/lib/docs/workspaces.md +30 -0
- package/scripts/lib/docs/worktree-notice.md +27 -0
- package/scripts/lib/docs/worktree.md +38 -0
- package/scripts/utils/docs/ast-scan-utils.md +50 -0
- package/scripts/utils/docs/ensure-gitignore-entries.md +28 -0
- package/scripts/utils/docs/find-package-json-paths.md +26 -0
- package/scripts/utils/docs/lock-cache-dir.md +25 -0
- package/scripts/utils/docs/pass.md +25 -0
- package/scripts/utils/docs/resolve-cargo-manifest.md +23 -0
- package/scripts/utils/docs/resolve-cmd.md +29 -0
- package/scripts/utils/docs/resolve-js-root.md +25 -0
- package/scripts/utils/docs/test-helpers.md +36 -0
- package/scripts/utils/docs/walk-cache.md +27 -0
- package/scripts/utils/docs/walkDir.md +32 -0
- package/scripts/utils/docs/with-lock.md +25 -0
- package/scripts/utils/docs/worktree-fingerprint.md +27 -0
- package/skills/docgen/js/docgen-batch.mjs +95 -0
- package/skills/docgen/js/docgen-extract.mjs +33 -18
- package/skills/docgen/js/docgen-gen.mjs +140 -154
- package/skills/docgen/js/docgen-ignore.mjs +1 -6
- package/skills/docgen/js/docgen-prompts.mjs +33 -22
- package/skills/docgen/js/docgen-scan.mjs +1 -8
- package/skills/docgen/js/docs/docgen-extract.md +28 -0
- package/skills/docgen/js/docs/docgen-gen.md +41 -0
- package/skills/docgen/js/docs/docgen-ignore.md +24 -0
- package/skills/docgen/js/docs/docgen-prompts.md +24 -0
- package/skills/docgen/js/docs/docgen-scan.md +48 -0
- package/skills/fix/js/docs/llm-worker.md +27 -0
- package/skills/fix/js/docs/orchestrator.md +32 -0
- package/skills/fix/js/docs/t0.md +29 -0
- package/skills/fix/js/llm-worker.mjs +64 -29
- package/skills/fix/js/orchestrator.mjs +45 -54
- package/skills/fix/js/t0.mjs +16 -32
- package/skills/start-check/js/check.mjs +1 -16
- package/skills/start-check/js/docs/check.md +34 -0
- package/skills/taze/js/diff.mjs +1 -15
- package/skills/taze/js/docs/diff.md +33 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# docgen-prompts.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Цей файл містить опис коду для конвеєра docgen. Він визначає точкові промпти для кожної секції коду, що генерується. Це дозволяє ефективно генерувати документацію на основі коду.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
Напиши вміст секції «Поведінка»: для кожної публічної функції — один короткий пункт «що вона робить». Якщо у фактах є свідомі пропуски шляхів — згадай їх там, де доречно (не вигадуй інших «не перевіряє»). НЕ пиши аргументи функцій у дужках, без regex. Без заголовка.
|
|
10
|
+
|
|
11
|
+
## Публічний API
|
|
12
|
+
|
|
13
|
+
- STYLE — Підготовка документації на першому етапі генерації: створення факт-листа та коду, а потім генерація точкових промптів для кожної секції.
|
|
14
|
+
- v2 — Мінімальний контекст секції: код розміщується лише в `Поведінку`. `Огляд` містить лише заголовок, `API` – список експортів, `Гарантії` – маркери. Це дозволяє один раз обробити інсульт коду, а не секцію за секцією.
|
|
15
|
+
- sectionMessages — Набори повідомлень для кожної секції з мінімальним контекстом. Код розміщується лише в `Поведінку`, решта – на факт-листи.
|
|
16
|
+
- oneShotMessages — База повідомлень для порівняння.
|
|
17
|
+
- oneShotPromptText — Текст user-промпту для one-shot (для резервного копіювання в хмарі через Anthropic SDK).
|
|
18
|
+
|
|
19
|
+
## Гарантії поведінки
|
|
20
|
+
|
|
21
|
+
- `docgen-конвеєр` кешує результати генерації промптів для кожної секції.
|
|
22
|
+
- `Огляд` містить лише заголовок секції.
|
|
23
|
+
- `API` містить лише список експортів секції.
|
|
24
|
+
- `Гарантії` містять лише маркери.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# docgen-scan.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
`docgen scanner` обходить проєкт, щоб створити JSON-список кодових файлів, розташованих у теці `docs/` поряд із джерелом. Цей список використовується іншими скілами для генерації документації. Інструмент забезпечує детермінований обхід проєкту та відстежує наявність файлів, не використовуючи мережеві ресурси чи LLM.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
- `isSourceFile`: Перевіряє, чи файл є джерелом коду, враховуючи розширення та тестові файли.
|
|
10
|
+
- `docPathForSource`: Генерує шлях до файлу документації для заданого джерела, враховуючи відносні шляхи.
|
|
11
|
+
- `scanForDocgen`: Рекурсивно обходить дерево проєкту, визначаючи кодові файли, які потрібно документувати, та їх відповідні шляхи до документації.
|
|
12
|
+
- `slugForModule`: Генерує унікальний slug для модуля на основі його відносного шляху.
|
|
13
|
+
- `findModuleRoots`: Знаходить абсолютні шляхи коренів модулів проєкту.
|
|
14
|
+
- `nearestModuleRoot`: Знаходить найближчий модуль-предок для заданого файлу.
|
|
15
|
+
- `scanForModules`: Лістить модулі проєкту, визначаючи їхні члени (sourcePath-и) та шляхи до документації.
|
|
16
|
+
- `resolveRoot`: Визначає абсолютний корінь проєкту на основі аргументів командного рядка.
|
|
17
|
+
- `runDocgenScanCli`: Запускає сканування docgen як CLI, виводить JSON-масив у stdout та повертає код виходу.
|
|
18
|
+
- `runDocgenModulesCli`: Запускає сканування модулів docgen як CLI, виводить JSON-масив у stdout та повертає код виходу.
|
|
19
|
+
|
|
20
|
+
## Публічний API
|
|
21
|
+
|
|
22
|
+
- isSourceFile — Визначає, чи є файл кодовим джерелом для створення документації.
|
|
23
|
+
- docPathForSource — Генерує шлях до md-файлу, пов'язаний з кодовим файлом.
|
|
24
|
+
- scanForDocgen — Рекурсивно обстежує структуру проєкту, виявляючи файли для документування.
|
|
25
|
+
- slugForModule — Створює унікальний ідентифікатор (slug) для модуля на основі його шляху.
|
|
26
|
+
- findModuleRoots — Знаходить корені модулів, визначаючи директорії з файлом `package.json`.
|
|
27
|
+
- nearestModuleRoot — Визначає найближчий модуль-предок для заданого файлу.
|
|
28
|
+
- scanForModules — Створює список логічних модулів проєкту, включаючи файли та інформацію про них.
|
|
29
|
+
- resolveRoot — Встановлює базову директорію для сканування, використовуючи аргумент командного рядка або поточну директорію.
|
|
30
|
+
- runDocgenScanCli — Запускає сканування для виявлення файлів документації та виводить результат у форматі JSON.
|
|
31
|
+
- runDocgenModulesCli — Запускає сканування модулів та виводить результат у форматі JSON.
|
|
32
|
+
|
|
33
|
+
## Гарантії поведінки
|
|
34
|
+
|
|
35
|
+
- `isSourceFile` повертає `true`, якщо вказаний шлях до файлу є джерелом коду.
|
|
36
|
+
- `isSourceFile` повертає `false` в іншому випадку.
|
|
37
|
+
- `docPathForSource` повертає шлях до відповідної папки `docs/` для вказаного джерела.
|
|
38
|
+
- `scanForDocgen` повертає JSON-список шляхів до кодових файлів, які потрібно обробити.
|
|
39
|
+
- `scanForDocgen` встановлює прапор `exists` у випадку, якщо файл або папка вже існують.
|
|
40
|
+
- `slugForModule` повертає унікальний URL-шлях для модуля.
|
|
41
|
+
- `findModuleRoots` повертає список кореневих модулів проєкту.
|
|
42
|
+
- `nearestModuleRoot` повертає найближчий кореневий модуль до вказаного шляху.
|
|
43
|
+
- `scanForModules` повертає список модулів проєкту.
|
|
44
|
+
- `resolveRoot` повертає кореневу папку проєкту.
|
|
45
|
+
- `runDocgenScanCli` запускає процес сканування для `docgen`.
|
|
46
|
+
- `runDocgenModulesCli` запускає процес сканування для модулів.
|
|
47
|
+
- У разі невдачі повертається `false` та `null`.
|
|
48
|
+
-
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# llm-worker.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Цей файл є LLM-робітником, який збирає контекст для n-fix оркестратора, зокрема правила з файлів .mdc та звіти про порушення. Він передає цей контекст великій мовній моделі (LLM) для генерації JSON з пропозиціями змін. Після отримання змін, скрипт застосовує їх до відповідних ресурсів.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
runLlmWorker: Виправляє одне rule-порушення через pi (C1 pattern). Збирає контекст з rule .mdc, файлів з violation output, та передає його в pi для отримання JSON зі змінами. Застосовує зміни, якщо вони були отримані від pi. Якщо pi не повернув JSON, або не зміг застосувати зміни, повертає помилку. Використовує модель Claude HAIKU за замовчуванням, або модель, вказану в опціях.
|
|
10
|
+
|
|
11
|
+
## Публічний API
|
|
12
|
+
|
|
13
|
+
MODEL_HAIKU — Генерує короткі вірші.
|
|
14
|
+
MODEL_SONNET — Генерує вірші у форматі сонету.
|
|
15
|
+
runLlmWorker — Виправляє порушення правил, використовуючи pi (C1 pattern).
|
|
16
|
+
|
|
17
|
+
## Гарантії поведінки
|
|
18
|
+
|
|
19
|
+
- Приймає контекст з файлів `.mdc` та файлів з violation.
|
|
20
|
+
- Повертає JSON з пропозиціями змін.
|
|
21
|
+
- Застосовує зміни, отримані від `pi`.
|
|
22
|
+
- Використовує LLM для генерації змін.
|
|
23
|
+
- Викликає LLM через `pi`.
|
|
24
|
+
- Не використовує tool-use.
|
|
25
|
+
- Не кидає винятків.
|
|
26
|
+
- У разі невдачі повертає `false` та `null`.
|
|
27
|
+
- Не використовує кешування.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# orchestrator.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл `convergence-loop` є автономним оркестратором для n-fix, який забезпечує перевірку та виправлення коду без участі LLM. Він ініціює детерміністичні перевірки, regex-парсинг та ітеративні процеси з використанням LLM для вирішення складних проблем. Цей компонент є ключовим елементом системи, що автоматизує процес забезпечення коректності коду n-fix.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
1. **Ініціалізація:** Запускається оркестратор, визначаються максимальна кількість ітерацій (за замовчуванням 3) та порогове значення для ескалації LLM (за замовчуванням 2 невдачі).
|
|
10
|
+
2. **Первинна перевірка:** Виконується детермінована перевірка (T0) на основі заданих правил, без залучення LLM. Результат перевірки записується.
|
|
11
|
+
3. **Автоматичний фікс (T0-auto):** Якщо перевірка виявила порушення, запускається автоматичний фікс, який використовує regex для виявлення та виправлення проблем.
|
|
12
|
+
4. **Ітерація:** Програма повторює наступні кроки до досягнення максимальної кількості ітерацій:
|
|
13
|
+
- **Перевірка (T0):** Виконується детермінована перевірка (T0) на основі поточного стану.
|
|
14
|
+
- **Автоматичний фікс (T0-auto):** Якщо перевірка виявила порушення, запускається автоматичний фікс.
|
|
15
|
+
- **Виклик LLM (T1):** Для кожного порушення, яке не було виправлено автоматичним фіксом, викликається LLM (haiku або sonnet, залежно від кількості попередніх невдач) для генерації рішення.
|
|
16
|
+
- **Оцінка рішення LLM:** LLM генерує рішення, яке оцінюється на предмет успіху.
|
|
17
|
+
- **Оновлення стану:** Якщо рішення LLM успішне, порушення виправляється, і кількість невдалих спроб LLM для цього правила встановлюється на нуль.
|
|
18
|
+
5. **Фінальна перевірка:** Після завершення всіх ітерацій виконується остаточна перевірка, щоб визначити, чи були виправлені всі порушення.
|
|
19
|
+
6. **Повернення результату:** Оркестратор повертає код 0, якщо всі правила були виправлені, і код 1, якщо хоча б одне правило залишилося не виправленим. У випадку помилки парсингу JSON, повертається null.
|
|
20
|
+
|
|
21
|
+
## Гарантії поведінки
|
|
22
|
+
|
|
23
|
+
- Оркестратор запускається при виклику `runOrchestratorCli`.
|
|
24
|
+
- Програма виконує цикл з перевірок, поки не буде досягнуто максимальну кількість ітерацій (`maxIter`).
|
|
25
|
+
- Після кожної ітерації (`T0`, `T0-auto`, `T1`) виконується перевірка.
|
|
26
|
+
- `T0` виконується детерміновано без використання LLM.
|
|
27
|
+
- `T0-auto` виконує regex-парсинг та застосовує програмний фікс у разі виявлення порушення.
|
|
28
|
+
- `T1` використовує LLM через `pi` (ескалація до `haiku` та `sonnet`).
|
|
29
|
+
- `check-gate` повторно запускає `T0` після кожної ітерації.
|
|
30
|
+
- У разі помилки програма повертає `false` або `null`.
|
|
31
|
+
- Програма кешує результати для оптимізації роботи в межах одного запуску.
|
|
32
|
+
- Програма не взаємодіє з мережею.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# t0.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл `t0-auto` забезпечує детермінований рівень виправлень для n-fix оркестратора, автоматично застосовуючи програмні фікси на основі аналізу вихідних даних `n-cursor fix --json`. Він парсить повідомлення про порушення, видобуваючи з них інформацію про цільові файли або рядки для вставки, і застосовує відповідні фікси. Це дозволяє швидко та передбачувано виправляти помилки, не використовуючи LLM, і є першим етапом у конвергентному циклі виправлення.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
applyT0Auto: Застосовує всі T0-auto паттерни до вхідного `violationOutput` та повертає об'єкт, що вказує, чи застосовано фікс, та список виконаних дій.
|
|
10
|
+
filterT0AutoRules: Повертає список id правил, для яких є хоч один T0-auto паттерн, на основі `failedRules`.
|
|
11
|
+
runT0AutoCli: Запускає `n-cursor fix --json`, застосовує T0-auto, повторно перевіряє check-gate та виводить підсумок. Повертає 0 у разі успіху, 1 у разі помилки.
|
|
12
|
+
|
|
13
|
+
## Публічний API
|
|
14
|
+
|
|
15
|
+
- applyT0Auto — Застосовує автоматичні правила виправлення до виявлених проблем.
|
|
16
|
+
- filterT0AutoRules — Визначає ідентифікатори правил, які мають T0-auto паттерни.
|
|
17
|
+
- runT0AutoCli — Запускає CLI-інструмент для автоматичного виправлення, включаючи перевірку та звітність.
|
|
18
|
+
|
|
19
|
+
## Гарантії поведінки
|
|
20
|
+
|
|
21
|
+
- `applyT0Auto` застосовує програмний фікс, якщо `violationOutput` містить інформацію, яку можна видобути за допомогою регулярних виразів, і `violation-message` містить цільове значення, що відповідає одному з ідентифікаторів правил T0-auto. Результат: `applied` - boolean, `actions` - string[] (список виконаних дій).
|
|
22
|
+
- `listT0AutoRules` повертає список ідентифікаторів правил T0-auto, які мають хоча б один паттерн.
|
|
23
|
+
- `runT0AutoCli` повертає код виходу 0, якщо процес виконано успішно, і 1, якщо виявлено порушення.
|
|
24
|
+
- T0-auto запускається першим у конвергентному циклі.
|
|
25
|
+
- T1 запускається лише для решти.
|
|
26
|
+
- Система перехоплює помилки та не кидає винятки.
|
|
27
|
+
- В системі немає кешування.
|
|
28
|
+
- Вхідні дані `applyT0Auto` повинні відповідати формату JSON, вихідному від `n-cursor fix --json`.
|
|
29
|
+
- Якщо `violation-message` не містить інформації, яку можна видобути за допомогою регулярних виразів, `applyT0Auto` не виконує жод
|
|
@@ -1,36 +1,47 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM-worker для n-fix оркестратора — C1 pattern:
|
|
3
|
-
* script збирає контекст (rule .mdc + файли з violation) →
|
|
4
|
-
* pi повертає JSON зі змінами →
|
|
5
|
-
* script застосовує.
|
|
6
|
-
*
|
|
7
|
-
* Всі LLM-виклики через `pi` (користувач налаштовує ключі самостійно).
|
|
8
|
-
* Tool-use не використовується — LLM отримує повний контекст у промпті.
|
|
9
|
-
*/
|
|
1
|
+
/** @see ./docs/llm-worker.md */
|
|
10
2
|
|
|
11
3
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
12
4
|
import { join } from 'node:path'
|
|
13
5
|
import { spawnSync } from 'node:child_process'
|
|
6
|
+
import { env } from 'node:process'
|
|
7
|
+
import { resolveModel } from '../../../lib/models.mjs'
|
|
14
8
|
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
// Тир за замовчуванням: min → avg при ескалації (каскад local→cloud).
|
|
10
|
+
// Перевизначення через N_CURSOR_FIX_MODEL / N_CURSOR_FIX_MODEL_HEAVY.
|
|
11
|
+
export const MODEL = env.N_CURSOR_FIX_MODEL ?? resolveModel('min')
|
|
12
|
+
export const MODEL_HEAVY = env.N_CURSOR_FIX_MODEL_HEAVY ?? resolveModel('avg')
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
15
|
* Витягує відносні шляхи файлів із violation output.
|
|
20
|
-
*
|
|
16
|
+
* Розуміє workspace-prefix: `[npm] skills/foo.mjs` → `npm/skills/foo.mjs`.
|
|
21
17
|
*
|
|
22
18
|
* @param {string} output violation output з fix check
|
|
23
|
-
* @returns {string[]} унікальні відносні шляхи
|
|
19
|
+
* @returns {string[]} унікальні відносні шляхи (від кореня проєкту)
|
|
24
20
|
*/
|
|
25
21
|
function extractFilePaths(output) {
|
|
26
22
|
const seen = new Set()
|
|
27
23
|
const results = []
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
|
|
25
|
+
// Патерн з workspace: [npm] skills/foo.mjs або [demo] src/bar.ts
|
|
26
|
+
const wsRe = /\[([\w-]+)\]\s+([\w./][\w./\-]*\.(?:json|js|mjs|ts|vue|yml|yaml|toml|mdc|md|sh|py))(?::\d+)?/gm
|
|
27
|
+
for (const m of output.matchAll(wsRe)) {
|
|
28
|
+
const p = `${m[1]}/${m[2]}`
|
|
29
|
+
if (!seen.has(p)) {
|
|
30
|
+
seen.add(p)
|
|
31
|
+
results.push(p)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Патерн без workspace: просто path/to/file.ext або ./file.ext
|
|
36
|
+
const re = /(?:^|\s)(\.?[\w][\w./\-]*\.(?:json|js|mjs|ts|vue|yml|yaml|toml|mdc|md|sh|py))(?::\d+)?/gm
|
|
30
37
|
for (const m of output.matchAll(re)) {
|
|
31
38
|
const p = m[1]
|
|
32
|
-
if (!seen.has(p)) {
|
|
39
|
+
if (!seen.has(p)) {
|
|
40
|
+
seen.add(p)
|
|
41
|
+
results.push(p)
|
|
42
|
+
}
|
|
33
43
|
}
|
|
44
|
+
|
|
34
45
|
return results
|
|
35
46
|
}
|
|
36
47
|
|
|
@@ -44,9 +55,10 @@ function extractFilePaths(output) {
|
|
|
44
55
|
* @returns {string}
|
|
45
56
|
*/
|
|
46
57
|
function buildPrompt(ruleId, ruleMdc, output, files) {
|
|
47
|
-
const filesBlock =
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
const filesBlock =
|
|
59
|
+
files.length === 0
|
|
60
|
+
? '(no files identified)'
|
|
61
|
+
: files.map(f => `<file path="${f.path}">\n${f.content}\n</file>`).join('\n\n')
|
|
50
62
|
|
|
51
63
|
return [
|
|
52
64
|
`You fix project structure violations. Return ONLY valid JSON — no explanation, no markdown.`,
|
|
@@ -69,7 +81,7 @@ function buildPrompt(ruleId, ruleMdc, output, files) {
|
|
|
69
81
|
`- "path" is relative to the project root`,
|
|
70
82
|
`- "content" is the complete new file content (not a diff)`,
|
|
71
83
|
`- Only include files that actually need to change`,
|
|
72
|
-
`- If nothing can be fixed automatically, return {"changes":[],"error":"reason"}
|
|
84
|
+
`- If nothing can be fixed automatically, return {"changes":[],"error":"reason"}`
|
|
73
85
|
].join('\n')
|
|
74
86
|
}
|
|
75
87
|
|
|
@@ -81,14 +93,25 @@ function buildPrompt(ruleId, ruleMdc, output, files) {
|
|
|
81
93
|
* @returns {{ text: string, error?: string }}
|
|
82
94
|
*/
|
|
83
95
|
function callPi(prompt, model) {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
)
|
|
96
|
+
const modelArgs = model ? ['--model', model] : []
|
|
97
|
+
const r = spawnSync('pi', ['-p', prompt, ...modelArgs, '--no-session', '--mode', 'text', '--no-tools'], {
|
|
98
|
+
encoding: 'utf8',
|
|
99
|
+
timeout: 120_000
|
|
100
|
+
})
|
|
89
101
|
if (r.error) return { text: '', error: r.error.message }
|
|
90
102
|
if (r.status !== 0) {
|
|
91
103
|
const stderr = r.stderr?.slice(0, 300) ?? ''
|
|
104
|
+
if (stderr.toLowerCase().includes('no api key') || stderr.toLowerCase().includes('api key')) {
|
|
105
|
+
const provider = model ? model.split('/')[0] : 'дефолтного провайдера'
|
|
106
|
+
return {
|
|
107
|
+
text: '',
|
|
108
|
+
error: [
|
|
109
|
+
`pi: немає ключа для ${provider}.`,
|
|
110
|
+
`Встановіть N_CLOUD_MIN_MODEL=provider/model-id`,
|
|
111
|
+
`(напр.: openai/gpt-5.4-mini, google/gemini-2.5-flash, ollama/gemma3:4b)`,
|
|
112
|
+
].join(' ')
|
|
113
|
+
}
|
|
114
|
+
}
|
|
92
115
|
return { text: '', error: `pi exit ${r.status}: ${stderr}` }
|
|
93
116
|
}
|
|
94
117
|
return { text: r.stdout?.trim() ?? '' }
|
|
@@ -103,19 +126,31 @@ function callPi(prompt, model) {
|
|
|
103
126
|
*/
|
|
104
127
|
function parseResponse(text) {
|
|
105
128
|
// Спроба 1: прямий JSON
|
|
106
|
-
try {
|
|
129
|
+
try {
|
|
130
|
+
return JSON.parse(text)
|
|
131
|
+
} catch {
|
|
132
|
+
/* fallthrough */
|
|
133
|
+
}
|
|
107
134
|
|
|
108
135
|
// Спроба 2: витягти з ```json ... ```
|
|
109
136
|
const m = text.match(/```(?:json)?\s*([\s\S]*?)```/)
|
|
110
137
|
if (m) {
|
|
111
|
-
try {
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(m[1].trim())
|
|
140
|
+
} catch {
|
|
141
|
+
/* fallthrough */
|
|
142
|
+
}
|
|
112
143
|
}
|
|
113
144
|
|
|
114
145
|
// Спроба 3: перший { ... } блок
|
|
115
146
|
const start = text.indexOf('{')
|
|
116
147
|
const end = text.lastIndexOf('}')
|
|
117
148
|
if (start !== -1 && end > start) {
|
|
118
|
-
try {
|
|
149
|
+
try {
|
|
150
|
+
return JSON.parse(text.slice(start, end + 1))
|
|
151
|
+
} catch {
|
|
152
|
+
/* fallthrough */
|
|
153
|
+
}
|
|
119
154
|
}
|
|
120
155
|
|
|
121
156
|
return null
|
|
@@ -131,7 +166,7 @@ function parseResponse(text) {
|
|
|
131
166
|
* @returns {Promise<{ ok: boolean, error?: string }>}
|
|
132
167
|
*/
|
|
133
168
|
export async function runLlmWorker(ruleId, violationOutput, projectRoot, opts = {}) {
|
|
134
|
-
const model = opts.model ??
|
|
169
|
+
const model = opts.model ?? MODEL
|
|
135
170
|
|
|
136
171
|
// 1. Читаємо rule .mdc
|
|
137
172
|
const mdcPath = join(projectRoot, '.cursor', 'rules', `n-${ruleId}.mdc`)
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Автономний оркестратор n-fix: convergence-loop без участі агента-LLM.
|
|
3
|
-
*
|
|
4
|
-
* Тири:
|
|
5
|
-
* T0 — детерміністична перевірка (runFixCheck, 0 LLM)
|
|
6
|
-
* T0-auto — regex-парсинг violation → програмний фікс (0 LLM)
|
|
7
|
-
* T1 — LLM через pi (haiku → sonnet ескалація)
|
|
8
|
-
* check-gate — re-run T0 після кожного тіру; loop до maxIter
|
|
9
|
-
*
|
|
10
|
-
* meta.json: { "orchestrator": true } — CLI маршрутизує `fix` сюди.
|
|
11
|
-
*/
|
|
1
|
+
/** @see ./docs/orchestrator.md */
|
|
12
2
|
|
|
13
3
|
import { spawnSync } from 'node:child_process'
|
|
14
4
|
import { fileURLToPath } from 'node:url'
|
|
@@ -26,81 +16,82 @@ const ESCALATE_AFTER = 2
|
|
|
26
16
|
* @returns {Promise<number>} 0 = all clean, 1 = unresolved
|
|
27
17
|
*/
|
|
28
18
|
export async function runOrchestratorCli(args, cwd) {
|
|
29
|
-
const { runLlmWorker,
|
|
19
|
+
const { runLlmWorker, MODEL, MODEL_HEAVY } = await import('./llm-worker.mjs')
|
|
30
20
|
|
|
31
21
|
const maxIterIdx = args.indexOf('--max-iter')
|
|
32
22
|
const maxIter =
|
|
33
|
-
maxIterIdx !== -1
|
|
34
|
-
? Number(args[maxIterIdx + 1] ?? DEFAULT_MAX_ITER) || DEFAULT_MAX_ITER
|
|
35
|
-
: DEFAULT_MAX_ITER
|
|
23
|
+
maxIterIdx !== -1 ? Number(args[maxIterIdx + 1] ?? DEFAULT_MAX_ITER) || DEFAULT_MAX_ITER : DEFAULT_MAX_ITER
|
|
36
24
|
const skipIdxs = new Set(maxIterIdx !== -1 ? [maxIterIdx, maxIterIdx + 1] : [])
|
|
37
25
|
const ruleFilter = args.filter((a, i) => !a.startsWith('-') && !skipIdxs.has(i))
|
|
38
26
|
|
|
39
|
-
console.log(`🔄 n-cursor fix`)
|
|
40
|
-
if (ruleFilter.length) console.log(` rules: ${ruleFilter.join(', ')}`)
|
|
41
|
-
|
|
42
27
|
/** @type {Map<string, number>} ruleId → кількість LLM-провалів підряд */
|
|
43
28
|
const failCount = new Map()
|
|
44
29
|
|
|
45
|
-
|
|
46
|
-
|
|
30
|
+
// ── Перша перевірка (тихо) ──
|
|
31
|
+
const initial = runFixCheck(cwd, ruleFilter)
|
|
32
|
+
if (!initial) {
|
|
33
|
+
console.error(`❌ fix: помилка перевірки`)
|
|
34
|
+
return 1
|
|
35
|
+
}
|
|
47
36
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (!state) {
|
|
51
|
-
console.error(`❌ fix: перевірка повернула помилку`)
|
|
52
|
-
return 1
|
|
53
|
-
}
|
|
37
|
+
let failed = initial.rules.filter(r => !r.ok)
|
|
38
|
+
const total = initial.total
|
|
54
39
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
40
|
+
// Нічого не зламано — коротка відповідь
|
|
41
|
+
if (failed.length === 0) {
|
|
42
|
+
console.log(`✅ fix: ${total} правил — все чисто`)
|
|
43
|
+
return 0
|
|
44
|
+
}
|
|
60
45
|
|
|
61
|
-
|
|
46
|
+
// Є порушення — показуємо прогрес
|
|
47
|
+
console.log(`🔄 fix: ${failed.length}/${total} порушень (${failed.map(r => r.ruleId).join(', ')})`)
|
|
48
|
+
if (ruleFilter.length) console.log(` filter: ${ruleFilter.join(', ')}`)
|
|
62
49
|
|
|
50
|
+
for (let iter = 1; iter <= maxIter; iter++) {
|
|
63
51
|
// ── T0-auto: детермінований фікс без LLM ──
|
|
64
|
-
spawnSync('bun', [N_CURSOR_BIN, 'fix-t0', ...ruleFilter], { cwd, stdio: '
|
|
52
|
+
spawnSync('bun', [N_CURSOR_BIN, 'fix-t0', ...ruleFilter], { cwd, stdio: 'pipe' })
|
|
53
|
+
|
|
54
|
+
const afterT0 = runFixCheck(cwd, ruleFilter)
|
|
55
|
+
const failedAfterT0 = afterT0?.rules.filter(r => !r.ok) ?? failed
|
|
56
|
+
const t0Fixed = failed.filter(r => !failedAfterT0.find(f => f.ruleId === r.ruleId))
|
|
65
57
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (failedAfterT0.length === 0) {
|
|
69
|
-
console.log(`✅ fix: всі правила закриті T0-auto`)
|
|
70
|
-
return 0
|
|
58
|
+
if (t0Fixed.length > 0) {
|
|
59
|
+
console.log(` ⚙️ T0-auto: ${t0Fixed.map(r => r.ruleId).join(', ')}`)
|
|
71
60
|
}
|
|
72
61
|
|
|
62
|
+
failed = failedAfterT0
|
|
63
|
+
if (failed.length === 0) break
|
|
64
|
+
|
|
73
65
|
// ── T1: LLM через pi ──
|
|
74
|
-
for (const rule of
|
|
66
|
+
for (const rule of failed) {
|
|
75
67
|
const prevFails = failCount.get(rule.ruleId) ?? 0
|
|
76
|
-
const model = prevFails >= ESCALATE_AFTER ?
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
console.log(`\n⚡ [${tier}] → ${rule.ruleId}`)
|
|
68
|
+
const model = prevFails >= ESCALATE_AFTER ? MODEL_HEAVY : MODEL
|
|
69
|
+
const label = model || 'pi'
|
|
80
70
|
|
|
81
71
|
const result = await runLlmWorker(rule.ruleId, rule.output, cwd, { model })
|
|
82
72
|
|
|
83
73
|
if (result.ok) {
|
|
84
|
-
console.log(`
|
|
74
|
+
console.log(` ⚡ LLM (${label}): ${rule.ruleId}`)
|
|
85
75
|
failCount.delete(rule.ruleId)
|
|
86
76
|
} else {
|
|
87
77
|
failCount.set(rule.ruleId, prevFails + 1)
|
|
88
|
-
|
|
78
|
+
const hint = (result.error ?? '').slice(0, 200)
|
|
79
|
+
console.log(` ⚡ LLM (${label}): ${rule.ruleId} ❌ ${hint}`)
|
|
89
80
|
}
|
|
90
81
|
}
|
|
91
|
-
}
|
|
92
82
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
// Перевірка після LLM
|
|
84
|
+
const afterLLM = runFixCheck(cwd, ruleFilter)
|
|
85
|
+
failed = afterLLM?.rules.filter(r => !r.ok) ?? failed
|
|
86
|
+
if (failed.length === 0) break
|
|
87
|
+
}
|
|
96
88
|
|
|
97
|
-
if (
|
|
98
|
-
console.log(
|
|
89
|
+
if (failed.length === 0) {
|
|
90
|
+
console.log(`✅ fix: ${total} правил — все чисто`)
|
|
99
91
|
return 0
|
|
100
92
|
}
|
|
101
93
|
|
|
102
|
-
console.log(
|
|
103
|
-
console.log(` ${finalFailed.map(r => r.ruleId).join(', ')}`)
|
|
94
|
+
console.log(`❌ fix: ${failed.length} невирішених — ${failed.map(r => r.ruleId).join(', ')}`)
|
|
104
95
|
return 1
|
|
105
96
|
}
|
|
106
97
|
|
|
@@ -116,7 +107,7 @@ function runFixCheck(cwd, ruleFilter = []) {
|
|
|
116
107
|
const r = spawnSync('bun', [N_CURSOR_BIN, '_fix-check', ...ruleFilter], {
|
|
117
108
|
cwd,
|
|
118
109
|
encoding: 'utf8',
|
|
119
|
-
timeout: 120_000
|
|
110
|
+
timeout: 120_000
|
|
120
111
|
})
|
|
121
112
|
const stdout = r.stdout?.trim()
|
|
122
113
|
if (!stdout) return null
|
package/skills/fix/js/t0.mjs
CHANGED
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* T0-auto: детермінований рівень виправлень для n-fix оркестратора.
|
|
3
|
-
*
|
|
4
|
-
* Парсить `output` з `n-cursor fix --json` → застосовує програмний фікс без LLM.
|
|
5
|
-
* Умова застосування: violation-message містить конкретне цільове значення
|
|
6
|
-
* (назву файлу, рядок для вставки, ім'я залежності), яке можна видобути regex.
|
|
7
|
-
*
|
|
8
|
-
* Ієрархія: T0 (rm/create, знаний тип) → T0-auto (parse violation) → T1 (LLM).
|
|
9
|
-
* T0-auto запускається першим у конвергентному циклі; T1 — лише для решти.
|
|
10
|
-
*
|
|
11
|
-
* Публічний API:
|
|
12
|
-
* applyT0Auto(ruleId, violationOutput, cwd) → { applied: boolean, actions: string[] }
|
|
13
|
-
* listT0AutoRules() → string[] (ids що мають хоч один паттерн)
|
|
14
|
-
* runT0AutoCli(args, cwd) → Promise<number> (exit 0=clean, 1=violation)
|
|
15
|
-
*/
|
|
1
|
+
/** @see ./docs/t0.md */
|
|
16
2
|
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
17
3
|
import { join } from 'node:path'
|
|
18
4
|
import { spawnSync } from 'node:child_process'
|
|
@@ -57,7 +43,7 @@ const PATTERNS = [
|
|
|
57
43
|
parsed.recommendations = [...recs, ...toAdd]
|
|
58
44
|
writeFileSync(extPath, JSON.stringify(parsed, null, 2) + '\n', 'utf8')
|
|
59
45
|
return { ok: true, action: `додано до extensions.json: ${toAdd.join(', ')}` }
|
|
60
|
-
}
|
|
46
|
+
}
|
|
61
47
|
},
|
|
62
48
|
|
|
63
49
|
// ── rm-forbidden-file ────────────────────────────────────────────────────────
|
|
@@ -80,8 +66,8 @@ const PATTERNS = [
|
|
|
80
66
|
}
|
|
81
67
|
if (removed.length === 0) return { ok: false, action: 'файлів не знайдено' }
|
|
82
68
|
return { ok: true, action: `видалено: ${removed.join(', ')}` }
|
|
83
|
-
}
|
|
84
|
-
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
85
71
|
]
|
|
86
72
|
|
|
87
73
|
/**
|
|
@@ -116,9 +102,7 @@ export function applyT0Auto(ruleId, violationOutput, cwd) {
|
|
|
116
102
|
* @returns {string[]}
|
|
117
103
|
*/
|
|
118
104
|
export function filterT0AutoRules(failedRules) {
|
|
119
|
-
return failedRules
|
|
120
|
-
.filter(r => PATTERNS.some(p => p.test(r.output)))
|
|
121
|
-
.map(r => r.ruleId)
|
|
105
|
+
return failedRules.filter(r => PATTERNS.some(p => p.test(r.output))).map(r => r.ruleId)
|
|
122
106
|
}
|
|
123
107
|
|
|
124
108
|
// ─── CLI entry-point ──────────────────────────────────────────────────────────
|
|
@@ -141,11 +125,11 @@ export async function runT0AutoCli(args, cwd) {
|
|
|
141
125
|
const verbose = args.includes('--verbose') || args.includes('-v')
|
|
142
126
|
|
|
143
127
|
// 1. Запустити fix --json
|
|
144
|
-
const fixResult = spawnSync(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
)
|
|
128
|
+
const fixResult = spawnSync('bun', [N_CURSOR_BIN, '_fix-check', ...ruleFilter], {
|
|
129
|
+
cwd,
|
|
130
|
+
encoding: 'utf8',
|
|
131
|
+
timeout: 120_000
|
|
132
|
+
})
|
|
149
133
|
const raw = fixResult.stdout?.trim()
|
|
150
134
|
if (!raw) {
|
|
151
135
|
console.error(`n-cursor fix-t0: fix --json повернув порожній stdout`)
|
|
@@ -191,11 +175,11 @@ export async function runT0AutoCli(args, cwd) {
|
|
|
191
175
|
}
|
|
192
176
|
|
|
193
177
|
// 4. Check-gate: перевірити лише ті правила, що ми чіпали
|
|
194
|
-
const recheck = spawnSync(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)
|
|
178
|
+
const recheck = spawnSync('bun', [N_CURSOR_BIN, '_fix-check', ...applied.map(a => a.ruleId)], {
|
|
179
|
+
cwd,
|
|
180
|
+
encoding: 'utf8',
|
|
181
|
+
timeout: 120_000
|
|
182
|
+
})
|
|
199
183
|
const recheckRaw = recheck.stdout?.trim()
|
|
200
184
|
if (!recheckRaw) {
|
|
201
185
|
console.error(`fix-t0: check-gate: fix --json повернув порожній stdout`)
|
|
@@ -223,7 +207,7 @@ export async function runT0AutoCli(args, cwd) {
|
|
|
223
207
|
const total = failed.length
|
|
224
208
|
console.log(
|
|
225
209
|
`✅ fix-t0: ${totalFixed}/${total} правил закрито T0-auto` +
|
|
226
|
-
|
|
210
|
+
(skipped.length > 0 ? `; ${skipped.join(', ')} → T1 (LLM)` : '')
|
|
227
211
|
)
|
|
228
212
|
return 0
|
|
229
213
|
}
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `n-cursor start-check scan|run` — детермінована частина smoke-перевірки
|
|
3
|
-
* bun-монорепо для скілу `n-start-check`.
|
|
4
|
-
*
|
|
5
|
-
* Мотивація: скіл раніше казав LLM-агенту вручну розгортати glob-воркспейси,
|
|
6
|
-
* читати кожен `package.json`, класифікувати `start` (сервер vs CLI), запускати
|
|
7
|
-
* процес із таймаутом через `perl alarm`, інтерпретувати exit-коди й парсити лог
|
|
8
|
-
* на рядки готовності/помилки. Усе це детерміновано — скрипт робить це надійно й
|
|
9
|
-
* крос-платформно (`spawnSync` з `timeout`), а агент лишається тільки з
|
|
10
|
-
* діагностикою: **чому** саме воркспейс упав.
|
|
11
|
-
*
|
|
12
|
-
* `scan` — `[{workspace, name, hasStart, startCmd, type}]`.
|
|
13
|
-
* `run <ws>` — `{workspace, type, exitCode, timedOut, status, ready, firstError,
|
|
14
|
-
* logTail, sideEffects}` (sideEffects — read-only git-diff проти стану до запуску;
|
|
15
|
-
* власне відкат лишається явним кроком скіла).
|
|
16
|
-
*/
|
|
1
|
+
/** @see ./docs/check.md */
|
|
17
2
|
import { spawnSync } from 'node:child_process'
|
|
18
3
|
import { existsSync } from 'node:fs'
|
|
19
4
|
import { readFile } from 'node:fs/promises'
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# check.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл `n-cursor start-check scan|run` є частиною smoke-перевірки для скілу `n-start-check`. Він автоматично аналізує та запускає воркспейси, визначаючи тип стартового скрипта (сервер чи CLI) та перевіряючи успішність його виконання з обмеженням часу. Результати аналізу та запуску записуються у форматі JSON для подальшого аналізу та діагностики проблем.
|
|
6
|
+
|
|
7
|
+
## Поведінка
|
|
8
|
+
|
|
9
|
+
- `classifyStartType` визначає, чи є команда `start` CLI чи довгим процесом (сервером).
|
|
10
|
+
- `scanStartWorkspaces` сканує монорепо, визначаючи воркспейси з `start`, їхні команди та типи.
|
|
11
|
+
- `parseStartLog` витягує готовність, першу помилку та хвіст логу з процесу.
|
|
12
|
+
- `runWorkspaceStart` запускає команду `start` воркспейсу з grace-таймаутом, класифікує результат та повертає інформацію про процес.
|
|
13
|
+
- `runStartCheckCli` виконує `scan` або `run` воркспейсу, приймаючи аргументи командного рядка.
|
|
14
|
+
|
|
15
|
+
## Публічний API
|
|
16
|
+
|
|
17
|
+
- classifyStartType — Визначає, чи є команда `start` сервером або CLI.
|
|
18
|
+
- scanStartWorkspaces — Перевіряє монорепо на наявність команди `start` та її типу у кожному воркспейсі.
|
|
19
|
+
- parseStartLog — Аналізує лог процесу `start` для виявлення статусу, помилок та кінця.
|
|
20
|
+
- runWorkspaceStart — Запускає команду `start` у воркспейсі з обмеженим часом та визначає результат.
|
|
21
|
+
- runStartCheckCli — Запускає CLI для сканування воркспейсів з `start` та індивідуального запуску воркспейсу з подальшим виводом результатів у JSON.
|
|
22
|
+
|
|
23
|
+
## Гарантії поведінки
|
|
24
|
+
|
|
25
|
+
- Скрипт детерміновано виконує перевірку запуску воркспейса.
|
|
26
|
+
- Скрипт використовує `spawnSync` з таймаутом для запуску процесів.
|
|
27
|
+
- Скрипт інтерпретує exit-коди та парсить логи.
|
|
28
|
+
- Результат перевірки воркспейса представлений у форматі `[{workspace, name, hasStart, startCmd, type}]`.
|
|
29
|
+
- У разі невдачі перевірки, скрипт повертає `false` або `null`.
|
|
30
|
+
- Скрипт не кидає винятків.
|
|
31
|
+
- Скрипт не використовує кешування.
|
|
32
|
+
- Скрипт не взаємодіє з мережею.
|
|
33
|
+
- Результат запуску воркспейса представлений у форматі `{workspace, type, exitCode, timedOut, status, ready, firstError, logTail, sideEffects}`.
|
|
34
|
+
- `sideEffects` містить відкат стану воркспейса до його початкового стану перед запуском.
|