mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 22:04:56 +00:00
feat: Add Python plugins from AutoMetabuilder + restructure workflow folder
Restructure workflow/ for multi-language plugin support:
- Rename src/ to core/ (engine code: DAG executor, registry, types)
- Create executor/{cpp,python,ts}/ for language-specific runtimes
- Consolidate plugins to plugins/{ts,python}/ by language then category
Add 80+ Python plugins from AutoMetabuilder in 14 categories:
- control: bot control, switch logic, state management
- convert: type conversions (json, boolean, dict, list, number, string)
- core: AI requests, context management, tool calls
- dict: dictionary operations (get, set, keys, values, merge)
- list: list operations (concat, find, sort, slice, filter)
- logic: boolean logic (and, or, xor, equals, comparisons)
- math: arithmetic operations (add, subtract, multiply, power, etc.)
- string: string manipulation (concat, split, replace, format)
- notifications: Slack, Discord integrations
- test: assertion helpers and test suite runner
- tools: file operations, git, docker, testing utilities
- utils: filtering, mapping, reducing, condition branching
- var: variable store operations (get, set, delete, exists)
- web: Flask server, environment variables, JSON handling
Add language executor runtimes:
- TypeScript: direct import execution (default, fast startup)
- Python: child process with JSON stdin/stdout communication
- C++: placeholder for native FFI bindings (Phase 3)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
1
workflow/plugins/python/utils/__init__.py
Normal file
1
workflow/plugins/python/utils/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Utility plugins: filtering, mapping, branching, MVP checking."""
|
||||
25
workflow/plugins/python/utils/utils_branch_condition.py
Normal file
25
workflow/plugins/python/utils/utils_branch_condition.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""Workflow plugin: branch condition."""
|
||||
import re
|
||||
|
||||
|
||||
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)))
|
||||
|
||||
return {"result": decision}
|
||||
37
workflow/plugins/python/utils/utils_check_mvp.py
Normal file
37
workflow/plugins/python/utils/utils_check_mvp.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""Workflow plugin: check if MVP is reached."""
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
def _is_mvp_reached() -> 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
|
||||
|
||||
|
||||
def run(_runtime, _inputs):
|
||||
"""Check if the MVP section in ROADMAP.md is completed."""
|
||||
mvp_reached = _is_mvp_reached()
|
||||
return {"mvp_reached": mvp_reached}
|
||||
33
workflow/plugins/python/utils/utils_filter_list.py
Normal file
33
workflow/plugins/python/utils/utils_filter_list.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""Workflow plugin: filter list."""
|
||||
import re
|
||||
|
||||
|
||||
def run(_runtime, inputs):
|
||||
"""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 = []
|
||||
|
||||
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}
|
||||
19
workflow/plugins/python/utils/utils_map_list.py
Normal file
19
workflow/plugins/python/utils/utils_map_list.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""Workflow plugin: map list."""
|
||||
|
||||
|
||||
def run(_runtime, inputs):
|
||||
"""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 = []
|
||||
|
||||
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.py
Normal file
7
workflow/plugins/python/utils/utils_not.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""Workflow plugin: boolean not."""
|
||||
|
||||
|
||||
def run(_runtime, inputs):
|
||||
"""Negate a boolean value."""
|
||||
value = inputs.get("value")
|
||||
return {"result": not bool(value)}
|
||||
18
workflow/plugins/python/utils/utils_reduce_list.py
Normal file
18
workflow/plugins/python/utils/utils_reduce_list.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""Workflow plugin: reduce list."""
|
||||
|
||||
|
||||
def run(_runtime, inputs):
|
||||
"""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}
|
||||
21
workflow/plugins/python/utils/utils_update_roadmap.py
Normal file
21
workflow/plugins/python/utils/utils_update_roadmap.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""Workflow plugin: update roadmap file."""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("metabuilder")
|
||||
|
||||
|
||||
def _update_roadmap(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.")
|
||||
|
||||
|
||||
def run(_runtime, inputs):
|
||||
"""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"}
|
||||
Reference in New Issue
Block a user