@@ -150,7 +150,7 @@ Individual tasks within a workflow. Available block types:
150150- ` Shell ` - Run shell commands
151151- ` LLMCall ` - Call AI/LLM APIs
152152- ` HttpCall ` - Make HTTP requests
153- - ` CreateFile ` , ` ReadFile ` , ` RenderTemplate ` - File operations
153+ - ` CreateFile ` , ` ReadFile ` , ` EditFile ` , ` RenderTemplate ` - File operations
154154- ` Workflow ` - Call other workflows (composition)
155155- ` Prompt ` - Interactive user prompts
156156- ` ReadJSONState ` , ` WriteJSONState ` , ` MergeJSONState ` - State management
@@ -422,6 +422,124 @@ blocks:
422422{{blocks.process_files.metadata.count}} # Total count
423423` ` `
424424
425+ # ## ✏️ Deterministic File Editing
426+
427+ The `EditFile` block provides powerful deterministic file editing with multiple operation strategies. Unlike simple find-replace, EditFile supports atomic transactions, backup creation, and comprehensive diff generation.
428+
429+ **Key Features:**
430+ - ✅ 6 operation types (replace_text, replace_lines, insert_lines, delete_lines, patch, regex_replace)
431+ - ✅ Atomic transactions (all-or-nothing by default)
432+ - ✅ Automatic backup creation before editing
433+ - ✅ Dry-run mode for previewing changes
434+ - ✅ Comprehensive diff generation
435+ - ✅ Path traversal protection
436+
437+ **Simple Text Replacement:**
438+ ` ` ` yaml
439+ - id: update_config
440+ type: EditFile
441+ inputs:
442+ path: "config.yaml"
443+ operations:
444+ - type: replace_text
445+ old_text: "debug: false"
446+ new_text: "debug: true"
447+ ` ` `
448+
449+ **Line-Based Operations:**
450+ ` ` ` yaml
451+ - id: update_imports
452+ type: EditFile
453+ inputs:
454+ path: "main.py"
455+ operations:
456+ # Replace specific lines
457+ - type: replace_lines
458+ start_line: 5
459+ end_line: 7
460+ content: |
461+ import os
462+ import sys
463+ from pathlib import Path
464+
465+ # Insert new lines
466+ - type: insert_lines
467+ line: 10
468+ content: |
469+ # New feature initialization
470+ initialize_feature()
471+
472+ # Delete lines
473+ - type: delete_lines
474+ start_line: 20
475+ end_line: 25
476+ ` ` `
477+
478+ **Regex Patterns:**
479+ ` ` ` yaml
480+ - id: update_version
481+ type: EditFile
482+ inputs:
483+ path: "pyproject.toml"
484+ operations:
485+ - type: regex_replace
486+ pattern: 'version = "(\d +\.\d +)\.\d +"'
487+ replacement: 'version = "\1 .99"'
488+ flags: [MULTILINE]
489+ ` ` `
490+
491+ **Patch Application:**
492+ ` ` ` yaml
493+ - id: apply_patch
494+ type: EditFile
495+ inputs:
496+ path: "source.py"
497+ operations:
498+ - type: patch
499+ patch: |
500+ --- a/source.py
501+ +++ b/source.py
502+ @@ -1,3 +1,3 @@
503+ -def old_function():
504+ +def new_function():
505+ pass
506+ ` ` `
507+
508+ **Advanced Options:**
509+ ` ` ` yaml
510+ - id: safe_edit
511+ type: EditFile
512+ inputs:
513+ path: "important.txt"
514+ operations: [...]
515+ create_if_missing: true # Create file if doesn't exist
516+ backup: true # Create .bak backup (default: true)
517+ dry_run: false # Preview without applying (default: false)
518+ atomic: true # All-or-nothing (default: true)
519+ encoding: "utf-8" # File encoding
520+ ` ` `
521+
522+ **Outputs:**
523+ ` ` ` yaml
524+ # Access edit statistics
525+ {{blocks.update_file.outputs.operations_applied}} # Number of operations
526+ {{blocks.update_file.outputs.lines_added}} # Lines added
527+ {{blocks.update_file.outputs.lines_removed}} # Lines removed
528+ {{blocks.update_file.outputs.lines_modified}} # Lines modified
529+ {{blocks.update_file.outputs.diff}} # Unified diff
530+ {{blocks.update_file.outputs.backup_path}} # Backup file path
531+ {{blocks.update_file.succeeded}} # Success status
532+ {{blocks.update_file.outputs.errors}} # Error messages (if any)
533+ ` ` `
534+
535+ **Use Cases:**
536+ - Configuration file updates
537+ - Code refactoring and renaming
538+ - Version number bumps
539+ - Import statement management
540+ - Automated code fixes
541+ - Multi-file synchronized updates
542+
425543# ## 🔄 Workflow Composition
426544
427545Build complex workflows from simple reusable pieces :
@@ -887,6 +1005,90 @@ outputs:
8871005 type: str
8881006` ` `
8891007
1008+ # ## Example 5: Automated Version Bump
1009+
1010+ ` ` ` yaml
1011+ name: version-bump
1012+ description: Automatically bump version in multiple files
1013+ tags: [versioning, automation]
1014+
1015+ inputs:
1016+ bump_type:
1017+ type: str
1018+ description: Type of version bump (major, minor, patch)
1019+ default: "patch"
1020+
1021+ blocks:
1022+ - id: get_current_version
1023+ type: Shell
1024+ inputs:
1025+ command: "grep -oP 'version = \"\\ K[^\" ]+' pyproject.toml"
1026+
1027+ - id: calculate_new_version
1028+ type: Shell
1029+ depends_on: [get_current_version]
1030+ inputs:
1031+ command: |
1032+ VERSION="{{blocks.get_current_version.outputs.stdout}}"
1033+ IFS='.' read -r major minor patch <<< "$VERSION"
1034+ case "{{inputs.bump_type}}" in
1035+ major) echo "$((major + 1)).0.0" ;;
1036+ minor) echo "$major.$((minor + 1)).0" ;;
1037+ patch) echo "$major.$minor.$((patch + 1))" ;;
1038+ esac
1039+
1040+ - id: update_pyproject
1041+ type: EditFile
1042+ depends_on: [calculate_new_version]
1043+ inputs:
1044+ path: "pyproject.toml"
1045+ backup: true
1046+ operations:
1047+ - type: regex_replace
1048+ pattern: 'version = "\d +\.\d +\.\d +"'
1049+ replacement: 'version = "{{blocks.calculate_new_version.outputs.stdout}}"'
1050+
1051+ - id: update_init
1052+ type: EditFile
1053+ depends_on: [calculate_new_version]
1054+ inputs:
1055+ path: "src/__init__.py"
1056+ backup: true
1057+ operations:
1058+ - type: regex_replace
1059+ pattern: '__version__ = "\d +\.\d +\.\d +"'
1060+ replacement: '__version__ = "{{blocks.calculate_new_version.outputs.stdout}}"'
1061+
1062+ - id: update_changelog
1063+ type: EditFile
1064+ depends_on: [calculate_new_version]
1065+ inputs:
1066+ path: "CHANGELOG.md"
1067+ backup: true
1068+ operations:
1069+ - type: insert_lines
1070+ line: 3
1071+ content: |
1072+ ## [{{blocks.calculate_new_version.outputs.stdout}}] - $(date +%Y-%m-%d)
1073+
1074+ ### Changed
1075+ - Version bump to {{blocks.calculate_new_version.outputs.stdout}}
1076+
1077+ outputs:
1078+ old_version:
1079+ value: "{{blocks.get_current_version.outputs.stdout}}"
1080+ type: str
1081+ new_version:
1082+ value: "{{blocks.calculate_new_version.outputs.stdout}}"
1083+ type: str
1084+ files_updated:
1085+ value: "{{blocks.update_pyproject.succeeded}} and {{blocks.update_init.succeeded}} and {{blocks.update_changelog.succeeded}}"
1086+ type: bool
1087+ total_changes:
1088+ value: "{{blocks.update_pyproject.outputs.lines_modified}} + {{blocks.update_init.outputs.lines_modified}} + {{blocks.update_changelog.outputs.lines_added}}"
1089+ type: num
1090+ ` ` `
1091+
8901092---
8911093
8921094# # Development
@@ -955,7 +1157,7 @@ workflows-mcp/
9551157│ ├── engine/ # Workflow execution engine
9561158│ │ ├── executor_base.py # Base executor class
9571159│ │ ├── executors_core.py # Shell, Workflow executors
958- │ │ ├── executors_file.py # File operation executors
1160+ │ │ ├── executors_file.py # File operation executors (CreateFile, ReadFile, EditFile, RenderTemplate)
9591161│ │ ├── executors_http.py # HTTP call executor
9601162│ │ ├── executors_llm.py # LLM call executor
9611163│ │ ├── executors_state.py # State management executors
0 commit comments