Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
| [arize-trace](../skills/arize-trace/SKILL.md)<br />`gh skills install github/awesome-copilot arize-trace` | Downloads, exports, and inspects existing Arize traces and spans to understand what an LLM app is doing or debug runtime issues. Covers exporting traces by ID, spans by ID, sessions by ID, and root-cause investigation using the ax CLI. Use when the user wants to look at existing trace data, see what their LLM app is doing, export traces, download spans, investigate errors, or analyze behavior regressions. | `references/ax-profiles.md`<br />`references/ax-setup.md` |
| [aspire](../skills/aspire/SKILL.md)<br />`gh skills install github/awesome-copilot aspire` | Aspire skill covering the Aspire CLI, AppHost orchestration, service discovery, integrations, MCP server, VS Code extension, Dev Containers, GitHub Codespaces, templates, dashboard, and deployment. Use when the user asks to create, run, debug, configure, deploy, or troubleshoot an Aspire distributed application. | `references/architecture.md`<br />`references/cli-reference.md`<br />`references/dashboard.md`<br />`references/deployment.md`<br />`references/integrations-catalog.md`<br />`references/mcp-server.md`<br />`references/polyglot-apis.md`<br />`references/testing.md`<br />`references/troubleshooting.md` |
| [aspnet-minimal-api-openapi](../skills/aspnet-minimal-api-openapi/SKILL.md)<br />`gh skills install github/awesome-copilot aspnet-minimal-api-openapi` | Create ASP.NET Minimal API endpoints with proper OpenAPI documentation | None |
| [astrbot-plugin-maker](../skills/astrbot-plugin-maker/SKILL.md)<br />`gh skills install github/awesome-copilot astrbot-plugin-maker` | Create, repair, and test AstrBot Python plugins from requirements or an existing repository. Use for Star handlers, commands, plugin configuration, storage, LLM tools, metadata, and plugin HTTP integrations; also prepare plugin releases when requested. | `assets/_conf_schema.json.template`<br />`assets/dev-commands.txt`<br />`assets/main.py.template`<br />`assets/metadata.yaml.template`<br />`assets/openapi_client.py.template`<br />`assets/plugin-readme.md.template`<br />`assets/plugin_logic.py.template`<br />`assets/requirements-dev.txt.template`<br />`assets/requirements.txt.template`<br />`assets/ruff.toml.template`<br />`assets/test_openapi_auth_and_shape.py.template`<br />`assets/test_plugin_behavior.py.template`<br />`assets/test_plugin_smoke.py.template`<br />`references/api-patterns.md`<br />`references/compliance-checklist.md`<br />`references/nl-to-implementation.md`<br />`references/openapi-integration.md`<br />`references/plugin-new-checklist.md`<br />`references/sources.md`<br />`references/testing-guide.md`<br />`scripts/scaffold_plugin.py`<br />`scripts/validate_plugin.py` |
| [audit-integrity](../skills/audit-integrity/SKILL.md)<br />`gh skills install github/awesome-copilot audit-integrity` | Shared audit integrity framework for all AppSec agents β€” enforces output quality, intellectual honesty, and continuous improvement through anti-rationalization guards, self-critique loops, retry protocols, non-negotiable behaviors, self-reflection quality gates (1-10 scoring, β‰₯8 threshold), and a self-learning system with lesson/memory governance for security analysis agents. | `references/anti-rationalization-guard.md`<br />`references/clarification-protocol.md`<br />`references/non-negotiable-behaviors.md`<br />`references/retry-protocol.md`<br />`references/self-critique-loop.md`<br />`references/self-learning-system.md`<br />`references/self-reflection-quality-gate.md` |
| [automate-this](../skills/automate-this/SKILL.md)<br />`gh skills install github/awesome-copilot automate-this` | Analyze a screen recording of a manual process and produce targeted, working automation scripts. Extracts frames and audio narration from video files, reconstructs the step-by-step workflow, and proposes automation at multiple complexity levels using tools already installed on the user machine. | None |
| [autoresearch](../skills/autoresearch/SKILL.md)<br />`gh skills install github/awesome-copilot autoresearch` | Autonomous iterative experimentation loop for any programming task. Guides the user through defining goals, measurable metrics, and scope constraints, then runs an autonomous loop of code changes, testing, measuring, and keeping/discarding results. Inspired by Karpathy's autoresearch. USE FOR: autonomous improvement, iterative optimization, experiment loop, auto research, performance tuning, automated experimentation, hill climbing, try things automatically, optimize code, run experiments, autonomous coding loop. DO NOT USE FOR: one-shot tasks, simple bug fixes, code review, or tasks without a measurable metric. | None |
Expand Down
96 changes: 96 additions & 0 deletions skills/astrbot-plugin-maker/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
name: astrbot-plugin-maker
description: 'Create, repair, and test AstrBot Python plugins from requirements or an existing repository. Use for Star handlers, commands, plugin configuration, storage, LLM tools, metadata, and plugin HTTP integrations; also prepare plugin releases when requested.'
---

# AstrBot Plugin Maker

Turn the requested behavior into a working AstrBot plugin, or make a focused repair
to an existing one. Preserve the user's chosen plugin, platform, version, and scope.
An ordinary command plugin uses AstrBot's Python API; HTTP OpenAPI is optional.

## Establish the target

- Inspect the existing `main.py`, `metadata.yaml`, `_conf_schema.json`, dependencies,
tests, and repository instructions before changing an existing plugin.
- Identify the trigger, expected reply or side effect, configuration, and supported
platform. Infer routine choices; ask only for missing information that changes the
behavior, compatibility, or publication destination. Do not require a questionnaire
or a separate plan approval for a clear implementation request.
- Check the installed AstrBot version or the runtime checkout's `pyproject.toml`.
The bundled examples were checked against **v4.28.0, Python 3.12+**; this is a
verification baseline, not a minimum imposed on every plugin. For an older target,
verify the APIs there before choosing `astrbot_version`.
- Use [sources](references/sources.md) to find the relevant official guide and pinned
implementation. Prefer the target runtime's source/signatures when examples differ.
If live sources are unavailable, use the recorded baseline and state that limit.

## Implement the requested behavior

For a new command plugin, use the small configurable greeting scaffold as a starting
point, then replace its behavior and tests with the requested feature:

```bash
python <skill-dir>/scripts/scaffold_plugin.py <plugin-dir> --author "Author" --description "Plugin purpose" --command greet
```

`<plugin-dir>` must be a new `astrbot_plugin_<name>` directory. The script refuses to
overwrite an existing path. It creates a real `main.py`, configuration, metadata,
business logic, offline tests, and a separately invoked AstrBot runtime smoke test.
It does not clone AstrBot, install dependencies, or publish anything.
Use `--repo` for a known repository URL, `--astrbot-version` for a verified target,
and `--with-openapi` only for a plugin that needs the optional HTTP client example.
For an existing plugin, edit it directly; do not regenerate over it.

The [scaffolder](scripts/scaffold_plugin.py) renders the bundled [templates](assets/),
including production modules, metadata/configuration, and offline/SDK test examples.
The [static validator](scripts/validate_plugin.py) checks a plugin without importing it.

Read only the references needed for the feature:

| Need | Reference |
| --- | --- |
| Translate an open-ended request into concrete behavior | [Requirement mapping](references/nl-to-implementation.md) |
| Metadata, supported platforms, dependencies, release preparation | [Plugin packaging](references/plugin-new-checklist.md) |
| Commands, lifecycle, configuration, storage, messages, LLM calls/tools | [Python API patterns](references/api-patterns.md) |
| External access to an AstrBot server | [HTTP API integration](references/openapi-integration.md) |
| Offline tests, real SDK smoke tests, reload troubleshooting | [Testing guide](references/testing-guide.md) |

Keep these framework constraints in the implementation:

- Put the `Star` subclass in `main.py`; register handlers as methods with `self, event`.
Current AstrBot discovers subclasses automatically. Do not add the deprecated
`@register` to a new plugin; preserve old-version compatibility when repairing one.
- Use AstrBot's config schema and the injected `AstrBotConfig`. Read the supplied
values, not hardcoded copies of defaults. Do not log config objects or credentials.
- Store durable data under `data/plugin_data/<plugin_name>` or the plugin KV API.
Use the runtime's path helper, not the process's current directory.
- Use async network clients with explicit timeouts. Create tasks/connections in
`initialize()` when needed and cancel/await/close them in `terminate()` so reloading
does not leave duplicate jobs or open sessions.
- Use generic message components where possible. Check the selected adapter before
using platform-specific calls or claiming support for additional platforms.
- Inspect a hook's contract before choosing `yield`, a return value, or
`await event.send(...)`; LLM lifecycle hooks cannot be treated as command generators.

## Validate and deliver

1. Run the existing relevant tests. For new logic, test actual production functions
and error paths; the supplied tests demonstrate this with the greeting module and
optional HTTP client. Never leave passing placeholder assertions in the deliverable.
2. Run `python <skill-dir>/scripts/validate_plugin.py <plugin-dir>` after installing
`PyYAML` and `packaging` in the development environment. This checks source syntax,
metadata types/version constraints, and basic config shape without importing the
plugin. It does **not** certify API compatibility or marketplace acceptance.
3. Run Ruff on changed Python files. If the target SDK is installed, run the separate
SDK smoke test. Exercise load/reload and the requested command when a selected
local instance is running or runtime integration testing is within the task's
scope. SDK availability alone does not call for starting a server. Use
[testing](references/testing-guide.md) for commands.
4. Apply the relevant [delivery checks](references/compliance-checklist.md). Report
changed files, usage/configuration, checks actually run, and any untested runtime
or adapter behavior. Missing runtime access must not be reported as a passing
integration test.

Prepare a release or PR when requested. Use the already authorized destination;
plugin implementation alone does not imply publication to AstrBot Cloud.
13 changes: 13 additions & 0 deletions skills/astrbot-plugin-maker/assets/_conf_schema.json.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"greeting": {
"description": "Greeting prefix",
"type": "string",
"default": "Hello"
},
"max_name_length": {
"description": "Maximum displayed sender name length",
"type": "int",
"default": 40,
"hint": "Choose an integer from 1 to 200."
}
}
25 changes: 25 additions & 0 deletions skills/astrbot-plugin-maker/assets/dev-commands.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Run only when the task needs a local AstrBot development runtime.
# Official source setup: https://docs.astrbot.app/deploy/astrbot/cli.html
# Baseline verified: v4.28.0 requires Python 3.12+ and uv.

# If no existing checkout was selected:
git clone --branch v4.28.0 --depth 1 https://github.com/AstrBotDevs/AstrBot.git AstrBot

# POSIX shell:
mkdir -p AstrBot/data/plugins
git clone <your_plugin_repo_url> AstrBot/data/plugins/<your_plugin_name>
cd AstrBot
uv sync
uv run main.py

# PowerShell (alternative to the POSIX block):
New-Item -ItemType Directory -Force -Path AstrBot/data/plugins
git clone <your_plugin_repo_url> AstrBot/data/plugins/<your_plugin_name>
Set-Location AstrBot
uv sync
uv run main.py

# Replace placeholders with the user's real plugin repo and directory.
# For another target, use its version and setup instructions.
# After startup: WebUI -> plugin management -> reload the plugin.
# Check logs and invoke the configured command to verify the actual behavior.
29 changes: 29 additions & 0 deletions skills/astrbot-plugin-maker/assets/main.py.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from astrbot.api import AstrBotConfig
from astrbot.api.event import AstrMessageEvent, filter
from astrbot.api.star import Context, Star

from .plugin_logic import build_greeting


class GreetingPlugin(Star):
"""A small command plugin with settings supplied by AstrBot."""

def __init__(self, context: Context, config: AstrBotConfig):
super().__init__(context)
self.config = config

@filter.command("{{command}}")
async def greet(self, event: AstrMessageEvent):
"""Greet the sender using the configured greeting and name length."""
try:
reply = build_greeting(
event.get_sender_name(),
self.config.get("greeting", "Hello"),
self.config.get("max_name_length", 40),
)
except (TypeError, ValueError):
yield event.plain_result(
"Please check greeting and max_name_length in plugin settings."
)
return
yield event.plain_result(reply)
11 changes: 11 additions & 0 deletions skills/astrbot-plugin-maker/assets/metadata.yaml.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: {{name_yaml}}
desc: {{description_yaml}}
version: {{version_yaml}}
author: {{author_yaml}}
astrbot_version: {{astrbot_version_yaml}}
{{repo_line}}
# Add only when appropriate; verify adapter keys for the target runtime.
# display_name: My Plugin
# short_desc: A one-line summary
# support_platforms:
# - telegram
44 changes: 44 additions & 0 deletions skills/astrbot-plugin-maker/assets/openapi_client.py.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Optional client for GET /api/v1/im/bots, verified against AstrBot v4.28.0.

The caller owns the AsyncClient lifetime. Other endpoints need their own parsers;
chat can return SSE and file endpoints return bytes.
"""

import httpx


class ApiAuthError(RuntimeError):
"""The API key is invalid or lacks the required scope."""


class ApiResponseError(ValueError):
"""The server returned an unexpected JSON envelope or bot list."""


async def get_bot_ids(
client: httpx.AsyncClient, base_url: str, api_key: str
) -> list[str]:
if not api_key.strip():
raise ValueError("Configure an AstrBot API key before making requests")
response = await client.get(
f"{base_url.rstrip('/')}/api/v1/im/bots",
headers={"X-API-Key": api_key},
timeout=15.0,
follow_redirects=False,
)
if response.status_code == 401:
raise ApiAuthError("AstrBot rejected the API key (401)")
if response.status_code == 403:
raise ApiAuthError("AstrBot API key requires the im scope (403)")
response.raise_for_status()
try:
payload = response.json()
except ValueError as exc:
raise ApiResponseError("Expected a JSON response from /im/bots") from exc
if not isinstance(payload, dict) or payload.get("status") != "ok":
raise ApiResponseError("AstrBot returned an unsuccessful response")
data = payload.get("data")
bot_ids = data.get("bot_ids") if isinstance(data, dict) else None
if not isinstance(bot_ids, list) or any(not isinstance(x, str) for x in bot_ids):
raise ApiResponseError("Expected data.bot_ids to be a list of strings")
return bot_ids
43 changes: 43 additions & 0 deletions skills/astrbot-plugin-maker/assets/plugin-readme.md.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# {{plugin_name}}

Configurable greeting example. Replace its command, business logic, and tests with
your intended feature before release. In AstrBot, send `/{{command}}` using your
configured command prefix.

Settings in `_conf_schema.json`:

- `greeting`: greeting prefix; default `Hello`.
- `max_name_length`: displayed name length, 1–200; default `40`.

## Development

Run from the plugin directory in an isolated development environment:

```bash
python -m pip install -r requirements-dev.txt
{{runtime_install}}
python -m pytest -q
python -m ruff check .
python -m ruff format --check .
```

The default tests exercise `plugin_logic.py` without importing AstrBot. If generated
with `--with-openapi`, they also test `openapi_client.py` through a mocked HTTP
transport. That client is an optional building block; wire it into your requested
handler/configuration before claiming an HTTP feature is implemented.

## Runtime smoke test

In a development environment with the **target AstrBot and its dependencies**
installed, run `python -m pytest runtime_tests -q`. The template baseline is AstrBot
4.28.0 on Python 3.12+. The test uses real SDK registration, config, and event/result
classes. It does not boot the loader or contact a messaging platform.

For a loader/reload check, place the plugin in the runtime's
`data/plugins/{{plugin_name}}`, start AstrBot, and use WebUI plugin management to
reload it. Confirm the command reply and a changed configuration value. Check the
log for load errors. Adapt the runtime test when changing the example class/handler.

Set real author, description, repository, supported platforms, and verified version
constraints before publication. Keep runtime data and credentials outside the plugin
source tree. No platform compatibility claim is implied by this scaffold.
10 changes: 10 additions & 0 deletions skills/astrbot-plugin-maker/assets/plugin_logic.py.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Business logic shared by the command handler and offline tests."""


def build_greeting(name: str, greeting: str, max_name_length: int) -> str:
if not isinstance(greeting, str):
raise TypeError("greeting must be a string")
if type(max_name_length) is not int or not 1 <= max_name_length <= 200:
raise ValueError("max_name_length must be an integer between 1 and 200")
display_name = (name.strip() or "friend")[:max_name_length]
return f"{greeting}, {display_name}!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pytest>=8,<10
PyYAML>=6,<7
packaging>=24
ruff>=0.15
# Install the target AstrBot separately to run runtime_tests/.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Runtime dependency for the optional openapi_client.py example.
# Add dependencies actually imported by your implementation.
httpx>=0.28,<1
7 changes: 7 additions & 0 deletions skills/astrbot-plugin-maker/assets/ruff.toml.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
target-version = "py312"

[lint]
select = ["E", "F", "I"]

[lint.isort]
known-first-party = ["plugin_logic", "openapi_client"]
Loading
Loading