@nitra/cursor 4.1.1 → 5.0.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 +20 -0
- package/bin/docs/n-cursor.md +1 -9
- package/bin/n-cursor.js +3 -25
- package/docs/stryker.config.md +37 -0
- package/docs/vitest.config.md +23 -0
- package/package.json +2 -1
- package/rules/docker/lib/docs/docker-mirror.md +1 -1
- package/rules/docker/lib/docs/docker-native-addon.md +1 -1
- package/rules/test/coverage/coverage.mjs +9 -19
- package/rules/test/test.mdc +1 -1
- package/scripts/dispatcher/trace.mjs +4 -16
- package/scripts/docs/build-agents-commands.md +1 -1
- package/scripts/docs/worktree-cli.md +1 -1
- package/scripts/lib/changed-files.mjs +19 -3
- package/scripts/lib/sync-gitignore-worktree.mjs +4 -5
- package/scripts/worktree-cli.mjs +1 -2
- package/skills/docgen/js/docgen-gen.mjs +7 -7
- package/scripts/dispatcher/docs/graph.md +0 -346
- package/scripts/dispatcher/docs/index.md +0 -236
- package/scripts/dispatcher/docs/trace.md +0 -296
- package/scripts/dispatcher/graph/lib/cmd-init.mjs +0 -112
- package/scripts/dispatcher/graph/lib/cmd-invalidate.mjs +0 -96
- package/scripts/dispatcher/graph/lib/cmd-kill.mjs +0 -141
- package/scripts/dispatcher/graph/lib/cmd-plan.mjs +0 -142
- package/scripts/dispatcher/graph/lib/cmd-run.mjs +0 -328
- package/scripts/dispatcher/graph/lib/cmd-scan.mjs +0 -115
- package/scripts/dispatcher/graph/lib/cmd-setup.mjs +0 -111
- package/scripts/dispatcher/graph/lib/cmd-signals.mjs +0 -328
- package/scripts/dispatcher/graph/lib/cmd-status.mjs +0 -131
- package/scripts/dispatcher/graph/lib/cmd-verify.mjs +0 -100
- package/scripts/dispatcher/graph/lib/cmd-watch.mjs +0 -128
- package/scripts/dispatcher/graph/lib/config.mjs +0 -103
- package/scripts/dispatcher/graph/lib/frontmatter.mjs +0 -224
- package/scripts/dispatcher/graph/lib/nnn.mjs +0 -127
- package/scripts/dispatcher/graph/lib/node-state.mjs +0 -157
- package/scripts/dispatcher/graph/lib/scanner.mjs +0 -235
- package/scripts/dispatcher/graph/lib/worktree-ops.mjs +0 -193
- package/scripts/dispatcher/graph-tasks.mjs +0 -92
- package/scripts/dispatcher/graph.mjs +0 -212
- package/scripts/dispatcher/index.mjs +0 -45
- package/scripts/dispatcher/lib/docs/active.md +0 -348
- package/scripts/dispatcher/lib/docs/artifact.md +0 -232
- package/scripts/dispatcher/lib/docs/budget.md +0 -167
- package/scripts/dispatcher/lib/docs/capability.md +0 -196
- package/scripts/dispatcher/lib/docs/commands.md +0 -210
- package/scripts/dispatcher/lib/docs/events.md +0 -183
- package/scripts/dispatcher/lib/docs/executor.md +0 -190
- package/scripts/dispatcher/lib/docs/gate.md +0 -231
- package/scripts/dispatcher/lib/docs/level.md +0 -335
- package/scripts/dispatcher/lib/docs/plan-panel.md +0 -181
- package/scripts/dispatcher/lib/docs/plan.md +0 -200
- package/scripts/dispatcher/lib/docs/planner.md +0 -269
- package/scripts/dispatcher/lib/docs/review.md +0 -255
- package/scripts/dispatcher/lib/docs/reviewer.md +0 -240
- package/scripts/dispatcher/lib/docs/snapshot.md +0 -247
- package/scripts/dispatcher/lib/docs/spec.md +0 -203
- package/scripts/dispatcher/lib/docs/state-store.md +0 -303
- package/scripts/dispatcher/lib/docs/subagent-runner.md +0 -173
- package/scripts/dispatcher/lib/events.mjs +0 -67
- package/scripts/dispatcher/lib/executor.mjs +0 -107
- package/scripts/dispatcher/lib/plan-panel.mjs +0 -76
- package/scripts/dispatcher/lib/state-store.mjs +0 -173
- package/scripts/dispatcher/lib/subagent-runner.mjs +0 -53
- package/scripts/graph/index.mjs +0 -115
- package/scripts/graph/lib/config.mjs +0 -62
- package/scripts/graph/lib/dag.mjs +0 -161
- package/scripts/graph/lib/frontmatter.mjs +0 -70
- package/scripts/graph/lib/nnn.mjs +0 -77
- package/scripts/graph/lib/state.mjs +0 -110
- package/scripts/graph/scan.mjs +0 -64
- package/scripts/graph/status.mjs +0 -86
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
# plan-panel.mjs
|
|
2
|
-
|
|
3
|
-
## Огляд
|
|
4
|
-
|
|
5
|
-
Модуль `plan-panel.mjs` реалізує **agent↔agent brainstorm** — багатоперсональну панель субагентів, які формують спільну відповідь на задачу. Концептуально це поєднання двох підходів:
|
|
6
|
-
|
|
7
|
-
- **bmad party-mode** — кілька «персон» одночасно дивляться на проблему з різних кутів;
|
|
8
|
-
- **superpowers dispatching** — окремий суддя-субагент синтезує думки персон в єдину фінальну відповідь.
|
|
9
|
-
|
|
10
|
-
Панель є спільним механізмом для двох фаз життєвого циклу задачі:
|
|
11
|
-
|
|
12
|
-
- `mode: 'spec'` — генерує 2–3 текстові підходи до розв’язання у форматі Markdown (для фази специфікації);
|
|
13
|
-
- `mode: 'plan'` — генерує покроковий JSON-план (для фази планування).
|
|
14
|
-
|
|
15
|
-
Модуль повторно використовує **runner-інтерфейс Фасада B**, який очікує об’єкт із методом `runStep(prompt, opts) => { ok, output }` (контракт, аналогічний `planner.mjs` і `active.mjs` у тому ж каталозі).
|
|
16
|
-
|
|
17
|
-
Принципово важлива поведінкова характеристика — **HITL (human-in-the-loop)**: панель тільки **повертає синтез** і ніколи не зберігає артефакт самостійно. Апрув людини та фіксація результату належать до контракту `flow.mdc` і виконуються окремими командами (`flow spec` / `flow plan`).
|
|
18
|
-
|
|
19
|
-
## Експорти / API
|
|
20
|
-
|
|
21
|
-
Модуль має один публічний експорт:
|
|
22
|
-
|
|
23
|
-
- `runPanel(input)` — асинхронна функція, що проводить панель і повертає синтез або `null` у разі провалу.
|
|
24
|
-
|
|
25
|
-
Внутрішні артефакти модуля (не експортуються):
|
|
26
|
-
|
|
27
|
-
- константа `PERSONAS` — список персон панелі;
|
|
28
|
-
- функція `judgePrompt(mode, proposals, task)` — формує промпт для судді залежно від режиму.
|
|
29
|
-
|
|
30
|
-
## Функції
|
|
31
|
-
|
|
32
|
-
### `runPanel({ task, cwd, runner, log, mode })`
|
|
33
|
-
|
|
34
|
-
**Сигнатура:**
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
async function runPanel({
|
|
38
|
-
task, // string — опис задачі для персон і судді
|
|
39
|
-
cwd, // string — робочий каталог, передається в runner.runStep як opts.cwd
|
|
40
|
-
runner, // { runStep: (prompt, opts?) => { ok, output } | Promise<{ ok, output }> }
|
|
41
|
-
log, // (msg: string) => void — логер помилок; за замовчуванням console.error
|
|
42
|
-
mode // 'spec' | 'plan' — режим синтезу; за замовчуванням 'plan'
|
|
43
|
-
}) => Promise<
|
|
44
|
-
| { task: string, acceptance?: string }[] // коли mode === 'plan' і JSON парситься
|
|
45
|
-
| string // коли mode === 'spec'
|
|
46
|
-
| null // у разі будь-якого фейлу
|
|
47
|
-
>
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
**Параметри:**
|
|
51
|
-
|
|
52
|
-
- `task` — текстовий опис задачі. Підставляється в системний промпт кожної персони (`<sys>\n\nЗадача: <task>`) та в промпт судді (рядок `Задача: <task>` у кінці).
|
|
53
|
-
- `cwd` — робоча директорія; модуль не використовує її напряму, лише передає у `runner.runStep` через `{ cwd }`.
|
|
54
|
-
- `runner` — обов’язкова ін’єкція з методом `runStep`. Якщо відсутній, функція логуює повідомлення `panel: нема runner — режим --panel недоступний` і повертає `null`.
|
|
55
|
-
- `log` — функція логування непомилкового рівня (фактично використовується тільки для діагностичних повідомлень про помилки). За замовчуванням — `console.error`.
|
|
56
|
-
- `mode` — `'spec'` або `'plan'`. За замовчуванням `'plan'`. Впливає на:
|
|
57
|
-
- інструкцію в `judgePrompt`;
|
|
58
|
-
- формат повернення (текст або розпарсений JSON-масив).
|
|
59
|
-
|
|
60
|
-
**Повертає:**
|
|
61
|
-
|
|
62
|
-
- Для `mode === 'plan'`: масив об’єктів `{ task: string, acceptance?: string }` після успішного `JSON.parse` фрагмента між першим `[` і останнім `]` у відповіді судді.
|
|
63
|
-
- Для `mode === 'spec'`: рядок `judge.output` (Markdown від судді) як є.
|
|
64
|
-
- `null` — у будь-якому з провальних сценаріїв (див. далі).
|
|
65
|
-
|
|
66
|
-
**Сценарії повернення `null`:**
|
|
67
|
-
|
|
68
|
-
1. Не передано `runner` → лог `panel: нема runner — режим --panel недоступний`.
|
|
69
|
-
2. Виклик судді завершився `{ ok: false }` → лог `panel: суддя-синтез завершився помилкою`.
|
|
70
|
-
3. Режим `plan`, але у виводі судді не знайдено JSON-меж (`[` / `]`) або `end < start` → лог `panel: суддя не повернув JSON-план`.
|
|
71
|
-
4. Режим `plan`, JSON знайдено, але `JSON.parse` кинув виняток → лог `panel: невалідний JSON синтезу`. Виняток ловиться `catch {}` і не пробрасується далі.
|
|
72
|
-
|
|
73
|
-
**Алгоритм (по кроках):**
|
|
74
|
-
|
|
75
|
-
1. Перевірити наявність `runner`; якщо нема — залогувати й вийти з `null`.
|
|
76
|
-
2. Паралельно через `Promise.all` опитати всі персони з `PERSONAS`:
|
|
77
|
-
- для кожної персони викликати `runner.runStep(<sys>\n\nЗадача: <task>, { cwd })`;
|
|
78
|
-
- сформувати рядок `### <name>\n<output або "(порожньо)" якщо !ok>`.
|
|
79
|
-
3. Склеїти всі думки персон через `\n\n` і викликати суддю промптом, побудованим у `judgePrompt(mode, proposals, task)`.
|
|
80
|
-
4. Якщо суддя `!ok` — лог і `null`.
|
|
81
|
-
5. Якщо `mode === 'spec'` — повернути `judge.output` без обробки.
|
|
82
|
-
6. Інакше (plan): знайти межі JSON-масиву (`indexOf('[')` та `lastIndexOf(']')`), валідувати їх; розпарсити `slice(start, end + 1)` і повернути; у разі помилки парсингу — лог і `null`.
|
|
83
|
-
|
|
84
|
-
**Side effects:**
|
|
85
|
-
|
|
86
|
-
- **Зовнішні виклики моделей через `runner.runStep`**: персон — `PERSONAS.length` (зараз 3), плюс один виклик судді. Усього 4 запити на один прогін `runPanel`.
|
|
87
|
-
- **Логування**: виключно через переданий `log` (за замовчуванням `console.error`). Інших звернень до stdout/stderr нема.
|
|
88
|
-
- **Файлову систему не чіпає**, мережу безпосередньо не використовує (опосередковано — через `runner`), артефактів не зберігає (HITL — фіксацію робить агент за `flow.mdc`).
|
|
89
|
-
- **Конкурентність**: запити персон виконуються паралельно (`Promise.all` + `map(async …)`); виклик судді — послідовно після завершення всіх персон.
|
|
90
|
-
|
|
91
|
-
### `judgePrompt(mode, proposals, task)`
|
|
92
|
-
|
|
93
|
-
**Сигнатура:**
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
function judgePrompt(
|
|
97
|
-
mode: 'spec' | 'plan',
|
|
98
|
-
proposals: string,
|
|
99
|
-
task: string
|
|
100
|
-
): string
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
**Параметри:**
|
|
104
|
-
|
|
105
|
-
- `mode` — режим синтезу. Визначає інструкцію в «голові» промпту:
|
|
106
|
-
- `'plan'` — три рядки інструкції: вимога ОДНОГО покрокового плану, обмеження ≤ 5 хв на крок із критерієм приймання, вимога повернути **ЛИШЕ JSON-масив без коментарів** у форматі `[{ "task": "...", "acceptance": "..." }, ...]`.
|
|
107
|
-
- `'spec'` (та будь-яке інше значення) — дві інструкції: 2–3 підходи з рекомендацією й коротким дизайном, повернути людино-читабельний Markdown.
|
|
108
|
-
- `proposals` — попередньо склеєні думки персон (зазвичай `proposals.join('\n\n')` із масиву `### name\noutput`).
|
|
109
|
-
- `task` — оригінальний опис задачі (рядок `Задача: <task>` додається в кінець промпту).
|
|
110
|
-
|
|
111
|
-
**Повертає:** рядок промпту, склеєний через `\n` у вигляді:
|
|
112
|
-
|
|
113
|
-
```
|
|
114
|
-
<head-line-1>
|
|
115
|
-
<head-line-2>
|
|
116
|
-
[<head-line-3 — лише для plan>]
|
|
117
|
-
|
|
118
|
-
<proposals>
|
|
119
|
-
|
|
120
|
-
Задача: <task>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
**Side effects:** чиста функція без побічних ефектів і зовнішніх викликів.
|
|
124
|
-
|
|
125
|
-
## Залежності
|
|
126
|
-
|
|
127
|
-
### Зовнішні модулі / пакети
|
|
128
|
-
|
|
129
|
-
- **Жодних `import`** із Node.js core, npm-пакетів чи інших файлів проєкту. Модуль повністю самодостатній.
|
|
130
|
-
|
|
131
|
-
### Глобали
|
|
132
|
-
|
|
133
|
-
- `console.error` — використовується як **дефолтне** значення параметра `log`. Якщо викликач передасть власний `log`, `console` не використовується.
|
|
134
|
-
- `Promise`, `JSON`, `Array.isArray` (не використовується), `String.prototype.indexOf` / `lastIndexOf` / `slice` — стандартні JS-примітиви.
|
|
135
|
-
|
|
136
|
-
### Контрактні (поведінкові) залежності
|
|
137
|
-
|
|
138
|
-
- **Runner-інтерфейс Фасада B**: `runner.runStep(prompt: string, opts?: { cwd: string }) => { ok: boolean, output: string } | Promise<{ ok, output }>`. Цей контракт реалізують сусідні модулі — наприклад, `planner.mjs` і `active.mjs` у каталозі `lib/`.
|
|
139
|
-
- **HITL за `flow.mdc`**: правила Flow вимагають, щоб фіксацію артефакту (`flow spec` / `flow plan`) виконував агент окремою командою; панель сама артефактів не пише.
|
|
140
|
-
|
|
141
|
-
### Внутрішні константи
|
|
142
|
-
|
|
143
|
-
- `PERSONAS` — масив із трьох записів `[name, systemPrompt]`:
|
|
144
|
-
- `architect` — «Запропонуй найчистішу архітектуру розв’язання. Стисло, по суті.»
|
|
145
|
-
- `skeptic` — «Назви ризики, граничні випадки і що може піти не так. Стисло.»
|
|
146
|
-
- `tester` — «Опиши, які тести доведуть коректність. Стисло.»
|
|
147
|
-
|
|
148
|
-
## Потік виконання / Використання
|
|
149
|
-
|
|
150
|
-
### Типовий потік виклику
|
|
151
|
-
|
|
152
|
-
1. Викликач (наприклад, CLI-команда фази `spec` чи `plan`) отримує `runner`, що інкапсулює запуск субагента (модель + транспорт), і поточний `cwd`.
|
|
153
|
-
2. Викликач формулює `task` і обирає `mode` (`'spec'` або `'plan'`).
|
|
154
|
-
3. Викликає `await runPanel({ task, cwd, runner, mode, log? })`.
|
|
155
|
-
4. Внутрішньо панель:
|
|
156
|
-
- паралельно опитує `architect`, `skeptic`, `tester` — кожна повертає текстову думку (`### <name>\n<output>` або `(порожньо)` у разі `!ok`);
|
|
157
|
-
- формує промпт судді через `judgePrompt`, який отримує всі думки + оригінальне завдання;
|
|
158
|
-
- запускає суддю одним викликом `runner.runStep`;
|
|
159
|
-
- на основі `mode` повертає або сирий Markdown (`spec`), або розпарсений JSON-масив кроків (`plan`).
|
|
160
|
-
5. Викликач отримує результат і:
|
|
161
|
-
- для `null` — повідомляє користувача та припиняє/повторює фазу;
|
|
162
|
-
- для `spec` (рядок) — показує користувачу й чекає `flow spec` для фіксації;
|
|
163
|
-
- для `plan` (масив кроків) — передає далі (виконавцю/планеру) і чекає `flow plan` для фіксації.
|
|
164
|
-
|
|
165
|
-
### Очікувані формати виводу судді
|
|
166
|
-
|
|
167
|
-
- **`plan`**: модель повинна повернути рядок, у якому міститься JSON-масив об’єктів вигляду `{ "task": "...", "acceptance": "..." }`. Парсер толерантний до «обгортки» (текст до першого `[` і після останнього `]` ігнорується), але самий JSON має бути валідним.
|
|
168
|
-
- **`spec`**: довільний Markdown-текст; жодних обмежень формату на стороні модуля.
|
|
169
|
-
|
|
170
|
-
### Гарантії та обмеження
|
|
171
|
-
|
|
172
|
-
- **Ніколи не кидає виняток назовні**: усі провальні шляхи завершуються `null` + лог через `log()`. `try/catch` навколо `JSON.parse` ловить навіть synthax-помилки парсингу.
|
|
173
|
-
- **Паралелізм персон**: усі персони запускаються одночасно — це впливає на навантаження `runner` (зараз 3 паралельні виклики моделі) та сумарну затримку (≈ max(persona-latency) + judge-latency, а не сума).
|
|
174
|
-
- **Не зберігає стан** між викликами; кожен виклик `runPanel` повністю самодостатній.
|
|
175
|
-
- **Не валідує внутрішню структуру** елементів JSON-плану — тип `{ task: string, acceptance?: string }[]` зазначений у JSDoc, але рантайм-перевірки полів немає. Викликач відповідає за подальшу валідацію.
|
|
176
|
-
|
|
177
|
-
### Розширюваність
|
|
178
|
-
|
|
179
|
-
- Додати персону: дописати запис у масив `PERSONAS`. Жодних інших змін не потрібно — обхід йде через `map`.
|
|
180
|
-
- Додати режим: розширити умову в `judgePrompt` (та, за потреби, гілку обробки виводу у `runPanel`).
|
|
181
|
-
- Підмінити модель/транспорт: реалізувати інший `runner` із тим самим контрактом `runStep` — модуль працює з будь-яким об’єктом, що задовольняє інтерфейс.
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
# plan.mjs — фаза `plan` команди `flow`
|
|
2
|
-
|
|
3
|
-
## Огляд
|
|
4
|
-
|
|
5
|
-
Модуль `plan.mjs` реалізує фазу **плану** lifecycle-команди `flow` (модель «Пасивний Турнікет», §4 правила `n-flow`). Це чистий «turnstile»-крок: він **не пише кодові артефакти** — лише фіксує план роботи у persistent-стані `.flow.json` поточного worktree та супутньому event-log.
|
|
6
|
-
|
|
7
|
-
Команда CLI:
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
flow plan [--panel] [<plan.md>]
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Високорівнева семантика:
|
|
14
|
-
|
|
15
|
-
1. Резолвить активний `flow`-state (`.flow.json`) у поточному `cwd` або за параметром `branch`.
|
|
16
|
-
2. Перевіряє, що state існує (`flow init` був запущений раніше). Якщо ще не зафіксована spec — попереджає, але не блокує.
|
|
17
|
-
3. Бере **кроки плану** одним із двох способів (brainstorm-моделей з `flow.mdc`):
|
|
18
|
-
- **human↔agent** — читає `docs/plans/<date>-<slug>.md` (передано аргументом або резолвиться по бренчу) і витягує `## Кроки` як список steps.
|
|
19
|
-
- **agent↔agent (`--panel`)** — викликає `runPanel` (панель персон + суддя) через subagent-runner і отримує синтезовані кроки.
|
|
20
|
-
4. Нормалізує steps через `parsePlan` (валідація формату/структури → масив об'єктів кроків).
|
|
21
|
-
5. Read-only `verifyTrace` перевіряє цілісність ланцюга `spec → plan → flow` (front-matter лінки). При розриві лише попереджає у лог (не падає).
|
|
22
|
-
6. Записує transition у state-store: `plan` ← `normalized`, `plan_doc` ← шлях до md (або `null` для panel-режиму), `status` ← `'planned'`. Тип події — `plan`, payload — `{ steps: <кількість> }`.
|
|
23
|
-
7. Логує підсумок і повертає `0`.
|
|
24
|
-
|
|
25
|
-
Будь-яка помилка (відсутність state, відсутність плану-доку, невалідний формат, неможливість підняти runner) логується через `log` і повертає `1`.
|
|
26
|
-
|
|
27
|
-
## Експорти / API
|
|
28
|
-
|
|
29
|
-
| Експорт | Тип | Призначення |
|
|
30
|
-
| ------------------- | ---------------- | ---------------------------------------------------------- |
|
|
31
|
-
| `plan(rest, deps?)` | `async function` | Виконати фазу `plan` поточного `flow`. Іменований експорт. |
|
|
32
|
-
|
|
33
|
-
Інших експортів немає (немає `default`).
|
|
34
|
-
|
|
35
|
-
### Сигнатура
|
|
36
|
-
|
|
37
|
-
```js
|
|
38
|
-
export async function plan(rest, deps = {}) → Promise<number>
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Параметри
|
|
42
|
-
|
|
43
|
-
- `rest: string[]` — позиційні аргументи CLI (після `plan`). Розпізнаються:
|
|
44
|
-
- `--panel` — увімкнути agent↔agent режим (панель персон).
|
|
45
|
-
- перший елемент із суфіксом `.md` — явний шлях до plan-доку (інакше резолвиться через `resolveArtifact(cwd, 'plans', state.branch)`).
|
|
46
|
-
- `deps?: object` — bag залежностей для ін'єкції в тестах:
|
|
47
|
-
- `cwd?: string` — стартовий `cwd` (default: `process.cwd()`).
|
|
48
|
-
- `branch?: string` — підказка про активний flow для авторезолву поза worktree.
|
|
49
|
-
- `log?: (msg: string) => void` — sink для повідомлень (default: `console.error`).
|
|
50
|
-
- `runner?: object` — готовий subagent-runner (для `--panel`). Якщо немає — створюється через `createRunner(deps)`.
|
|
51
|
-
- `trace?: (cwd: string) => number` — кастомна імплементація trace-перевірки для `verifyTrace`.
|
|
52
|
-
- `now?: () => number` — джерело часу для transition-таймстампу (default: `Date.now`).
|
|
53
|
-
|
|
54
|
-
### Повертає
|
|
55
|
-
|
|
56
|
-
`Promise<number>` — exit code:
|
|
57
|
-
|
|
58
|
-
- `0` — план зафіксовано, state переведено у `planned`.
|
|
59
|
-
- `1` — будь-яка помилка: нема state-файлу/active flow, нема plan-доку у режимі без `--panel`, runner не піднявся, `parsePlan` упав на валідації, panel повернув пустий результат.
|
|
60
|
-
|
|
61
|
-
### Side effects
|
|
62
|
-
|
|
63
|
-
- Читає файлову систему: `.flow.json` (через `readState`), доки плану через `readFileSync`, `existsSync`.
|
|
64
|
-
- Пише через `recordTransition`: оновлює `.flow.json` та аппендить запис у `flowEventsPath(cwd)`.
|
|
65
|
-
- Викликає `log(...)` (за замовчуванням — `console.error`).
|
|
66
|
-
- У режимі `--panel` запускає підпроцес/subagent-runner через `createRunner` (мережа/LLM-виклики залежно від реалізації runner).
|
|
67
|
-
|
|
68
|
-
## Функції
|
|
69
|
-
|
|
70
|
-
У файлі **одна** експортована функція `plan` — описана вище. Внутрішніх допоміжних функцій немає.
|
|
71
|
-
|
|
72
|
-
### `plan(rest, deps)` — деталі потоку
|
|
73
|
-
|
|
74
|
-
**Сигнатура:** `(rest: string[], deps?: PlanDeps) → Promise<number>`
|
|
75
|
-
|
|
76
|
-
**Послідовність:**
|
|
77
|
-
|
|
78
|
-
1. **Резолв cwd і логера** — `cwd0 = deps.cwd ?? processCwd()`, `log = deps.log ?? console.error`.
|
|
79
|
-
2. **Резолв активного flow** — `resolveActiveFlowState({ cwd: cwd0, branch: deps.branch }, deps)`.
|
|
80
|
-
- Якщо `resolved.statePath` пустий — log `plan: <error>` і `return 1`.
|
|
81
|
-
- Якщо `resolved.autoResolved === true` — log повідомлення про авторезолв (cwd поза worktree).
|
|
82
|
-
- `cwd` для подальших операцій = `resolved.worktreeDir ?? cwd0`.
|
|
83
|
-
3. **Читання state** — `readState(statePath)`; якщо `null` → `'plan: стану нема — спершу `flow init`'` і `return 1`.
|
|
84
|
-
4. **Soft-гейт по spec** — якщо `state.status !== 'spec'` і `!state.spec_doc` — лише попередження (`'plan: дизайн ще не зафіксовано — рекомендовано спершу `flow spec` (не блокує)'`), виконання продовжується.
|
|
85
|
-
5. **Резолв plan-доку** — `doc = rest.find(a => a.endsWith('.md')) ?? resolveArtifact(cwd, 'plans', state.branch)`.
|
|
86
|
-
6. **Збір steps**:
|
|
87
|
-
- **Гілка `--panel`:**
|
|
88
|
-
- Якщо `deps.runner` не передано — пробує `createRunner(deps)`, на throw → log і `return 1`.
|
|
89
|
-
- `steps = await runPanel({ task: state.branch, cwd, runner, log, mode: 'plan' })`.
|
|
90
|
-
- Якщо `steps` falsy → `return 1` (panel вирішив не комітити).
|
|
91
|
-
- **Гілка з документом:**
|
|
92
|
-
- Якщо `!doc || !existsSync(doc)` → log `'plan: нема docs/plans/<date>-<slug>.md — спершу пройди brainstorm (див. flow.mdc)'` і `return 1`.
|
|
93
|
-
- `steps = extractSteps(readFileSync(doc, 'utf8'))`.
|
|
94
|
-
7. **Нормалізація** — `parsePlan(JSON.stringify(steps))` у try/catch; помилка → log і `return 1`. Результат — `normalized` (масив крокових об'єктів).
|
|
95
|
-
8. **Trace-перевірка** — `verifyTrace(cwd, deps.trace)`; якщо `false` — лише warning з пр **U+26A0**: `'⚠️ plan: trace виявив розрив ланцюга — перевір лінки spec/plan/flow'`.
|
|
96
|
-
9. **Transition** — `recordTransition`:
|
|
97
|
-
- target: `{ statePath, eventsPath: flowEventsPath(cwd) }`.
|
|
98
|
-
- event: `{ type: 'plan', steps: normalized.length }`.
|
|
99
|
-
- reducer: `s => ({ ...s, plan: normalized, plan_doc: doc ?? null, status: 'planned' })`.
|
|
100
|
-
- clock: `deps.now ?? Date.now`.
|
|
101
|
-
10. **Лог підсумку** — `'plan: зафіксовано <N> кроків → status: planned'`, **return 0**.
|
|
102
|
-
|
|
103
|
-
**Інваріанти:**
|
|
104
|
-
|
|
105
|
-
- Функція не модифікує state у разі будь-якого `return 1` до кроку 9.
|
|
106
|
-
- `plan_doc` свідомо може бути `null` (panel-режим без файлу).
|
|
107
|
-
- Trace-розрив **не блокує** запис — це м'який сигнал розробнику.
|
|
108
|
-
|
|
109
|
-
## Залежності
|
|
110
|
-
|
|
111
|
-
### Зовнішні (node:)
|
|
112
|
-
|
|
113
|
-
- `node:fs` — `existsSync`, `readFileSync` (читання plan-доку).
|
|
114
|
-
- `node:process` — `cwd as processCwd` (default-резолв робочої директорії).
|
|
115
|
-
|
|
116
|
-
### Внутрішні модулі lib/
|
|
117
|
-
|
|
118
|
-
- `./artifact.mjs` — `extractSteps` (парс `## Кроки` з md), `resolveArtifact` (резолв `docs/<kind>/<date>-<slug>.md` за бренчем), `verifyTrace` (read-only валідація ланцюга front-matter).
|
|
119
|
-
- `./events.mjs` — `flowEventsPath` (шлях до event-log поточного flow).
|
|
120
|
-
- `./planner.mjs` — `parsePlan` (нормалізація+валідація steps).
|
|
121
|
-
- `./plan-panel.mjs` — `runPanel` (agent↔agent режим, mode: `'plan'`).
|
|
122
|
-
- `./subagent-runner.mjs` — `createRunner` (фабрика runner-а для LLM-викликів у panel).
|
|
123
|
-
- `./state-store.mjs` — `readState` (читання `.flow.json`), `recordTransition` (атомарне оновлення стану + event).
|
|
124
|
-
- `./flow-resolve.mjs` — `resolveActiveFlowState` (резолв активного flow зі `cwd`/`branch`, авторезолв за межами worktree).
|
|
125
|
-
|
|
126
|
-
Жодних transitive npm-пакетів напряму звідси не використовується.
|
|
127
|
-
|
|
128
|
-
## Потік виконання / Використання
|
|
129
|
-
|
|
130
|
-
### Як цей модуль використовується
|
|
131
|
-
|
|
132
|
-
Файл є **обробником субкоманди** dispatcher-а команди `flow`. Очікувано викликається з вищого рівня (CLI-entry, наприклад `npm/scripts/dispatcher/cli.mjs` чи аналогічного), куди передаються розпарсені аргументи. Виклик можна представити так:
|
|
133
|
-
|
|
134
|
-
```js
|
|
135
|
-
import { plan } from './lib/plan.mjs'
|
|
136
|
-
|
|
137
|
-
const code = await plan(process.argv.slice(3))
|
|
138
|
-
process.exit(code)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Сценарії використання
|
|
142
|
-
|
|
143
|
-
1. **Human↔agent (типовий ручний flow)**
|
|
144
|
-
|
|
145
|
-
```
|
|
146
|
-
flow init my-feature # створив .flow.json
|
|
147
|
-
flow spec # зафіксував docs/specs/<...>.md
|
|
148
|
-
# brainstorm у Cursor/Claude → з'явився docs/plans/<date>-my-feature.md з '## Кроки'
|
|
149
|
-
flow plan # резолвить plan-doc за branch, фіксує steps, status → planned
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
2. **Agent↔agent (panel)**
|
|
153
|
-
|
|
154
|
-
```
|
|
155
|
-
flow plan --panel
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Без plan-доку: панель персон через `runPanel({ mode: 'plan' })` синтезує `steps`. Записує `plan_doc: null`.
|
|
159
|
-
|
|
160
|
-
3. **Явний шлях**
|
|
161
|
-
```
|
|
162
|
-
flow plan docs/plans/2026-06-03-my-feature.md
|
|
163
|
-
```
|
|
164
|
-
Перший `*.md`-аргумент у `rest` має пріоритет над `resolveArtifact`.
|
|
165
|
-
|
|
166
|
-
### Стан до/після
|
|
167
|
-
|
|
168
|
-
| Поле `.flow.json` | До `plan` | Після `plan` |
|
|
169
|
-
| ----------------- | --------------------------------- | ---------------------------------------- |
|
|
170
|
-
| `status` | `'spec'` (рекомендовано) або інше | `'planned'` |
|
|
171
|
-
| `plan` | відсутнє/попереднє | `normalized` (масив steps) |
|
|
172
|
-
| `plan_doc` | відсутнє | абсолютний шлях до md або `null` (panel) |
|
|
173
|
-
|
|
174
|
-
В event-log дописується `{ type: 'plan', steps: <N>, ts: <now> }` (фактичний формат — в `recordTransition`).
|
|
175
|
-
|
|
176
|
-
### Exit code контракт
|
|
177
|
-
|
|
178
|
-
- `0` — успіх, можна переходити до наступної фази (`flow code`/`flow review` за конвенцією `n-flow`).
|
|
179
|
-
- `1` — будь-яка помилка; стан **не модифікується**, кроки не записані.
|
|
180
|
-
|
|
181
|
-
### Обробка помилок
|
|
182
|
-
|
|
183
|
-
Жодних винятків назовні: усі try/catch перетворені у `log + return 1`. Виключення — баги нижчих рівнів (`recordTransition`, `readState`) — на цьому шарі не загорнуті, тож можуть пропагуватися як unhandled rejection у CLI.
|
|
184
|
-
|
|
185
|
-
## Rebuild Test
|
|
186
|
-
|
|
187
|
-
Цього модуля **достатньо** для відтворення з нуля за цією специфікацією за умови наявності таких контрактів-сусідів:
|
|
188
|
-
|
|
189
|
-
- `resolveActiveFlowState({ cwd, branch }, deps) → { statePath, worktreeDir?, autoResolved?, label?, error? }`.
|
|
190
|
-
- `readState(statePath) → object | null` з принаймні полями `{ branch, status, spec_doc? }`.
|
|
191
|
-
- `resolveArtifact(cwd, kind, branch) → string | undefined` (абсолютний шлях до `docs/<kind>/<date>-<slug>.md`).
|
|
192
|
-
- `extractSteps(mdString) → Array` (парсить `## Кроки`).
|
|
193
|
-
- `parsePlan(jsonString) → Array` (валідація; кидає `Error` з полем `.message`).
|
|
194
|
-
- `runPanel({ task, cwd, runner, log, mode }) → Promise<Array | undefined>`.
|
|
195
|
-
- `createRunner(deps) → Promise<runner>` (може кидати `Error`).
|
|
196
|
-
- `verifyTrace(cwd, traceFn?) → boolean`.
|
|
197
|
-
- `flowEventsPath(cwd) → string`.
|
|
198
|
-
- `recordTransition({ statePath, eventsPath }, event, reducer, now) → void`.
|
|
199
|
-
|
|
200
|
-
Маючи їх — модуль повністю відновлюється з опису вище: 10 кроків функції `plan`, дві гілки збору `steps`, exit-code-контракт, side-effects через `recordTransition`.
|