workflow-ai 1.0.27 → 1.0.29

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.
@@ -31,10 +31,7 @@ rules:
31
31
  - condition: skipped
32
32
  to_dir: done
33
33
  reason: "review skipped"
34
- - condition: failed
35
- to_dir: backlog
36
- reason: "review failed"
37
- # null (нет ревью) — не перемещаем
34
+ # failed и null — не перемещаем
38
35
 
39
36
  # Правила для done/
40
37
  done:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workflow-ai",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "description": "AI Agent Workflow Coordinator — kanban-based pipeline for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/src/runner.mjs CHANGED
@@ -655,13 +655,16 @@ class FileGuard {
655
655
  }
656
656
 
657
657
  /**
658
- * Проверяет целостность защищённых файлов и откатывает несанкционированные изменения
658
+ * Проверяет целостность защищённых файлов и откатывает несанкционированные изменения.
659
+ * Обнаруживает как изменения/удаления существующих файлов, так и создание новых.
659
660
  * @returns {string[]} Список изменённых (и откаченных) файлов
660
661
  */
661
662
  checkAndRollback() {
662
- if (!this.enabled || this.snapshots.size === 0) return [];
663
+ if (!this.enabled) return [];
663
664
 
664
665
  const violations = [];
666
+
667
+ // 1. Проверяем файлы из снимка (изменённые или удалённые)
665
668
  for (const [filePath, originalHash] of this.snapshots) {
666
669
  const currentHash = this._hashFile(filePath);
667
670
  if (currentHash !== originalHash) {
@@ -671,6 +674,22 @@ class FileGuard {
671
674
  }
672
675
  }
673
676
 
677
+ // 2. Обнаруживаем новые файлы в защищённых директориях
678
+ for (const pattern of this.patterns) {
679
+ const baseDir = pattern.includes('*')
680
+ ? path.resolve(this.projectRoot, this._getBaseDir(pattern))
681
+ : path.resolve(this.projectRoot, pattern);
682
+
683
+ const currentFiles = this._getAllFiles(baseDir);
684
+ for (const filePath of currentFiles) {
685
+ if (this.matchesProtected(filePath) && !this.snapshots.has(filePath)) {
686
+ violations.push(filePath);
687
+ console.warn(`[FileGuard] WARNING: New file in protected area: ${filePath}`);
688
+ this._removeNewFile(filePath);
689
+ }
690
+ }
691
+ }
692
+
674
693
  if (violations.length > 0) {
675
694
  console.warn(`[FileGuard] WARNING: Rolled back ${violations.length} protected file(s): ${violations.join(', ')}`);
676
695
  } else {
@@ -680,6 +699,19 @@ class FileGuard {
680
699
  return violations;
681
700
  }
682
701
 
702
+ /**
703
+ * Удаляет файл, созданный агентом в защищённой директории
704
+ * @param {string} filePath - Путь к файлу
705
+ */
706
+ _removeNewFile(filePath) {
707
+ try {
708
+ fs.unlinkSync(filePath);
709
+ console.warn(`[FileGuard] WARNING: Removed unauthorized new file: ${filePath}`);
710
+ } catch (err) {
711
+ console.error(`[FileGuard] ERROR: Failed to remove ${filePath}: ${err.message}`);
712
+ }
713
+ }
714
+
683
715
  /**
684
716
  * Откатывает файл к последнему зафиксированному состоянию через git
685
717
  * @param {string} filePath - Путь к файлу
@@ -908,8 +940,14 @@ class StageExecutor {
908
940
  const err = new Error(`Agent exited with code ${code}`);
909
941
  err.code = 'NON_ZERO_EXIT';
910
942
  err.exitCode = code;
943
+ err.stderr = stderr;
911
944
  if (this.logger) {
912
945
  this.logger.error(`Agent exited with code ${code}`, stageId);
946
+ if (stderr.trim()) {
947
+ for (const line of stderr.trim().split('\n')) {
948
+ this.logger.error(` stderr: ${line}`, stageId);
949
+ }
950
+ }
913
951
  }
914
952
  reject(err);
915
953
  return;