molecule-core/workspace-template/agents_md.py
2026-04-17 16:20:39 +00:00

75 lines
2.9 KiB
Python

"""AGENTS.md auto-generation for Molecule AI workspaces.
Implements the AAIF / Linux Foundation AGENTS.md standard so that peer agents
and orchestration tools can discover this workspace's identity, role, A2A
endpoint, and available tools without reading the full system prompt.
Usage::
from agents_md import generate_agents_md
generate_agents_md(config_dir="/configs", output_path="/workspace/AGENTS.md")
The function is called automatically at container startup (see main.py).
"""
import logging
import os
from pathlib import Path
logger = logging.getLogger(__name__)
def generate_agents_md(config_dir: str, output_path: str) -> None:
"""Generate (or regenerate) AGENTS.md from the workspace config.yaml.
Always overwrites ``output_path`` — no stale-file guard. Re-calling
after editing config.yaml produces a fresh file reflecting the changes.
Args:
config_dir: Directory containing config.yaml (same convention as
``load_config`` in config.py).
output_path: Absolute path where AGENTS.md will be written.
The parent directory is expected to exist.
"""
from config import load_config
cfg = load_config(config_dir)
# ── A2A Endpoint ─────────────────────────────────────────────────────────
# AGENT_URL env var takes priority (production deployments behind a proxy).
# Otherwise derive from the configured a2a.port (default 8000).
endpoint = os.environ.get("AGENT_URL") or f"http://localhost:{cfg.a2a.port}/a2a"
# ── Role ─────────────────────────────────────────────────────────────────
# Fall back to description when the role field is absent so legacy
# config.yaml files (without a role key) still produce meaningful output.
role = cfg.role if cfg.role else cfg.description
# ── MCP Tools ────────────────────────────────────────────────────────────
# tools (skill names) + plugins (installed plugin names) form the combined
# capability surface visible to peer agents.
all_tools = list(cfg.tools) + list(cfg.plugins)
if all_tools:
tools_section = "\n".join(f"- {t}" for t in all_tools)
else:
tools_section = "None"
content = (
f"# {cfg.name}\n"
f"\n"
f"**Role:** {role}\n"
f"\n"
f"## Description\n"
f"{cfg.description}\n"
f"\n"
f"## A2A Endpoint\n"
f"{endpoint}\n"
f"\n"
f"## MCP Tools\n"
f"{tools_section}\n"
)
Path(output_path).write_text(content, encoding="utf-8")
logger.info("Generated AGENTS.md at %s for workspace %r", output_path, cfg.name)