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:
@@ -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.
|
||||
Reference in New Issue
Block a user