diff --git a/.github/workflows/upload_to_pypi.yml b/.github/workflows/upload_to_pypi.yml
deleted file mode 100644
index 03fad4eba0c..00000000000
--- a/.github/workflows/upload_to_pypi.yml
+++ /dev/null
@@ -1,164 +0,0 @@
-name: Publish to PyPI
-
-# Triggered by CalVer tag pushes from scripts/release.py (e.g. v2026.5.15)
-# Can also be triggered manually from the Actions tab as an escape hatch.
-on:
- push:
- tags:
- - "v20*" # CalVer tags: v2026.5.15, v2026.5.15.2, etc.
- workflow_dispatch:
- inputs:
- confirm_tag:
- description: "Tag to publish (e.g. v2026.5.15). Must already exist."
- required: true
- type: string
-
-# Restrict default token to read-only; each job escalates as needed.
-permissions:
- contents: read
-
-# Prevent overlapping publishes (e.g. two same-day tags pushed quickly).
-concurrency:
- group: pypi-publish
- cancel-in-progress: false
-
-jobs:
- build:
- name: Build distribution 📦
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- persist-credentials: false
- # On workflow_dispatch, check out the confirmed tag.
- ref: ${{ inputs.confirm_tag || github.ref }}
- fetch-tags: true
-
- - name: Validate tag exists
- if: github.event_name == 'workflow_dispatch'
- run: |
- if ! git tag -l "${{ inputs.confirm_tag }}" | grep -q .; then
- echo "::error::Tag '${{ inputs.confirm_tag }}' does not exist in the repo"
- exit 1
- fi
-
- - name: Set up Python
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- with:
- python-version: "3.13"
-
- - name: Install uv
- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # 8.2.0
-
- - name: Set up Node.js
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
- with:
- node-version: "22"
-
- - name: Build web dashboard
- run: cd web && npm ci && npm run build
-
- - name: Build TUI bundle
- run: cd ui-tui && npm ci && npm run build
-
- - name: Bundle TUI into hermes_cli
- run: |
- mkdir -p hermes_cli/tui_dist
- cp ui-tui/dist/entry.js hermes_cli/tui_dist/entry.js
-
- - name: Verify frontend assets exist
- run: |
- test -f hermes_cli/web_dist/index.html || { echo "ERROR: web_dist not built"; exit 1; }
- test -f hermes_cli/tui_dist/entry.js || { echo "ERROR: tui_dist not built"; exit 1; }
-
- - name: Bundle install scripts into wheel
- run: |
- mkdir -p hermes_cli/scripts
- cp scripts/install.sh hermes_cli/scripts/install.sh
- cp scripts/install.ps1 hermes_cli/scripts/install.ps1
-
- - name: Build wheel and sdist
- run: uv build --sdist --wheel
-
- - name: Upload distribution artifacts
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
- with:
- name: python-package-distributions
- path: dist/
-
- publish:
- name: Publish to PyPI
- needs: build
- runs-on: ubuntu-latest
- environment:
- name: pypi
- url: https://pypi.org/p/hermes-agent
- permissions:
- id-token: write # OIDC trusted publishing
-
- steps:
- - name: Download distribution artifacts
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
- with:
- name: python-package-distributions
- path: dist/
-
- - name: Publish to PyPI
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
- with:
- skip-existing: true
-
- sign:
- name: Sign and attach to GitHub Release
- # Only runs on tag pushes — release.py creates the GitHub Release,
- # and workflow_dispatch won't have a matching release to attach to.
- if: startsWith(github.ref, 'refs/tags/')
- needs: publish
- runs-on: ubuntu-latest
- permissions:
- contents: write # attach assets to the existing release
- id-token: write # sigstore signing
-
- steps:
- - name: Download distribution artifacts
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
- with:
- name: python-package-distributions
- path: dist/
-
- - name: Wait for GitHub Release to exist
- env:
- GITHUB_TOKEN: ${{ github.token }}
- # release.py creates the GitHub Release after pushing the tag,
- # but this workflow starts from the tag push — wait for it.
- run: |
- for i in $(seq 1 30); do
- if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
- echo "Release $GITHUB_REF_NAME found"
- exit 0
- fi
- echo "Waiting for release... ($i/30)"
- sleep 10
- done
- echo "::warning::Release $GITHUB_REF_NAME not found after 5 minutes — skipping signature upload"
- echo "skip_sign=true" >> "$GITHUB_ENV"
-
- - name: Sign with Sigstore
- if: env.skip_sign != 'true'
- uses: sigstore/gh-action-sigstore-python@04cffa1d795717b140764e8b640de88853c92acc # v3.3.0
- with:
- inputs: >-
- ./dist/*.tar.gz
- ./dist/*.whl
-
- - name: Attach signed artifacts to GitHub Release
- if: env.skip_sign != 'true'
- env:
- GITHUB_TOKEN: ${{ github.token }}
- # release.py already created the GitHub Release — just upload
- # the Sigstore signatures alongside the existing assets.
- run: >-
- gh release upload
- "$GITHUB_REF_NAME" dist/*.sigstore.json
- --repo "$GITHUB_REPOSITORY"
- --clobber
diff --git a/acp_registry/agent.json b/acp_registry/agent.json
deleted file mode 100644
index dc1e05bb27b..00000000000
--- a/acp_registry/agent.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "id": "hermes-agent",
- "name": "Hermes Agent",
- "version": "0.18.0",
- "description": "Self-improving open-source AI agent by Nous Research with ACP editor integration, persistent memory, skills, and rich tool support.",
- "repository": "https://github.com/NousResearch/hermes-agent",
- "website": "https://hermes-agent.nousresearch.com/docs/user-guide/features/acp",
- "authors": ["Nous Research"],
- "license": "MIT",
- "distribution": {
- "uvx": {
- "package": "hermes-agent[acp]==0.18.0",
- "args": ["hermes-acp"]
- }
- }
-}
diff --git a/acp_registry/icon.svg b/acp_registry/icon.svg
deleted file mode 100644
index f42c0daea45..00000000000
--- a/acp_registry/icon.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/hermes_cli/banner.py b/hermes_cli/banner.py
index 06fc84a4ac1..7c611107868 100644
--- a/hermes_cli/banner.py
+++ b/hermes_cli/banner.py
@@ -1,3 +1,5 @@
+
+
"""Welcome banner, ASCII art, skills summary, and update check for the CLI.
Pure display functions with no HermesCLI state dependency.
@@ -882,6 +884,7 @@ def build_welcome_banner(console: "Console", model: str, cwd: str,
from hermes_cli.config import (
detect_install_method,
format_unsupported_install_warning,
+ get_project_root,
is_unsupported_install_method,
get_project_root
)
diff --git a/hermes_cli/config.py b/hermes_cli/config.py
index 4625e58bd9b..80c430b7919 100644
--- a/hermes_cli/config.py
+++ b/hermes_cli/config.py
@@ -548,12 +548,14 @@ def recommended_update_command() -> str:
#
# pip/PyPI and Homebrew are NOT an officially supported distribution method
# (see website/docs/getting-started/platform-support.md, "Unsupported"
-# section). pip exists on PyPI for internal/CI reasons, not end-user installs;
-# Homebrew is a legacy packaging path. Unlike NixOS/Homebrew "managed mode"
-# (which hard-blocks config writes), this is a warn-don't-block deprecation
-# notice surfaced everywhere the user might see install-method state: the CLI
-# banner, the TUI/desktop session info panel, and ``hermes update``. NixOS
-# stays fully supported (Tier 2) and must never hit this path.
+# section). Hermes Agent is no longer published to PyPI or packaged for
+# Homebrew — existing installs from either keep running on whatever version
+# they last pulled, but will never see a newer one. Unlike NixOS/Homebrew
+# "managed mode" (which hard-blocks config writes), this is a warn-don't-block
+# deprecation notice surfaced everywhere the user might see install-method
+# state: the CLI banner, the TUI/desktop session info panel, and
+# ``hermes update``. NixOS stays fully supported (Tier 2) and must never hit
+# this path.
PLATFORM_SUPPORT_DOCS_URL = "https://hermes-agent.nousresearch.com/docs/getting-started/platform-support"
diff --git a/packaging/homebrew/README.md b/packaging/homebrew/README.md
deleted file mode 100644
index e53d3fd0b3e..00000000000
--- a/packaging/homebrew/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
-Homebrew packaging notes for Hermes Agent.
-
-Use `packaging/homebrew/hermes-agent.rb` as a tap or `homebrew-core` starting point.
-
-Key choices:
-- Stable builds should target the semver-named sdist asset attached to each GitHub release, not the CalVer tag tarball.
-- `faster-whisper` now lives in the `voice` extra, which keeps wheel-only transitive dependencies out of the base Homebrew formula.
-- The wrapper exports `HERMES_BUNDLED_SKILLS`, `HERMES_OPTIONAL_SKILLS`, and `HERMES_MANAGED=homebrew` so packaged installs keep runtime assets and defer upgrades to Homebrew.
-
-Typical update flow:
-1. Bump the formula `url`, `version`, and `sha256`.
-2. Refresh Python resources with `brew update-python-resources --print-only hermes-agent`.
-3. Keep `ignore_packages: %w[certifi cryptography pydantic]`.
-4. Verify `brew audit --new --strict hermes-agent` and `brew test hermes-agent`.
diff --git a/packaging/homebrew/hermes-agent.rb b/packaging/homebrew/hermes-agent.rb
deleted file mode 100644
index 7c00fc6acf8..00000000000
--- a/packaging/homebrew/hermes-agent.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-class HermesAgent < Formula
- include Language::Python::Virtualenv
-
- desc "Self-improving AI agent that creates skills from experience"
- homepage "https://hermes-agent.nousresearch.com"
- # Stable source should point at the semver-named sdist asset attached by
- # scripts/release.py, not the CalVer tag tarball.
- url "https://github.com/NousResearch/hermes-agent/releases/download/v2026.3.30/hermes_agent-0.6.0.tar.gz"
- sha256 ""
- license "MIT"
-
- depends_on "certifi" => :no_linkage
- depends_on "cryptography" => :no_linkage
- depends_on "libyaml"
- depends_on "python@3.14"
-
- pypi_packages ignore_packages: %w[certifi cryptography pydantic]
-
- # Refresh resource stanzas after bumping the source url/version:
- # brew update-python-resources --print-only hermes-agent
-
- def install
- venv = virtualenv_create(libexec, "python3.14")
- venv.pip_install resources
- venv.pip_install buildpath
-
- pkgshare.install "skills", "optional-skills"
-
- %w[hermes hermes-agent hermes-acp].each do |exe|
- next unless (libexec/"bin"/exe).exist?
-
- (bin/exe).write_env_script(
- libexec/"bin"/exe,
- HERMES_BUNDLED_SKILLS: pkgshare/"skills",
- HERMES_OPTIONAL_SKILLS: pkgshare/"optional-skills",
- HERMES_MANAGED: "homebrew"
- )
- end
- end
-
- test do
- assert_match "Hermes Agent v#{version}", shell_output("#{bin}/hermes version")
-
- managed = shell_output("#{bin}/hermes update 2>&1")
- assert_match "managed by Homebrew", managed
- assert_match "brew upgrade hermes-agent", managed
- end
-end
diff --git a/scripts/check-windows-footguns.py b/scripts/check-windows-footguns.py
index 7ae7ca50c4e..c0ca6622ec3 100644
--- a/scripts/check-windows-footguns.py
+++ b/scripts/check-windows-footguns.py
@@ -576,7 +576,6 @@ def main(argv: list[str]) -> int:
REPO_ROOT / "plugins",
REPO_ROOT / "scripts",
REPO_ROOT / "acp_adapter",
- REPO_ROOT / "acp_registry",
]
roots = [r for r in roots if r.exists()]
elif args.diff:
diff --git a/scripts/release.py b/scripts/release.py
index 11f374e9f20..4f05af8dbfe 100755
--- a/scripts/release.py
+++ b/scripts/release.py
@@ -21,7 +21,6 @@ Usage:
"""
import argparse
-import json
import re
import shutil
import subprocess
@@ -34,11 +33,6 @@ REPO_ROOT = Path(__file__).resolve().parent.parent
VERSION_FILE = REPO_ROOT / "hermes_cli" / "__init__.py"
PYPROJECT_FILE = REPO_ROOT / "pyproject.toml"
-# ACP Registry manifest must stay version-locked with pyproject.toml.
-# tests/acp/test_registry_manifest.py enforces this lockstep so the release
-# bump touches both files atomically.
-ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
-
# ──────────────────────────────────────────────────────────────────────
# Git email → GitHub username mapping
# ──────────────────────────────────────────────────────────────────────
@@ -1973,70 +1967,6 @@ def update_version_files(semver: str, calver_date: str):
)
desktop_pkg.write_text(pkg_text, encoding="utf-8")
- # Update ACP Registry manifest + npm launcher (must stay version-locked
- # with pyproject — enforced by tests/acp/test_registry_manifest.py).
- _update_acp_registry_versions(semver)
-
-
-def _update_acp_registry_versions(semver: str) -> None:
- """Bump the ACP Registry manifest's version + uvx package pin in lockstep
- with pyproject.
-
- Skips silently if the manifest is missing — older release branches predate
- the ACP Registry assets.
- """
- if ACP_REGISTRY_MANIFEST.exists():
- manifest = json.loads(ACP_REGISTRY_MANIFEST.read_text(encoding="utf-8"))
- manifest["version"] = semver
- uvx = manifest.get("distribution", {}).get("uvx", {})
- if "package" in uvx:
- uvx["package"] = f"hermes-agent[acp]=={semver}"
- # Preserve trailing newline + 2-space indent the file already uses.
- ACP_REGISTRY_MANIFEST.write_text(
- json.dumps(manifest, indent=2) + "\n", encoding="utf-8"
- )
-
-
-def build_release_artifacts(semver: str) -> list[Path]:
- """Build sdist/wheel artifacts for the current release.
-
- Tries ``uv build`` first (matching the CI workflow), falls back to
- ``python -m build`` if uv is unavailable.
- """
- dist_dir = REPO_ROOT / "dist"
- shutil.rmtree(dist_dir, ignore_errors=True)
-
- # Prefer uv build (matches CI workflow), fall back to python -m build.
- uv_bin = shutil.which("uv")
- if uv_bin:
- cmd = [uv_bin, "build", "--sdist", "--wheel"]
- else:
- cmd = [sys.executable, "-m", "build", "--sdist", "--wheel"]
-
- result = subprocess.run(
- cmd,
- cwd=str(REPO_ROOT),
- capture_output=True,
- text=True,
- )
- if result.returncode != 0:
- print(" ⚠ Could not build Python release artifacts.")
- stderr = result.stderr.strip()
- stdout = result.stdout.strip()
- if stderr:
- print(f" {stderr.splitlines()[-1]}")
- elif stdout:
- print(f" {stdout.splitlines()[-1]}")
- print(" Install uv or the 'build' package to attach sdist/wheel assets.")
- return []
-
- artifacts = sorted(p for p in dist_dir.iterdir() if p.is_file())
- matching = [p for p in artifacts if semver in p.name]
- if not matching:
- print(" ⚠ Built artifacts did not match the expected release version.")
- return []
- return matching
-
def resolve_author(name: str, email: str) -> str:
"""Resolve a git author to a GitHub @mention."""
@@ -2376,8 +2306,6 @@ def main():
# Commit version bump
add_files = [str(VERSION_FILE), str(PYPROJECT_FILE)]
- if ACP_REGISTRY_MANIFEST.exists():
- add_files.append(str(ACP_REGISTRY_MANIFEST))
add_result = git_result("add", *add_files)
if add_result.returncode != 0:
print(f" ✗ Failed to stage version files: {add_result.stderr.strip()}")
@@ -2410,14 +2338,6 @@ def main():
print(" Continue manually after fixing access:")
print(" git push origin HEAD --tags")
- # Build semver-named Python artifacts so downstream packagers
- # (e.g. Homebrew) can target them without relying on CalVer tag names.
- artifacts = build_release_artifacts(new_version)
- if artifacts:
- print(" ✓ Built release artifacts:")
- for artifact in artifacts:
- print(f" - {artifact.relative_to(REPO_ROOT)}")
-
# Create GitHub release
changelog_file = REPO_ROOT / ".release_notes.md"
changelog_file.write_text(changelog, encoding="utf-8")
@@ -2427,7 +2347,6 @@ def main():
"--title", f"Hermes Agent v{new_version} ({calver_date})",
"--notes-file", str(changelog_file),
]
- gh_cmd.extend(str(path) for path in artifacts)
gh_bin = shutil.which("gh")
if gh_bin:
@@ -2452,9 +2371,9 @@ def main():
print(f" Tag was created locally. Create the release manually:")
print(
f" gh release create {tag_name} --title 'Hermes Agent v{new_version} ({calver_date})' "
- f"--notes-file .release_notes.md {' '.join(str(path) for path in artifacts)}"
+ f"--notes-file .release_notes.md"
)
- print(f"\n ✓ Release artifacts prepared for manual publish: v{new_version} ({tag_name})")
+ print(f"\n ✓ Release prepared for manual publish: v{new_version} ({tag_name})")
else:
print(f"\n{'='*60}")
print(f" Dry run complete. To publish, add --publish")
diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh
index a54b7163d3f..d7b471a8e0a 100755
--- a/scripts/run_tests.sh
+++ b/scripts/run_tests.sh
@@ -37,23 +37,26 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+if [ -z "$HERMES_PYTHON" ]; then
# ── Activate venv ───────────────────────────────────────────────────────────
-VENV=""
-for candidate in "$REPO_ROOT/.venv" "$REPO_ROOT/venv" "$HOME/.hermes/hermes-agent/venv"; do
- if [ -f "$candidate/bin/activate" ]; then
- VENV="$candidate"
- break
+ VENV=""
+ for candidate in "$REPO_ROOT/.venv" "$REPO_ROOT/venv" "$HOME/.hermes/hermes-agent/venv"; do
+ if [ -f "$candidate/bin/activate" ]; then
+ VENV="$candidate"
+ break
+ fi
+ done
+
+ if [ -z "$VENV" ]; then
+ echo "error: no virtualenv found in $REPO_ROOT/.venv or $REPO_ROOT/venv" >&2
+ exit 1
fi
-done
-if [ -z "$VENV" ]; then
- echo "error: no virtualenv found in $REPO_ROOT/.venv or $REPO_ROOT/venv" >&2
- exit 1
+ PYTHON="$VENV/bin/python"
+else
+ PYTHON=$HERMES_PYTHON
fi
-PYTHON="$VENV/bin/python"
-
-
# ── Live-gateway plugin (computed before we drop env) ───────────────────────
EXTRA_PYTHONPATH=""
EXTRA_PYTEST_PLUGINS=""
diff --git a/tests/acp/test_registry_manifest.py b/tests/acp/test_registry_manifest.py
deleted file mode 100644
index 633b4a8494c..00000000000
--- a/tests/acp/test_registry_manifest.py
+++ /dev/null
@@ -1,90 +0,0 @@
-"""Tests for ACP Registry metadata shipped with Hermes."""
-
-from __future__ import annotations
-
-import json
-import re
-import tomllib
-from pathlib import Path
-import xml.etree.ElementTree as ET
-
-ROOT = Path(__file__).resolve().parents[2]
-MANIFEST = ROOT / "acp_registry" / "agent.json"
-ICON = ROOT / "acp_registry" / "icon.svg"
-FORBIDDEN_MANIFEST_KEYS = {"schema_version", "display_name"}
-ALLOWED_DISTRIBUTIONS = {"binary", "npx", "uvx"}
-
-
-def _manifest() -> dict:
- return json.loads(MANIFEST.read_text(encoding="utf-8"))
-
-
-def _pyproject_version() -> str:
- data = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))
- return data["project"]["version"]
-
-
-def test_agent_json_matches_official_registry_required_fields():
- data = _manifest()
-
- assert FORBIDDEN_MANIFEST_KEYS.isdisjoint(data)
- assert data["id"] == "hermes-agent"
- assert re.fullmatch(r"[a-z][a-z0-9-]*", data["id"])
- assert data["name"] == "Hermes Agent"
- assert data["description"]
- assert data["repository"] == "https://github.com/NousResearch/hermes-agent"
- assert data["website"].startswith("https://hermes-agent.nousresearch.com/")
- assert data["authors"] == ["Nous Research"]
- assert data["license"] == "MIT"
- assert set(data["distribution"]) <= ALLOWED_DISTRIBUTIONS
-
-
-def test_agent_json_uses_uvx_distribution_without_local_command_fields():
- data = _manifest()
-
- assert set(data["distribution"]) == {"uvx"}
- uvx = data["distribution"]["uvx"]
- # Schema allows {package, args, env}; we use {package, args}.
- assert set(uvx) <= {"package", "args", "env"}
- assert "package" in uvx
- assert uvx["package"] == f"hermes-agent[acp]=={data['version']}"
- assert uvx["args"] == ["hermes-acp"]
- # Old command-shape fields must not leak back in.
- assert "type" not in data["distribution"]
- assert "command" not in data["distribution"]
-
-
-def test_agent_json_version_matches_pyproject():
- assert _manifest()["version"] == _pyproject_version()
-
-
-def test_agent_json_pins_uvx_package_to_pyproject_version():
- """The registry CI rejects ``@latest`` and floating pins; the manifest must
- always reference the exact PyPI version listed in pyproject.toml."""
- assert _manifest()["distribution"]["uvx"]["package"] == (
- f"hermes-agent[acp]=={_pyproject_version()}"
- )
-
-
-def test_icon_svg_is_16x16_current_color():
- root = ET.fromstring(ICON.read_text(encoding="utf-8"))
-
- assert root.attrib["viewBox"] == "0 0 16 16"
- assert root.attrib["width"] == "16"
- assert root.attrib["height"] == "16"
-
-
-def test_icon_svg_has_no_hardcoded_colors_or_gradients():
- text = ICON.read_text(encoding="utf-8")
-
- assert "linearGradient" not in text
- assert "radialGradient" not in text
- assert "url(#" not in text
- assert not re.search(r"#[0-9a-fA-F]{3,8}\b", text)
-
- root = ET.fromstring(text)
- for element in root.iter():
- for attr in ("fill", "stroke"):
- value = element.attrib.get(attr)
- if value is not None:
- assert value in {"currentColor", "none"}
diff --git a/tests/scripts/test_release_acp_registry.py b/tests/scripts/test_release_acp_registry.py
deleted file mode 100644
index 4d20cda25bd..00000000000
--- a/tests/scripts/test_release_acp_registry.py
+++ /dev/null
@@ -1,113 +0,0 @@
-"""Tests for the ACP Registry version-lockstep bump in scripts/release.py.
-
-The official ACP Registry manifest must match ``pyproject.toml`` exactly —
-``tests/acp/test_registry_manifest.py`` enforces this at lint time, and the
-upstream registry CI rejects ``@latest`` / floating pins. The release script
-is the single place that bumps the manifest in lockstep with pyproject; if
-that bump ever silently breaks, weekly releases fail the manifest test
-until someone hand-edits the JSON.
-"""
-
-from __future__ import annotations
-
-import importlib.util
-import json
-from pathlib import Path
-
-
-def _load_release_module(monkeypatch, tmp_root: Path):
- """Import scripts/release.py with REPO_ROOT pinned to a temp tree."""
- spec = importlib.util.spec_from_file_location(
- "_release_under_test",
- Path(__file__).resolve().parents[2] / "scripts" / "release.py",
- )
- assert spec and spec.loader
- module = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(module)
-
- monkeypatch.setattr(module, "REPO_ROOT", tmp_root)
- monkeypatch.setattr(
- module, "ACP_REGISTRY_MANIFEST", tmp_root / "acp_registry" / "agent.json"
- )
- return module
-
-
-def _write_manifest(root: Path, version: str) -> None:
- manifest_dir = root / "acp_registry"
- manifest_dir.mkdir(parents=True)
- (manifest_dir / "agent.json").write_text(
- json.dumps(
- {
- "id": "hermes-agent",
- "name": "Hermes Agent",
- "version": version,
- "description": "test",
- "distribution": {
- "uvx": {
- "package": f"hermes-agent[acp]=={version}",
- "args": ["hermes-acp"],
- }
- },
- },
- indent=2,
- )
- + "\n",
- encoding="utf-8",
- )
-
-
-def test_update_acp_registry_versions_bumps_manifest_and_pin(monkeypatch, tmp_path):
- _write_manifest(tmp_path, "0.13.0")
- module = _load_release_module(monkeypatch, tmp_path)
-
- module._update_acp_registry_versions("0.14.0")
-
- manifest = json.loads(
- (tmp_path / "acp_registry" / "agent.json").read_text(encoding="utf-8")
- )
- assert manifest["version"] == "0.14.0"
- assert manifest["distribution"]["uvx"]["package"] == "hermes-agent[acp]==0.14.0"
- # args stay untouched so we don't accidentally rewrite them.
- assert manifest["distribution"]["uvx"]["args"] == ["hermes-acp"]
-
-
-def test_update_acp_registry_versions_is_silent_when_manifest_missing(
- monkeypatch, tmp_path
-):
- """Older release branches predate the ACP Registry asset — must no-op."""
- module = _load_release_module(monkeypatch, tmp_path)
-
- # No fixture written; function should not raise.
- module._update_acp_registry_versions("0.14.0")
-
-
-def test_update_version_files_bumps_manifest_alongside_pyproject(
- monkeypatch, tmp_path
-):
- """End-to-end: update_version_files() is the function release.py actually
- calls, so it must drive the manifest bump too."""
- _write_manifest(tmp_path, "0.13.0")
- (tmp_path / "pyproject.toml").write_text(
- '[project]\nname = "hermes-agent"\nversion = "0.13.0"\n', encoding="utf-8"
- )
- version_dir = tmp_path / "hermes_cli"
- version_dir.mkdir()
- (version_dir / "__init__.py").write_text(
- '__version__ = "0.13.0"\n__release_date__ = "2026-05-14"\n',
- encoding="utf-8",
- )
-
- module = _load_release_module(monkeypatch, tmp_path)
- monkeypatch.setattr(module, "VERSION_FILE", version_dir / "__init__.py")
- monkeypatch.setattr(module, "PYPROJECT_FILE", tmp_path / "pyproject.toml")
-
- module.update_version_files("0.14.0", "2026-05-21")
-
- pyproject_text = (tmp_path / "pyproject.toml").read_text(encoding="utf-8")
- assert 'version = "0.14.0"' in pyproject_text
-
- manifest = json.loads(
- (tmp_path / "acp_registry" / "agent.json").read_text(encoding="utf-8")
- )
- assert manifest["version"] == "0.14.0"
- assert manifest["distribution"]["uvx"]["package"] == "hermes-agent[acp]==0.14.0"
diff --git a/website/docs/developer-guide/acp-internals.md b/website/docs/developer-guide/acp-internals.md
index 89ae398b6af..f177c33dc3b 100644
--- a/website/docs/developer-guide/acp-internals.md
+++ b/website/docs/developer-guide/acp-internals.md
@@ -17,7 +17,6 @@ Key implementation files:
- `acp_adapter/permissions.py`
- `acp_adapter/tools.py`
- `acp_adapter/auth.py`
-- `acp_registry/agent.json`
## Boot flow
@@ -31,8 +30,6 @@ hermes acp / hermes-acp / python -m acp_adapter
-> acp.run_agent(agent, use_unstable_protocol=True)
```
-The Zed ACP Registry path launches the same adapter through `uvx --from 'hermes-agent[acp]==' hermes-acp`, pointed at the `hermes-agent` PyPI release.
-
Stdout is reserved for ACP JSON-RPC transport. Human-readable logs go to stderr.
## Major components
@@ -149,7 +146,7 @@ Instead it reuses Hermes' runtime resolver:
- `acp_adapter/auth.py`
- `hermes_cli/runtime_provider.py`
-So ACP advertises and uses the currently configured Hermes provider/credentials. It also always advertises a terminal setup auth method (`hermes-setup`, args `--setup`) so first-run registry clients can open Hermes' interactive model/provider configuration before starting a normal ACP session.
+So ACP advertises and uses the currently configured Hermes provider/credentials. It also always advertises a terminal setup auth method (`hermes-setup`, args `--setup`) so first-run clients can open Hermes' interactive model/provider configuration before starting a normal ACP session.
## Working directory binding
diff --git a/website/docs/getting-started/platform-support.md b/website/docs/getting-started/platform-support.md
index d0df354e592..588a154ad77 100644
--- a/website/docs/getting-started/platform-support.md
+++ b/website/docs/getting-started/platform-support.md
@@ -44,7 +44,7 @@ PRs to fix them will _not_ be accepted, and any code that keeps compatibility wi
- installs via the AUR (we might upstream patches if it helps out <3)
- macOS on x86 (Intel) processors
-- installs via `pypi` (e.g. `uv tool install hermes-agent`, `pip install hermse-agent`, etc.)
-- installs via `brew` (`brew install hermes-agent`)
+- installs via `pypi` (e.g. `uv tool install hermes-agent`, `pip install hermse-agent`, etc.) — Hermes Agent is no longer published to PyPI, so these installs will not receive further updates
+- installs via `brew` (`brew install hermes-agent`) — Hermes Agent is no longer packaged for Homebrew, so these installs will not receive further updates
If you are using an unsupported distribution method, please read the [the installation guide](./installation.md) to learn how to switch to a supported one.
diff --git a/website/docs/user-guide/features/acp.md b/website/docs/user-guide/features/acp.md
index 4dce234ef52..1b01f757e34 100644
--- a/website/docs/user-guide/features/acp.md
+++ b/website/docs/user-guide/features/acp.md
@@ -45,14 +45,6 @@ This installs the `agent-client-protocol` dependency and enables:
- `hermes-acp`
- `python -m acp_adapter`
-For Zed registry installs, Zed launches Hermes through the official ACP Registry entry. That entry uses a `uvx` distribution that runs:
-
-```bash
-uvx --from 'hermes-agent[acp]==' hermes-acp
-```
-
-Make sure `uv` is available on `PATH` before using the registry install path.
-
## Launching the ACP server
Any of the following starts Hermes in ACP mode:
@@ -89,7 +81,7 @@ hermes acp --setup-browser # interactive (prompts before ~400 MB downl
hermes acp --setup-browser --yes # accept the download non-interactively
```
-This is the standalone command. The Zed registry's terminal-auth flow (`hermes acp --setup`) also offers the browser bootstrap as a follow-up question after model selection, so most users never need to run `--setup-browser` directly.
+This is the standalone command. Hermes' terminal-auth flow (`hermes acp --setup`) also offers the browser bootstrap as a follow-up question after model selection, so most users never need to run `--setup-browser` directly.
What it does:
@@ -126,19 +118,7 @@ If you want to define Hermes manually, add it through VS Code settings under `ac
### Zed
-Zed v0.221.x and newer installs external agents through the official ACP Registry.
-
-1. Open the Agent Panel.
-2. Click **Add Agent**, or run the `zed: acp registry` command.
-3. Search for **Hermes Agent**.
-4. Install it and start a new Hermes external-agent thread.
-
-Prerequisites:
-
-- Configure Hermes provider credentials first with `hermes model`, or set them in `~/.hermes/.env` / `~/.hermes/config.yaml`.
-- Install `uv` so the registry launcher can run `uvx --from 'hermes-agent[acp]==' hermes-acp`.
-
-For local development before the registry entry is available, use a custom agent server in Zed settings:
+Zed supports external agents via a custom agent server entry in Zed settings:
```json
{
@@ -152,32 +132,11 @@ For local development before the registry entry is available, use a custom agent
}
```
+Configure Hermes provider credentials first with `hermes model`, or set them in `~/.hermes/.env` / `~/.hermes/config.yaml`.
+
### JetBrains
-Use an ACP-compatible plugin and point it at:
-
-```text
-/path/to/hermes-agent/acp_registry
-```
-
-## Registry manifest
-
-The source copy of Hermes' official ACP Registry metadata lives at:
-
-```text
-acp_registry/agent.json
-acp_registry/icon.svg
-```
-
-The upstream registry PR copies those files into the top-level `hermes-agent/` directory in `agentclientprotocol/registry`.
-
-The registry entry uses a `uvx` distribution that points directly at the `hermes-agent` PyPI release:
-
-```text
-uvx --from 'hermes-agent[acp]==' hermes-acp
-```
-
-The registry CI verifies that the pinned version exists on PyPI, so the manifest's `version` and uvx `package` pin must always match `pyproject.toml`. `scripts/release.py` keeps them in lockstep automatically.
+Use an ACP-compatible plugin and point it at the local `hermes acp` command (see the VS Code / Zed `command`/`args` examples above — the same shape applies).
## Configuration and credentials
@@ -188,7 +147,7 @@ ACP mode uses the same Hermes configuration as the CLI:
- `~/.hermes/skills/`
- `~/.hermes/state.db`
-Provider resolution uses Hermes' normal runtime resolver, so ACP inherits the currently configured provider and credentials. Hermes also advertises a terminal auth method (`--setup`) for first-run registry clients; this opens Hermes' interactive model/provider setup.
+Provider resolution uses Hermes' normal runtime resolver, so ACP inherits the currently configured provider and credentials. Hermes also advertises a terminal auth method (`--setup`) so first-run clients can open Hermes' interactive model/provider setup.
## Session behavior
@@ -239,11 +198,9 @@ The ACP bridge maps these options onto Hermes' internal approval semantics — `
Check:
-- In Zed, open the ACP Registry with `zed: acp registry` and search for **Hermes Agent**.
-- For manual/local development, verify the custom `agent_servers` command points to `hermes acp`.
+- Verify the custom `agent_servers` (Zed) or equivalent editor config points to `hermes acp`.
- Hermes is installed and on your PATH.
- The ACP extra is installed (`pip install -e '.[acp]'`).
-- `uv` is installed if launching from the official Zed registry entry.
### ACP starts but immediately errors
@@ -264,11 +221,7 @@ ACP mode uses Hermes' existing provider setup. Configure credentials with:
hermes model
```
-or by editing `~/.hermes/.env`. Registry clients can also trigger Hermes' terminal auth flow, which runs the same interactive provider/model setup.
-
-### Zed registry launcher cannot find uv
-
-Install `uv` from the official uv installation docs, then retry the Hermes Agent thread from Zed.
+or by editing `~/.hermes/.env`. ACP clients can also trigger Hermes' terminal auth flow, which runs the same interactive provider/model setup.
## See also
diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/developer-guide/acp-internals.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/developer-guide/acp-internals.md
index 8230d5534c1..7b4035f7935 100644
--- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/developer-guide/acp-internals.md
+++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/developer-guide/acp-internals.md
@@ -17,7 +17,6 @@ ACP 适配器将 Hermes 的同步 `AIAgent` 封装为异步 JSON-RPC stdio 服
- `acp_adapter/permissions.py`
- `acp_adapter/tools.py`
- `acp_adapter/auth.py`
-- `acp_registry/agent.json`
## 启动流程
@@ -31,8 +30,6 @@ hermes acp / hermes-acp / python -m acp_adapter
-> acp.run_agent(agent, use_unstable_protocol=True)
```
-Zed ACP Registry 路径通过 `uvx --from 'hermes-agent[acp]==' hermes-acp` 启动同一适配器,指向 `hermes-agent` PyPI 发布包。
-
stdout 保留用于 ACP JSON-RPC 传输。人类可读的日志输出至 stderr。
## 主要组件
@@ -149,7 +146,7 @@ ACP 不实现自己的认证存储。
- `acp_adapter/auth.py`
- `hermes_cli/runtime_provider.py`
-因此 ACP 通告并使用当前配置的 Hermes provider/凭据。它还始终通告一个终端 setup 认证方法(`hermes-setup`,参数 `--setup`),以便首次运行的 registry 客户端在启动正常 ACP 会话前可以打开 Hermes 的交互式模型/provider 配置。
+因此 ACP 通告并使用当前配置的 Hermes provider/凭据。它还始终通告一个终端 setup 认证方法(`hermes-setup`,参数 `--setup`),以便首次运行的客户端在启动正常 ACP 会话前可以打开 Hermes 的交互式模型/provider 配置。
## 工作目录绑定
diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/acp.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/acp.md
index 629430438cb..9acdd4cf23e 100644
--- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/acp.md
+++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/acp.md
@@ -45,14 +45,6 @@ pip install -e '.[acp]'
- `hermes-acp`
- `python -m acp_adapter`
-对于 Zed registry 安装,Zed 通过官方 ACP Registry 条目启动 Hermes。该条目使用 `uvx` 发行版运行:
-
-```bash
-uvx --from 'hermes-agent[acp]==' hermes-acp
-```
-
-使用 registry 安装路径前,请确保 `uv` 已在 `PATH` 中可用。
-
## 启动 ACP 服务器
以下任意命令均可以 ACP 模式启动 Hermes:
@@ -87,7 +79,7 @@ hermes acp --setup-browser # 交互式(下载约 400 MB 前会提示
hermes acp --setup-browser --yes # 非交互式接受下载
```
-这是独立命令。Zed registry 的终端认证流程(`hermes acp --setup`)在模型选择后也会将浏览器引导作为后续问题提供,因此大多数用户无需直接运行 `--setup-browser`。
+这是独立命令。Hermes 的终端认证流程(`hermes acp --setup`)在模型选择后也会将浏览器引导作为后续问题提供,因此大多数用户无需直接运行 `--setup-browser`。
具体操作:
@@ -124,19 +116,7 @@ hermes acp --setup-browser --yes # 非交互式接受下载
### Zed
-Zed v0.221.x 及更新版本通过官方 ACP Registry 安装外部 agent。
-
-1. 打开 Agent 面板。
-2. 点击 **Add Agent**,或运行 `zed: acp registry` 命令。
-3. 搜索 **Hermes Agent**。
-4. 安装后启动新的 Hermes 外部 agent 线程。
-
-前提条件:
-
-- 先通过 `hermes model` 配置 Hermes provider 凭据,或在 `~/.hermes/.env` / `~/.hermes/config.yaml` 中设置。
-- 安装 `uv`,以便 registry 启动器可以运行 `uvx --from 'hermes-agent[acp]==' hermes-acp`。
-
-在 registry 条目可用之前进行本地开发时,在 Zed 设置中使用自定义 agent 服务器:
+Zed 通过 Zed 设置中的自定义 agent 服务器条目支持外部 agent:
```json
{
@@ -150,32 +130,11 @@ Zed v0.221.x 及更新版本通过官方 ACP Registry 安装外部 agent。
}
```
+先通过 `hermes model` 配置 Hermes provider 凭据,或在 `~/.hermes/.env` / `~/.hermes/config.yaml` 中设置。
+
### JetBrains
-使用兼容 ACP 的插件并将其指向:
-
-```text
-/path/to/hermes-agent/acp_registry
-```
-
-## Registry 清单
-
-Hermes 官方 ACP Registry 元数据的源文件位于:
-
-```text
-acp_registry/agent.json
-acp_registry/icon.svg
-```
-
-上游 registry PR 将这些文件复制到 `agentclientprotocol/registry` 中的顶层 `hermes-agent/` 目录。
-
-Registry 条目使用直接指向 `hermes-agent` PyPI 发行版的 `uvx` 发行版:
-
-```text
-uvx --from 'hermes-agent[acp]==' hermes-acp
-```
-
-Registry CI 会验证固定版本是否存在于 PyPI,因此清单的 `version` 和 uvx `package` 固定版本必须始终与 `pyproject.toml` 匹配。`scripts/release.py` 会自动保持它们同步。
+使用兼容 ACP 的插件,并将其指向本地的 `hermes acp` 命令(参见上面 VS Code / Zed 的 `command`/`args` 示例——形式相同)。
## 配置与凭据
@@ -186,7 +145,7 @@ ACP 模式使用与 CLI 相同的 Hermes 配置:
- `~/.hermes/skills/`
- `~/.hermes/state.db`
-Provider 解析使用 Hermes 的正常运行时解析器,因此 ACP 继承当前配置的 provider 和凭据。Hermes 还为首次运行的 registry 客户端提供终端认证方法(`--setup`);这将打开 Hermes 的交互式模型/provider 设置。
+Provider 解析使用 Hermes 的正常运行时解析器,因此 ACP 继承当前配置的 provider 和凭据。Hermes 还提供终端认证方法(`--setup`),以便首次运行的客户端打开 Hermes 的交互式模型/provider 设置。
## 会话行为
@@ -237,11 +196,9 @@ ACP 桥接将这些选项映射到 Hermes 的内部审批语义——`allow_alwa
检查:
-- 在 Zed 中,使用 `zed: acp registry` 打开 ACP Registry 并搜索 **Hermes Agent**。
-- 对于手动/本地开发,验证自定义 `agent_servers` 命令是否指向 `hermes acp`。
+- 验证自定义 `agent_servers`(Zed)或等效的编辑器配置是否指向 `hermes acp`。
- Hermes 已安装且在 PATH 中。
- ACP 扩展已安装(`pip install -e '.[acp]'`)。
-- 如果从官方 Zed registry 条目启动,`uv` 已安装。
### ACP 启动后立即报错
@@ -262,14 +219,10 @@ ACP 模式使用 Hermes 现有的 provider 设置。通过以下方式配置凭
hermes model
```
-或编辑 `~/.hermes/.env`。Registry 客户端也可以触发 Hermes 的终端认证流程,该流程运行相同的交互式 provider/模型设置。
-
-### Zed registry 启动器找不到 uv
-
-从官方 uv 安装文档安装 `uv`,然后从 Zed 重试 Hermes Agent 线程。
+或编辑 `~/.hermes/.env`。ACP 客户端也可以触发 Hermes 的终端认证流程,该流程运行相同的交互式 provider/模型设置。
## 另请参阅
- [ACP 内部机制](../../developer-guide/acp-internals.md)
- [Provider 运行时解析](../../developer-guide/provider-runtime.md)
-- [工具运行时](../../developer-guide/tools-runtime.md)
\ No newline at end of file
+- [工具运行时](../../developer-guide/tools-runtime.md)