defmodule MicrowavepropWeb.AgentSkillsController do @moduledoc """ Publishes an Agent Skills Discovery (v0.2.0) index at `/.well-known/agent-skills/index.json` plus the referenced skill documents. Each document is a short markdown file describing one concrete task an agent can perform against this service's public endpoints. Digests are computed at compile time from the source files. """ use MicrowavepropWeb, :controller @schema_url "https://agentskills.io/schemas/v0.2.0/skills.schema.json" @skill_sources [ %{ name: "fetch-contacts", description: "Retrieve every amateur-radio contact in the propagation calibration dataset as a JSON array of compact tuples.", path: "priv/agent-skills/fetch-contacts.md" }, %{ name: "read-algorithm", description: "Obtain the full NTMS propagation scoring algorithm documentation, including factor weights and ITU-R model references, as markdown.", path: "priv/agent-skills/read-algorithm.md" } ] for %{path: path} <- @skill_sources do @external_resource path end @skills Enum.map(@skill_sources, fn %{name: name, description: desc, path: path} -> body = File.read!(path) sha = :sha256 |> :crypto.hash(body) |> Base.encode16(case: :lower) %{name: name, description: desc, body: body, sha256: sha} end) @spec index(Plug.Conn.t(), map()) :: Plug.Conn.t() def index(conn, _params) do entries = Enum.map(@skills, fn s -> %{ name: s.name, type: "text/markdown", description: s.description, url: url(conn, ~p"/.well-known/agent-skills/#{s.name}/SKILL.md"), sha256: s.sha256 } end) body = Jason.encode!(%{"$schema" => @schema_url, skills: entries}) conn |> put_resp_content_type("application/json") |> put_resp_header("cache-control", "public, max-age=3600") |> send_resp(200, body) end @spec skill(Plug.Conn.t(), map()) :: Plug.Conn.t() def skill(conn, %{"name" => name}) do case Enum.find(@skills, &(&1.name == name)) do nil -> send_resp(conn, 404, "") s -> conn |> put_resp_content_type("text/markdown", "utf-8") |> put_resp_header("cache-control", "public, max-age=3600") |> send_resp(200, s.body) end end end