Add comprehensive tests and documentation for workflow plugins

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-10 13:04:55 +00:00
parent 4776082c34
commit fb43cf9ca3
2 changed files with 1141 additions and 0 deletions

View File

@@ -0,0 +1,822 @@
# Workflow Plugins Documentation
This document describes all available workflow plugins for building declarative n8n-style workflows.
## Categories
- [Core Plugins](#core-plugins) - AI and context management
- [Tool Plugins](#tool-plugins) - File system and SDLC operations
- [Logic Plugins](#logic-plugins) - Boolean logic and comparisons
- [List Plugins](#list-plugins) - Collection operations
- [Dictionary Plugins](#dictionary-plugins) - Object/map operations
- [String Plugins](#string-plugins) - Text manipulation
- [Math Plugins](#math-plugins) - Arithmetic operations
- [Conversion Plugins](#conversion-plugins) - Type conversions
- [Control Flow Plugins](#control-flow-plugins) - Branching and switching
- [Variable Plugins](#variable-plugins) - State management
- [Backend Plugins](#backend-plugins) - System initialization
- [Utility Plugins](#utility-plugins) - General utilities
---
## Core Plugins
### `core.load_context`
Load SDLC context (roadmap, issues, PRs) from GitHub.
**Outputs:**
- `context` - String containing SDLC context
### `core.seed_messages`
Initialize empty message array for AI conversation.
**Outputs:**
- `messages` - Empty array
### `core.append_context_message`
Add context to messages array.
**Inputs:**
- `messages` - Message array
- `context` - Context text
**Outputs:**
- `messages` - Updated array
### `core.append_user_instruction`
Add user instruction to messages.
**Inputs:**
- `messages` - Message array
**Outputs:**
- `messages` - Updated array
### `core.ai_request`
Make AI request with messages.
**Inputs:**
- `messages` - Message array
**Outputs:**
- `response` - AI response message
- `has_tool_calls` - Boolean
- `tool_calls_count` - Number
### `core.run_tool_calls`
Execute tool calls from AI response.
**Inputs:**
- `response` - AI response message
**Outputs:**
- `tool_results` - Array of results
### `core.append_tool_results`
Add tool results to messages.
**Inputs:**
- `messages` - Message array
- `tool_results` - Tool results array
**Outputs:**
- `messages` - Updated array
---
## Tool Plugins
### `tools.list_files`
List files in directory.
**Inputs:**
- `path` - Directory path
**Outputs:**
- `files` - Array of file paths
### `tools.read_file`
Read file contents.
**Inputs:**
- `path` - File path
**Outputs:**
- `content` - File content
### `tools.run_tests`
Execute test suite.
**Outputs:**
- `success` - Boolean
- `output` - Test output
### `tools.run_lint`
Run linter.
**Outputs:**
- `success` - Boolean
- `output` - Lint output
### `tools.create_branch`
Create Git branch.
**Inputs:**
- `branch_name` - Branch name
**Outputs:**
- `success` - Boolean
### `tools.create_pull_request`
Create GitHub pull request.
**Inputs:**
- `title` - PR title
- `body` - PR description
**Outputs:**
- `pr_number` - PR number
---
## Logic Plugins
### `logic.and`
Logical AND operation.
**Inputs:**
- `values` - Array of boolean values
**Outputs:**
- `result` - Boolean (all values are true)
### `logic.or`
Logical OR operation.
**Inputs:**
- `values` - Array of boolean values
**Outputs:**
- `result` - Boolean (any value is true)
### `logic.xor`
Logical XOR operation.
**Inputs:**
- `a` - First boolean
- `b` - Second boolean
**Outputs:**
- `result` - Boolean (exactly one is true)
### `logic.equals`
Equality comparison.
**Inputs:**
- `a` - First value
- `b` - Second value
**Outputs:**
- `result` - Boolean (a == b)
### `logic.gt`
Greater than comparison.
**Inputs:**
- `a` - First value
- `b` - Second value
**Outputs:**
- `result` - Boolean (a > b)
### `logic.lt`
Less than comparison.
**Inputs:**
- `a` - First value
- `b` - Second value
**Outputs:**
- `result` - Boolean (a < b)
### `logic.gte`
Greater than or equal comparison.
**Inputs:**
- `a` - First value
- `b` - Second value
**Outputs:**
- `result` - Boolean (a >= b)
### `logic.lte`
Less than or equal comparison.
**Inputs:**
- `a` - First value
- `b` - Second value
**Outputs:**
- `result` - Boolean (a <= b)
### `logic.in`
Membership test.
**Inputs:**
- `value` - Value to find
- `collection` - Array or string
**Outputs:**
- `result` - Boolean (value in collection)
---
## List Plugins
### `list.find`
Find first item matching condition.
**Inputs:**
- `items` - Array of objects
- `key` - Property name
- `value` - Value to match
**Outputs:**
- `result` - Found item or null
- `found` - Boolean
### `list.some`
Check if any item matches.
**Inputs:**
- `items` - Array
- `key` - Optional property name
- `value` - Optional value to match
**Outputs:**
- `result` - Boolean
### `list.every`
Check if all items match.
**Inputs:**
- `items` - Array
- `key` - Optional property name
- `value` - Optional value to match
**Outputs:**
- `result` - Boolean
### `list.concat`
Concatenate multiple lists.
**Inputs:**
- `lists` - Array of arrays
**Outputs:**
- `result` - Concatenated array
### `list.slice`
Extract slice from list.
**Inputs:**
- `items` - Array
- `start` - Start index (default: 0)
- `end` - End index (optional)
**Outputs:**
- `result` - Sliced array
### `list.sort`
Sort list.
**Inputs:**
- `items` - Array
- `key` - Optional sort key
- `reverse` - Boolean (default: false)
**Outputs:**
- `result` - Sorted array
### `list.length`
Get list length.
**Inputs:**
- `items` - Array
**Outputs:**
- `result` - Number (length)
---
## Dictionary Plugins
### `dict.get`
Get value from dictionary.
**Inputs:**
- `object` - Dictionary
- `key` - Key name
- `default` - Default value (optional)
**Outputs:**
- `result` - Value
- `found` - Boolean
### `dict.set`
Set value in dictionary.
**Inputs:**
- `object` - Dictionary
- `key` - Key name
- `value` - Value to set
**Outputs:**
- `result` - Updated dictionary
### `dict.merge`
Merge multiple dictionaries.
**Inputs:**
- `objects` - Array of dictionaries
**Outputs:**
- `result` - Merged dictionary
### `dict.keys`
Get dictionary keys.
**Inputs:**
- `object` - Dictionary
**Outputs:**
- `result` - Array of keys
### `dict.values`
Get dictionary values.
**Inputs:**
- `object` - Dictionary
**Outputs:**
- `result` - Array of values
### `dict.items`
Get dictionary items as [key, value] pairs.
**Inputs:**
- `object` - Dictionary
**Outputs:**
- `result` - Array of [key, value] arrays
---
## String Plugins
### `string.concat`
Concatenate strings.
**Inputs:**
- `strings` - Array of strings
- `separator` - Separator string (default: "")
**Outputs:**
- `result` - Concatenated string
### `string.split`
Split string.
**Inputs:**
- `text` - Input string
- `separator` - Split separator (default: " ")
- `max_splits` - Max splits (optional)
**Outputs:**
- `result` - Array of strings
### `string.replace`
Replace occurrences in string.
**Inputs:**
- `text` - Input string
- `old` - String to replace
- `new` - Replacement string
- `count` - Max replacements (default: -1 for all)
**Outputs:**
- `result` - Modified string
### `string.trim`
Trim whitespace.
**Inputs:**
- `text` - Input string
- `mode` - "both", "start", or "end" (default: "both")
**Outputs:**
- `result` - Trimmed string
### `string.upper`
Convert to uppercase.
**Inputs:**
- `text` - Input string
**Outputs:**
- `result` - Uppercase string
### `string.lower`
Convert to lowercase.
**Inputs:**
- `text` - Input string
**Outputs:**
- `result` - Lowercase string
### `string.format`
Format string with variables.
**Inputs:**
- `template` - Template string with {placeholders}
- `variables` - Dictionary of variables
**Outputs:**
- `result` - Formatted string
### `string.length`
Get string length.
**Inputs:**
- `text` - Input string
**Outputs:**
- `result` - Number (length)
---
## Math Plugins
### `math.add`
Add numbers.
**Inputs:**
- `numbers` - Array of numbers
**Outputs:**
- `result` - Sum
### `math.subtract`
Subtract numbers.
**Inputs:**
- `a` - Minuend
- `b` - Subtrahend
**Outputs:**
- `result` - Difference (a - b)
### `math.multiply`
Multiply numbers.
**Inputs:**
- `numbers` - Array of numbers
**Outputs:**
- `result` - Product
### `math.divide`
Divide numbers.
**Inputs:**
- `a` - Dividend
- `b` - Divisor
**Outputs:**
- `result` - Quotient (a / b)
### `math.modulo`
Modulo operation.
**Inputs:**
- `a` - Dividend
- `b` - Divisor
**Outputs:**
- `result` - Remainder (a % b)
### `math.power`
Power operation.
**Inputs:**
- `a` - Base
- `b` - Exponent
**Outputs:**
- `result` - a^b
### `math.min`
Find minimum value.
**Inputs:**
- `numbers` - Array of numbers
**Outputs:**
- `result` - Minimum value
### `math.max`
Find maximum value.
**Inputs:**
- `numbers` - Array of numbers
**Outputs:**
- `result` - Maximum value
### `math.abs`
Absolute value.
**Inputs:**
- `value` - Number
**Outputs:**
- `result` - |value|
### `math.round`
Round number.
**Inputs:**
- `value` - Number
- `precision` - Decimal places (default: 0)
**Outputs:**
- `result` - Rounded number
---
## Conversion Plugins
### `convert.to_string`
Convert to string.
**Inputs:**
- `value` - Any value
**Outputs:**
- `result` - String
### `convert.to_number`
Convert to number.
**Inputs:**
- `value` - String or number
- `default` - Default value (default: 0)
**Outputs:**
- `result` - Number
### `convert.to_boolean`
Convert to boolean.
**Inputs:**
- `value` - Any value
**Outputs:**
- `result` - Boolean
### `convert.to_list`
Convert to list.
**Inputs:**
- `value` - Any value
**Outputs:**
- `result` - Array
### `convert.to_dict`
Convert to dictionary.
**Inputs:**
- `value` - List of [key, value] pairs or dict
**Outputs:**
- `result` - Dictionary
### `convert.parse_json`
Parse JSON string.
**Inputs:**
- `text` - JSON string
**Outputs:**
- `result` - Parsed object
### `convert.to_json`
Convert to JSON string.
**Inputs:**
- `value` - Any value
- `indent` - Indentation (optional)
**Outputs:**
- `result` - JSON string
---
## Control Flow Plugins
### `control.switch`
Switch/case statement.
**Inputs:**
- `value` - Value to match
- `cases` - Dictionary of case values
- `default` - Default value (optional)
**Outputs:**
- `result` - Matched case value
- `matched` - Boolean
### `utils.branch_condition`
Branch based on condition.
**Inputs:**
- `condition` - Boolean
**Outputs:**
- Routes to output 0 (true) or 1 (false)
---
## Variable Plugins
### `var.get`
Get variable from workflow store.
**Inputs:**
- `key` - Variable name
- `default` - Default value (optional)
**Outputs:**
- `result` - Variable value
- `exists` - Boolean
### `var.set`
Set variable in workflow store.
**Inputs:**
- `key` - Variable name
- `value` - Value to set
**Outputs:**
- `result` - Set value
- `key` - Variable name
### `var.delete`
Delete variable from workflow store.
**Inputs:**
- `key` - Variable name
**Outputs:**
- `result` - Boolean (success)
- `deleted` - Boolean
### `var.exists`
Check if variable exists.
**Inputs:**
- `key` - Variable name
**Outputs:**
- `result` - Boolean
---
## Backend Plugins
### `backend.create_github`
Initialize GitHub client.
**Outputs:**
- `result` - GitHub client
- `initialized` - Boolean
### `backend.create_openai`
Initialize OpenAI client.
**Outputs:**
- `result` - OpenAI client
- `initialized` - Boolean
### `backend.load_metadata`
Load metadata.json.
**Outputs:**
- `result` - Metadata dictionary
### `backend.load_messages`
Load translation messages.
**Outputs:**
- `result` - Messages dictionary
### `backend.load_tools`
Load tool definitions.
**Outputs:**
- `result` - Tools array
### `backend.load_prompt`
Load prompt.yml.
**Outputs:**
- `result` - Prompt dictionary
### `backend.build_tool_map`
Build tool registry map.
**Outputs:**
- `result` - Tool map dictionary
### `backend.load_plugins`
Load and register plugins.
**Outputs:**
- `result` - Boolean (success)
---
## Utility Plugins
### `utils.filter_list`
Filter list by condition.
**Inputs:**
- `items` - Array
- `mode` - Filter mode
- `pattern` - Pattern/condition
**Outputs:**
- `result` - Filtered array
### `utils.map_list`
Map/transform list items.
**Inputs:**
- `items` - Array
- `transform` - Transformation
**Outputs:**
- `result` - Transformed array
### `utils.reduce_list`
Reduce list to single value.
**Inputs:**
- `items` - Array
- `separator` - Join separator
**Outputs:**
- `result` - Reduced value
### `utils.not`
Logical NOT operation.
**Inputs:**
- `value` - Boolean value
**Outputs:**
- `result` - Negated boolean
---
## Variable Binding
All plugins support variable binding using `$variable_name` syntax in inputs. Variables are stored in the workflow runtime store and can be accessed across nodes.
Example:
```json
{
"parameters": {
"text": "$user_input",
"template": "Hello {name}!",
"variables": {
"name": "$user_name"
}
}
}
```
## Error Handling
Plugins may return an `error` field in their output when an error occurs. Check for this field to handle errors gracefully in your workflow.
Example:
```json
{
"result": null,
"error": "Division by zero"
}
```

View File

@@ -0,0 +1,319 @@
"""Test new workflow plugins for software development primitives."""
from autometabuilder.workflow.plugin_registry import PluginRegistry, load_plugin_map
from autometabuilder.workflow.runtime import WorkflowRuntime
import logging
class MockLogger:
"""Mock logger for testing."""
def info(self, *args, **kwargs):
pass
def debug(self, *args, **kwargs):
pass
def error(self, *args, **kwargs):
pass
def create_test_runtime():
"""Create a test runtime with empty context."""
logger = MockLogger()
return WorkflowRuntime(context={}, store={}, tool_runner=None, logger=logger)
def test_plugin_map_loads_all_new_plugins():
"""Test that plugin map includes all new plugins."""
plugin_map = load_plugin_map()
# Test logic plugins
assert "logic.and" in plugin_map
assert "logic.or" in plugin_map
assert "logic.xor" in plugin_map
assert "logic.equals" in plugin_map
assert "logic.gt" in plugin_map
assert "logic.lt" in plugin_map
# Test list plugins
assert "list.find" in plugin_map
assert "list.some" in plugin_map
assert "list.every" in plugin_map
assert "list.concat" in plugin_map
assert "list.slice" in plugin_map
assert "list.sort" in plugin_map
assert "list.length" in plugin_map
# Test dict plugins
assert "dict.get" in plugin_map
assert "dict.set" in plugin_map
assert "dict.merge" in plugin_map
# Test string plugins
assert "string.concat" in plugin_map
assert "string.split" in plugin_map
assert "string.upper" in plugin_map
assert "string.lower" in plugin_map
# Test math plugins
assert "math.add" in plugin_map
assert "math.subtract" in plugin_map
assert "math.multiply" in plugin_map
assert "math.divide" in plugin_map
# Test conversion plugins
assert "convert.to_string" in plugin_map
assert "convert.to_number" in plugin_map
assert "convert.parse_json" in plugin_map
assert "convert.to_json" in plugin_map
# Test control flow plugins
assert "control.switch" in plugin_map
# Test variable plugins
assert "var.get" in plugin_map
assert "var.set" in plugin_map
# Test backend plugins
assert "backend.load_metadata" in plugin_map
assert "backend.load_messages" in plugin_map
def test_logic_and_plugin():
"""Test logic.and plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("logic.and")
assert plugin is not None
result = plugin(runtime, {"values": [True, True, True]})
assert result["result"] is True
result = plugin(runtime, {"values": [True, False, True]})
assert result["result"] is False
def test_logic_or_plugin():
"""Test logic.or plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("logic.or")
assert plugin is not None
result = plugin(runtime, {"values": [False, False, True]})
assert result["result"] is True
result = plugin(runtime, {"values": [False, False, False]})
assert result["result"] is False
def test_logic_equals_plugin():
"""Test logic.equals plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("logic.equals")
assert plugin is not None
result = plugin(runtime, {"a": 5, "b": 5})
assert result["result"] is True
result = plugin(runtime, {"a": 5, "b": 10})
assert result["result"] is False
def test_math_add_plugin():
"""Test math.add plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("math.add")
assert plugin is not None
result = plugin(runtime, {"numbers": [1, 2, 3, 4, 5]})
assert result["result"] == 15
def test_math_multiply_plugin():
"""Test math.multiply plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("math.multiply")
assert plugin is not None
result = plugin(runtime, {"numbers": [2, 3, 4]})
assert result["result"] == 24
def test_string_concat_plugin():
"""Test string.concat plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("string.concat")
assert plugin is not None
result = plugin(runtime, {"strings": ["Hello", "World"], "separator": " "})
assert result["result"] == "Hello World"
def test_string_upper_plugin():
"""Test string.upper plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("string.upper")
assert plugin is not None
result = plugin(runtime, {"text": "hello"})
assert result["result"] == "HELLO"
def test_list_length_plugin():
"""Test list.length plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("list.length")
assert plugin is not None
result = plugin(runtime, {"items": [1, 2, 3, 4, 5]})
assert result["result"] == 5
def test_list_concat_plugin():
"""Test list.concat plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("list.concat")
assert plugin is not None
result = plugin(runtime, {"lists": [[1, 2], [3, 4], [5, 6]]})
assert result["result"] == [1, 2, 3, 4, 5, 6]
def test_dict_get_plugin():
"""Test dict.get plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("dict.get")
assert plugin is not None
result = plugin(runtime, {"object": {"name": "John", "age": 30}, "key": "name"})
assert result["result"] == "John"
assert result["found"] is True
result = plugin(runtime, {"object": {"name": "John"}, "key": "missing", "default": "N/A"})
assert result["result"] == "N/A"
assert result["found"] is False
def test_dict_set_plugin():
"""Test dict.set plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("dict.set")
assert plugin is not None
result = plugin(runtime, {"object": {"a": 1}, "key": "b", "value": 2})
assert result["result"] == {"a": 1, "b": 2}
def test_var_get_set_plugin():
"""Test var.get and var.set plugins."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
set_plugin = registry.get("var.set")
get_plugin = registry.get("var.get")
assert set_plugin is not None
assert get_plugin is not None
# Set a variable
set_result = set_plugin(runtime, {"key": "test_var", "value": 42})
assert set_result["result"] == 42
# Get the variable
get_result = get_plugin(runtime, {"key": "test_var"})
assert get_result["result"] == 42
assert get_result["exists"] is True
def test_convert_to_json_and_parse():
"""Test JSON conversion plugins."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
to_json = registry.get("convert.to_json")
parse_json = registry.get("convert.parse_json")
assert to_json is not None
assert parse_json is not None
# Convert to JSON
data = {"name": "Test", "value": 123}
json_result = to_json(runtime, {"value": data})
json_str = json_result["result"]
# Parse JSON back
parse_result = parse_json(runtime, {"text": json_str})
assert parse_result["result"] == data
def test_convert_to_number():
"""Test number conversion."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("convert.to_number")
assert plugin is not None
result = plugin(runtime, {"value": "42"})
assert result["result"] == 42
result = plugin(runtime, {"value": "3.14"})
assert result["result"] == 3.14
def test_control_switch():
"""Test control.switch plugin."""
plugin_map = load_plugin_map()
registry = PluginRegistry(plugin_map)
runtime = create_test_runtime()
plugin = registry.get("control.switch")
assert plugin is not None
cases = {
"option1": "Result 1",
"option2": "Result 2",
"option3": "Result 3"
}
result = plugin(runtime, {"value": "option2", "cases": cases})
assert result["result"] == "Result 2"
assert result["matched"] is True
result = plugin(runtime, {"value": "unknown", "cases": cases, "default": "Default"})
assert result["result"] == "Default"
assert result["matched"] is False