Skip to main content

Plugin Framework

Maestro supports custom agents through a Python plugin system. You can add linters, security scanners, notification hooks, or entirely new pipeline stages.

How plugins work

Plugins are Python packages that register entry points. When Maestro starts, it discovers all installed plugins and makes them available as pipeline stages.

Each plugin implements a simple interface:

from maestro.plugins import AgentPlugin, TaskContext, AgentResult

class MyCustomAgent(AgentPlugin):
"""A custom agent that runs after implementation."""

name = "my-custom-agent"
stage = "post_implement" # when in the pipeline to run

async def execute(self, context: TaskContext) -> AgentResult:
# Your logic here
pr_diff = await context.get_pr_diff()

# Do something with the diff
issues = self.analyze(pr_diff)

if issues:
return AgentResult(
status="needs_changes",
comments=issues,
)

return AgentResult(status="passed")

Creating a plugin

1. Create a Python package

my-maestro-plugin/
pyproject.toml
my_plugin/
__init__.py
agent.py

2. Define the entry point

In pyproject.toml:

[project]
name = "my-maestro-plugin"
version = "0.1.0"

[project.entry-points."maestro.agents"]
my-custom-agent = "my_plugin.agent:MyCustomAgent"

3. Implement the agent

In my_plugin/agent.py:

from maestro.plugins import AgentPlugin, TaskContext, AgentResult

class MyCustomAgent(AgentPlugin):
name = "my-custom-agent"
stage = "post_implement"

async def execute(self, context: TaskContext) -> AgentResult:
# Access task metadata
task = context.task
project = context.project
workspace = context.workspace

# Access the PR diff
diff = await context.get_pr_diff()

# Access files in the workspace
content = await context.read_file("src/main.py")

# Leave comments on the PR
await context.add_comment(
file="src/main.py",
line=42,
body="Consider adding error handling here.",
)

return AgentResult(
status="passed",
summary="Custom analysis complete. 1 suggestion added.",
)

4. Install the plugin

cd my-maestro-plugin
pip install -e .

Restart Maestro and the plugin will be discovered automatically.

Plugin API

TaskContext

The TaskContext object provides access to everything the agent needs:

MethodDescription
context.taskThe current task object
context.projectThe project configuration
context.workspaceThe workspace (repo path, remote URL)
await context.get_pr_diff()Get the full PR diff as a string
await context.read_file(path)Read a file from the workspace
await context.list_files(glob)List files matching a glob pattern
await context.add_comment(file, line, body)Add an inline PR comment
await context.add_activity(message)Add an entry to the task activity log

AgentResult

Return an AgentResult to indicate the outcome:

FieldTypeDescription
statusstr"passed", "needs_changes", or "failed"
summarystrHuman-readable summary of what the agent did
commentslistOptional list of inline comments
metadatadictOptional key-value data to attach to the task

Pipeline stages

Plugins can hook into these stages:

StageWhen it runs
pre_implementBefore the implementation agent
post_implementAfter implementation, before review
pre_reviewBefore the review agent
post_reviewAfter review approval
pre_deployBefore merging the PR
post_deployAfter successful deployment

Example plugins

Linter plugin

class LinterAgent(AgentPlugin):
name = "linter"
stage = "post_implement"

async def execute(self, context: TaskContext) -> AgentResult:
import subprocess
result = subprocess.run(
["ruff", "check", context.workspace.path],
capture_output=True, text=True
)
if result.returncode != 0:
return AgentResult(
status="needs_changes",
summary=f"Linter found issues:\n{result.stdout}",
)
return AgentResult(status="passed", summary="No lint issues.")

Slack notification plugin

class SlackNotifier(AgentPlugin):
name = "slack-notify"
stage = "post_deploy"

async def execute(self, context: TaskContext) -> AgentResult:
import httpx
await httpx.AsyncClient().post(
SLACK_WEBHOOK_URL,
json={"text": f"Deployed: {context.task.title}"},
)
return AgentResult(status="passed", summary="Slack notification sent.")