Files
hermes-agent/scripts/gen_plugin_api_docs.py
unsupportedpastels 0502fe0597 feat(plugins): one published list of what plugins may import
The public list `hermes plugins validate` checks plugin imports against
was a hardcoded frozenset inside hermes_cli/plugin_validate_private_imports.py,
with no page that showed it to plugin authors.

Move the list, unchanged (89 entries), into hermes_cli/data/plugin_api.json
(already shipped as package data via `data/*.json`) and load it once in the
validator. scripts/gen_plugin_api_docs.py renders
website/docs/developer-guide/plugins/public-api.md from the same file, and a
test fails when the page is stale. Validate output is unchanged for the same
inputs.
2026-11-05 18:47:10 -05:00

70 lines
2.5 KiB
Python
Executable File

#!/usr/bin/env -S bash -c 'exec "$BASH" "$(dirname "$0")/run-in-hermes-env" python3 "$0" "$@"'
"""Regenerate website/docs/developer-guide/plugins/public-api.md from hermes_cli/data/plugin_api.json.
The same file is the public list ``hermes plugins validate`` checks plugin imports against, so the
page and the warning cannot drift. ``--check`` writes nothing and exits 1 when the page is stale.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
DATA = ROOT / "hermes_cli" / "data" / "plugin_api.json"
OUT = ROOT / "website" / "docs" / "developer-guide" / "plugins" / "public-api.md"
HEADER = """---
sidebar_label: "Public plugin API"
title: "Public plugin API"
description: "The Hermes modules and names a plugin may import; hermes plugins validate warns on any other Hermes import"
---
<!-- GENERATED by scripts/gen_plugin_api_docs.py from hermes_cli/data/plugin_api.json — DO NOT EDIT. -->
<!-- tests/hermes_cli/test_plugin_validate_private_imports.py fails when this page is stale. -->
# Public plugin API
These are the Hermes modules and names a plugin may import. `hermes plugins validate` reads the same
list (`hermes_cli/data/plugin_api.json`) and warns, without failing, on any other import of a Hermes
module (catalog rule 9: public surfaces only). Imports of the standard library, third-party packages,
the plugin's own modules, and imports under `if TYPE_CHECKING:` are never flagged.
A module row allows the module and every name in it that does not start with an underscore. A row
with a name allows that one name only.
| Module | Name | Listed from |
|---|---|---|
"""
def render() -> str:
data = json.loads(DATA.read_text(encoding="utf-8-sig"))
rows = []
for group in data["groups"]:
for entry in group["entries"]:
module, _, name = entry.partition(":")
rows.append(f"| `{module}` | {f'`{name}`' if name else 'whole module'} | {group['source']} |")
return HEADER + "\n".join(rows) + "\n"
def check(out: Path = OUT) -> int:
committed = out.read_text(encoding="utf-8-sig") if out.exists() else ""
if committed == render():
return 0
print(f"{out.relative_to(ROOT)} is stale; run scripts/gen_plugin_api_docs.py", file=sys.stderr)
return 1
def main(argv) -> int:
if "--check" in argv:
return check()
OUT.write_text(render(), encoding="utf-8")
print(f"wrote {OUT.relative_to(ROOT)}")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))