mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 22:04:56 +00:00
refactor(workflow): convert all plugins to class/struct + factory pattern
- Python: class extending NodeExecutor + factory.py (80+ plugins) - TypeScript: class implements NodeExecutor + factory.ts (7 groups, 116 classes) - Go: struct with methods + factory.go (36 plugins) - Rust: struct impl NodeExecutor trait + factory.rs (54 plugins) - Mojo: struct + factory.mojo (11 plugins) All package.json files now include: - files array listing source files - metadata.class/struct field - metadata.entrypoint field This enables a unified plugin loading system across all languages with no import side effects (Spring-style DI pattern). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
"""Factory for BranchCondition plugin."""
|
||||
|
||||
from .utils_branch_condition import BranchCondition
|
||||
|
||||
|
||||
def create():
|
||||
return BranchCondition()
|
||||
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@metabuilder/utils_branch_condition",
|
||||
"version": "1.0.0",
|
||||
"description": "utils_branch_condition plugin",
|
||||
"description": "Evaluate branch conditions using various comparison modes",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["utils", "workflow", "plugin"],
|
||||
"main": "utils_branch_condition.py",
|
||||
"files": ["utils_branch_condition.py", "factory.py"],
|
||||
"metadata": {
|
||||
"plugin_type": "utils.branch_condition",
|
||||
"category": "utils"
|
||||
"category": "utils",
|
||||
"class": "BranchCondition",
|
||||
"entrypoint": "execute"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,35 @@
|
||||
"""Workflow plugin: branch condition."""
|
||||
|
||||
import re
|
||||
|
||||
from ...base import NodeExecutor
|
||||
|
||||
def run(_runtime, inputs):
|
||||
"""Evaluate a branch condition."""
|
||||
value = inputs.get("value")
|
||||
mode = inputs.get("mode", "is_truthy")
|
||||
compare = inputs.get("compare", "")
|
||||
decision = False
|
||||
|
||||
if mode == "is_empty":
|
||||
decision = not value if isinstance(value, (list, dict, str)) else not bool(value)
|
||||
elif mode == "is_truthy":
|
||||
decision = bool(value)
|
||||
elif mode == "equals":
|
||||
decision = str(value) == compare
|
||||
elif mode == "not_equals":
|
||||
decision = str(value) != compare
|
||||
elif mode == "contains":
|
||||
decision = compare in str(value)
|
||||
elif mode == "regex":
|
||||
decision = bool(re.search(compare, str(value)))
|
||||
class BranchCondition(NodeExecutor):
|
||||
"""Evaluate a branch condition using various comparison modes."""
|
||||
|
||||
return {"result": decision}
|
||||
node_type = "utils.branch_condition"
|
||||
category = "utils"
|
||||
description = "Evaluate a branch condition using various comparison modes"
|
||||
|
||||
def execute(self, inputs, runtime=None):
|
||||
"""Evaluate a branch condition."""
|
||||
value = inputs.get("value")
|
||||
mode = inputs.get("mode", "is_truthy")
|
||||
compare = inputs.get("compare", "")
|
||||
decision = False
|
||||
|
||||
if mode == "is_empty":
|
||||
decision = not value if isinstance(value, (list, dict, str)) else not bool(value)
|
||||
elif mode == "is_truthy":
|
||||
decision = bool(value)
|
||||
elif mode == "equals":
|
||||
decision = str(value) == compare
|
||||
elif mode == "not_equals":
|
||||
decision = str(value) != compare
|
||||
elif mode == "contains":
|
||||
decision = compare in str(value)
|
||||
elif mode == "regex":
|
||||
decision = bool(re.search(compare, str(value)))
|
||||
|
||||
return {"result": decision}
|
||||
|
||||
7
workflow/plugins/python/utils/utils_check_mvp/factory.py
Normal file
7
workflow/plugins/python/utils/utils_check_mvp/factory.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""Factory for CheckMvp plugin."""
|
||||
|
||||
from .utils_check_mvp import CheckMvp
|
||||
|
||||
|
||||
def create():
|
||||
return CheckMvp()
|
||||
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@metabuilder/utils_check_mvp",
|
||||
"version": "1.0.0",
|
||||
"description": "utils_check_mvp plugin",
|
||||
"description": "Check if the MVP section in ROADMAP.md is completed",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["utils", "workflow", "plugin"],
|
||||
"main": "utils_check_mvp.py",
|
||||
"files": ["utils_check_mvp.py", "factory.py"],
|
||||
"metadata": {
|
||||
"plugin_type": "utils.check_mvp",
|
||||
"category": "utils"
|
||||
"category": "utils",
|
||||
"class": "CheckMvp",
|
||||
"entrypoint": "execute"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,46 @@
|
||||
"""Workflow plugin: check if MVP is reached."""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from ...base import NodeExecutor
|
||||
|
||||
def _is_mvp_reached() -> bool:
|
||||
|
||||
class CheckMvp(NodeExecutor):
|
||||
"""Check if the MVP section in ROADMAP.md is completed."""
|
||||
if not os.path.exists("ROADMAP.md"):
|
||||
|
||||
node_type = "utils.check_mvp"
|
||||
category = "utils"
|
||||
description = "Check if the MVP section in ROADMAP.md is completed"
|
||||
|
||||
def execute(self, inputs, runtime=None):
|
||||
"""Check if the MVP section in ROADMAP.md is completed."""
|
||||
mvp_reached = self._is_mvp_reached()
|
||||
return {"mvp_reached": mvp_reached}
|
||||
|
||||
def _is_mvp_reached(self) -> bool:
|
||||
"""Check if the MVP section in ROADMAP.md is completed."""
|
||||
if not os.path.exists("ROADMAP.md"):
|
||||
return False
|
||||
|
||||
with open("ROADMAP.md", "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
# Find the header line containing (MVP)
|
||||
header_match = re.search(r"^## .*?\(MVP\).*?$", content, re.MULTILINE | re.IGNORECASE)
|
||||
if not header_match:
|
||||
return False
|
||||
|
||||
start_pos = header_match.end()
|
||||
next_header_match = re.search(r"^## ", content[start_pos:], re.MULTILINE)
|
||||
if next_header_match:
|
||||
mvp_section = content[start_pos : start_pos + next_header_match.start()]
|
||||
else:
|
||||
mvp_section = content[start_pos:]
|
||||
|
||||
if "[ ]" in mvp_section:
|
||||
return False
|
||||
if "[x]" in mvp_section:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
with open("ROADMAP.md", "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
# Find the header line containing (MVP)
|
||||
header_match = re.search(r"^## .*?\(MVP\).*?$", content, re.MULTILINE | re.IGNORECASE)
|
||||
if not header_match:
|
||||
return False
|
||||
|
||||
start_pos = header_match.end()
|
||||
next_header_match = re.search(r"^## ", content[start_pos:], re.MULTILINE)
|
||||
if next_header_match:
|
||||
mvp_section = content[start_pos : start_pos + next_header_match.start()]
|
||||
else:
|
||||
mvp_section = content[start_pos:]
|
||||
|
||||
if "[ ]" in mvp_section:
|
||||
return False
|
||||
if "[x]" in mvp_section:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def run(_runtime, _inputs):
|
||||
"""Check if the MVP section in ROADMAP.md is completed."""
|
||||
mvp_reached = _is_mvp_reached()
|
||||
return {"mvp_reached": mvp_reached}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
"""Factory for FilterList plugin."""
|
||||
|
||||
from .utils_filter_list import FilterList
|
||||
|
||||
|
||||
def create():
|
||||
return FilterList()
|
||||
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@metabuilder/utils_filter_list",
|
||||
"version": "1.0.0",
|
||||
"description": "utils_filter_list plugin",
|
||||
"description": "Filter items using a match mode",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["utils", "workflow", "plugin"],
|
||||
"main": "utils_filter_list.py",
|
||||
"files": ["utils_filter_list.py", "factory.py"],
|
||||
"metadata": {
|
||||
"plugin_type": "utils.filter_list",
|
||||
"category": "utils"
|
||||
"category": "utils",
|
||||
"class": "FilterList",
|
||||
"entrypoint": "execute"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,43 @@
|
||||
"""Workflow plugin: filter list."""
|
||||
|
||||
import re
|
||||
|
||||
from ...base import NodeExecutor
|
||||
|
||||
def run(_runtime, inputs):
|
||||
|
||||
class FilterList(NodeExecutor):
|
||||
"""Filter items using a match mode."""
|
||||
items = inputs.get("items", [])
|
||||
if not isinstance(items, list):
|
||||
items = [items] if items else []
|
||||
|
||||
mode = inputs.get("mode", "contains")
|
||||
pattern = inputs.get("pattern", "")
|
||||
filtered = []
|
||||
node_type = "utils.filter_list"
|
||||
category = "utils"
|
||||
description = "Filter items using a match mode"
|
||||
|
||||
for item in items:
|
||||
candidate = str(item)
|
||||
matched = False
|
||||
if mode == "contains":
|
||||
matched = pattern in candidate
|
||||
elif mode == "regex":
|
||||
matched = bool(re.search(pattern, candidate))
|
||||
elif mode == "equals":
|
||||
matched = candidate == pattern
|
||||
elif mode == "not_equals":
|
||||
matched = candidate != pattern
|
||||
elif mode == "starts_with":
|
||||
matched = candidate.startswith(pattern)
|
||||
elif mode == "ends_with":
|
||||
matched = candidate.endswith(pattern)
|
||||
if matched:
|
||||
filtered.append(item)
|
||||
def execute(self, inputs, runtime=None):
|
||||
"""Filter items using a match mode."""
|
||||
items = inputs.get("items", [])
|
||||
if not isinstance(items, list):
|
||||
items = [items] if items else []
|
||||
|
||||
return {"items": filtered}
|
||||
mode = inputs.get("mode", "contains")
|
||||
pattern = inputs.get("pattern", "")
|
||||
filtered = []
|
||||
|
||||
for item in items:
|
||||
candidate = str(item)
|
||||
matched = False
|
||||
if mode == "contains":
|
||||
matched = pattern in candidate
|
||||
elif mode == "regex":
|
||||
matched = bool(re.search(pattern, candidate))
|
||||
elif mode == "equals":
|
||||
matched = candidate == pattern
|
||||
elif mode == "not_equals":
|
||||
matched = candidate != pattern
|
||||
elif mode == "starts_with":
|
||||
matched = candidate.startswith(pattern)
|
||||
elif mode == "ends_with":
|
||||
matched = candidate.endswith(pattern)
|
||||
if matched:
|
||||
filtered.append(item)
|
||||
|
||||
return {"items": filtered}
|
||||
|
||||
7
workflow/plugins/python/utils/utils_map_list/factory.py
Normal file
7
workflow/plugins/python/utils/utils_map_list/factory.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""Factory for MapList plugin."""
|
||||
|
||||
from .utils_map_list import MapList
|
||||
|
||||
|
||||
def create():
|
||||
return MapList()
|
||||
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@metabuilder/utils_map_list",
|
||||
"version": "1.0.0",
|
||||
"description": "utils_map_list plugin",
|
||||
"description": "Map items to formatted strings",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["utils", "workflow", "plugin"],
|
||||
"main": "utils_map_list.py",
|
||||
"files": ["utils_map_list.py", "factory.py"],
|
||||
"metadata": {
|
||||
"plugin_type": "utils.map_list",
|
||||
"category": "utils"
|
||||
"category": "utils",
|
||||
"class": "MapList",
|
||||
"entrypoint": "execute"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
"""Workflow plugin: map list."""
|
||||
|
||||
from ...base import NodeExecutor
|
||||
|
||||
def run(_runtime, inputs):
|
||||
|
||||
class MapList(NodeExecutor):
|
||||
"""Map items to formatted strings."""
|
||||
items = inputs.get("items", [])
|
||||
if not isinstance(items, list):
|
||||
items = [items] if items else []
|
||||
|
||||
template = inputs.get("template", "{item}")
|
||||
mapped = []
|
||||
node_type = "utils.map_list"
|
||||
category = "utils"
|
||||
description = "Map items to formatted strings"
|
||||
|
||||
for item in items:
|
||||
try:
|
||||
mapped.append(template.format(item=item))
|
||||
except Exception:
|
||||
mapped.append(str(item))
|
||||
def execute(self, inputs, runtime=None):
|
||||
"""Map items to formatted strings."""
|
||||
items = inputs.get("items", [])
|
||||
if not isinstance(items, list):
|
||||
items = [items] if items else []
|
||||
|
||||
return {"items": mapped}
|
||||
template = inputs.get("template", "{item}")
|
||||
mapped = []
|
||||
|
||||
for item in items:
|
||||
try:
|
||||
mapped.append(template.format(item=item))
|
||||
except Exception:
|
||||
mapped.append(str(item))
|
||||
|
||||
return {"items": mapped}
|
||||
|
||||
7
workflow/plugins/python/utils/utils_not/factory.py
Normal file
7
workflow/plugins/python/utils/utils_not/factory.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""Factory for Not plugin."""
|
||||
|
||||
from .utils_not import Not
|
||||
|
||||
|
||||
def create():
|
||||
return Not()
|
||||
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@metabuilder/utils_not",
|
||||
"version": "1.0.0",
|
||||
"description": "utils_not plugin",
|
||||
"description": "Negate a boolean value",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["utils", "workflow", "plugin"],
|
||||
"main": "utils_not.py",
|
||||
"files": ["utils_not.py", "factory.py"],
|
||||
"metadata": {
|
||||
"plugin_type": "utils.not",
|
||||
"category": "utils"
|
||||
"category": "utils",
|
||||
"class": "Not",
|
||||
"entrypoint": "execute"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
"""Workflow plugin: boolean not."""
|
||||
|
||||
from ...base import NodeExecutor
|
||||
|
||||
def run(_runtime, inputs):
|
||||
|
||||
class Not(NodeExecutor):
|
||||
"""Negate a boolean value."""
|
||||
value = inputs.get("value")
|
||||
return {"result": not bool(value)}
|
||||
|
||||
node_type = "utils.not"
|
||||
category = "utils"
|
||||
description = "Negate a boolean value"
|
||||
|
||||
def execute(self, inputs, runtime=None):
|
||||
"""Negate a boolean value."""
|
||||
value = inputs.get("value")
|
||||
return {"result": not bool(value)}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
"""Factory for ReduceList plugin."""
|
||||
|
||||
from .utils_reduce_list import ReduceList
|
||||
|
||||
|
||||
def create():
|
||||
return ReduceList()
|
||||
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@metabuilder/utils_reduce_list",
|
||||
"version": "1.0.0",
|
||||
"description": "utils_reduce_list plugin",
|
||||
"description": "Reduce a list into a string",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["utils", "workflow", "plugin"],
|
||||
"main": "utils_reduce_list.py",
|
||||
"files": ["utils_reduce_list.py", "factory.py"],
|
||||
"metadata": {
|
||||
"plugin_type": "utils.reduce_list",
|
||||
"category": "utils"
|
||||
"category": "utils",
|
||||
"class": "ReduceList",
|
||||
"entrypoint": "execute"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
"""Workflow plugin: reduce list."""
|
||||
|
||||
from ...base import NodeExecutor
|
||||
|
||||
def run(_runtime, inputs):
|
||||
|
||||
class ReduceList(NodeExecutor):
|
||||
"""Reduce a list into a string."""
|
||||
items = inputs.get("items", [])
|
||||
if not isinstance(items, list):
|
||||
items = [items] if items else []
|
||||
|
||||
separator = inputs.get("separator", "")
|
||||
# Handle escape sequences
|
||||
if separator == "\\n":
|
||||
separator = "\n"
|
||||
elif separator == "\\t":
|
||||
separator = "\t"
|
||||
node_type = "utils.reduce_list"
|
||||
category = "utils"
|
||||
description = "Reduce a list into a string"
|
||||
|
||||
reduced = separator.join([str(item) for item in items])
|
||||
return {"result": reduced}
|
||||
def execute(self, inputs, runtime=None):
|
||||
"""Reduce a list into a string."""
|
||||
items = inputs.get("items", [])
|
||||
if not isinstance(items, list):
|
||||
items = [items] if items else []
|
||||
|
||||
separator = inputs.get("separator", "")
|
||||
# Handle escape sequences
|
||||
if separator == "\\n":
|
||||
separator = "\n"
|
||||
elif separator == "\\t":
|
||||
separator = "\t"
|
||||
|
||||
reduced = separator.join([str(item) for item in items])
|
||||
return {"result": reduced}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
"""Factory for UpdateRoadmap plugin."""
|
||||
|
||||
from .utils_update_roadmap import UpdateRoadmap
|
||||
|
||||
|
||||
def create():
|
||||
return UpdateRoadmap()
|
||||
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@metabuilder/utils_update_roadmap",
|
||||
"version": "1.0.0",
|
||||
"description": "utils_update_roadmap plugin",
|
||||
"description": "Update ROADMAP.md with new content",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["utils", "workflow", "plugin"],
|
||||
"main": "utils_update_roadmap.py",
|
||||
"files": ["utils_update_roadmap.py", "factory.py"],
|
||||
"metadata": {
|
||||
"plugin_type": "utils.update_roadmap",
|
||||
"category": "utils"
|
||||
"category": "utils",
|
||||
"class": "UpdateRoadmap",
|
||||
"entrypoint": "execute"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
"""Workflow plugin: update roadmap file."""
|
||||
|
||||
import logging
|
||||
|
||||
from ...base import NodeExecutor
|
||||
|
||||
logger = logging.getLogger("metabuilder")
|
||||
|
||||
|
||||
def _update_roadmap(content: str):
|
||||
class UpdateRoadmap(NodeExecutor):
|
||||
"""Update ROADMAP.md with new content."""
|
||||
with open("ROADMAP.md", "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
logger.info("ROADMAP.md updated successfully.")
|
||||
|
||||
node_type = "utils.update_roadmap"
|
||||
category = "utils"
|
||||
description = "Update ROADMAP.md with new content"
|
||||
|
||||
def run(_runtime, inputs):
|
||||
"""Update ROADMAP.md with new content."""
|
||||
content = inputs.get("content")
|
||||
if not content:
|
||||
return {"error": "Content is required"}
|
||||
def execute(self, inputs, runtime=None):
|
||||
"""Update ROADMAP.md with new content."""
|
||||
content = inputs.get("content")
|
||||
if not content:
|
||||
return {"error": "Content is required"}
|
||||
|
||||
_update_roadmap(content)
|
||||
return {"result": "ROADMAP.md updated successfully"}
|
||||
self._update_roadmap(content)
|
||||
return {"result": "ROADMAP.md updated successfully"}
|
||||
|
||||
def _update_roadmap(self, content: str):
|
||||
"""Update ROADMAP.md with new content."""
|
||||
with open("ROADMAP.md", "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
logger.info("ROADMAP.md updated successfully.")
|
||||
|
||||
Reference in New Issue
Block a user