modules/tool-skills/tests/fixtures/skills/module-development/SKILL.md
Guide for creating new Amplifier modules including protocol implementation, entry points, mount functions, and testing patterns. Use when creating new modules or understanding module architecture.
npx skillsauth add microsoft/amplifier-bundle-skills module-developmentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Determine which protocol your module implements:
from typing import Any
from amplifier_core import ModuleCoordinator, ToolResult
class MyTool:
"""Tool for doing something useful."""
name = "my-tool"
description = "Does something useful"
def __init__(self: "MyTool", config: dict[str, Any]) -> None:
"""Initialize tool with configuration."""
self.config = config
self.timeout = config.get("timeout", 30)
@property
def input_schema(self: "MyTool") -> dict:
"""Return JSON schema for tool parameters."""
return {
"type": "object",
"properties": {
"param": {"type": "string", "description": "Parameter description"}
},
"required": ["param"]
}
async def execute(self: "MyTool", input: dict[str, Any]) -> ToolResult:
"""Execute tool operation."""
param = input.get("param")
if not param:
return ToolResult(
success=False,
error={"message": "param is required"}
)
# Implementation here
result = f"Processed: {param}"
return ToolResult(
success=True,
output={"result": result}
)
async def mount(coordinator: ModuleCoordinator, config: dict[str, Any] | None = None) -> None:
"""
Mount the tool module.
Args:
coordinator: Module coordinator providing infrastructure
config: Module configuration from profile
Returns:
Optional cleanup function
"""
config = config or {}
tool = MyTool(config)
await coordinator.mount("tools", tool, name=tool.name)
logger.info("Mounted MyTool")
return
# pyproject.toml
[project]
name = "amplifier-module-tool-mytool"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"amplifier-core",
]
[project.entry-points."amplifier.modules"]
tool-mytool = "amplifier_module_tool_mytool:mount"
[tool.uv.sources]
amplifier-core = { path = "../amplifier-core", editable = true }
Required files:
amplifier-module-tool-mytool/
├── amplifier_module_tool_mytool/
│ ├── __init__.py # mount() + tool class
│ └── (optional modules)
├── tests/
│ └── test_mytool.py
├── pyproject.toml
├── Makefile
└── README.md
The coordinator provides infrastructure context:
async def mount(coordinator: ModuleCoordinator, config: dict[str, Any] | None = None) -> None:
# Access infrastructure
session_id = coordinator.session_id # Current session
parent_id = coordinator.parent_id # Parent session (if child)
session_config = coordinator.config # Full session configuration
loader = coordinator.loader # Dynamic module loading
# Emit events
await coordinator.hooks.emit("module:mounted", {
"module_type": "tool",
"module_name": "my-tool"
})
# Register capabilities (optional)
coordinator.register_capability("my-tool.version", "1.0.0")
# Register cleanup (optional)
async def cleanup():
logger.info("Cleaning up MyTool")
coordinator.register_cleanup(cleanup)
Use TestCoordinator:
import pytest
from amplifier_core.testing import TestCoordinator
from amplifier_module_tool_mytool import mount
@pytest.mark.asyncio
async def test_tool_execution():
coordinator = TestCoordinator()
# Mount module
await mount(coordinator, {"timeout": 10})
# Get tool
tool = coordinator.get("tools", "my-tool")
assert tool is not None
# Execute
result = await tool.execute({"param": "test"})
assert result.success
assert "Processed: test" in result.output["result"]
class MyTool:
def __init__(self: "MyTool", config: dict[str, Any]) -> None:
# Required config
self.required = config.get("required_param")
if not self.required:
raise ValueError("required_param must be provided")
# Optional config with defaults
self.timeout = config.get("timeout", 30)
self.retries = config.get("retries", 3)
async def execute(self: "MyTool", input: dict[str, Any]) -> ToolResult:
# Events emitted by orchestrator via hooks, not by tools directly
# Tools just return results
result = await self._process(input)
return ToolResult(success=True, output=result)
# Return error, don't raise
return ToolResult(
success=False,
error={"message": "Clear error message", "code": "ERROR_CODE"}
)
# Log for debugging
logger.error(f"Failed to process {input}: {e}")
return ToolResult(success=False, error={"message": str(e)})
tools
Plan a batch of independent work into isolated lanes, get your approval, then run each lane as its own autonomous /goal session — one git worktree, one branch, one tmux session each — and verify and merge the results yourself. Use when work decomposes into pieces that can run at the same time: "run these in parallel", "goal-batch", "launch lanes for these", "work these N tasks simultaneously", "batch these as goals". Nothing launches until you have seen the lane split and said go. This is NOT fire-and-forget: the orchestrating session re-runs the full test suite itself after every merge and never accepts a lane's own claim that it finished. NOT for bounded edits that each end in their own PR — use mass-change for that. Requires git, tmux, the amplifier CLI on PATH, and the goalify and monitor skills.
development
Momentum-driven engineering reviewer that holds one uncompromising gate — is it REAL, proven end-to-end as a user would — while driving work forward. Demands proof over claims, plumbing before polish, fail-loud over fallbacks, trust in the model over instructions, and protects the critical path so good-but-costly ideas don't stall the work. Warm, blunt, forward-driving — not a curmudgeon. A lens for any checkpoint — brainstorm, design, plan, implement, debug, or ship — not just the finish. Use when: pressure-testing whether an idea/design/plan is provable and on the critical path, whether you're building in the right order, whether a fix is real or a band-aid, or whether work is actually done/ready — any time the worry is "are we fooling ourselves about what's real?"
development
Convene the Product Development Council (six orthogonal product-delivery lenses, anchored by a mandatory problem-validation gate) on a target — cold independent fan-out, debate-to-consensus, synthesized verdict with recorded dissent and a roster manifest.
development
Convene the Product Development Council on the CURRENT conversation / work-in-progress — the plan, roadmap, or scope decision you've been building in this session. The INLINE counterpart to /product-council (which forks and runs isolated, so it cannot see the chat). Use when you want the council to critique what we're working on right now.