Kategoriserer danske regnskabs-/ERP-skills, ERP-data-afstemnings-
skill og to devops-workflows (gitea-issue-agent, kanban-workflows)
som Radix-medlemmers AI-agenter kan dele.
Struktur:
skills/<kategori>/<skill-navn>/SKILL.md
skills/<kategori>/<skill-navn>/{references,templates,scripts}/
Indekseret af index.json (genereret af scripts/rebuild_index.py).
Kør `python3 scripts/rebuild_index.py --check` i CI for at fange
synkroniseringsfejl.
13 KiB
name, description, version, author, license, metadata
| name | description | version | author | license | metadata | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| gitea-issue-agent | Build a Hermes-managed autonomous issue handler for a self-hosted Gitea instance — scan issues, analyze text + screenshot attachments, post a proposal for human approval, implement, test, push a branch, and open a Gitea PR. Use when the user wants Hermes to act on Gitea issues end-to-end with a human-in-the-loop approval gate, especially with image/visual context in the issue text. | 0.1.0 | Hermes Agent | MIT |
|
Gitea Issue Agent
End-to-end workflow for an autonomous Gitea issue handler that lives inside Hermes. Combines:
- A Hermes plugin exposing Gitea REST API tools (status, scan, get, fetch images, comment, create PR).
- A skill that orchestrates the human-in-the-loop loop: propose → wait for approval → implement → test → push → open PR.
- An optional cron job that triggers the loop on a schedule.
When to use
- User wants Hermes to monitor issues across one or more Gitea repos and propose fixes autonomously.
- Issue text often includes screenshots or design mock-ups (image support is a first-class requirement).
- A human (the user) must approve each fix before code is written.
- Push and PR creation should target a working branch the user can review and merge manually.
Do not use for: GitHub (use the gitea-equivalent catalog MCP for that),
issues that are just questions/discussions (no code work expected), or
workflows where the user wants fully autonomous implementation with no
human gate.
Architecture
┌────────────────────────────────────────┐
│ ~/.hermes/plugins/gitea-issue-agent/ │
Cron tick → │ ├ plugin.yaml │
│ ├ __init__.py (registers 7 tools) │
│ └ README.md │
└─────────────┬──────────────────────────┘
│ exposes
▼
gitea_agent_status gitea_issue_agent_scan
gitea_list_repositories gitea_get_issue
gitea_fetch_issue_images gitea_comment_issue
gitea_create_pull_request
│ consumed by
▼
┌────────────────────────────────────────┐
│ Hermes skill gitea-issue-agent-loop │
│ (orchestrates: scan → propose → wait │
│ → implement → test → push → PR) │
└─────────────┬──────────────────────────┘
│ triggers
▼
┌────────────────────────────────────────┐
│ Cron job (optional) "every 30m" │
│ "Scan Gitea issues, propose solutions │
│ for new ones, implement approved." │
└────────────────────────────────────────┘
The plugin does not implement the loop — it only exposes the API surface. The loop lives in the skill and the cron prompt. This separation keeps the plugin small and lets the loop evolve without code changes to the plugin.
Required environment variables
Add to ~/.hermes/.env and restart the gateway:
GITEA_BASE_URL=https://git.example.dk
GITEA_TOKEN=*** service-bruger PAT, scope: read+write issues/PRs/attachments>
GITEA_APPROVER_LOGIN=<the user's Gitea login, e.g. "dennis">
GITEA_DEFAULT_BASE_BRANCH=main
# Optional: comma-separated allowlist. If empty, /user/repos is scanned.
GITEA_REPOS=owner/repo1,owner/repo2
GITEA_AGENT_REPO_LIMIT=200
GITEA_AGENT_ISSUE_LIMIT_PER_REPO=50
Two Gitea identities, kept separate:
GITEA_TOKEN→ service-bruger account (e.g.hermes-issues). Posts comments and PRs as a bot.teaCLI → the user's personal token, e.g.dennis. Used for local git push of branches the user owns.
Mixing them causes comments to look like they came from the human
personally. See references/identity-and-safety.md.
Tool set (the plugin registers these)
| Tool | Purpose |
|---|---|
gitea_agent_status |
Verify config + API connectivity; reports authenticated_user. |
gitea_list_repositories |
List repos visible to the token (or GITEA_REPOS allowlist). |
gitea_issue_agent_scan |
Scan open issues, classify each as new / awaiting_approval / approved / pr_ready. Returns short summary + image count. |
gitea_get_issue |
Fetch one issue + comments + image links + classification status. |
gitea_fetch_issue_images |
Download all screenshots/attachments from the issue + comments to a local cache, so vision_analyze can inspect them. |
gitea_comment_issue |
Post a comment; optional marker arg (proposal or pr_ready) prepends an HTML marker the scanner uses to track state. |
gitea_create_pull_request |
Open a PR from an already-pushed branch. The branch must be created and pushed by the agent via git/terminal first. |
Every tool returns JSON of the shape {"success": true, ...} or
{"success": false, "error": "..."}. JSON is the only contract; the
agent's loop should branch on success and surface error verbatim.
The workflow (the loop the skill/cron implements)
- Scan:
gitea_issue_agent_scanto findstatus == "new"issues. - For each new issue:
gitea_get_issuefor full text + comments.gitea_fetch_issue_imagesto download any screenshots to~/.hermes/cache/gitea-issue-agent/<owner>/<repo>/<index>/.vision_analyzeon each image (loaded into context) so the proposal is grounded in what the screenshot actually shows.- Draft a proposal (in Danish if the user is Danish-speaking) covering:
- summary of the bug/feature
- root-cause hypothesis
- proposed change (files, tests, branch name)
- explicit approval request
gitea_comment_issuewithmarker="proposal". The plugin prepends the<!-- hermes:gitea-issue-agent v1 status=proposal -->marker so the next scan classifies this issue asawaiting_approval.
- Stop and wait for approval. Do not implement. The skill exits
cleanly; the cron re-scans later and the human will have either:
- Posted an approval comment (matched by
APPROVAL_REagainstGITEA_APPROVER_LOGIN), or - Applied one of the approval labels (
hermes-approved,approved-by-dennis,dennis-approved).
- Posted an approval comment (matched by
- On the next scan, issues that satisfy the approval rule are
classified as
approved. The skill then:cdinto the local clone of the repo (clone it first viagit cloneif not present —tea clonewill fail on non-standard SSH ports, seegitea-tea-macos-setup).- Create a branch
hermes/issue-<n>-<slug>. - Implement, run tests, commit, push (with token in URL or via
~/.netrc). gitea_create_pull_requestwithhead=hermes/issue-<n>-<slug>,base=main,bodylinking to the issue,draft=true.gitea_comment_issuewithmarker="pr_ready"and the PR URL.
- Stop. The user reviews the PR in Gitea and merges by hand.
The two state markers (proposal, pr_ready) are the only state
machine. They survive Gitea restarts, plugin reloads, and Hermes
restarts, because they live in the issue's comment history.
Approval semantics (default)
- A comment from
GITEA_APPROVER_LOGINmatching(?is)(@?hermes|agent|bot).{0,80}(godkend|approved|ok|go)counts as approval. Danish is first-class because the user is Danish-speaking. - Any of these labels applied by a maintainer also counts:
hermes-approved,approved-by-dennis,dennis-approved. - Approval is only counted if it is after the latest
proposalcomment. Re-approving an old proposal does nothing. - If
GITEA_APPROVER_LOGINis not set, free-form approval comments are ignored — only labels count. This is the safe default until the user has decided which login should be allowed to approve.
For a longer rationale, see references/identity-and-safety.md.
Install checklist (do these in order)
- Decide on a service-bruger name (e.g.
hermes-issues) and create a Gitea PAT for it withrepository(read+write) +issue(write) scope. - Tell the user the env vars they need to set in
~/.hermes/.env(see above). The agent cannot edit.envitself — Hermes blocks it at the tool layer. Seereferences/identity-and-safety.mdfor why. - Drop the plugin files into
~/.hermes/plugins/gitea-issue-agent/(plugin.yaml+__init__.py+README.md). - Tell the user to add
- gitea-issue-agentunderplugins.enabledin~/.hermes/config.yaml. The agent cannot editconfig.yamleither — same reason. - Restart the gateway:
hermes gateway restart. - Verify with
hermes chat -q "Kald gitea_agent_status" -t gitea_issue_agent—api_ok: true,authenticated_user: hermes-issues(or whatever the service-bruger is named). - Verify the local CLI path:
tea login add --name <user> --url ...,tea whoami, thengit cloneany visible repo (don't rely ontea clone— seegitea-tea-macos-setupfor the SSH-port-2224 trap). - Only then create the cron job, and only after the user has approved running it unattended.
Verification
Use the bundled script scripts/verify-plugin-and-gitea.sh to confirm
the plugin is loaded, the API is reachable, and at least one repo is
visible. It should print OK lines and exit 0.
Pitfalls (learned)
- Hermes blocks the agent from writing to
~/.hermes/config.yamland~/.hermes/.env. The right move when blocked is to give the user the exact one-line edit and stop. Do not tryexecute_code,patch, orwrite_filea different way — the guard fires on all of them. Seereferences/identity-and-safety.md. tea clonefails opaquely when SSH is on a non-standard port (e.g. 2224). Usegit clonewith the HTTPSclone_urlfrom the Gitea API. Strip the token fromoriginimmediately.- Stale
osxkeychaincredentials can sabotagegit pusheven when the token is correct. Switch to~/.netrcwithchmod 600. See the troubleshooting reference ingitea-tea-macos-setup. - Service-bruger identity matters. The plugin's
authenticated_userfield should NOT be your personal login. If it is, create a separate service-bruger or you'll get bot comments attributed to you. - Free-form approval comments must be gated by a known login (or by maintainer-applied labels) — otherwise a co-worker saying "looks good!" could trigger an autonomous push.
- First run: verify, don't cron. Always run a manual scan + a manually-approved test issue before scheduling cron. A cron job firing off PRs in the user's name is the failure mode to avoid.
- Image analysis is mandatory for screenshot issues. The plugin
extracts image URLs and downloads them locally; the skill MUST call
vision_analyzeon each before writing the proposal, otherwise the proposal will be blind to what the screenshot shows. - Plugin state is in issue comments, not in plugin memory. The proposal/PR markers make the workflow restart-safe.
Related skills
gitea-tea-macos-setup— the localtea+git+osxkeychainsetup that the agent relies on. Includes the SSH-port-2224 trap and thegetlogin(2)gotcha.software-engineering-workflows— the plan / spike / TDD / debugging patterns the implement-and-test phase of the loop should follow.software-development/simplify-code— useful to invoke on the agent's own code changes before opening the PR.
Files in this skill
scripts/verify-plugin-and-gitea.sh— one-shot verification of the full setup (plugin loaded + API reachable + repos visible + clone works). Run it after every change to the env or plugin.references/identity-and-safety.md— service-bruger vs personal identity split, Hermes safety-guard reality, approval-gate design rationale.templates/proposal-comment.md— starter template for thegitea_comment_issuebody when posting a proposal. Copy, fill in the issue-specific bits, send.