molecule-core/workspace/agents_md.py
Hongming Wang d8026347e5 chore: open-source restructure — rename dirs, remove internal files, scrub secrets
Renames:
- platform/ → workspace-server/ (Go module path stays as "platform" for
  external dep compat — will update after plugin module republish)
- workspace-template/ → workspace/

Removed (moved to separate repos or deleted):
- PLAN.md — internal roadmap (move to private project board)
- HANDOFF.md, AGENTS.md — one-time internal session docs
- .claude/ — gitignored entirely (local agent config)
- infra/cloudflare-worker/ → Molecule-AI/molecule-tenant-proxy
- org-templates/molecule-dev/ → standalone template repo
- .mcp-eval/ → molecule-mcp-server repo
- test-results/ — ephemeral, gitignored

Security scrubbing:
- Cloudflare account/zone/KV IDs → placeholders
- Real EC2 IPs → <EC2_IP> in all docs
- CF token prefix, Neon project ID, Fly app names → redacted
- Langfuse dev credentials → parameterized
- Personal runner username/machine name → generic

Community files:
- CONTRIBUTING.md — build, test, branch conventions
- CODE_OF_CONDUCT.md — Contributor Covenant 2.1

All Dockerfiles, CI workflows, docker-compose, railway.toml, render.yaml,
README, CLAUDE.md updated for new directory names.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 00:24:44 -07: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)