hermes-github-app-plugin 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,83 @@
1
+ name: CD
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - '*'
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ validate-tag:
13
+ name: Validate release tag
14
+ runs-on: ubuntu-latest
15
+ outputs:
16
+ release_tag: ${{ steps.validate.outputs.release_tag }}
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: '3.12'
22
+ - name: Validate tag and package version
23
+ id: validate
24
+ run: |
25
+ python - <<'PY'
26
+ import os
27
+ import re
28
+ import tomllib
29
+ from pathlib import Path
30
+
31
+ tag = os.environ["GITHUB_REF_NAME"]
32
+ version = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]
33
+ is_release = re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", tag) is not None
34
+ with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
35
+ print(f"release_tag={str(is_release).lower()}", file=output)
36
+ if not is_release:
37
+ print(f"Tag {tag!r} is not a PyPI release tag; skipping publish.")
38
+ raise SystemExit(0)
39
+ if version != tag:
40
+ raise SystemExit(f"pyproject version {version!r} does not match tag {tag!r}")
41
+ print(f"Release version verified: {version}")
42
+ PY
43
+
44
+ build:
45
+ name: Build distribution
46
+ needs: validate-tag
47
+ if: needs.validate-tag.outputs.release_tag == 'true'
48
+ runs-on: ubuntu-latest
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ - uses: actions/setup-python@v5
52
+ with:
53
+ python-version: '3.12'
54
+ cache: pip
55
+ - name: Install build tools
56
+ run: python -m pip install build twine
57
+ - name: Build package
58
+ run: python -m build
59
+ - name: Check distribution metadata
60
+ run: python -m twine check dist/*
61
+ - name: Upload distribution artifact
62
+ uses: actions/upload-artifact@v4
63
+ with:
64
+ name: python-distribution
65
+ path: dist/*
66
+ if-no-files-found: error
67
+
68
+ publish-pypi:
69
+ name: Publish to PyPI
70
+ needs: build
71
+ runs-on: ubuntu-latest
72
+ environment: pypi
73
+ permissions:
74
+ contents: read
75
+ id-token: write
76
+ steps:
77
+ - name: Download distribution artifact
78
+ uses: actions/download-artifact@v4
79
+ with:
80
+ name: python-distribution
81
+ path: dist
82
+ - name: Publish package distributions to PyPI
83
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ jobs:
9
+ lint-static-test:
10
+ name: Lint, static analysis, and tests
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.10", "3.12"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ cache: pip
22
+ - name: Install package
23
+ run: python -m pip install -e '.[dev]'
24
+ - name: Ruff format check
25
+ run: ruff format --check .
26
+ - name: Ruff lint
27
+ run: ruff check .
28
+ - name: Mypy
29
+ run: mypy
30
+ - name: Pytest
31
+ run: pytest
@@ -0,0 +1,9 @@
1
+ .coverage
2
+ .mypy_cache/
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ __pycache__/
6
+ *.egg-info/
7
+ dist/
8
+ build/
9
+ .venv/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hermes GitHub App Plugin Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: hermes-github-app-plugin
3
+ Version: 0.1.0
4
+ Summary: Hermes plugin for per-agent GitHub App identity, gh/git wrappers, and GitHub App-aware tools.
5
+ Author: Hermes GitHub App Plugin Contributors
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: httpx>=0.25
10
+ Requires-Dist: pyjwt[crypto]>=2.8
11
+ Requires-Dist: pyyaml>=6.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: mypy>=1.8; extra == 'dev'
14
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
15
+ Requires-Dist: pytest>=8.0; extra == 'dev'
16
+ Requires-Dist: ruff>=0.8; extra == 'dev'
17
+ Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
18
+ Description-Content-Type: text/markdown
19
+
20
+ # Hermes GitHub App Plugin
21
+
22
+ Hermes plugin for using **per-agent GitHub App identities** instead of a human `gh`/SSH identity.
23
+
24
+ Each Hermes agent runs the same package but is configured with its own GitHub App:
25
+
26
+ ```yaml
27
+ github_app:
28
+ client_id: "Iv1.exampleclientid"
29
+ installation_id: "987654"
30
+ private_key_path: "~/.hermes/secrets/agent-github-app.private-key.pem"
31
+ app_slug: "hermes-agent"
32
+ ```
33
+
34
+ Environment variables with the same meaning are also supported:
35
+
36
+ - `GITHUB_APP_CLIENT_ID`
37
+ - `GITHUB_APP_INSTALLATION_ID`
38
+ - `GITHUB_APP_PRIVATE_KEY_PATH`
39
+ - `GITHUB_APP_PRIVATE_KEY` (PEM contents; useful for CI)
40
+
41
+ Repository access is controlled by the GitHub App installation scope in GitHub. If an agent should not access a repository, remove that repository from the GitHub App installation scope.
42
+
43
+ ## Client ID vs. installation ID
44
+
45
+ `client_id` identifies the GitHub App registration. GitHub recommends using the GitHub App **client ID** as the JWT `iss` claim when authenticating as an app.
46
+
47
+ `installation_id` identifies one installation of that app on a specific user or organization account. It is required when exchanging the app JWT for an installation access token via `POST /app/installations/{installation_id}/access_tokens`.
48
+
49
+ In other words: `client_id` answers "which GitHub App is signing this JWT?" while `installation_id` answers "which installed copy of that app should this token act as?" The same GitHub App can have multiple installation IDs if it is installed on multiple accounts.
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ pip install hermes-github-app-plugin
55
+ hermes plugins enable github-app
56
+ hermes-github-app setup
57
+ hermes-github-app doctor --repo OWNER/REPO
58
+ ```
59
+
60
+ `setup` walks through the required values one by one. Optional prompts are explicitly marked with `(optional)`:
61
+
62
+ ```text
63
+ GitHub App client ID:
64
+ GitHub App installation ID:
65
+ GitHub App private key path:
66
+ GitHub App slug (optional):
67
+ ```
68
+
69
+ For scripted installs, pass flags and skip the network verification until secrets are mounted:
70
+
71
+ ```bash
72
+ hermes-github-app setup --non-interactive --skip-verify \
73
+ --client-id Iv1.exampleclientid \
74
+ --installation-id 987654 \
75
+ --private-key-path ~/.hermes/secrets/agent-github-app.private-key.pem \
76
+ --app-slug hermes-agent
77
+ ```
78
+
79
+ `doctor` checks local installation state and, unless `--skip-network` is set, verifies that an installation token can be minted and the optional repository probe is reachable.
80
+
81
+ ## CLI and wrappers
82
+
83
+ ```bash
84
+ hermes-github-app setup
85
+ hermes-github-app doctor --repo OWNER/REPO
86
+ hermes-github-app status
87
+ hermes-github-app token --repo OWNER/REPO
88
+ hermes-github-app api --repo OWNER/REPO /repos/OWNER/REPO
89
+
90
+ gh-app --repo OWNER/REPO pr list -R OWNER/REPO
91
+ git-app --repo OWNER/REPO push origin my-branch
92
+ ```
93
+
94
+ `gh-app` injects an ephemeral installation token as `GH_TOKEN` and `GITHUB_TOKEN` for the child `gh` process.
95
+ `git-app` injects a temporary askpass helper so HTTPS Git operations authenticate as the GitHub App installation token without writing credentials into the remote URL.
96
+
97
+ ## Migrating existing Hermes skills and jobs
98
+
99
+ To keep agents from falling back to local human credentials, update existing GitHub-related Hermes skills, cron jobs, and subagent prompts with these rules:
100
+
101
+ - Use `github_app_*` tools for GitHub API operations when possible.
102
+ - Replace authenticated `gh ...` examples with `gh-app --repo OWNER/REPO -- ...`.
103
+ - Replace `git push` examples with `git-app --repo OWNER/REPO -- push ...`, or another HTTPS credential-helper flow backed by a freshly minted installation token.
104
+ - Do not use `gh auth status` as proof of write identity; it reports local `gh` credentials and may show a human account.
105
+ - Avoid SSH remotes for bot-managed worktrees. SSH uses local SSH keys, not the GitHub App token.
106
+ - Add a pre-write check with `github_app_verify_identity` or `hermes-github-app status --repo OWNER/REPO`.
107
+ - Avoid `@me` assumptions because the GitHub App bot is not the human operator.
108
+ - Require write summaries to include the returned `auth_mode`, `app_slug`, `installation_id`, repository, operation, and URL/path.
109
+
110
+ ## Releasing to PyPI
111
+
112
+ The package is built with Hatchling and publishes through the `CD` GitHub Actions workflow using PyPI Trusted Publishing / OIDC. The workflow listens to all pushed tags but only builds and publishes when the tag matches:
113
+
114
+ ```text
115
+ ^[0-9]+\.[0-9]+\.[0-9]+$
116
+ ```
117
+
118
+ The tag must also match `project.version` in `pyproject.toml`.
119
+
120
+ Before the first release, configure PyPI Trusted Publishing for this repository and workflow:
121
+
122
+ - PyPI project name: `hermes-github-app-plugin`
123
+ - Owner/repository: this GitHub repository
124
+ - Workflow name: `cd.yaml`
125
+ - Environment name: `pypi`
126
+
127
+ Release example:
128
+
129
+ ```bash
130
+ git tag 0.1.0
131
+ git push origin 0.1.0
132
+ ```
133
+
134
+ Tags like `v0.1.0`, `0.1`, or `0.1.0rc1` will not publish.
135
+
136
+ ## Hermes tools
137
+
138
+ The plugin registers these tools:
139
+
140
+ - `github_app_status`
141
+ - `github_app_verify_identity`
142
+ - `github_app_api`
143
+ - `github_app_graphql`
144
+ - `github_app_create_issue`
145
+ - `github_app_comment_issue`
146
+ - `github_app_create_pr`
147
+ - `github_app_comment_pr`
148
+
149
+ All mutating tools return auth metadata showing App mode, installation ID, app slug, and target repository.
@@ -0,0 +1,130 @@
1
+ # Hermes GitHub App Plugin
2
+
3
+ Hermes plugin for using **per-agent GitHub App identities** instead of a human `gh`/SSH identity.
4
+
5
+ Each Hermes agent runs the same package but is configured with its own GitHub App:
6
+
7
+ ```yaml
8
+ github_app:
9
+ client_id: "Iv1.exampleclientid"
10
+ installation_id: "987654"
11
+ private_key_path: "~/.hermes/secrets/agent-github-app.private-key.pem"
12
+ app_slug: "hermes-agent"
13
+ ```
14
+
15
+ Environment variables with the same meaning are also supported:
16
+
17
+ - `GITHUB_APP_CLIENT_ID`
18
+ - `GITHUB_APP_INSTALLATION_ID`
19
+ - `GITHUB_APP_PRIVATE_KEY_PATH`
20
+ - `GITHUB_APP_PRIVATE_KEY` (PEM contents; useful for CI)
21
+
22
+ Repository access is controlled by the GitHub App installation scope in GitHub. If an agent should not access a repository, remove that repository from the GitHub App installation scope.
23
+
24
+ ## Client ID vs. installation ID
25
+
26
+ `client_id` identifies the GitHub App registration. GitHub recommends using the GitHub App **client ID** as the JWT `iss` claim when authenticating as an app.
27
+
28
+ `installation_id` identifies one installation of that app on a specific user or organization account. It is required when exchanging the app JWT for an installation access token via `POST /app/installations/{installation_id}/access_tokens`.
29
+
30
+ In other words: `client_id` answers "which GitHub App is signing this JWT?" while `installation_id` answers "which installed copy of that app should this token act as?" The same GitHub App can have multiple installation IDs if it is installed on multiple accounts.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install hermes-github-app-plugin
36
+ hermes plugins enable github-app
37
+ hermes-github-app setup
38
+ hermes-github-app doctor --repo OWNER/REPO
39
+ ```
40
+
41
+ `setup` walks through the required values one by one. Optional prompts are explicitly marked with `(optional)`:
42
+
43
+ ```text
44
+ GitHub App client ID:
45
+ GitHub App installation ID:
46
+ GitHub App private key path:
47
+ GitHub App slug (optional):
48
+ ```
49
+
50
+ For scripted installs, pass flags and skip the network verification until secrets are mounted:
51
+
52
+ ```bash
53
+ hermes-github-app setup --non-interactive --skip-verify \
54
+ --client-id Iv1.exampleclientid \
55
+ --installation-id 987654 \
56
+ --private-key-path ~/.hermes/secrets/agent-github-app.private-key.pem \
57
+ --app-slug hermes-agent
58
+ ```
59
+
60
+ `doctor` checks local installation state and, unless `--skip-network` is set, verifies that an installation token can be minted and the optional repository probe is reachable.
61
+
62
+ ## CLI and wrappers
63
+
64
+ ```bash
65
+ hermes-github-app setup
66
+ hermes-github-app doctor --repo OWNER/REPO
67
+ hermes-github-app status
68
+ hermes-github-app token --repo OWNER/REPO
69
+ hermes-github-app api --repo OWNER/REPO /repos/OWNER/REPO
70
+
71
+ gh-app --repo OWNER/REPO pr list -R OWNER/REPO
72
+ git-app --repo OWNER/REPO push origin my-branch
73
+ ```
74
+
75
+ `gh-app` injects an ephemeral installation token as `GH_TOKEN` and `GITHUB_TOKEN` for the child `gh` process.
76
+ `git-app` injects a temporary askpass helper so HTTPS Git operations authenticate as the GitHub App installation token without writing credentials into the remote URL.
77
+
78
+ ## Migrating existing Hermes skills and jobs
79
+
80
+ To keep agents from falling back to local human credentials, update existing GitHub-related Hermes skills, cron jobs, and subagent prompts with these rules:
81
+
82
+ - Use `github_app_*` tools for GitHub API operations when possible.
83
+ - Replace authenticated `gh ...` examples with `gh-app --repo OWNER/REPO -- ...`.
84
+ - Replace `git push` examples with `git-app --repo OWNER/REPO -- push ...`, or another HTTPS credential-helper flow backed by a freshly minted installation token.
85
+ - Do not use `gh auth status` as proof of write identity; it reports local `gh` credentials and may show a human account.
86
+ - Avoid SSH remotes for bot-managed worktrees. SSH uses local SSH keys, not the GitHub App token.
87
+ - Add a pre-write check with `github_app_verify_identity` or `hermes-github-app status --repo OWNER/REPO`.
88
+ - Avoid `@me` assumptions because the GitHub App bot is not the human operator.
89
+ - Require write summaries to include the returned `auth_mode`, `app_slug`, `installation_id`, repository, operation, and URL/path.
90
+
91
+ ## Releasing to PyPI
92
+
93
+ The package is built with Hatchling and publishes through the `CD` GitHub Actions workflow using PyPI Trusted Publishing / OIDC. The workflow listens to all pushed tags but only builds and publishes when the tag matches:
94
+
95
+ ```text
96
+ ^[0-9]+\.[0-9]+\.[0-9]+$
97
+ ```
98
+
99
+ The tag must also match `project.version` in `pyproject.toml`.
100
+
101
+ Before the first release, configure PyPI Trusted Publishing for this repository and workflow:
102
+
103
+ - PyPI project name: `hermes-github-app-plugin`
104
+ - Owner/repository: this GitHub repository
105
+ - Workflow name: `cd.yaml`
106
+ - Environment name: `pypi`
107
+
108
+ Release example:
109
+
110
+ ```bash
111
+ git tag 0.1.0
112
+ git push origin 0.1.0
113
+ ```
114
+
115
+ Tags like `v0.1.0`, `0.1`, or `0.1.0rc1` will not publish.
116
+
117
+ ## Hermes tools
118
+
119
+ The plugin registers these tools:
120
+
121
+ - `github_app_status`
122
+ - `github_app_verify_identity`
123
+ - `github_app_api`
124
+ - `github_app_graphql`
125
+ - `github_app_create_issue`
126
+ - `github_app_comment_issue`
127
+ - `github_app_create_pr`
128
+ - `github_app_comment_pr`
129
+
130
+ All mutating tools return auth metadata showing App mode, installation ID, app slug, and target repository.
@@ -0,0 +1,27 @@
1
+ name: github-app
2
+ version: 0.1.0
3
+ description: "GitHub App identity integration for Hermes: per-agent app token minting, GitHub API tools, gh/git wrappers, CLI, and workflow skill."
4
+ author: Hermes GitHub App Plugin Contributors
5
+ provides_tools:
6
+ - github_app_status
7
+ - github_app_verify_identity
8
+ - github_app_api
9
+ - github_app_graphql
10
+ - github_app_create_issue
11
+ - github_app_comment_issue
12
+ - github_app_create_pr
13
+ - github_app_comment_pr
14
+ provides_cli:
15
+ - hermes-github-app
16
+ - gh-app
17
+ - git-app
18
+ requires_env:
19
+ - name: GITHUB_APP_CLIENT_ID
20
+ description: "GitHub App client ID for this Hermes agent. Can also be set in ~/.hermes/config.yaml under github_app.client_id."
21
+ secret: false
22
+ - name: GITHUB_APP_INSTALLATION_ID
23
+ description: "GitHub App installation ID for this Hermes agent. Can also be set in ~/.hermes/config.yaml under github_app.installation_id."
24
+ secret: false
25
+ - name: GITHUB_APP_PRIVATE_KEY_PATH
26
+ description: "Path to the GitHub App private key PEM. Can also be set in ~/.hermes/config.yaml under github_app.private_key_path."
27
+ secret: true
@@ -0,0 +1,60 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "hermes-github-app-plugin"
7
+ version = "0.1.0"
8
+ description = "Hermes plugin for per-agent GitHub App identity, gh/git wrappers, and GitHub App-aware tools."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Hermes GitHub App Plugin Contributors" }]
13
+ dependencies = [
14
+ "httpx>=0.25",
15
+ "PyJWT[crypto]>=2.8",
16
+ "PyYAML>=6.0",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ dev = [
21
+ "mypy>=1.8",
22
+ "pytest>=8.0",
23
+ "pytest-cov>=5.0",
24
+ "ruff>=0.8",
25
+ "types-PyYAML>=6.0",
26
+ ]
27
+
28
+ [project.entry-points."hermes_agent.plugins"]
29
+ github-app = "hermes_github_app_plugin:register"
30
+
31
+ [project.scripts]
32
+ gh-app = "hermes_github_app_plugin.cli:gh_app_main"
33
+ git-app = "hermes_github_app_plugin.cli:git_app_main"
34
+ hermes-github-app = "hermes_github_app_plugin.cli:main"
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["src/hermes_github_app_plugin"]
38
+
39
+ [tool.hatch.build.targets.wheel.force-include]
40
+ "plugin.yaml" = "hermes_github_app_plugin/plugin.yaml"
41
+
42
+ [tool.ruff]
43
+ line-length = 100
44
+ target-version = "py310"
45
+
46
+ [tool.ruff.lint]
47
+ select = ["E", "F", "I", "B", "UP", "SIM", "PL", "RUF"]
48
+ ignore = ["PLR0913"]
49
+
50
+ [tool.ruff.lint.per-file-ignores]
51
+ "tests/**/*.py" = ["PLR2004"]
52
+
53
+ [tool.mypy]
54
+ python_version = "3.10"
55
+ strict = true
56
+ packages = ["hermes_github_app_plugin"]
57
+
58
+ [tool.pytest.ini_options]
59
+ addopts = "--cov=hermes_github_app_plugin --cov-report=term-missing"
60
+ testpaths = ["tests"]
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: github-app-workflow
3
+ description: Use per-agent GitHub App identity for Hermes GitHub operations.
4
+ version: 0.1.0
5
+ author: Hermes GitHub App Plugin Contributors
6
+ ---
7
+
8
+ # GitHub App Workflow
9
+
10
+ Use this skill for GitHub operations from Hermes agents that are configured with a per-agent GitHub App.
11
+
12
+ ## First-time setup
13
+
14
+ Run `hermes-github-app setup` to write `github_app` config into `~/.hermes/config.yaml`. The setup walkthrough marks optional values with `(optional)`; the required values are GitHub App client ID, installation ID, and private key path.
15
+
16
+ After setup, run `hermes-github-app doctor --repo OWNER/REPO` to verify console scripts, config loading, private-key permissions, token minting, and repository access. Use `--skip-network` only for container/image builds where secrets or network access are not available yet.
17
+
18
+ ## Rules
19
+
20
+ 1. Prefer `github_app_*` plugin tools for GitHub API operations.
21
+ 2. Prefer `gh-app` over bare `gh` from the terminal.
22
+ 3. Prefer `git-app` or HTTPS/App-token credentials over SSH for GitHub writes.
23
+ 4. Do not rely on `gh auth status` as proof of the desired identity; it often reports the human account.
24
+ 5. Verify App mode before writes with `github_app_verify_identity` or `hermes-github-app status --repo OWNER/REPO`.
25
+ 6. Do not use `@me` assumptions; the actor is the app bot, not a human user.
26
+ 7. Expect comments, PRs, and API writes to appear as `<app-slug>[bot]`.
27
+ 8. Treat the GitHub App installation scope as the repository access boundary. The plugin does not maintain a separate local repository/owner allowlist.
28
+
29
+ ## Updating existing Hermes skills
30
+
31
+ Patch any GitHub-related skill, cron prompt, or subagent instruction that mentions `gh`, `git push`, GitHub SSH remotes, or `@me` assumptions:
32
+
33
+ - Replace bare `gh ...` examples with `gh-app --repo OWNER/REPO -- ...` when the command needs GitHub authentication.
34
+ - Replace `git push` examples with `git-app --repo OWNER/REPO -- push ...`, or document an equivalent HTTPS credential-helper flow that uses a freshly minted installation token.
35
+ - Add a pre-write verification step: `github_app_verify_identity` or `hermes-github-app status --repo OWNER/REPO`.
36
+ - Do not treat `gh auth status` as proof of the write identity. It reports local `gh` credentials and may be a human account.
37
+ - Remove or flag SSH remote examples for bot-managed worktrees. SSH uses local SSH keys, not the GitHub App installation token.
38
+ - Replace `@me` queries with explicit usernames, teams, or repository-scoped queries because the app bot is not the human operator.
39
+ - Require final write summaries to include `auth_mode`, `app_slug`, `installation_id`, `repository`, operation, and URL/path.
40
+
41
+ For subagents, include the same rules in the delegated prompt because subagents run in separate sessions and may not inherit the parent agent's assumptions.
42
+
43
+ ## Verification
44
+
45
+ Before reporting success for a write, include:
46
+
47
+ - repository
48
+ - operation
49
+ - URL or API path
50
+ - `auth_mode: github_app`
51
+ - app slug if known
52
+ - installation ID
@@ -0,0 +1,60 @@
1
+ """Hermes GitHub App plugin."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from . import schemas, tools
8
+ from .cli import main as cli_main
9
+ from .cli import register_cli
10
+
11
+ _TOOLSET = "github_app"
12
+ _TOOLS = (
13
+ ("github_app_status", schemas.GITHUB_APP_STATUS, tools.github_app_status, "🤖"),
14
+ (
15
+ "github_app_verify_identity",
16
+ schemas.GITHUB_APP_VERIFY_IDENTITY,
17
+ tools.github_app_verify_identity,
18
+ "✅",
19
+ ),
20
+ ("github_app_api", schemas.GITHUB_APP_API, tools.github_app_api, "🐙"),
21
+ ("github_app_graphql", schemas.GITHUB_APP_GRAPHQL, tools.github_app_graphql, "📊"),
22
+ (
23
+ "github_app_create_issue",
24
+ schemas.GITHUB_APP_CREATE_ISSUE,
25
+ tools.github_app_create_issue,
26
+ "📝",
27
+ ),
28
+ (
29
+ "github_app_comment_issue",
30
+ schemas.GITHUB_APP_COMMENT_ISSUE,
31
+ tools.github_app_comment_issue,
32
+ "💬",
33
+ ),
34
+ ("github_app_create_pr", schemas.GITHUB_APP_CREATE_PR, tools.github_app_create_pr, "🔀"),
35
+ ("github_app_comment_pr", schemas.GITHUB_APP_COMMENT_PR, tools.github_app_comment_pr, "💬"),
36
+ )
37
+
38
+
39
+ def register(ctx: object) -> None:
40
+ """Register Hermes tools, CLI, and bundled skill."""
41
+ for name, schema, handler, emoji in _TOOLS:
42
+ ctx.register_tool( # type: ignore[attr-defined]
43
+ name=name,
44
+ toolset=_TOOLSET,
45
+ schema=schema,
46
+ handler=handler,
47
+ emoji=emoji,
48
+ )
49
+
50
+ ctx.register_cli_command( # type: ignore[attr-defined]
51
+ name="hermes-github-app",
52
+ help="Manage the Hermes GitHub App integration",
53
+ setup_fn=register_cli,
54
+ handler_fn=cli_main,
55
+ description="Mint and verify per-agent GitHub App installation tokens.",
56
+ )
57
+
58
+ skill_path = Path(__file__).parent / "skills" / "github-app-workflow"
59
+ if skill_path.exists():
60
+ ctx.register_skill("github-app-workflow", skill_path) # type: ignore[attr-defined]