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:
2026-01-21 16:08:08 +00:00
parent c760bd7cd0
commit 5ac579a2ed
171 changed files with 2188 additions and 1565 deletions

View File

@@ -0,0 +1 @@
"""Utility plugins: filtering, mapping, branching, MVP checking."""

View 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}

View 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}

View 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}

View 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}

View 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)}

View 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}

View 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"}