Initial import: 10 Radix skills across 3 categories

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.
This commit is contained in:
2026-06-15 14:41:04 +02:00
commit 236053cd7f
30 changed files with 4460 additions and 0 deletions
+248
View File
@@ -0,0 +1,248 @@
---
name: gitea-issue-agent
description: 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.
version: 0.1.0
author: Hermes Agent
license: MIT
metadata:
hermes:
tags: [gitea, issues, automation, hermes-plugin, pr-workflow, agent-loop, approval-gate]
related_skills: [gitea-tea-macos-setup, software-engineering-workflows]
---
# 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:
```bash
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.
- `tea` CLI → 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)
1. **Scan**: `gitea_issue_agent_scan` to find `status == "new"` issues.
2. **For each new issue**:
1. `gitea_get_issue` for full text + comments.
2. `gitea_fetch_issue_images` to download any screenshots to
`~/.hermes/cache/gitea-issue-agent/<owner>/<repo>/<index>/`.
3. `vision_analyze` on each image (loaded into context) so the
proposal is grounded in what the screenshot actually shows.
4. 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
5. `gitea_comment_issue` with `marker="proposal"`. The plugin
prepends the `<!-- hermes:gitea-issue-agent v1 status=proposal -->`
marker so the next scan classifies this issue as `awaiting_approval`.
3. **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_RE` against
`GITEA_APPROVER_LOGIN`), or
- Applied one of the approval labels (`hermes-approved`,
`approved-by-dennis`, `dennis-approved`).
4. **On the next scan**, issues that satisfy the approval rule are
classified as `approved`. The skill then:
1. `cd` into the local clone of the repo (clone it first via
`git clone` if not present — `tea clone` will fail on non-standard
SSH ports, see `gitea-tea-macos-setup`).
2. Create a branch `hermes/issue-<n>-<slug>`.
3. Implement, run tests, commit, push (with token in URL or via
`~/.netrc`).
4. `gitea_create_pull_request` with `head=hermes/issue-<n>-<slug>`,
`base=main`, `body` linking to the issue, `draft=true`.
5. `gitea_comment_issue` with `marker="pr_ready"` and the PR URL.
5. **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_LOGIN` matching
`(?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 `proposal`
comment. Re-approving an old proposal does nothing.
- If `GITEA_APPROVER_LOGIN` is 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)
1. Decide on a service-bruger name (e.g. `hermes-issues`) and create a
Gitea PAT for it with `repository` (read+write) + `issue` (write) scope.
2. Tell the user the env vars they need to set in `~/.hermes/.env` (see
above). The agent **cannot** edit `.env` itself — Hermes blocks it
at the tool layer. See `references/identity-and-safety.md` for why.
3. Drop the plugin files into `~/.hermes/plugins/gitea-issue-agent/`
(`plugin.yaml` + `__init__.py` + `README.md`).
4. Tell the user to add `- gitea-issue-agent` under `plugins.enabled`
in `~/.hermes/config.yaml`. The agent **cannot** edit `config.yaml`
either — same reason.
5. Restart the gateway: `hermes gateway restart`.
6. 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).
7. Verify the local CLI path: `tea login add --name <user> --url ...`,
`tea whoami`, then `git clone` any visible repo (don't rely on
`tea clone` — see `gitea-tea-macos-setup` for the SSH-port-2224 trap).
8. 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.yaml` and
`~/.hermes/.env`.** The right move when blocked is to give the user
the exact one-line edit and stop. Do not try `execute_code`, `patch`,
or `write_file` a different way — the guard fires on all of them.
See `references/identity-and-safety.md`.
- **`tea clone` fails opaquely when SSH is on a non-standard port
(e.g. 2224).** Use `git clone` with the HTTPS `clone_url` from the
Gitea API. Strip the token from `origin` immediately.
- **Stale `osxkeychain` credentials can sabotage `git push` even when
the token is correct.** Switch to `~/.netrc` with `chmod 600`. See
the troubleshooting reference in `gitea-tea-macos-setup`.
- **Service-bruger identity matters.** The plugin's `authenticated_user`
field 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_analyze` on 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 local `tea` + `git` + `osxkeychain`
setup that the agent relies on. Includes the SSH-port-2224 trap and
the `getlogin(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 the
`gitea_comment_issue` body when posting a proposal. Copy, fill in
the issue-specific bits, send.