workflow-ai 1.0.58 → 1.0.60
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/README.md +1 -2
- package/package.json +1 -1
- package/src/init.mjs +0 -1
- package/src/runner.mjs +63 -27
- package/configs/config.yaml +0 -134
- package/configs/pipeline.yaml +0 -579
- package/configs/ticket-movement-rules.yaml +0 -80
- package/src/scripts/archive-plan-tickets.js +0 -102
- package/src/scripts/check-anomalies.js +0 -161
- package/src/scripts/check-conditions.js +0 -258
- package/src/scripts/check-plan-decomposed.js +0 -179
- package/src/scripts/check-plan-templates.js +0 -295
- package/src/scripts/check-relevance.js +0 -308
- package/src/scripts/get-next-id.js +0 -110
- package/src/scripts/move-ticket.js +0 -260
- package/src/scripts/move-to-ready.js +0 -115
- package/src/scripts/move-to-review.js +0 -151
- package/src/scripts/pick-next-task.js +0 -723
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ The `workflow run` command executes a multi-stage pipeline:
|
|
|
81
81
|
6. **move-to-ready** — move tickets from backlog to ready
|
|
82
82
|
7. **pick-next-task** — select next ticket for execution
|
|
83
83
|
8. **move-to-in-progress** — start execution
|
|
84
|
-
9. **check-relevance** — verify ticket is still relevant
|
|
84
|
+
9. **check-relevance** — verify ticket is still relevant (script-based, no LLM)
|
|
85
85
|
10. **execute-task** — perform the work via AI agent
|
|
86
86
|
11. **move-to-review** — submit for review
|
|
87
87
|
12. **review-result** — validate results against Definition of Done
|
|
@@ -108,7 +108,6 @@ Built-in skills for different task types:
|
|
|
108
108
|
| Skill | Description |
|
|
109
109
|
|-------|-------------|
|
|
110
110
|
| `analyze-report` | Report analysis |
|
|
111
|
-
| `check-relevance` | Ticket relevance verification |
|
|
112
111
|
| `coach` | Skill management and improvement |
|
|
113
112
|
| `create-plan` | Plan creation |
|
|
114
113
|
| `create-report` | Report generation |
|
package/package.json
CHANGED
package/src/init.mjs
CHANGED
|
@@ -96,7 +96,6 @@ function generateSkillsTable(workflowRoot) {
|
|
|
96
96
|
'pick-next-task': 'Выбор следующей задачи',
|
|
97
97
|
'decompose-gaps': 'Декомпозиция пробелов',
|
|
98
98
|
'review-result': 'Ревью результата',
|
|
99
|
-
'check-relevance': 'Проверка актуальности',
|
|
100
99
|
'coach': 'Коуч скилов',
|
|
101
100
|
'deep-research': 'Глубокий ресерч'
|
|
102
101
|
};
|
package/src/runner.mjs
CHANGED
|
@@ -543,30 +543,46 @@ class ResultParser {
|
|
|
543
543
|
// FileGuard — защита файлов от несанкционированного изменения агентами
|
|
544
544
|
// ============================================================================
|
|
545
545
|
class FileGuard {
|
|
546
|
-
constructor(patterns, projectRoot = process.cwd(), trustedAgents = []) {
|
|
547
|
-
|
|
548
|
-
this.patterns = (patterns || []).map(p => p.replace(/\\/g, '/'));
|
|
549
|
-
this.enabled = this.patterns.length > 0;
|
|
546
|
+
constructor(patterns, projectRoot = process.cwd(), trustedAgents = [], trustedStages = []) {
|
|
547
|
+
this.enabled = patterns && patterns.length > 0;
|
|
550
548
|
this.snapshots = new Map();
|
|
549
|
+
this.patterns = (patterns || []).map(p => {
|
|
550
|
+
if (typeof p === 'string') {
|
|
551
|
+
return { pattern: p.replace(/\\/g, '/'), mode: 'full' };
|
|
552
|
+
}
|
|
553
|
+
return { pattern: p.pattern.replace(/\\/g, '/'), mode: p.mode || 'full' };
|
|
554
|
+
});
|
|
551
555
|
// projectRoot — корневая директория проекта, относительно которой указаны паттерны
|
|
552
556
|
this.projectRoot = projectRoot;
|
|
553
557
|
// Доверенные агенты — для них FileGuard не откатывает изменения
|
|
554
558
|
this.trustedAgents = trustedAgents;
|
|
559
|
+
// Доверенные стейджи — для них FileGuard не откатывает изменения
|
|
560
|
+
this.trustedStages = trustedStages;
|
|
555
561
|
}
|
|
556
562
|
|
|
557
563
|
/**
|
|
558
|
-
* Проверяет, является ли агент доверенным (пропускает FileGuard)
|
|
564
|
+
* Проверяет, является ли агент или стейдж доверенным (пропускает FileGuard)
|
|
559
565
|
* Поддерживает glob-паттерны: "script-*" соответствует "script-move", "script-pick" и т.д.
|
|
560
566
|
* @param {string} agentId - ID агента
|
|
567
|
+
* @param {string} [stageId] - ID стейджа (опционально)
|
|
561
568
|
* @returns {boolean}
|
|
562
569
|
*/
|
|
563
|
-
isTrusted(agentId) {
|
|
564
|
-
|
|
570
|
+
isTrusted(agentId, stageId) {
|
|
571
|
+
// Проверка по trustedAgents (glob-паттерны)
|
|
572
|
+
const agentMatch = this.trustedAgents.some(pattern => {
|
|
565
573
|
if (pattern.endsWith('*')) {
|
|
566
574
|
return agentId.startsWith(pattern.slice(0, -1));
|
|
567
575
|
}
|
|
568
576
|
return agentId === pattern;
|
|
569
577
|
});
|
|
578
|
+
if (agentMatch) return true;
|
|
579
|
+
|
|
580
|
+
// Проверка по trustedStages (точное совпадение)
|
|
581
|
+
if (stageId && this.trustedStages.includes(stageId)) {
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return false;
|
|
570
586
|
}
|
|
571
587
|
|
|
572
588
|
/**
|
|
@@ -575,9 +591,8 @@ class FileGuard {
|
|
|
575
591
|
* @returns {boolean}
|
|
576
592
|
*/
|
|
577
593
|
matchesProtected(filePath) {
|
|
578
|
-
// Получаем относительный путь от projectRoot для сопоставления с паттерном
|
|
579
594
|
const relativePath = path.relative(this.projectRoot, filePath).replace(/\\/g, '/');
|
|
580
|
-
return this.patterns.some(
|
|
595
|
+
return this.patterns.some(p => this._matchGlob(relativePath, p.pattern));
|
|
581
596
|
}
|
|
582
597
|
|
|
583
598
|
/**
|
|
@@ -615,10 +630,12 @@ class FileGuard {
|
|
|
615
630
|
* Рекурсивно получает все файлы в директории
|
|
616
631
|
* @param {string} dir - Директория для сканирования
|
|
617
632
|
* @returns {string[]} Список путей к файлам (нормализованных через /)
|
|
618
|
-
|
|
633
|
+
*/
|
|
619
634
|
_getAllFiles(dir) {
|
|
620
635
|
const files = [];
|
|
621
636
|
if (!fs.existsSync(dir)) return files;
|
|
637
|
+
const stats = fs.statSync(dir);
|
|
638
|
+
if (!stats.isDirectory()) return files;
|
|
622
639
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
623
640
|
for (const entry of entries) {
|
|
624
641
|
const entryPath = path.join(dir, entry.name).replace(/\\/g, '/');
|
|
@@ -649,20 +666,26 @@ class FileGuard {
|
|
|
649
666
|
if (!this.enabled) return;
|
|
650
667
|
this.snapshots.clear();
|
|
651
668
|
|
|
652
|
-
for (const pattern of this.patterns) {
|
|
669
|
+
for (const { pattern, mode } of this.patterns) {
|
|
653
670
|
if (!pattern.includes('*')) {
|
|
654
|
-
// Прямой путь к файлу — преобразуем в абсолютный
|
|
655
671
|
const absolutePath = path.resolve(this.projectRoot, pattern);
|
|
656
672
|
if (fs.existsSync(absolutePath)) {
|
|
657
|
-
|
|
673
|
+
if (mode === 'structure') {
|
|
674
|
+
this.snapshots.set(absolutePath, { hash: this._hashFile(absolutePath), content: fs.readFileSync(absolutePath, null), mode: 'structure' });
|
|
675
|
+
} else {
|
|
676
|
+
this.snapshots.set(absolutePath, this._hashFile(absolutePath));
|
|
677
|
+
}
|
|
658
678
|
}
|
|
659
679
|
} else {
|
|
660
|
-
// Glob-паттерн: сканируем базовую директорию относительно projectRoot
|
|
661
680
|
const baseDir = path.resolve(this.projectRoot, this._getBaseDir(pattern));
|
|
662
681
|
const files = this._getAllFiles(baseDir);
|
|
663
682
|
for (const filePath of files) {
|
|
664
683
|
if (this.matchesProtected(filePath)) {
|
|
665
|
-
|
|
684
|
+
if (mode === 'structure') {
|
|
685
|
+
this.snapshots.set(filePath, { hash: this._hashFile(filePath), content: fs.readFileSync(filePath, null), mode: 'structure' });
|
|
686
|
+
} else {
|
|
687
|
+
this.snapshots.set(filePath, this._hashFile(filePath));
|
|
688
|
+
}
|
|
666
689
|
}
|
|
667
690
|
}
|
|
668
691
|
}
|
|
@@ -681,18 +704,30 @@ class FileGuard {
|
|
|
681
704
|
|
|
682
705
|
const violations = [];
|
|
683
706
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
707
|
+
for (const [filePath, snapshot] of this.snapshots) {
|
|
708
|
+
const mode = snapshot.mode || 'full';
|
|
709
|
+
if (mode === 'structure') {
|
|
710
|
+
if (!fs.existsSync(filePath)) {
|
|
711
|
+
violations.push(filePath);
|
|
712
|
+
console.warn(`[FileGuard] WARNING: Protected file deleted: ${filePath}`);
|
|
713
|
+
try {
|
|
714
|
+
fs.writeFileSync(filePath, snapshot.content);
|
|
715
|
+
console.warn(`[FileGuard] WARNING: Restored deleted file: ${filePath}`);
|
|
716
|
+
} catch (err) {
|
|
717
|
+
console.error(`[FileGuard] ERROR: Failed to restore ${filePath}: ${err.message}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
} else {
|
|
721
|
+
const currentHash = this._hashFile(filePath);
|
|
722
|
+
if (currentHash !== snapshot) {
|
|
723
|
+
violations.push(filePath);
|
|
724
|
+
console.warn(`[FileGuard] WARNING: Protected file modified: ${filePath}`);
|
|
725
|
+
this._rollbackFile(filePath);
|
|
726
|
+
}
|
|
691
727
|
}
|
|
692
728
|
}
|
|
693
729
|
|
|
694
|
-
|
|
695
|
-
for (const pattern of this.patterns) {
|
|
730
|
+
for (const { pattern, mode } of this.patterns) {
|
|
696
731
|
const baseDir = pattern.includes('*')
|
|
697
732
|
? path.resolve(this.projectRoot, this._getBaseDir(pattern))
|
|
698
733
|
: path.resolve(this.projectRoot, pattern);
|
|
@@ -848,8 +883,8 @@ class StageExecutor {
|
|
|
848
883
|
console.log(` Skill: ${stage.skill}`);
|
|
849
884
|
}
|
|
850
885
|
|
|
851
|
-
// Снимаем snapshot защищённых файлов перед выполнением (кроме trusted agents)
|
|
852
|
-
const skipGuard = this.fileGuard && this.fileGuard.isTrusted(agentId);
|
|
886
|
+
// Снимаем snapshot защищённых файлов перед выполнением (кроме trusted agents и trusted stages)
|
|
887
|
+
const skipGuard = this.fileGuard && this.fileGuard.isTrusted(agentId, stageId);
|
|
853
888
|
if (this.fileGuard && !skipGuard) {
|
|
854
889
|
this.fileGuard.takeSnapshot();
|
|
855
890
|
}
|
|
@@ -1195,7 +1230,8 @@ class PipelineRunner {
|
|
|
1195
1230
|
// Инициализация FileGuard для защиты файлов от изменений агентами
|
|
1196
1231
|
const protectedPatterns = this.pipeline.protected_files || [];
|
|
1197
1232
|
const trustedAgents = this.pipeline.trusted_agents || [];
|
|
1198
|
-
|
|
1233
|
+
const trustedStages = this.pipeline.trusted_stages || [];
|
|
1234
|
+
this.fileGuard = new FileGuard(protectedPatterns, projectRoot, trustedAgents, trustedStages);
|
|
1199
1235
|
this.projectRoot = projectRoot;
|
|
1200
1236
|
this.currentExecutor = null;
|
|
1201
1237
|
|
package/configs/config.yaml
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# Конфигурация системы координации агентов
|
|
2
|
-
# Версия: 1.0
|
|
3
|
-
|
|
4
|
-
version: "1.0"
|
|
5
|
-
|
|
6
|
-
# Информация о проекте
|
|
7
|
-
project:
|
|
8
|
-
name: ""
|
|
9
|
-
description: ""
|
|
10
|
-
created_at: ""
|
|
11
|
-
|
|
12
|
-
# Типы задач и их настройки
|
|
13
|
-
task_types:
|
|
14
|
-
arch:
|
|
15
|
-
prefix: ARCH
|
|
16
|
-
description: "Задачи планирования и архитектуры"
|
|
17
|
-
|
|
18
|
-
impl:
|
|
19
|
-
prefix: IMPL
|
|
20
|
-
description: "Написание и изменение кода"
|
|
21
|
-
|
|
22
|
-
fix:
|
|
23
|
-
prefix: FIX
|
|
24
|
-
description: "Исправление ошибок и багов"
|
|
25
|
-
|
|
26
|
-
review:
|
|
27
|
-
prefix: REVIEW
|
|
28
|
-
description: "Ревью кода и документации"
|
|
29
|
-
|
|
30
|
-
docs:
|
|
31
|
-
prefix: DOCS
|
|
32
|
-
description: "Создание и обновление документации"
|
|
33
|
-
|
|
34
|
-
admin:
|
|
35
|
-
prefix: ADMIN
|
|
36
|
-
description: "Административные задачи"
|
|
37
|
-
|
|
38
|
-
qa:
|
|
39
|
-
prefix: QA
|
|
40
|
-
description: "Написание тестов, проверка coverage, E2E-сценарии, тест-планы, валидация качества"
|
|
41
|
-
|
|
42
|
-
coach:
|
|
43
|
-
prefix: COACH
|
|
44
|
-
description: "Улучшение и аудит скилов (обрабатывается коуч-скилом)"
|
|
45
|
-
|
|
46
|
-
human:
|
|
47
|
-
prefix: HUMAN
|
|
48
|
-
description: "Задачи, требующие действия человека"
|
|
49
|
-
|
|
50
|
-
rsh:
|
|
51
|
-
prefix: RSH
|
|
52
|
-
description: "Deep Research — глубокие исследования, анализ рынка/конкурентов/трендов, сбор бенчмарков, текстовые отчёты"
|
|
53
|
-
|
|
54
|
-
# Приоритеты задач
|
|
55
|
-
priorities:
|
|
56
|
-
1:
|
|
57
|
-
name: critical
|
|
58
|
-
description: "Блокирует всю работу"
|
|
59
|
-
color: "#FF0000"
|
|
60
|
-
2:
|
|
61
|
-
name: high
|
|
62
|
-
description: "Важно для прогресса"
|
|
63
|
-
color: "#FF6600"
|
|
64
|
-
3:
|
|
65
|
-
name: medium
|
|
66
|
-
description: "Стандартный приоритет"
|
|
67
|
-
color: "#FFCC00"
|
|
68
|
-
4:
|
|
69
|
-
name: low
|
|
70
|
-
description: "Когда будет время"
|
|
71
|
-
color: "#00CC00"
|
|
72
|
-
5:
|
|
73
|
-
name: someday
|
|
74
|
-
description: "Может быть когда-нибудь"
|
|
75
|
-
color: "#CCCCCC"
|
|
76
|
-
|
|
77
|
-
# Статусы задач
|
|
78
|
-
statuses:
|
|
79
|
-
backlog:
|
|
80
|
-
folder: backlog
|
|
81
|
-
description: "Ожидает выполнения условий"
|
|
82
|
-
ready:
|
|
83
|
-
folder: ready
|
|
84
|
-
description: "Готов к выполнению"
|
|
85
|
-
in-progress:
|
|
86
|
-
folder: in-progress
|
|
87
|
-
description: "В работе"
|
|
88
|
-
blocked:
|
|
89
|
-
folder: blocked
|
|
90
|
-
description: "Заблокирована"
|
|
91
|
-
review:
|
|
92
|
-
folder: review
|
|
93
|
-
description: "На проверке"
|
|
94
|
-
done:
|
|
95
|
-
folder: done
|
|
96
|
-
description: "Завершена"
|
|
97
|
-
|
|
98
|
-
# Типы условий для задач
|
|
99
|
-
condition_types:
|
|
100
|
-
tasks_completed:
|
|
101
|
-
description: "Указанные задачи должны быть выполнены"
|
|
102
|
-
|
|
103
|
-
date_after:
|
|
104
|
-
description: "Дата должна быть после указанной"
|
|
105
|
-
|
|
106
|
-
date_before:
|
|
107
|
-
description: "Дата должна быть до указанной"
|
|
108
|
-
|
|
109
|
-
file_exists:
|
|
110
|
-
description: "Указанный файл должен существовать"
|
|
111
|
-
|
|
112
|
-
file_not_exists:
|
|
113
|
-
description: "Указанный файл не должен существовать"
|
|
114
|
-
|
|
115
|
-
file_contains:
|
|
116
|
-
description: "Файл должен содержать указанный текст"
|
|
117
|
-
|
|
118
|
-
manual_approval:
|
|
119
|
-
description: "Требуется ручное подтверждение"
|
|
120
|
-
|
|
121
|
-
# Настройки путей
|
|
122
|
-
paths:
|
|
123
|
-
plans: plans
|
|
124
|
-
tickets: tickets
|
|
125
|
-
reports: reports
|
|
126
|
-
templates: templates
|
|
127
|
-
plan_templates: plans/templates
|
|
128
|
-
archive: archive
|
|
129
|
-
|
|
130
|
-
# Настройки отчётности
|
|
131
|
-
reporting:
|
|
132
|
-
daily_report_time: "18:00"
|
|
133
|
-
sprint_length_days: 7
|
|
134
|
-
archive_completed_after_days: 30
|