Plugins¶
letscode's plugin system is built on pluggy. A plugin is a Python package that registers tools, slash commands, skills, frontends, or lifecycle hooks via standard Python entry points. letscode discovers plugins at startup, calls their registration hooks, and lets the lifecycle hooks intercede during agent turns.
-
Step-by-step: what to register, what hooks fire when, how to distribute via PyPI.
-
Reference plugin: letscode-memory
The canonical worked example — cross-session memory in ~150 lines.
-
Stable surface, versioning rule, the ten lifecycle hooks, design rationale.
What you can register¶
Five extension types:
| Type | Hook | What it adds |
|---|---|---|
| Tool | letscode_register_tools |
LLM-callable tools (e.g. read, write). |
| Command | letscode_register_commands |
Slash commands (e.g. /help, /skills). |
| Skill | letscode_register_skills |
Skill source dirs or inline skills. |
| Frontend | letscode_register_frontends |
Named frontend factories. |
| Lifecycle hook | @hookimpl on any of the lifecycle hookspecs |
Cross-cutting behavior. |
The first four are named things added to a registry. The fifth is behavior layered on the agent loop. See Extension model for the full hook inventory.
TL;DR — your first plugin¶
import pluggy
from pydantic import BaseModel
from letscode.agent.tools import ToolContext, ToolResult, tool
from letscode.llm.types import TextPart
hookimpl = pluggy.HookimplMarker("letscode")
class _GreetParams(BaseModel):
name: str
@tool(name="greet", description="Say hello to someone.")
async def _greet(params: _GreetParams, ctx: ToolContext) -> ToolResult:
del ctx
return ToolResult(content=[TextPart(text=f"Hello, {params.name}!")])
@hookimpl
def letscode_register_tools(registry):
registry.add(_greet)
The LLM can now call the greet tool. That's the whole story for tools.
For everything else — commands, skills, frontends, lifecycle hooks, message types, packaging, testing — see the authoring guide.