workflow-ai 1.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.
@@ -0,0 +1,422 @@
1
+ # =============================================================================
2
+ # Pipeline Configuration — формат конфигурации пайплайна
3
+ # =============================================================================
4
+ # Описывает цепочку этапов (stages), доступных агентов, goto-логику переходов,
5
+ # retry-стратегии и настройки выполнения.
6
+ #
7
+ # Runner читает этот файл и автономно оркестрирует работу:
8
+ # node .workflow/src/runner.mjs --config .workflow/config/pipeline.yaml
9
+ #
10
+ # Переменные в goto.params:
11
+ # $result.* — поля из результата текущего stage
12
+ # $context.* — переменные из секции context (обновляются через params)
13
+ # $counter.* — значения счётчиков (обновляются стейджами type: update-counter)
14
+ # =============================================================================
15
+
16
+ pipeline:
17
+ name: "default"
18
+ version: "1.0"
19
+
20
+ # ===========================================================================
21
+ # Агенты (agents)
22
+ # ===========================================================================
23
+ # Каждый агент — это CLI-инструмент, который runner вызывает через child_process.
24
+ # Формат:
25
+ # <agent-id>:
26
+ # command: <string> — исполняемый файл (должен быть в PATH)
27
+ # args: <string[]> — аргументы командной строки
28
+ # workdir: <string> — рабочая директория (по умолчанию ".")
29
+ # description: <string> — описание агента (опционально)
30
+ #
31
+ # Runner добавляет промпт последним аргументом автоматически.
32
+ # ===========================================================================
33
+ agents:
34
+ claude-sonnet:
35
+ command: "claude"
36
+ args: ["--model", "claude-sonnet-4-6", "--permission-mode", "acceptEdits", "-p"]
37
+ workdir: "."
38
+ description: "Claude Sonnet — быстрая модель для простых задач"
39
+
40
+ claude-opus:
41
+ command: "claude"
42
+ args: ["--model", "claude-opus-4-6", "--permission-mode", "acceptEdits", "-p"]
43
+ workdir: "."
44
+ description: "Claude Opus — мощная модель для сложных задач"
45
+
46
+ qwen-code:
47
+ command: "qwen"
48
+ args: ["-y", "-d", "-p"]
49
+ workdir: "."
50
+ description: "Qwen Code — альтернативный агент для независимой проверки"
51
+
52
+ kilo-code:
53
+ command: "kilo"
54
+ args: ["-m", "kilo/minimax/minimax-m2.5:free", "--agent", "orchestrator", "run"]
55
+ workdir: "."
56
+ description: "Kilo Code — агент с мульти-режимами (architect, code, debug)"
57
+
58
+ kilo-glm:
59
+ command: "kilo"
60
+ args: ["-m", "zai-coding-plan/glm-4.7", "--agent", "orchestrator", "run"]
61
+ workdir: "."
62
+ description: "Kilo GLM"
63
+
64
+ kilo-deepseek:
65
+ command: "kilo"
66
+ args: ["-m", "deepseek/deepseek-reasoner", "--agent", "code", "run"]
67
+ workdir: "."
68
+ description: "Kilo deepseek"
69
+
70
+ script-move:
71
+ command: "node"
72
+ args: [".workflow/src/scripts/move-ticket.js"]
73
+ workdir: "."
74
+ description: "Скрипт для перемещения тикетов между директориями"
75
+
76
+ script-pick:
77
+ command: "node"
78
+ args: [".workflow/src/scripts/pick-next-task.js"]
79
+ workdir: "."
80
+ description: "Скрипт для выбора следующего тикета из ready/"
81
+
82
+ script-move-ready:
83
+ command: "node"
84
+ args: [".workflow/src/scripts/move-to-ready.js"]
85
+ workdir: "."
86
+ description: "Скрипт для перемещения тикетов из backlog/ в ready/"
87
+
88
+ script-move-to-review:
89
+ command: "node"
90
+ args: [".workflow/src/scripts/move-to-review.js"]
91
+ workdir: "."
92
+ description: "Скрипт для перемещения завершённых тикетов из in-progress/ в review/"
93
+
94
+ # ===========================================================================
95
+ # Этапы (stages)
96
+ # ===========================================================================
97
+ # Каждый stage описывает один шаг пайплайна.
98
+ # Формат:
99
+ # <stage-id>:
100
+ # description: <string> — что делает этот этап
101
+ # agent: <agent-id> — какой агент выполняет (ссылка на agents.*)
102
+ # skill: <skill-id> — какой skill использовать (файл .workflow/src/skills/<skill>/SKILL.md)
103
+ # goto: — логика переходов по результату
104
+ # <status>: — ключ = статус результата агента
105
+ # stage: <stage-id> — следующий stage
106
+ # params: <map> — параметры, передаваемые в context следующего stage
107
+ # default: — fallback если статус не совпал ни с одним ключом
108
+ #
109
+ # Специальные значения stage:
110
+ # "end" — завершение пайплайна
111
+ #
112
+ # Встроенный тип стейджа (type: update-counter):
113
+ # Инкрементирует счётчик без вызова агента.
114
+ # type: update-counter — встроенный обработчик runner'а
115
+ # counter: <string> — имя счётчика
116
+ # max: <int> — при достижении max возвращает статус "max_reached"
117
+ # goto:
118
+ # default: <stage> — следующий стейдж (счётчик < max)
119
+ # max_reached: <stage> — стейдж при достижении max
120
+ # ===========================================================================
121
+ stages:
122
+
123
+ # -------------------------------------------------------------------------
124
+ # 0. pick-first-task
125
+ # Выбирает следующую задачу из ready/ по приоритету и зависимостям
126
+ # Если ready/ пуст — проверяет review/
127
+ # -------------------------------------------------------------------------
128
+ pick-first-task:
129
+ description: "Выбрать задачу задачу из ready по приоритету"
130
+ agent: script-pick
131
+ goto:
132
+ found:
133
+ stage: move-to-in-progress
134
+ params:
135
+ ticket_id: "$result.ticket_id"
136
+ target: in-progress
137
+ in_review:
138
+ stage: review-result
139
+ params:
140
+ ticket_id: "$result.ticket_id"
141
+ attempt: "1"
142
+ completed_in_progress:
143
+ stage: move-to-review
144
+ params:
145
+ ticket_id: "$result.ticket_id"
146
+ empty: check-conditions
147
+ error: create-report
148
+
149
+ # -------------------------------------------------------------------------
150
+ # 1. check-conditions
151
+ # Проверяет условия тикетов в backlog/, выводит список готовых
152
+ # -------------------------------------------------------------------------
153
+ check-conditions:
154
+ description: "Проверить условия тикетов в backlog, вывести готовые"
155
+ agent: kilo-code
156
+ fallback_agent: qwen-code
157
+ skill: check-conditions
158
+ goto:
159
+ has_ready:
160
+ stage: move-to-ready
161
+ params:
162
+ ready_tickets: "$result.ready_tickets"
163
+ default: create-report
164
+
165
+ # -------------------------------------------------------------------------
166
+ # 1b. move-to-ready
167
+ # Перемещает тикеты из backlog/ в ready/ (по списку из check-conditions)
168
+ # -------------------------------------------------------------------------
169
+ move-to-ready:
170
+ description: "Переместить готовые тикеты из backlog в ready"
171
+ agent: script-move-ready
172
+ timeout: 120
173
+ goto:
174
+ default: pick-next-task
175
+
176
+ # -------------------------------------------------------------------------
177
+ # 2. pick-next-task
178
+ # Выбирает следующую задачу из ready/ по приоритету и зависимостям
179
+ # Если ready/ пуст — проверяет review/
180
+ # -------------------------------------------------------------------------
181
+ pick-next-task:
182
+ description: "Выбрать следующую задачу из ready по приоритету"
183
+ agent: script-pick
184
+ goto:
185
+ found:
186
+ stage: move-to-in-progress
187
+ params:
188
+ ticket_id: "$result.ticket_id"
189
+ target: in-progress
190
+ in_review:
191
+ stage: review-result
192
+ params:
193
+ ticket_id: "$result.ticket_id"
194
+ attempt: "1"
195
+ completed_in_progress:
196
+ stage: move-to-review
197
+ params:
198
+ ticket_id: "$result.ticket_id"
199
+ empty: check-conditions
200
+ error: create-report
201
+
202
+ # -------------------------------------------------------------------------
203
+ # 2b. move-to-in-progress
204
+ # Перемещает тикет из ready/ в in-progress/ перед выполнением
205
+ # -------------------------------------------------------------------------
206
+ move-to-in-progress:
207
+ description: "Переместить тикет из ready в in-progress"
208
+ agent: script-move
209
+ timeout: 120
210
+ goto:
211
+ default:
212
+ stage: execute-task
213
+ params:
214
+ ticket_id: "$context.ticket_id"
215
+ error:
216
+ stage: execute-task
217
+ params:
218
+ ticket_id: "$context.ticket_id"
219
+
220
+
221
+ # -------------------------------------------------------------------------
222
+ # 3. execute-task
223
+ # Выполняет выбранную задачу (код, документация, план и т.д.)
224
+ # -------------------------------------------------------------------------
225
+ execute-task:
226
+ description: "Выполнить выбранную задачу"
227
+ agent: qwen-code
228
+ fallback_agent: kilo-deepseek
229
+ skill: execute-task
230
+ goto:
231
+ default:
232
+ stage: move-to-review
233
+ params:
234
+ ticket_id: "$context.ticket_id"
235
+ attempt: "$counter.task_attempts"
236
+ target: review
237
+ error:
238
+ stage: move-to-review
239
+ params:
240
+ ticket_id: "$context.ticket_id"
241
+ attempt: "$counter.task_attempts"
242
+ target: review
243
+
244
+ # -------------------------------------------------------------------------
245
+ # 3b. move-to-review
246
+ # Перемещает тикет в review/ после выполнения задачи
247
+ # -------------------------------------------------------------------------
248
+ move-to-review:
249
+ description: "Переместить тикет в review после выполнения"
250
+ agent: script-move-to-review
251
+ timeout: 120
252
+ goto:
253
+ default:
254
+ stage: review-result
255
+ params:
256
+ ticket_id: "$context.ticket_id"
257
+ attempt: "$counter.task_attempts"
258
+
259
+ # -------------------------------------------------------------------------
260
+ # 4. review-result
261
+ # Проверяет результат выполнения на соответствие DoD тикета.
262
+ # Использует независимый агент (qwen-code) для объективной оценки.
263
+ # При failed — переходит на increment-task-attempts для учёта попытки.
264
+ # -------------------------------------------------------------------------
265
+ review-result:
266
+ description: "Проверить результат выполнения на соответствие DoD тикета"
267
+ agent: claude-sonnet
268
+ fallback_agent: qwen-code
269
+ skill: review-result
270
+ goto:
271
+ passed:
272
+ stage: move-ticket
273
+ params:
274
+ ticket_id: "$context.ticket_id"
275
+ target: done
276
+ failed:
277
+ stage: increment-task-attempts
278
+ params:
279
+ ticket_id: "$context.ticket_id"
280
+ default:
281
+ stage: move-ticket
282
+ params:
283
+ ticket_id: "$context.ticket_id"
284
+ target: blocked
285
+
286
+ # -------------------------------------------------------------------------
287
+ # 4b. increment-task-attempts
288
+ # Отдельный стейдж обновления счётчика попыток выполнения тикета.
289
+ # При достижении max — блокирует тикет. Иначе — возвращает в очередь.
290
+ # -------------------------------------------------------------------------
291
+ increment-task-attempts:
292
+ description: "Обновить счётчик попыток выполнения тикета"
293
+ type: update-counter
294
+ counter: task_attempts
295
+ max: 3
296
+ goto:
297
+ default:
298
+ stage: move-ticket
299
+ params:
300
+ ticket_id: "$context.ticket_id"
301
+ target: ready
302
+ max_reached:
303
+ stage: move-ticket
304
+ params:
305
+ ticket_id: "$context.ticket_id"
306
+ target: blocked
307
+
308
+ # -------------------------------------------------------------------------
309
+ # 5. move-ticket
310
+ # Перемещает тикет в целевую директорию (done/ или blocked/)
311
+ # -------------------------------------------------------------------------
312
+ move-ticket:
313
+ description: "Переместить тикет (done / blocked)"
314
+ agent: script-move
315
+ timeout: 120
316
+ goto:
317
+ default: pick-next-task
318
+
319
+ # -------------------------------------------------------------------------
320
+ # 6. create-report
321
+ # Создаёт итоговый отчёт по выполненным задачам текущего цикла
322
+ # -------------------------------------------------------------------------
323
+ create-report:
324
+ description: "Создать итоговый отчёт по выполненным задачам"
325
+ agent: claude-sonnet
326
+ fallback_agent: qwen-code
327
+ skill: create-report
328
+ goto:
329
+ default:
330
+ stage: analyze-report
331
+ params:
332
+ report_id: "$result.report_id"
333
+
334
+ # -------------------------------------------------------------------------
335
+ # 7. analyze-report
336
+ # Анализирует отчёт: план выполнен полностью или есть пробелы?
337
+ # При has_gaps — переходит на increment-plan-iterations для учёта итерации.
338
+ # -------------------------------------------------------------------------
339
+ analyze-report:
340
+ description: "Проанализировать отчёт: план выполнен полностью?"
341
+ agent: claude-opus
342
+ fallback_agent: qwen-code
343
+ skill: analyze-report
344
+ goto:
345
+ completed: end
346
+ has_gaps:
347
+ stage: increment-plan-iterations
348
+ params:
349
+ gaps: "$result.gaps"
350
+ report_id: "$context.report_id"
351
+
352
+ # -------------------------------------------------------------------------
353
+ # 7b. increment-plan-iterations
354
+ # Отдельный стейдж обновления счётчика итераций анализа плана.
355
+ # При достижении max — завершает пайплайн. Иначе — декомпозирует пробелы.
356
+ # -------------------------------------------------------------------------
357
+ increment-plan-iterations:
358
+ description: "Обновить счётчик итераций анализа плана"
359
+ type: update-counter
360
+ counter: plan_iterations
361
+ max: 3
362
+ goto:
363
+ default:
364
+ stage: decompose-gaps
365
+ max_reached: end
366
+
367
+ # -------------------------------------------------------------------------
368
+ # 8. decompose-gaps
369
+ # Декомпозирует недочёты из анализа отчёта в новые тикеты (backlog/)
370
+ # -------------------------------------------------------------------------
371
+ decompose-gaps:
372
+ description: "Декомпозировать недочёты из отчёта в новые тикеты"
373
+ agent: claude-sonnet
374
+ fallback_agent: qwen-code
375
+ skill: decompose-gaps
376
+ goto:
377
+ default: check-conditions
378
+
379
+ # ===========================================================================
380
+ # Точка входа (entry)
381
+ # ===========================================================================
382
+ # Stage, с которого начинается выполнение пайплайна.
383
+ # ===========================================================================
384
+ entry: pick-first-task
385
+
386
+ # ===========================================================================
387
+ # Контекст (context)
388
+ # ===========================================================================
389
+ # Переменные, доступные всем stages через $context.*.
390
+ # Runner автоматически обновляет context через params в goto-переходах.
391
+ # Начальные значения задаются здесь или через CLI:
392
+ # node runner.mjs --plan PLAN-003
393
+ # ===========================================================================
394
+ context:
395
+ plan_id: "" # ID плана (задаётся при запуске)
396
+
397
+ # ===========================================================================
398
+ # Защита файлов (protected_files)
399
+ # ===========================================================================
400
+ # Glob-паттерны файлов, в которые агентам запрещена запись.
401
+ # Runner проверяет эти паттерны и блокирует изменения.
402
+ # Пути указываются относительно корня проекта (без префикса .workflow/)
403
+ # ===========================================================================
404
+ protected_files:
405
+ - ".workflow/plans/**" # Планы — только для чтения
406
+ - ".workflow/config/**" # Конфигурация
407
+
408
+ # ===========================================================================
409
+ # Настройки выполнения (execution)
410
+ # ===========================================================================
411
+ # Параметры, управляющие поведением runner'а.
412
+ #
413
+ # max_steps: — абсолютный лимит шагов за один запуск (защита от зацикливания)
414
+ # delay_between_stages: — пауза между этапами в секундах
415
+ # timeout_per_stage: — таймаут на один этап в секундах (по истечении — ошибка)
416
+ # log_file: — путь к файлу логов (относительно корня проекта)
417
+ # ===========================================================================
418
+ execution:
419
+ max_steps: 100
420
+ delay_between_stages: 5
421
+ timeout_per_stage: 600
422
+ log_file: ".workflow/logs/pipeline.log"
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "workflow-ai",
3
+ "version": "1.0.0",
4
+ "description": "AI Agent Workflow Coordinator — kanban-based pipeline for AI coding agents",
5
+ "type": "module",
6
+ "bin": { "workflow": "./bin/workflow.mjs" },
7
+ "files": [
8
+ "bin/",
9
+ "src/cli.mjs",
10
+ "src/init.mjs",
11
+ "src/runner.mjs",
12
+ "src/lib/",
13
+ "src/scripts/",
14
+ "src/skills/",
15
+ "templates/",
16
+ "configs/",
17
+ "agent-templates/"
18
+ ],
19
+ "engines": { "node": ">=18.0.0" },
20
+ "scripts": {
21
+ "test": "node --test src/tests/*.test.mjs"
22
+ },
23
+ "dependencies": { "js-yaml": "^4.1.0" }
24
+ }
package/src/cli.mjs ADDED
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { initProject } from './init.mjs';
4
+ import { runPipeline } from './runner.mjs';
5
+ import { readFileSync } from 'node:fs';
6
+ import { join, dirname } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const pkgPath = join(__dirname, '..', 'package.json');
11
+
12
+ const HELP_TEXT = `workflow-ai v1.0.0
13
+
14
+ Usage:
15
+ workflow init [path] [--force] Initialize .workflow/ in target directory
16
+ workflow run [options] Run the AI pipeline
17
+ workflow help Show this help
18
+ workflow version Show version
19
+
20
+ Run options:
21
+ --plan <plan> Plan ID to execute
22
+ --config <path> Config file path
23
+ --project <path> Project root (default: auto-detect)
24
+ `;
25
+
26
+ function showHelp() {
27
+ console.log(HELP_TEXT);
28
+ }
29
+
30
+ function showVersion() {
31
+ try {
32
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
33
+ console.log(`workflow-ai v${pkg.version}`);
34
+ } catch (err) {
35
+ console.error('Error reading package.json:', err.message);
36
+ process.exit(1);
37
+ }
38
+ }
39
+
40
+ function parseArgs(argv) {
41
+ const args = { _: [] };
42
+ let i = 0;
43
+ while (i < argv.length) {
44
+ const arg = argv[i];
45
+ if (arg.startsWith('--')) {
46
+ const key = arg.slice(2);
47
+ if (i + 1 < argv.length && !argv[i + 1].startsWith('--')) {
48
+ args[key] = argv[i + 1];
49
+ i += 2;
50
+ } else {
51
+ args[key] = true;
52
+ i += 1;
53
+ }
54
+ } else {
55
+ args._.push(arg);
56
+ i += 1;
57
+ }
58
+ }
59
+ return args;
60
+ }
61
+
62
+ async function runInit(args) {
63
+ const targetPath = args._[0] || process.cwd();
64
+ const force = args.force === true;
65
+ const result = initProject(targetPath, { force });
66
+
67
+ if (result.errors && result.errors.length > 0) {
68
+ console.error('Errors:', result.errors.join(', '));
69
+ process.exit(1);
70
+ }
71
+
72
+ console.log('✅ Initialization completed:');
73
+ result.steps.forEach(step => console.log(` • ${step}`));
74
+ if (result.warnings && result.warnings.length > 0) {
75
+ console.warn('Warnings:', result.warnings.join(', '));
76
+ }
77
+ }
78
+
79
+ async function runRun(args) {
80
+ // Expose wf's node_modules to child ESM scripts via a custom loader
81
+ const loaderPath = join(__dirname, 'wf-loader.mjs');
82
+ const loaderUrl = `file:///${loaderPath.replace(/\\/g, '/')}`;
83
+ process.env.NODE_OPTIONS = process.env.NODE_OPTIONS
84
+ ? `${process.env.NODE_OPTIONS} --import ${loaderUrl}`
85
+ : `--import ${loaderUrl}`;
86
+
87
+ const argv = [];
88
+ if (args.plan) {
89
+ argv.push('--plan', args.plan);
90
+ }
91
+ if (args.config) {
92
+ argv.push('--config', args.config);
93
+ }
94
+ if (args.project) {
95
+ argv.push('--project', args.project);
96
+ }
97
+
98
+ await runPipeline(argv);
99
+ }
100
+
101
+ export function run(argv) {
102
+ const args = parseArgs(argv);
103
+ const command = args._.shift();
104
+
105
+ switch (command) {
106
+ case 'init':
107
+ runInit(args);
108
+ break;
109
+ case 'run':
110
+ runRun(args);
111
+ break;
112
+ case 'help':
113
+ showHelp();
114
+ break;
115
+ case 'version':
116
+ showVersion();
117
+ break;
118
+ default:
119
+ if (!command) {
120
+ showHelp();
121
+ } else {
122
+ console.error(`Unknown command: ${command}`);
123
+ console.error('Run "workflow help" for usage.');
124
+ process.exit(1);
125
+ }
126
+ }
127
+ }
128
+
129
+ if (import.meta.url === `file://${process.argv[1]}`) {
130
+ run(process.argv.slice(2));
131
+ }