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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workflow-ai",
3
- "version": "1.0.58",
3
+ "version": "1.0.60",
4
4
  "description": "AI Agent Workflow Coordinator — kanban-based pipeline for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
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
- return this.trustedAgents.some(pattern => {
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(pattern => this._matchGlob(relativePath, pattern));
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
- this.snapshots.set(absolutePath, this._hashFile(absolutePath));
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
- this.snapshots.set(filePath, this._hashFile(filePath));
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
- // 1. Проверяем файлы из снимка (изменённые или удалённые)
685
- for (const [filePath, originalHash] of this.snapshots) {
686
- const currentHash = this._hashFile(filePath);
687
- if (currentHash !== originalHash) {
688
- violations.push(filePath);
689
- console.warn(`[FileGuard] WARNING: Protected file modified: ${filePath}`);
690
- this._rollbackFile(filePath);
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
- // 2. Обнаруживаем новые файлы в защищённых директориях
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
- this.fileGuard = new FileGuard(protectedPatterns, projectRoot, trustedAgents);
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
 
@@ -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