refactor(skills): retrofit the corpus to the ADR-0020 context contract (#129)
Retrofits all 39 skills to ADR-0020's description/body context contract, then fixes what six rounds of independent review found in that retrofit — including four ways the hot gate itself failed open. Closes #99, #107, #108, #110, #111, #114, #115, #120. ## The retrofit (waves 1-5) | | Start | Now | |---|---|---| | Description FAILs (>400 chars) | 26 | **0** | | Body FAILs (>900 words, body-only) | 9 | **0** | | Dangling routing targets | 2 | **0** | | `Kyberforge.CompositionNote` | 10 | **0** | | Preload tax | 21,005 chars | **~10,500** | Under the 12,000-char success criterion. Per-wave detail is on #99. ## The review fixes **The gate failed open four ways, three of them found after the retrofit shipped.** An unrecognised follower token made a dangling target vanish. A skill directory with no `SKILL.md` resolved as a valid target, so a commit could be green locally and red in a fresh clone — three existing fixtures were relying on that, one of which made the install-leak A/B pass vacuously. Then the free-standing `/name` sweep turned out to be gated on the sentence carrying a boundary marker, so route notation in any other sentence was invisible — not an ERROR, not a SUGGESTION, not an INFO — which left the documented "`/name` always blocks" promise false from a second direction. All four fixed and pinned. **Two checks were silently not running.** `validate-provenance.sh` checks 7-8 were dead across nine skills. Waking them exposed a deeper problem: they assume `Research doc:` names a source index, but 30 of 121 entries point at topic content documents, so every new check-7 INFO was a false positive and check 8 was saved from a false-FAIL flood only by an *unannounced* skip. Checks 7/8 are now scoped to source indexes and every skip announces itself (#121). **The retrofit's own anti-goal, four times.** ADR-0020 warns that a blunt gate gets satisfied by deleting content rather than relocating it. `diagnose` and `skill-audit` relocated prose and then read it unconditionally; `prototype` and `vale-config` deleted rules outright that survived nowhere. All four addressed. ## Verification - `bash tests/run-tests.sh --strict` — 24 suites, 0 skipped, 0 failed - `bash tests/run-bats.sh` — 325 tests, 0 failures - `pre-commit run --all-files` — 17/17 - `pre-commit run --hook-stage pre-push --all-files` — 16/16, with `apm marketplace check` and `apm pack --check-clean` run against the remote, not skipped - `scripts/skill-size-check.sh` over all 39 skills — rc 0, 0 ERROR/FAIL, SUGGESTION-only - Preload tax measured at **10,498 chars**, max description 390 — both inside budget - Every new test proven non-vacuous by a deliberate mutation of the behaviour it covers **Per-commit sync, stated accurately:** the ten commits from the latest review round each pass `check-plugin-content-sync` in isolation, verified by checking each out in a detached worktree with a clean between. The earlier gitea window (`dfacf05..bedbd1d`, nine commits) does **not** — its mirror was regenerated in one batch at `bbc7300`. An earlier revision of this description claimed the property held for every commit; it does not, and a bisect through that window lands on a red commit. **Squash-merge** to collapse it, or accept that this range is not bisectable. ## Version bump Six plugins and the catalog take a **patch**, not a minor. The branch is **89 commits — 40 `fix` / 30 `refactor` / 12 `docs` / 5 `chore` / 2 `test` — zero `feat`, zero `!`, zero `BREAKING CHANGE`** — and adds no skill, agent, command or hook. (Two earlier revisions of this section cited a stale histogram, most recently 78 commits; the figures above are measured at HEAD.) Both rules this repo ships (`forge/references/version-bump.md`, landing in this PR, and `git-commits/references/conventional-commits-spec.md`) make that a patch, and the catalog set is unchanged at 7 entries. Not settled by that: four published files were removed from the installed tree, three moved, and `caveman` gained `disable-model-invocation`, retiring its old triggers. Under a strict reading those are major-class and currently ship under `refactor:` with no marker. Whether the deployed skill surface is a public contract is written down nowhere — worth deciding, but it outlives this PR. ## Deliberately not in scope #112 (cherry-pick ownership, now resolved in favour of `git-commits`), #113 (`rtk git` normalisation), #116 (research fan-out), #101 (audit-skill merge), #122 (non-spec skill-root files), #123 (no PRD producer) stay open. #117 is the one worth reading: the contract's remedy is to move prose into `references/`, which is exactly where neither the size gate nor Vale looks — and the blind spot is wider than #117 currently records, since there is no root `.vale.ini` at all, so every ADR, `CONTEXT.md` and `README.md` is unlinted too. That blind spot let this branch carry two `level: error` `Kyberforge.SentenceOpenerThereIs` violations into `references/` files it created — `provider-adapter-author/references/provider-matrix.md:31` and `agent-audit/references/finding-criteria.md:95`. Both are reworded in `afadaae`, confirmed by routing each file through the audit's own `vale-wrap.sh` (1 error each before, 0 after). Five further occurrences sit in `references/` files already on `main`; those are the pre-existing corpus and stay with #117, which is the real fix. Also unfixed and not this PR's: `apm install` appends a duplicate `SessionStart` entry to `.claude/settings.json`, so a fresh clone cannot get pre-push green without an edit AGENTS.md warns against. Reproduces identically on `main`. Co-authored-by: Defame1297 <gitea@rkdr.net> Reviewed-on: https://git.dev.rkdr.net/Defame1297/holocron/pulls/129 Co-authored-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net> Co-committed-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
This commit was merged in pull request #129.
This commit is contained in:
@@ -4,27 +4,40 @@ Manage Gitea repository branches and inspect commit history via the Gitea MCP se
|
||||
|
||||
## What it does
|
||||
|
||||
This skill handles branch lifecycle operations (list, create, delete) and read-only commit
|
||||
This skill handles branch lifecycle operations (list, create, rename, delete) and read-only commit
|
||||
history (list commits, get a single commit's full detail) against a Gitea repository. It resolves
|
||||
`owner`/`repo` from the git remote, dispatches to the right MCP tool, and applies safety and
|
||||
pagination conventions specific to Gitea's API (e.g. refusing to delete a protected branch without
|
||||
explicit confirmation, and treating unexpected 404s as possible masked 403s).
|
||||
|
||||
## Boundaries
|
||||
|
||||
This skill operates on the Gitea server via the MCP tools, never on your local checkout. Branch
|
||||
and commit-history work against the working copy belongs to `git-branches` and `git-history`.
|
||||
Branch references that only exist relative to a pull request — a PR's head or base branch, and
|
||||
cross-repo fork PR heads in particular — belong to `gitea-prs`; `list_branches` cannot see a fork's
|
||||
head at all.
|
||||
|
||||
The skill triggers on phrasings like "list branches", "create a branch", "rename a branch", "delete a branch",
|
||||
"what commits are on this branch", "show commit <sha>", and "what changed in that commit", even
|
||||
when the user does not say "Gitea", as long as the repo's remote is a Gitea instance.
|
||||
|
||||
## Before you start
|
||||
|
||||
Requires a Gitea MCP server configured with a token that has `write:repository` scope. This is
|
||||
confirmed for `list_branches`, `create_branch`, and `delete_branch` (Gitea gates reads behind write
|
||||
confirmed for `list_branches`, `create_branch`, and `delete_branch` (and inferred for
|
||||
`rename_branch`) (Gitea gates reads behind write
|
||||
scope for repo-scoped operations); `list_commits` and `get_commit` are inferred to need the same
|
||||
scope by analogy, not explicitly confirmed — see `references/commits.md`. Requires a git remote
|
||||
named `origin` pointing at the Gitea instance.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
```text
|
||||
/gitea-branches
|
||||
```
|
||||
|
||||
Describe your task: list/create/delete a branch, or list/inspect commits. See `SKILL.md`'s
|
||||
Describe your task: list/create/rename/delete a branch, or list/inspect commits. See `SKILL.md`'s
|
||||
dispatch table for the full set of recognized invocations.
|
||||
|
||||
## Files
|
||||
@@ -32,6 +45,6 @@ dispatch table for the full set of recognized invocations.
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents — dispatch table, gotchas |
|
||||
| `references/branches.md` | Verified call signatures and mechanics for list/create/delete branch |
|
||||
| `references/branches.md` | Verified call signatures and mechanics for list/create/rename/delete branch |
|
||||
| `references/commits.md` | Verified call signatures and mechanics for list/get commit |
|
||||
| `references/sources.md` | Research sources backing the branch/commit guidance |
|
||||
|
||||
@@ -2,37 +2,30 @@
|
||||
name: gitea-branches
|
||||
|
||||
description: >
|
||||
Use when managing Gitea repository branches — listing, creating, or deleting
|
||||
branches — or inspecting commit history within a Gitea repo: listing commits
|
||||
(optionally filtered by branch or file path) or getting full detail for a
|
||||
single commit by SHA. Triggers on "list branches", "create a branch",
|
||||
"delete a branch", "what commits are on this branch", "show commit <sha>",
|
||||
"what changed in that commit" — even if the user doesn't say "Gitea"
|
||||
explicitly, as long as the repo's remote is a Gitea instance. Do not use for
|
||||
local git branch/commit operations on your working copy (use git-branches or
|
||||
git-history) or for PR-side branch references like cross-repo fork PR heads
|
||||
(use gitea-prs).
|
||||
Use when listing, creating, renaming, or deleting branches in a Gitea repository,
|
||||
or reading its commit history — "what commits are on this branch", "what changed
|
||||
in that commit" — even when the user does not say "Gitea". Not a
|
||||
local checkout's branches -> `git-branches`. Not local history ->
|
||||
`git-history`. Not a PR's head or base -> `gitea-prs`.
|
||||
|
||||
compatibility: Requires Gitea MCP server configured with a token with write:repository scope; this is confirmed to gate list_branches, create_branch, and delete_branch (Gitea gates reads behind write scope for repo-scoped operations), and is inferred by analogy (not explicitly confirmed by source docs) to also gate list_commits and get_commit. Requires git remote "origin" pointing to the Gitea instance.
|
||||
compatibility: Requires Gitea MCP server configured with a token with write:repository scope; this is confirmed to gate list_branches, create_branch, and delete_branch (Gitea gates reads behind write scope for repo-scoped operations), and is inferred by analogy (not explicitly confirmed by source docs) to also gate rename_branch, list_commits, and get_commit. Requires git remote "origin" pointing to the Gitea instance.
|
||||
|
||||
metadata:
|
||||
category: integration
|
||||
version: "0.1.1"
|
||||
version: "0.1.2"
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
|
||||
allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit
|
||||
allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__gitea__rename_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Never delete a protected branch (`main`/`master` by name, or `protected: true` from `list_branches`) without explicit confirmation.** `delete_branch` is a direct API call, not a local `git push` — there is no client-side force-push guard protecting it. Name-matching `main`/`master` is a convenient default but not authoritative — a repo can protect a differently-named default branch. When in doubt, call `list_branches` first and check `protected` on the target; treat deletion of any protected branch as a hard refusal unless the user explicitly confirms in the conversation.
|
||||
- **404 may actually mean 403.** Gitea hides permission errors as not-found to avoid leaking resource existence. If any of these five tools returns 404 unexpectedly, check token scope (see `references/branches.md` / `references/commits.md`) before concluding the branch or commit doesn't exist.
|
||||
- **Pagination is manual.** `list_branches` and `list_commits` return one page at a time — no auto-pagination in the MCP layer. When you need a complete list, iterate `page: 1, 2, ...` until the returned count is less than `per_page`.
|
||||
- **Owner/repo always come from the git remote, never from `get_me`.** Resolve them via `git remote get-url origin` (Step 1 below). `get_me`/`list_my_repos` are blocked under the token scopes this skill assumes.
|
||||
- **`create_branch`'s source is `old_branch`, not "wherever gitea-mcp feels like."** Omitting `old_branch` forks from the repo's server-side default branch — not necessarily the branch you're currently working on locally. If you want to branch from your current checkout, pass `old_branch` explicitly.
|
||||
- **404 often means 403.** Gitea masks permission errors as not-found; on an unexpected one, check token scope before reporting a branch or commit missing.
|
||||
- **Nothing auto-paginates.** `list_branches` and `list_commits` return one page; iterate `page` until the returned count is below `per_page`.
|
||||
- **`delete_branch` has no force-push guard.** Treat deleting a protected branch as a hard refusal unless the user explicitly confirms it in the conversation. Check `protected` from `list_branches` first — a protected branch need not be named `main`.
|
||||
|
||||
## Step 1 — Resolve owner and repo
|
||||
|
||||
@@ -42,7 +35,7 @@ Before any tool call, extract `owner` and `repo` from the git remote:
|
||||
git remote get-url origin
|
||||
```
|
||||
|
||||
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
|
||||
`get_me` and `list_my_repos` are blocked under the token scope this skill assumes, so the remote is the only source. If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
|
||||
|
||||
## Step 2 — Dispatch
|
||||
|
||||
@@ -50,17 +43,18 @@ If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea r
|
||||
|---|---|
|
||||
| `/gitea-branches` or `/gitea-branches list` | List branches |
|
||||
| `/gitea-branches create <name> [from <base>]` | Create branch |
|
||||
| `/gitea-branches rename <name> to <new-name>` | Rename branch |
|
||||
| `/gitea-branches delete <name>` | Delete branch |
|
||||
| `/gitea-branches commits [on <branch>] [touching <path>]` | List commit history |
|
||||
| `/gitea-branches commit <sha>` | Get full detail for one commit |
|
||||
|
||||
For branch operations (list/create/delete), read `references/branches.md`.
|
||||
For branch operations (list/create/rename/delete), read `references/branches.md` — it carries the call signatures, the `old_branch` source rule, and the protected-branch refusal in full.
|
||||
For commit operations (list/get), read `references/commits.md`.
|
||||
|
||||
## Step 3 — Report
|
||||
|
||||
For reads: display branches as name + protected flag; display commits as SHA (short), message summary, author, date.
|
||||
|
||||
For writes (create/delete): confirm the action taken, the branch name, and (for create) the base it forked from.
|
||||
For writes (create/rename/delete): confirm the action taken, the branch name, and (for create) the base it forked from or (for rename) the name it had before.
|
||||
|
||||
For errors: surface the HTTP code and message. If a 404 is unexpected, re-check token scope per the Gotchas above before reporting "not found" to the user.
|
||||
For errors: surface the HTTP code and message, applying the 404 gotcha above before reporting "not found" to the user.
|
||||
|
||||
@@ -7,10 +7,11 @@ source_keys:
|
||||
|
||||
# Branch operations
|
||||
|
||||
Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`
|
||||
at authoring time, not copied from research docs — this is deliberate: research docs are generated
|
||||
from source code at a point in time and can drift from the server actually deployed. Re-verify
|
||||
against the live schema if these tools appear to behave differently than documented here.
|
||||
Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`,
|
||||
not copied from research docs — this is deliberate: research docs are generated from source code at
|
||||
a point in time and can drift from the server actually deployed. **Last verified against gitea-mcp
|
||||
v1.7.0**, as reported by `get_gitea_mcp_server_version`. Re-verify against the live schema if the
|
||||
deployed version differs or these tools behave differently than documented here.
|
||||
|
||||
## `list_branches`
|
||||
|
||||
@@ -21,7 +22,7 @@ against the live schema if these tools appear to behave differently than documen
|
||||
- `per_page` (number, optional, default: `30`)
|
||||
|
||||
**Call:**
|
||||
```
|
||||
```text
|
||||
list_branches owner: <owner> repo: <repo>
|
||||
```
|
||||
|
||||
@@ -41,7 +42,7 @@ count is less than `per_page`.
|
||||
branch server-side (not necessarily your current local checkout)
|
||||
|
||||
**Call:**
|
||||
```
|
||||
```text
|
||||
create_branch owner: <owner> repo: <repo> branch: <new-name> old_branch: <source-branch>
|
||||
```
|
||||
|
||||
@@ -53,6 +54,29 @@ top-level request with no working branch context), omit `old_branch` and let it
|
||||
|
||||
A branch name collision returns `409 Conflict`.
|
||||
|
||||
## `rename_branch`
|
||||
|
||||
**Parameters:**
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `branch` (string, required) — the branch's current name
|
||||
- `new_name` (string, required) — the name to move it to
|
||||
|
||||
**Call:**
|
||||
```text
|
||||
rename_branch owner: <owner> repo: <repo> branch: <current-name> new_name: <new-name>
|
||||
```
|
||||
|
||||
A rename moves the ref server-side; it is not a delete-plus-create, and no commit history is
|
||||
rewritten. What it does to things *pointing at* the old name — open pull requests using it as head or
|
||||
base, a branch protection rule matching it, CI config, and tracking branches on every other clone —
|
||||
is **not confirmed** by this skill's sources: the deployed tool describes itself only as "Rename an
|
||||
existing branch in a repository". Treat a rename of a branch with open PRs or a protection rule as a
|
||||
change needing verification afterward (`list_branches`, plus `gitea-prs` for the PR side), and
|
||||
confirm with the user first, exactly as for `delete_branch` below. A collision with an existing
|
||||
branch name is expected to return `409 Conflict` by analogy with `create_branch`, not separately
|
||||
confirmed.
|
||||
|
||||
## `delete_branch`
|
||||
|
||||
**Parameters:**
|
||||
@@ -61,11 +85,11 @@ A branch name collision returns `409 Conflict`.
|
||||
- `branch` (string, required)
|
||||
|
||||
**Call:**
|
||||
```
|
||||
```text
|
||||
delete_branch owner: <owner> repo: <repo> branch: <name>
|
||||
```
|
||||
|
||||
Before calling this, see the hard-refusal Gotcha in SKILL.md. If the target branch's name isn't
|
||||
Before calling this, see the `delete_branch` Gotcha in SKILL.md. If the target branch's name isn't
|
||||
obviously a scratch/feature branch, call `list_branches` first and check `protected` on the
|
||||
matching entry — name-matching `main`/`master` alone isn't authoritative, since a repo can protect
|
||||
a differently-named default branch. Confirm explicitly with the user before deleting anything
|
||||
@@ -73,9 +97,12 @@ protected, every time, regardless of how the request is phrased.
|
||||
|
||||
## Token scope
|
||||
|
||||
All three — `list_branches`, `create_branch`, `delete_branch` — require `write:repository`. Gitea
|
||||
`list_branches`, `create_branch` and `delete_branch` all require `write:repository`. Gitea
|
||||
gates reads behind write scope for repo-scoped operations, so `list_branches` needs the same scope
|
||||
as the write operations, not `write:issue` alone. An earlier version of this doc claimed
|
||||
`write:issue` alone was sufficient for `list_branches`, based on empirical testing under a token
|
||||
that held both `write:issue` and `write:repository` simultaneously — that test didn't isolate the
|
||||
variable, so it couldn't actually establish `write:issue` alone as sufficient.
|
||||
|
||||
`rename_branch` is a write on the same repo-scoped surface and is inferred to need `write:repository`
|
||||
too — inferred by analogy, not separately confirmed.
|
||||
|
||||
@@ -8,8 +8,9 @@ source_keys:
|
||||
# Commit operations
|
||||
|
||||
Read-only commit history, scoped to a repo (optionally to one branch or one path). Call signatures
|
||||
below were verified live against the deployed `gitea-mcp` server via `ToolSearch` at authoring time,
|
||||
not copied from research docs, for the same drift-avoidance reason noted in `references/branches.md`.
|
||||
below were verified live against the deployed `gitea-mcp` server via `ToolSearch`, not copied from
|
||||
research docs, for the same drift-avoidance reason noted in `references/branches.md`. **Last verified
|
||||
against gitea-mcp v1.7.0**, as reported by `get_gitea_mcp_server_version`.
|
||||
|
||||
This domain has no prior skill precedent — it's new coverage added alongside branches because commit
|
||||
history is naturally scoped to a branch (a "what happened on this branch" question), not because it
|
||||
@@ -27,7 +28,7 @@ shares any tool family with branch create/delete.
|
||||
- `per_page` (number, optional, default: `30`, minimum: `1`)
|
||||
|
||||
**Call:**
|
||||
```
|
||||
```text
|
||||
list_commits owner: <owner> repo: <repo> sha: <branch-or-sha> path: <optional-path>
|
||||
```
|
||||
|
||||
@@ -42,7 +43,7 @@ Dispatch defaults:
|
||||
**Response:** one object per commit: `sha`, `html_url`, `created`, `message` (when available),
|
||||
`author` (`{name, email, date}`, when available).
|
||||
|
||||
Paginate per the manual-pagination Gotcha in SKILL.md if you need more than one page of history.
|
||||
Paginate per the pagination Gotcha in SKILL.md if you need more than one page of history.
|
||||
|
||||
## `get_commit`
|
||||
|
||||
@@ -52,7 +53,7 @@ Paginate per the manual-pagination Gotcha in SKILL.md if you need more than one
|
||||
- `sha` (string, required)
|
||||
|
||||
**Call:**
|
||||
```
|
||||
```text
|
||||
get_commit owner: <owner> repo: <repo> sha: <commit-sha>
|
||||
```
|
||||
|
||||
@@ -64,10 +65,10 @@ edge cases, `get_commit` will not.
|
||||
## Token scope
|
||||
|
||||
Both tools are believed to require `write:repository`, even though they're read-only — inferred by
|
||||
analogy with the scope-gating principle in `overview.md` (Gitea gates reads behind write scope for
|
||||
repo-scoped operations), not a claim `overview.md` makes for commits by name: its explicit
|
||||
`write:repository` enumeration lists PR, branch, file, release, and tag operations, but doesn't
|
||||
mention commits. An earlier version of this doc claimed `write:issue` alone worked, based on
|
||||
analogy with the scope-gating principle confirmed for branch operations in `branches.md`'s Token
|
||||
scope section (Gitea gates reads behind write scope for repo-scoped operations), not a claim any
|
||||
doc in this skill makes for commits by name: nothing here enumerates commits under
|
||||
`write:repository` explicitly. An earlier version of this doc claimed `write:issue` alone worked, based on
|
||||
empirical testing under a token that held both `write:issue` and `write:repository`
|
||||
simultaneously — that test didn't isolate the variable either. Treat this as unverified until
|
||||
tested under a token scoped to `write:issue` only (no `write:repository`).
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
**Note on call signatures:** per `docs/adr/0011-gitea-skill-deep-modules.md`, the tool parameter
|
||||
signatures in `references/branches.md` and `references/commits.md` were re-verified live via
|
||||
`ToolSearch` against the deployed `gitea-mcp` server at authoring time — they are not copied
|
||||
verbatim from `api-reference.md` below. This resolves issue #6 comment #849's root-cause finding
|
||||
`ToolSearch` against the deployed `gitea-mcp` server — **last verified against v1.7.0**, as reported
|
||||
by `get_gitea_mcp_server_version` — rather than copied verbatim from `api-reference.md` below. This resolves issue #6 comment #849's root-cause finding
|
||||
that a prior skill was authored from API docs that had drifted from the actual MCP tool schema.
|
||||
The research docs cited here informed gotchas, response shapes, and workflow context, not the
|
||||
parameter lists themselves.
|
||||
@@ -11,7 +11,7 @@ parameter lists themselves.
|
||||
## gitea-mcp-repo
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp
|
||||
- **Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags. Informed the dispatch table and pagination / 404-may-mean-403 gotchas in SKILL.md, and the list/create/delete branch and list/get commit mechanics (including 409 conflict and default-branch fallback behavior) in references/branches.md and references/commits.md.
|
||||
- **Description:** Official gitea-mcp repository; operation/*.go source files documenting the MCP tools, their parameters, and CLI flags. Extracted at v1.3.0; the parameter lists carried into this skill are re-verified live against the deployed server, last at v1.7.0. Informed the dispatch table and pagination / 404-may-mean-403 gotchas in SKILL.md, and the list/create/delete branch and list/get commit mechanics (including 409 conflict and default-branch fallback behavior) in references/branches.md and references/commits.md.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md, references/branches.md, references/commits.md
|
||||
- **Status:** `extracted`
|
||||
@@ -19,7 +19,7 @@ parameter lists themselves.
|
||||
## gitea-mcp-slim-go
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/repo/slim.go
|
||||
- **Description:** Slim response shape structs from gitea-mcp source; defines exactly which fields the MCP server returns for branches (name, protected, commit_sha) and commits (sha, html_url, created, message, author), and informed get_commit's always-populated guarantee vs. list_commits' conditional fields.
|
||||
- **Description:** Slim response shape structs from gitea-mcp source, extracted at v1.3.0; defines exactly which fields the MCP server returns for branches (name, protected, commit_sha) and commits (sha, html_url, created, message, author), and informed get_commit's always-populated guarantee vs. list_commits' conditional fields.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** references/branches.md, references/commits.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
@@ -8,7 +8,7 @@ This skill handles file-domain operations within the Gitea integration suite: re
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
```text
|
||||
/gitea-files
|
||||
```
|
||||
|
||||
@@ -19,5 +19,6 @@ Describe the file task: read a file or directory, walk a tree, create/update a f
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `references/examples.md` | Canonical call sequences: branch + file + PR, recovering a missing SHA before an update, deleting a file |
|
||||
| `references/reading.md` | Loaded for the read flow: the three read tools, `ref`/`tree_sha` selection, tree pagination, and why a listing is not a SHA source |
|
||||
| `references/writing.md` | Loaded for the write flow: the SHA-first create/update/delete sequences, `new_branch_name`, the worked branch + file + PR sequence, and failed-write triage |
|
||||
| `references/sources.md` | Research sources backing the SHA/concurrency and direct-commit-vs-PR guidance |
|
||||
|
||||
@@ -2,15 +2,9 @@
|
||||
name: gitea-files
|
||||
|
||||
description: >
|
||||
Use when reading or writing individual files or directory trees in a Gitea repository via the
|
||||
Gitea MCP server: reading a file's contents, listing a directory, walking a full repository
|
||||
tree, creating a new file, updating an existing file, or deleting a file. Triggers on "read this
|
||||
file from the repo", "what's in this directory", "show me the repo tree", "create/update a file
|
||||
in Gitea", "commit this file to the branch", "delete this file from the repo" — even when the
|
||||
user doesn't say "Gitea" explicitly, as long as the target is a Gitea-hosted repository. Do not
|
||||
use for local filesystem file operations (use Read/Write/Edit), for branch or commit history
|
||||
(use gitea-branches), or for opening a pull request around a file change (use gitea-prs after
|
||||
the file write completes here).
|
||||
Use when reading or writing files or directories in a Gitea repository via the MCP server,
|
||||
rather than the local filesystem (for a local path use Read/Write/Edit) — even when the user
|
||||
does not say "Gitea". Not commit history -> `gitea-branches`. Not pull requests -> `gitea-prs`.
|
||||
|
||||
compatibility: Requires the Gitea MCP server configured with a token scoped to at least
|
||||
write:repository. Tested with a token holding write:issue + write:repository; write:issue
|
||||
@@ -28,29 +22,25 @@ allowed-tools: mcp__gitea__get_file_contents mcp__gitea__get_dir_contents mcp__g
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **A 404 from any read call may actually be a 403 in disguise.** `get_file_contents`, `get_dir_contents`, and `get_repository_tree` all gate on `write:repository` scope, not just read access — some Gitea endpoints return 404 instead of 403 when the token's scope is insufficient, to avoid leaking whether the resource exists. If a read fails with 404 on a path you're confident is correct, check the token's configured scopes before concluding the file or directory doesn't exist.
|
||||
- **SHA is the concurrency token for every write — and it lives at the top level of `get_file_contents`'s response, not nested under `content`.** `create_or_update_file` without `sha` is always treated as a *create*: if the path already exists, Gitea returns HTTP 409. `delete_file` has no optional path at all — omitting `sha` returns HTTP 422. The safe sequence for any update or delete is always: call `get_file_contents` first, read the top-level `sha` field, then pass that exact value to the write call. Never guess or reuse a stale SHA — a mismatched SHA is rejected the same as a missing one.
|
||||
- **A write can also fail because the branch requires signed commits — a separate failure mode from a bad SHA.** `create_or_update_file` and `delete_file` create commits server-side via a bare API token call with no 2FA/PGP context. If the target branch's protection rule requires signed commits, Gitea rejects the write outright — surfaced as a generic 403 or 422, not an error naming "signed commit required," and reads against that same branch keep succeeding right up until you try to write. When a write fails without a clean 409 (missing/stale SHA) or 404 (bad path) explanation, check whether the branch's protection rule requires signed commits before assuming the SHA is wrong and retrying.
|
||||
- **A large `create_or_update_file` payload can hit a reverse-proxy 413 that has nothing to do with Gitea.** `content` is base64-encoded, which inflates the payload ~33% over the raw file size; a 413 is commonly a reverse-proxy body-size limit in front of the Gitea instance, not a Gitea-side rejection. No amount of retrying, or changing the SHA, path, or branch, will fix it — it needs the proxy's config raised, which is outside this skill's or the calling agent's control. Surface that distinction to the user instead of retrying the same call.
|
||||
- **`get_dir_contents` and `get_repository_tree` are not SHA sources for a specific file's write.** `get_dir_contents` entries carry no `sha` at all. `get_repository_tree` entries do carry a `sha` (a blob/tree hash), but fetching it means an extra round trip with no content — `get_file_contents` is the canonical path since it returns the decoded content and the write-ready `sha` in one call.
|
||||
- **`owner` and `repo` are always caller-supplied inputs, never resolved here.** This skill doesn't infer them from a git remote. If invoked directly by a human, ask for them if not stated. If invoked by `gitea-workflow` or an orchestrating agent, expect them to already be resolved and passed in.
|
||||
- **Direct commits to a branch are a first-class action, not a workaround.** Gitea's own web UI defaults to editing files directly against a branch — `create_or_update_file`/`delete_file` used that way is normal, not an API escape hatch to avoid. The SHA-currency requirement above is the actual risk to manage, not the act of committing directly.
|
||||
- **`ref` (reads) vs. `branch_name` (writes) are different parameters for the same concept.** `get_file_contents`, `get_dir_contents`, and `get_repository_tree` (as `tree_sha`) all accept a branch name, tag, or commit SHA to select what to read. `create_or_update_file` and `delete_file` instead take `branch_name` — the branch the commit lands on. Don't conflate the two when chaining a read into a write.
|
||||
- **Content is base64.** `create_or_update_file`'s `content` parameter is base64-encoded file content, not raw text — encode before calling. `get_file_contents`'s response content is likewise base64-encoded (decode after reading), unless `withLines: true` is passed for a numbered-line view.
|
||||
- **A 404 may mean an under-scoped token, not a missing path.** Every tool here gates on `write:repository`, and Gitea masks insufficient scope as 404. Check scopes first.
|
||||
- **Reads take `ref` (`tree_sha` on `get_repository_tree`), writes take `branch_name`.** One concept, three names — carry the wrong key and the branch is dropped.
|
||||
- **`content` is base64 both ways — except under `withLines: true`.** Encode before a write, decode after a read; but with `withLines: true` `content` is already plain JSON text and the reported `"encoding": "base64"` is a lie. Decoding it yields garbage.
|
||||
|
||||
## Reading
|
||||
## Inputs
|
||||
|
||||
- **Single file:** `get_file_contents(owner, repo, ref, path)`. Pass `withLines: true` only when you need line numbers for referencing specific lines (e.g. quoting a snippet back to the user); omit it for a normal content fetch.
|
||||
- **One directory level:** `get_dir_contents(owner, repo, ref, path)` — returns immediate entries only (name, path, type, size), no recursion, no SHA, no content.
|
||||
- **Whole tree:** `get_repository_tree(owner, repo, tree_sha, recursive)` — `tree_sha` accepts a SHA, branch, or tag name despite the name. Set `recursive: true` to walk subdirectories in one call. Response includes `truncated: true` when a page doesn't hold every entry — page through with `page`/`per_page` (default `page: 1`, `per_page: 30`) until you get fewer results than `per_page`.
|
||||
`owner`, `repo` and the target branch are caller-supplied. This skill never infers them from a git remote: ask the human when they are not stated, and expect an orchestrating caller to have resolved them already.
|
||||
|
||||
## Writing
|
||||
## Dispatch
|
||||
|
||||
- **Creating a new file:** call `create_or_update_file(owner, repo, path, content, message, branch_name)` with `sha` omitted entirely.
|
||||
- **Updating an existing file:** call `get_file_contents(owner, repo, ref: branch_name, path)` first, take the top-level `sha`, then call `create_or_update_file(..., sha: <that value>)`.
|
||||
- **Deleting a file:** call `get_file_contents` first the same way, then `delete_file(owner, repo, path, message, branch_name, sha: <that value>)` — `sha` is required, no create-style fallback exists.
|
||||
- **Creating a new branch as part of the write:** pass `new_branch_name` on `create_or_update_file` to branch off before the commit lands, instead of calling a separate branch-creation step.
|
||||
Read the reference for the row you land on before making the call.
|
||||
|
||||
If the change needs review before merging, or targets a protected branch, hand off to `gitea-prs` after the write lands here to open the pull request — this skill's scope ends at the commit.
|
||||
| Condition | Flow | Reference |
|
||||
|---|---|---|
|
||||
| Read one file, list one directory level, or walk the repository tree | Read | `references/reading.md` — the three read tools, their pagination behaviour, and why neither a directory listing nor a tree entry supplies the SHA a write needs |
|
||||
| Create, update, or delete a file | Write | `references/writing.md` — the SHA-first sequence every update and delete depends on, the worked multi-call sequence, and how to triage a write that fails |
|
||||
|
||||
If you need the full multi-call sequence rather than the single-call summary above — e.g. branching off as part of a file push ahead of opening a PR, or recovering a SHA you didn't capture earlier — read `references/examples.md`.
|
||||
A request that reads and then writes runs both flows in that order: fetch the file first, then write with the SHA that call returned.
|
||||
|
||||
## Handoff
|
||||
|
||||
Scope ends at the commit. Gitea's own web UI edits files directly against a branch, so committing straight to a branch is the normal path rather than an escape hatch — hand off to `gitea-prs` when the change needs review before merging or the target branch is protected, not by default.
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
---
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# Canonical call sequences
|
||||
|
||||
## Push a file to a new branch, then open a PR
|
||||
|
||||
```
|
||||
1. get_repository_tree or get_file_contents on the base branch — only needed if the
|
||||
new file is actually replacing an existing one; skip for a brand-new path.
|
||||
|
||||
2. create_or_update_file
|
||||
owner, repo
|
||||
path: "docs/example.md"
|
||||
content: "<base64-encoded content>"
|
||||
message: "docs: add example"
|
||||
branch_name: "main"
|
||||
new_branch_name: "feat/add-example" ← branches off before the commit lands
|
||||
(sha omitted — this is a new file)
|
||||
|
||||
3. Hand off to gitea-prs to open a PR from "feat/add-example" into "main".
|
||||
```
|
||||
|
||||
`new_branch_name` on `create_or_update_file` replaces a separate branch-creation call — the branch is created and the commit lands on it in one step.
|
||||
|
||||
## Update a file when you don't already have its SHA
|
||||
|
||||
SHA is mandatory for updates. If it wasn't captured earlier in the conversation:
|
||||
|
||||
```
|
||||
1. get_file_contents
|
||||
owner, repo
|
||||
ref: "main"
|
||||
path: "docs/example.md"
|
||||
→ read the top-level `sha` field (not content.sha)
|
||||
|
||||
2. create_or_update_file
|
||||
owner, repo
|
||||
path: "docs/example.md"
|
||||
content: "<new base64-encoded content>"
|
||||
message: "docs: update example"
|
||||
branch_name: "main"
|
||||
sha: "<sha from step 1>"
|
||||
```
|
||||
|
||||
Do not guess or omit the SHA — the write either fails (409 on create-path fallback) or is rejected outright.
|
||||
|
||||
## Delete a file
|
||||
|
||||
Same SHA-first pattern, no fallback path:
|
||||
|
||||
```
|
||||
1. get_file_contents owner, repo, ref: "main", path: "docs/old-example.md"
|
||||
→ read the top-level `sha` field
|
||||
|
||||
2. delete_file
|
||||
owner, repo
|
||||
path: "docs/old-example.md"
|
||||
message: "docs: remove old example"
|
||||
branch_name: "main"
|
||||
sha: "<sha from step 1>"
|
||||
```
|
||||
50
plugins/gitea/.apm/skills/gitea-files/references/reading.md
Normal file
50
plugins/gitea/.apm/skills/gitea-files/references/reading.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# Reading files, directories and trees
|
||||
|
||||
All three read calls select what to read with `ref` — a branch name, tag, or commit SHA. On
|
||||
`get_repository_tree` the same value goes in `tree_sha` despite the name.
|
||||
|
||||
## Single file
|
||||
|
||||
`get_file_contents(owner, repo, ref, path)`.
|
||||
|
||||
The response carries the file's `sha` at the **top level**, not nested under `content`. That field
|
||||
is the write-ready SHA, so capture it whenever a write may follow.
|
||||
|
||||
Content comes back base64-encoded — decode it — **unless `withLines: true` was passed**, in which
|
||||
case `content` is already plain text: a JSON array of `{"line": N, "content": "..."}` objects.
|
||||
The response reports `"encoding": "base64"` either way, so that field is wrong under `withLines`
|
||||
and decoding on its word yields garbage. Pass `withLines: true` only when you need numbered lines
|
||||
to quote specific lines back to the user; omit it for a normal content fetch.
|
||||
|
||||
## One directory level
|
||||
|
||||
`get_dir_contents(owner, repo, ref, path)` returns the immediate entries only — name, path, type,
|
||||
size. No recursion, no content, no `sha`.
|
||||
|
||||
## Whole repository tree
|
||||
|
||||
`get_repository_tree(owner, repo, tree_sha, recursive)`. Set `recursive: true` to walk
|
||||
subdirectories in one call.
|
||||
|
||||
The response sets `truncated: true` when one page does not hold every entry. Page through with
|
||||
`page`/`per_page` (defaults `1` and `30`) until a page returns fewer entries than `per_page`.
|
||||
|
||||
## Neither listing is a SHA source for a write
|
||||
|
||||
`get_dir_contents` entries carry no `sha` at all. `get_repository_tree` entries do carry a blob or
|
||||
tree hash, but reaching it costs an extra round trip and returns no content. `get_file_contents` is
|
||||
the canonical path for a write's SHA: one call returns the decoded content and the write-ready
|
||||
`sha` together.
|
||||
|
||||
## A 404 that is really a 403
|
||||
|
||||
These reads gate on `write:repository`, not on read access alone, and some Gitea endpoints answer
|
||||
an under-scoped token with 404 instead of 403 so they do not leak whether the resource exists. A
|
||||
404 on a path you are confident about is a scope problem until proven otherwise — check the token's
|
||||
configured scopes before concluding the file or directory does not exist.
|
||||
@@ -2,15 +2,16 @@
|
||||
|
||||
## gitea-mcp-repo
|
||||
|
||||
**Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags.
|
||||
**Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags. Tool parameters and SHA/concurrency behavior were cross-checked live against the deployed MCP tool schemas via `ToolSearch`, per this repo's process for resolving schema-vs-docs drift, rather than copied from the derived research doc.
|
||||
|
||||
**Source:** https://gitea.com/gitea/gitea-mcp
|
||||
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Gotchas, Reading, Writing — tool parameters and SHA/concurrency behavior, cross-checked live against the deployed MCP tool schemas via ToolSearch)
|
||||
- references/examples.md (canonical call sequences)
|
||||
- SKILL.md (Gotchas — cross-flow parameter and encoding traps; Dispatch)
|
||||
- references/reading.md (read-tool parameters, `ref`/`tree_sha` selection, tree pagination)
|
||||
- references/writing.md (write-tool parameters, SHA/concurrency behavior, canonical call sequences, failed-write triage)
|
||||
|
||||
## gitea-mcp-slim-go
|
||||
|
||||
@@ -21,8 +22,9 @@
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Gotchas — top-level `sha` field location, `get_dir_contents`/`get_repository_tree` not being usable SHA sources for a file write)
|
||||
- references/examples.md (SHA-first update/delete sequences)
|
||||
- SKILL.md (Gotchas — base64 response encoding)
|
||||
- references/reading.md (top-level `sha` field location, `get_dir_contents`/`get_repository_tree` not being usable SHA sources for a file write)
|
||||
- references/writing.md (SHA-first update/delete sequences)
|
||||
|
||||
## context7-websites-gitea
|
||||
|
||||
@@ -33,7 +35,7 @@
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Gotchas — "Direct commits to a branch are a first-class action, not a workaround")
|
||||
- SKILL.md (Handoff — committing straight to a branch is the normal path, not an escape hatch)
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
@@ -43,4 +45,4 @@
|
||||
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:** (none)
|
||||
- **Contributing files:** (none)
|
||||
|
||||
79
plugins/gitea/.apm/skills/gitea-files/references/writing.md
Normal file
79
plugins/gitea/.apm/skills/gitea-files/references/writing.md
Normal file
@@ -0,0 +1,79 @@
|
||||
---
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# Creating, updating and deleting files
|
||||
|
||||
`sha` is the optimistic-concurrency token for every write, and it comes from
|
||||
`get_file_contents`'s **top-level** `sha` field — never from `content.sha`, a directory listing, or
|
||||
a tree entry. Do not guess or reuse a stale value: a mismatched SHA is rejected exactly like a
|
||||
missing one.
|
||||
|
||||
`content` is base64-encoded, and the branch the commit lands on is `branch_name`, not `ref`.
|
||||
|
||||
## Create a new file
|
||||
|
||||
Call `create_or_update_file(owner, repo, path, content, message, branch_name)` with `sha` omitted
|
||||
entirely. An omitted `sha` always means *create*, so the call returns HTTP 409 if the path already
|
||||
exists.
|
||||
|
||||
To branch off as part of the same write, pass `new_branch_name`: the branch is created and the
|
||||
commit lands on it in one call, replacing a separate branch-creation step.
|
||||
|
||||
## Update an existing file
|
||||
|
||||
1. `get_file_contents(owner, repo, ref: <branch>, path)` → read the top-level `sha`.
|
||||
2. `create_or_update_file(owner, repo, path, content, message, branch_name, sha: <that value>)`.
|
||||
|
||||
## Delete a file
|
||||
|
||||
Same SHA-first pattern, with no create-style fallback. `sha` is schema-**required** on
|
||||
`delete_file`, unlike `create_or_update_file` where omitting it means *create* — so an omitted `sha`
|
||||
is rejected client-side by input validation and the call never reaches Gitea. The HTTP 422 that is
|
||||
actually reachable here is the stale-`sha` case.
|
||||
|
||||
1. `get_file_contents(owner, repo, ref: <branch>, path)` → read the top-level `sha`.
|
||||
2. `delete_file(owner, repo, path, message, branch_name, sha: <that value>)`.
|
||||
|
||||
## Worked sequence — new file on a new branch, then a PR
|
||||
|
||||
```text
|
||||
1. create_or_update_file
|
||||
owner, repo
|
||||
path: "docs/example.md"
|
||||
content: "<base64-encoded content>"
|
||||
message: "docs: add example"
|
||||
branch_name: "main"
|
||||
new_branch_name: "feat/add-example" ← branches off before the commit lands
|
||||
(sha omitted — this is a new file)
|
||||
|
||||
2. Hand off to gitea-prs to open a PR from "feat/add-example" into "main".
|
||||
```
|
||||
|
||||
Step 1 needs no preceding read: a brand-new path has no SHA. Fetch the current file first only
|
||||
when the write replaces an existing one.
|
||||
|
||||
## Triaging a failed write
|
||||
|
||||
| Symptom | Cause | Action |
|
||||
|---|---|---|
|
||||
| HTTP 409 | `sha` omitted on a path that already exists | Fetch the current SHA, retry as an update |
|
||||
| HTTP 422 | Stale `sha` — the file changed between the read and the write | Re-fetch the SHA immediately before the write |
|
||||
| 403 or 422 with no SHA explanation | Branch protection requires signed commits | Stop and report |
|
||||
| HTTP 413 | Reverse-proxy body limit in front of Gitea | Report; retrying cannot fix it |
|
||||
| Client-side input-validation error naming `sha` | `sha` omitted on `delete_file`, where it is schema-required | Fetch the current SHA and retry — nothing was sent to Gitea |
|
||||
| HTTP 404 | Wrong path, or a token without `write:repository` | Verify the path, then the token's scopes |
|
||||
|
||||
**Signed commits.** These writes create commits server-side from a bare API token with no 2FA or
|
||||
PGP context. If the target branch's protection rule requires signed commits, Gitea rejects the
|
||||
write as a generic 403 or 422 that never names signing, and reads against that same branch keep
|
||||
succeeding right up until the write. When a write fails without a clean 409 or 404 explanation,
|
||||
check the branch's protection rule before assuming the SHA is wrong and retrying.
|
||||
|
||||
**Payload size.** base64 inflates `content` roughly 33% over the raw file size, and a 413 is
|
||||
usually a reverse-proxy body-size limit in front of the Gitea instance rather than a Gitea-side
|
||||
rejection. No amount of retrying, or changing the SHA, path, or branch, will fix it — the proxy's
|
||||
config has to be raised, which is outside this skill's control. Surface that distinction instead
|
||||
of retrying the same call.
|
||||
@@ -10,26 +10,35 @@ its state, adding/editing comments, applying labels, and searching issues/PRs ac
|
||||
The create flow closes out four enrichments deferred from issue #6 comment #848: label inference
|
||||
and milestone assignment (both by composing `gitea-labels-milestones`), an assignee workaround for
|
||||
the blocked `get_me` scope, and the "Depends on #N" dependency-linking convention. It supersedes the
|
||||
`issue`/`issue <N>`/`issue close <N>`/`issue comment <N>` dispatch in the old flat
|
||||
`plugins/bin/skills/gitea/SKILL.md`, removed per
|
||||
`docs/adr/0011-gitea-skill-deep-modules.md`.
|
||||
`issue`/`issue <N>`/`issue close <N>`/`issue comment <N>` dispatch this plugin's old single flat
|
||||
Gitea skill carried, retired when the plugin was split into per-domain deep modules.
|
||||
|
||||
## Before you start
|
||||
|
||||
Requires a Gitea MCP server configured with a token holding `write:issue` + `write:repository`.
|
||||
Requires a git remote named `origin` pointing at the Gitea instance, unless an orchestrating caller
|
||||
(e.g. `gitea-workflow`) already resolved `owner`/`repo` for you. Label and milestone management
|
||||
(creating/editing a label, creating/closing a milestone) is out of scope here — that's
|
||||
`gitea-labels-milestones`, which this skill composes rather than duplicates.
|
||||
(e.g. `gitea-workflow`) already resolved `owner`/`repo` for you.
|
||||
|
||||
## How it composes
|
||||
|
||||
This skill composes `gitea-labels-milestones` for *all* label inference, label-name-to-ID
|
||||
resolution, and milestone lookup, rather than duplicating that taxonomy or its resolution logic —
|
||||
see `references/enrichments.md` for the call protocol. Managing the label and milestone definitions
|
||||
themselves (create/edit/delete a label, create/close a milestone) is out of scope here and goes to
|
||||
`gitea-labels-milestones` directly.
|
||||
|
||||
One boundary the description does not spend characters on, because it was never going to win an
|
||||
issue request: local git branch or commit work belongs to `gitea-branches` (Gitea-side) or
|
||||
`git-branches` (working copy).
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
```text
|
||||
/gitea-issues
|
||||
```
|
||||
|
||||
Describe your task: list issues, create one, get/comment/close a specific issue number, or search
|
||||
across repos. See `SKILL.md`'s dispatch table for the full set of recognized invocations.
|
||||
Describe your task: list issues, create one, get/comment/close/label a specific issue number, or
|
||||
search across repos. See `SKILL.md`'s dispatch table for the full set of recognized invocations.
|
||||
|
||||
## Files
|
||||
|
||||
|
||||
@@ -2,15 +2,10 @@
|
||||
name: gitea-issues
|
||||
|
||||
description: >
|
||||
Use when reading or writing Gitea issues: listing repo issues, getting a single issue's details/
|
||||
comments/labels, creating an issue, updating its state, adding or editing comments, applying
|
||||
labels via issue_write, or searching issues/PRs across repositories. Triggers on "create an
|
||||
issue", "what issues are open", "get issue #N", "close issue #N", "comment on issue #N", "search
|
||||
issues for X" — even when the user doesn't say "Gitea" explicitly. Composes gitea-labels-
|
||||
milestones for all label inference/resolution and milestone lookup — do not use this skill to
|
||||
manage label or milestone definitions themselves (create/edit/delete a label, create/close a
|
||||
milestone), that's gitea-labels-milestones directly. Do not use for pull requests (use gitea-prs)
|
||||
or for local git branch/commit work (use gitea-branches or git-branches).
|
||||
Use when reading or writing Gitea issues — "create an issue", "what issues are open",
|
||||
"close issue #N", "comment on issue #N", "label issue #N", "search issues for X" — even when
|
||||
the user does not say "Gitea". Not pull requests -> `gitea-prs`.
|
||||
Not label or milestone definitions -> `gitea-labels-milestones`.
|
||||
|
||||
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token
|
||||
scopes. Requires git remote "origin" pointing to the Gitea instance for owner/repo resolution
|
||||
@@ -19,7 +14,7 @@ compatibility: Requires Gitea MCP server configured with write:issue and write:r
|
||||
|
||||
metadata:
|
||||
category: integration
|
||||
version: "0.1.0"
|
||||
version: "0.1.3"
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
@@ -31,75 +26,49 @@ allowed-tools: Bash mcp__gitea__list_issues mcp__gitea__issue_read mcp__gitea__i
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`list_issues` has no `type` or `milestones` parameter — despite `api-reference.md` documenting both.** The live MCP schema (re-verified via `ToolSearch` at authoring time — see `references/sources.md`) only accepts `owner`, `repo` (required), `state` (default `"all"`), `labels` (array of label *names*), `since`, `before` (ISO 8601), `page`, `per_page` (default 30). This tool provides no way to filter issues-vs-PRs or by milestone. Since issues and PRs share one number space, `list_issues` results can include PR entries with no client-side filter to exclude them. If you need to know whether a specific number is a PR, call `issue_read method: "get"` and check `is_pull` — that field only appears on the single-item response, never in a list item. This exact drift (a prior skill trusted the research doc's `type` param and broke) is why this skill's reference files were re-verified live rather than copied from `api-reference.md`.
|
||||
- **`search_issues` does have a working `type` filter** (`"issues"` | `"pulls"`) — unlike `list_issues`. Its `labels` parameter is also shaped differently: a comma-separated string, not an array of names.
|
||||
- **Labels are numeric IDs on write, name strings on read.** `issue_write`'s `labels` parameter (used by `add_labels`/`replace_labels`) takes IDs. `list_issues`/`issue_read` return names. Never resolve this yourself — compose `gitea-labels-milestones` (see `references/enrichments.md`) to get IDs.
|
||||
- **Milestone on `issue_read` is `{id, title}`** — an object, not a bare string. This skill only ever needs the `id`. (The bare-title-string case only happens on the PR side, which is `gitea-prs`' problem, not this skill's.)
|
||||
- **Closing-keyword auto-close behavior is plausible but unconfirmed in our research docs.** Our research docs confirm Gitea does NOT auto-close an issue on a plain PR merge (unlike GitHub) — closing keywords like `Fixes #N`/`Closes #N` in a commit message are not documented one way or the other. After a PR merges (a `gitea-prs` operation), always re-check the issue's state here via `issue_read method: "get"` before deciding whether to close it manually — closing an already-closed issue is a harmless no-op, but don't assume a manual close is always needed.
|
||||
- **Pagination is manual.** `list_issues` and `search_issues` return one page at a time. Iterate `page: 1, 2, ...` until the returned count is less than `per_page`.
|
||||
- **HTTP 404 may actually mean 403.** Gitea hides permission errors as not-found. If a call 404s unexpectedly, verify the token holds `write:issue` scope (see `references/issues.md`'s Token scope note) before concluding the issue doesn't exist.
|
||||
- **`list_issues` mixes in PRs unless you filter.** Issues and PRs share one number space; pass `type: "issues"` to exclude PRs (or `"pulls"`). `is_pull` is returned only by `issue_read method: "get"` — on a list item the only tell is `html_url`'s path segment (`/issues/` vs `/pulls/`).
|
||||
- **Label IDs and names are not interchangeable.** `issue_write` takes numeric IDs only; `list_issues` and `search_issues` filter by name; `issue_read "get"` returns names but `"get_labels"` returns full objects with IDs. Resolve via `gitea-labels-milestones` unless the caller named exact labels.
|
||||
- **A merge does not itself close the issue.** Gitea has no close-on-merge event, but a `Fixes #N` in the merged commits can, depending on merge style (`gitea-prs`). Re-read its state after a merge before closing it manually.
|
||||
- **A 404 may really be a 403.** Gitea hides permission errors as not-found — check the token's `write:issue` scope before concluding the issue does not exist.
|
||||
|
||||
## Step 1 — Resolve owner and repo
|
||||
|
||||
Before any tool call, extract `owner` and `repo` from the git remote (skip this if an orchestrating caller already passed them in):
|
||||
An orchestrating caller may pass `owner` and `repo` in already, and the `search` row is cross-repository and needs only a query — both skip this step. Otherwise, before any tool call:
|
||||
|
||||
```bash
|
||||
git remote get-url origin
|
||||
```
|
||||
|
||||
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
|
||||
If origin is unset or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
|
||||
|
||||
## Step 2 — Dispatch
|
||||
|
||||
| Invocation | Action |
|
||||
|---|---|
|
||||
| `/gitea-issues` or `/gitea-issues list` | List issues (optional state filter) |
|
||||
| `/gitea-issues create` | Create an issue from conversation context — infers labels, checks milestone fit, applies a configured default assignee if set |
|
||||
| `/gitea-issues <N>` | Get issue details (flag it as a PR if `is_pull: true`) |
|
||||
| `/gitea-issues <N> comments` | Get an issue's comments |
|
||||
| `/gitea-issues close <N>` | Close an issue |
|
||||
| `/gitea-issues comment <N>` | Add a comment from conversation context |
|
||||
| `/gitea-issues search <query>` | Cross-repo search via `search_issues` |
|
||||
One invocation takes one row. Read only the reference(s) that row names — the call signatures were verified against the live MCP schema and differ from the published API docs in ways the body does not restate.
|
||||
|
||||
For full parameter detail on `list_issues`/`issue_read`/`issue_write`, read `references/issues.md`. For `search_issues`, read `references/search.md`. For the create-flow enrichments (label inference, milestone assignment, assignee workaround, dependency-linking), read `references/enrichments.md`.
|
||||
| Invocation | Flow | Read |
|
||||
|---|---|---|
|
||||
| `/gitea-issues` or `/gitea-issues list` | List issues with `type: "issues"` so PRs are excluded, optionally filtered by state | `references/issues.md` |
|
||||
| `/gitea-issues <N>` | Get one issue, routing to `gitea-prs` when the number turns out to be a PR | `references/issues.md` |
|
||||
| `/gitea-issues <N> comments` | Get an issue's comments | `references/issues.md` |
|
||||
| `/gitea-issues <N> labels` | Get an issue's labels as full objects, IDs included | `references/issues.md` |
|
||||
| `/gitea-issues close <N>` | Close an issue by updating its state | `references/issues.md` |
|
||||
| `/gitea-issues comment <N>` | Add a comment drawn from conversation context, never one invented to fill the gap — a comment on a live issue is not cheap to undo | `references/issues.md` |
|
||||
| `/gitea-issues label <N>` | Apply, replace or remove labels using IDs resolved by `gitea-labels-milestones` | `references/issues.md` |
|
||||
| `/gitea-issues create` | Create an issue — Step 3 first | `references/enrichments.md`, then `references/issues.md` |
|
||||
| `/gitea-issues search <query>` | Cross-repo search, narrowed by owner, state, type or labels | `references/search.md` |
|
||||
|
||||
## Step 3 — Execute
|
||||
## Step 3 — Create
|
||||
|
||||
### list (default)
|
||||
Only the create flow reaches this step.
|
||||
|
||||
Call `list_issues owner: <owner> repo: <repo> state: <"open"|"closed"|"all", default "all">`. Remember: results may include PR entries (see Gotchas) — if the caller needs issues only, this tool cannot filter that server-side; note the limitation rather than silently mislabeling PR entries as issues.
|
||||
|
||||
### create
|
||||
|
||||
1. Extract `title` and `body` from conversation context (the most recent task, bug description, or explicit statement). Fall back to an empty body if nothing is available.
|
||||
2. Run the enrichment sequence in `references/enrichments.md`: infer labels (composing `gitea-labels-milestones`), check for a clearly-fitting open milestone (composing the same skill), and check for a configured default assignee.
|
||||
3. Call `issue_write method: "create" owner: <owner> repo: <repo> title: <title> body: <body> labels: [<resolved IDs, or omit>] milestone: <resolved ID, or omit> assignees: [<default login, or omit>]`.
|
||||
4. Fire immediately — no confirmation step for the create itself.
|
||||
|
||||
### `<N>` (get)
|
||||
|
||||
Call `issue_read method: "get" owner: <owner> repo: <repo> issue_number: <N>`. If `is_pull: true`, report that this number is actually a PR and suggest `gitea-prs` for full detail.
|
||||
|
||||
### `<N> comments`
|
||||
|
||||
Call `issue_read method: "get_comments" owner: <owner> repo: <repo> issue_number: <N>`.
|
||||
|
||||
### close `<N>`
|
||||
|
||||
Call `issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed"`. No `method: "close"` exists.
|
||||
|
||||
### comment `<N>`
|
||||
|
||||
Extract the comment body from conversation context (same sourcing as create). Call `issue_write method: "add_comment" owner: <owner> repo: <repo> issue_number: <N> body: <body>`.
|
||||
|
||||
### search `<query>`
|
||||
|
||||
Call `search_issues query: <query>`, adding `owner`, `state`, `type`, or `labels` filters if the request narrows scope (e.g. "search open PRs for X" → `type: "pulls" state: "open"`).
|
||||
1. Take `title` and `body` from conversation context — the most recent task, bug report, or explicit statement. An empty body is an acceptable fallback, an invented one is not.
|
||||
2. Run the enrichments in `references/enrichments.md`, then create with the resolved IDs per `references/issues.md`. Omitting a parameter always beats guessing its value — a wrong milestone or assignee is harder to notice than a missing one.
|
||||
3. Fire immediately, with no confirmation step. A create is cheap to undo by closing, so a gate here only costs a round trip.
|
||||
|
||||
## Step 4 — Report
|
||||
|
||||
For reads: a compact table or numbered list — number, title, state, labels, milestone.
|
||||
Reads: a compact table or numbered list — number, title, state, labels, milestone.
|
||||
|
||||
For writes: confirm what was created/updated with the issue number and URL if returned.
|
||||
Writes: what was created or updated, with the issue number and the URL when one is returned.
|
||||
|
||||
For errors: surface the HTTP code and message; check token scope per the Gotchas if a 404 looks wrong.
|
||||
Errors: the HTTP code and message as returned, without paraphrasing either.
|
||||
|
||||
@@ -19,8 +19,7 @@ plausibly fits).
|
||||
reference files directly by path. A plugin install copies each skill's directory into an isolated
|
||||
cache — any file path that leaves this skill's own directory breaks post-install. Instead, compose
|
||||
`gitea-labels-milestones` as a skill: describe the task to it (its own `SKILL.md` and description
|
||||
trigger it) and consume the resolved IDs it returns. This mirrors how `gitea-labels-milestones`'s
|
||||
own description already frames the relationship ("`gitea-issues` and `gitea-prs` both compose it").
|
||||
trigger it) and consume the resolved IDs it returns.
|
||||
|
||||
## 1. Label inference
|
||||
|
||||
@@ -85,8 +84,8 @@ repo-scoped number space. Use the bare `#N` form for same-repo dependencies; use
|
||||
a dependency in a different repo.
|
||||
|
||||
When creating an issue that depends on another, append a line like:
|
||||
```
|
||||
```text
|
||||
Depends on #42
|
||||
```
|
||||
to the body before calling `issue_write method: "create"`. There is no separate field or follow-up
|
||||
call — the rendering happens automatically once the body is saved.
|
||||
to the body before calling `issue_write method: "create"`. No separate field or follow-up call is
|
||||
involved — the rendering happens automatically once the body is saved.
|
||||
|
||||
@@ -7,11 +7,13 @@ source_keys:
|
||||
|
||||
# Issue operations
|
||||
|
||||
Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`
|
||||
at authoring time, not copied from `api-reference.md` — this is deliberate: research docs are
|
||||
generated from source at a point in time and can drift from the server actually deployed (see the
|
||||
`list_issues` gotcha below, which is the exact drift this policy exists to catch). Re-verify against
|
||||
the live schema if these tools appear to behave differently than documented here.
|
||||
Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`,
|
||||
not copied from `api-reference.md` — this is deliberate: research docs are generated from source at
|
||||
a point in time and can drift from the server actually deployed. Last verified against gitea-mcp
|
||||
**v1.7.0**, as reported by `get_gitea_mcp_server_version`. Drift runs in both directions: this file
|
||||
previously recorded `list_issues` as having neither a `type` nor a `milestones` parameter, and
|
||||
v1.7.0 has both. Re-verify against the live schema if these tools appear to behave differently than
|
||||
documented here.
|
||||
|
||||
## `list_issues`
|
||||
|
||||
@@ -21,18 +23,22 @@ the live schema if these tools appear to behave differently than documented here
|
||||
- `state` (string, optional, default `"all"`) — conventional values `"open"`/`"closed"`/`"all"`, not
|
||||
schema-enforced as an enum
|
||||
- `labels` (array of strings, optional) — filter by label *name* (not ID)
|
||||
- `milestones` (array of strings, optional) — filter by milestone name or numeric ID, both passed as
|
||||
strings
|
||||
- `type` (string, enum `"issues"` | `"pulls"`, optional) — omit it and the response mixes both
|
||||
- `since` (string, optional) — ISO 8601, issues updated after this time
|
||||
- `before` (string, optional) — ISO 8601, issues updated before this time
|
||||
- `page` (number, optional, default `1`)
|
||||
- `per_page` (number, optional, default `30`)
|
||||
|
||||
**There is no `type` parameter and no `milestones` parameter**, despite both appearing in
|
||||
`api-reference.md`. This tool cannot filter issues-vs-PRs or by milestone — see the Gotchas section
|
||||
of SKILL.md for the consequence (PR entries can appear in results with no way to exclude them here).
|
||||
**Pass `type: "issues"` on any listing meant to show issues.** Issues and PRs share one repo number
|
||||
space and the unfiltered response interleaves them; the only thing distinguishing them on a list
|
||||
item is the `html_url` path segment (`/issues/` vs `/pulls/`), since `is_pull` is not returned on
|
||||
list items — see the Gotchas section of SKILL.md.
|
||||
|
||||
**Call:**
|
||||
```
|
||||
list_issues owner: <owner> repo: <repo> state: "open"
|
||||
```text
|
||||
list_issues owner: <owner> repo: <repo> state: "open" type: "issues"
|
||||
```
|
||||
|
||||
**Response (list item):** `number`, `title`, `state`, `html_url`, `user`, `comments`, `created_at`,
|
||||
@@ -60,7 +66,7 @@ number is backed by a pull request — absent, not `false`, on true issues).
|
||||
to name strings, unlike the labels array on `get`).
|
||||
|
||||
**Call:**
|
||||
```
|
||||
```text
|
||||
issue_read method: "get" owner: <owner> repo: <repo> issue_number: <N>
|
||||
```
|
||||
|
||||
@@ -89,7 +95,7 @@ gotcha in SKILL.md.
|
||||
- `remove_deadline` (boolean, optional)
|
||||
|
||||
**Create:**
|
||||
```
|
||||
```text
|
||||
issue_write method: "create"
|
||||
owner: <owner> repo: <repo>
|
||||
title: <title> body: <body>
|
||||
@@ -99,24 +105,31 @@ issue_write method: "create"
|
||||
```
|
||||
|
||||
**Close:**
|
||||
```
|
||||
```text
|
||||
issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed"
|
||||
```
|
||||
There is no `method: "close"` — using one will error.
|
||||
No `method: "close"` exists — using one errors.
|
||||
|
||||
**Comment:**
|
||||
```
|
||||
```text
|
||||
issue_write method: "add_comment" owner: <owner> repo: <repo> issue_number: <N> body: <text>
|
||||
```
|
||||
|
||||
**Apply resolved label IDs directly** (bypassing `references/enrichments.md`'s inference step, e.g.
|
||||
when the caller already named exact labels):
|
||||
```
|
||||
```text
|
||||
issue_write method: "add_labels" owner: <owner> repo: <repo> issue_number: <N> labels: [<IDs>]
|
||||
```
|
||||
To replace all labels atomically instead of adding: `method: "replace_labels"`.
|
||||
To remove one: `method: "remove_label" label_id: <single ID>`.
|
||||
|
||||
**Default to `add_labels`.** `replace_labels` clears every label not in the array, so it drops
|
||||
labels the caller never mentioned. Reach for it only when the caller asked for the issue's label
|
||||
set to become exactly what they listed. In particular, do not use it to enforce one-label-per-scope:
|
||||
exclusivity is a per-label property — the server drops the sibling itself for a label whose
|
||||
`exclusive` field is `true`, and a label whose `exclusive` is `false` (every `Kind/*` on this
|
||||
instance) is legitimately stackable. See `gitea-labels-milestones` for how to read that field.
|
||||
|
||||
## Token scope
|
||||
|
||||
All of `list_issues`, `issue_read`, and `issue_write` are verified working under a token holding
|
||||
|
||||
@@ -13,8 +13,8 @@ time (see `references/sources.md`) — confirmed to match `api-reference.md`.
|
||||
**Parameters:**
|
||||
- `query` (string, required) — the only hard-required parameter
|
||||
- `state` (string, enum `"open"` | `"closed"` | `"all"`, optional)
|
||||
- `type` (string, enum `"issues"` | `"pulls"`, optional) — **this tool has a working type filter**,
|
||||
unlike `list_issues` (see `references/issues.md`)
|
||||
- `type` (string, enum `"issues"` | `"pulls"`, optional) — the same filter, with the same values,
|
||||
that `list_issues` takes (see `references/issues.md`)
|
||||
- `labels` (string, optional) — comma-separated label **names** — a plain string, not the array form
|
||||
`list_issues` uses
|
||||
- `owner` (string, optional) — restrict results to one owner
|
||||
@@ -22,12 +22,12 @@ time (see `references/sources.md`) — confirmed to match `api-reference.md`.
|
||||
- `per_page` (number, optional, default `30`)
|
||||
|
||||
**Call:**
|
||||
```
|
||||
```text
|
||||
search_issues query: <text>
|
||||
```
|
||||
|
||||
**Narrowing the search:**
|
||||
```
|
||||
```text
|
||||
search_issues query: <text> owner: <owner> state: "open" type: "pulls" labels: "bug,urgent"
|
||||
```
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# Sources
|
||||
|
||||
**Note on call signatures:** per `docs/adr/0011-gitea-skill-deep-modules.md`, the tool parameter
|
||||
signatures in `references/issues.md` and `references/search.md` were re-verified live via
|
||||
`ToolSearch` against the deployed `gitea-mcp` server at authoring time — they are not copied
|
||||
verbatim from `api-reference.md`. This resolves issue #6 comment #849's root-cause finding that a
|
||||
prior skill was authored from API docs that had drifted from the actual MCP tool schema; the live
|
||||
check caught exactly this drift on `list_issues` (see SKILL.md Gotchas — the research doc documents
|
||||
a `type` and a `milestones` parameter that do not exist on the deployed server).
|
||||
signatures in `references/issues.md` and `references/search.md` are re-verified live via
|
||||
`ToolSearch` against the deployed `gitea-mcp` server — they are not copied verbatim from
|
||||
`api-reference.md`. This resolves issue #6 comment #849's root-cause finding that a prior skill was
|
||||
authored from API docs that had drifted from the actual MCP tool schema. That re-verification is
|
||||
ongoing, not one-off: an earlier live check recorded `list_issues` as lacking the `type` and
|
||||
`milestones` parameters `api-reference.md` documents, and both are present on the deployed
|
||||
gitea-mcp **v1.7.0**, which is the version these signatures are current as of.
|
||||
|
||||
## gitea-mcp-repo
|
||||
|
||||
|
||||
@@ -4,11 +4,17 @@ Read and write Gitea labels and milestones, and resolve label/milestone identity
|
||||
|
||||
## What it does
|
||||
|
||||
This skill handles label and milestone CRUD (`label_read`/`label_write`, `milestone_read`/`milestone_write`) — listing repo or org labels, creating/editing/deleting a label, resolving a label name to the numeric ID required for any write, and listing/creating/updating/closing/deleting a milestone. It also owns label inference: mapping conversation context (bug report, feature request, urgency language) to this repo's `Kind/*`/`Priority/*`/`Status/*` taxonomy. It is a cross-cutting shared skill composed by `gitea-issues` and `gitea-prs`, which call into it for label/milestone resolution before their own `issue_write`/`pull_request_write` calls apply the resolved IDs.
|
||||
This skill handles label and milestone CRUD (`label_read`/`label_write`, `milestone_read`/`milestone_write`) — listing repo or org labels, creating/editing/deleting a label, resolving a label name to the numeric ID required to apply it to an issue or PR, and listing/creating/updating/closing/deleting a milestone. It also owns label inference: mapping conversation context (bug report, feature request, urgency language) to this repo's `Kind/*`/`Priority/*`/`Status/*` taxonomy.
|
||||
|
||||
## Composition
|
||||
|
||||
This is a cross-cutting shared skill. `gitea-issues` and `gitea-prs` both compose it whenever they need to apply a label or assign a milestone, rather than duplicating label/milestone logic: they call in for name/title → ID resolution, then their own `issue_write`/`pull_request_write` calls apply the resolved IDs. The split is deliberate — identity resolution lives here once, and the write that attaches an ID to a specific issue or PR lives with the skill that owns that object.
|
||||
|
||||
That relationship is documented here rather than in the skill description, which is preloaded into every session and carries routing information only: an agent reaches this skill because the user asked about labels or milestones, not because two other skills call it.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
```text
|
||||
/gitea-labels-milestones
|
||||
```
|
||||
|
||||
|
||||
@@ -2,15 +2,10 @@
|
||||
name: gitea-labels-milestones
|
||||
|
||||
description: >
|
||||
Use when reading or writing Gitea labels or milestones — listing repo/org labels, creating,
|
||||
editing, or deleting a label, resolving label names to the numeric IDs required for applying
|
||||
them to an issue or PR, or listing, creating, updating, closing, or deleting a milestone. This is
|
||||
a cross-cutting shared skill: `gitea-issues` and `gitea-prs` both compose it whenever they need to
|
||||
apply labels or assign a milestone, rather than duplicating label/milestone logic. Also use for
|
||||
label inference — mapping a bug report, feature request, or urgency signal in conversation
|
||||
context to the repo's `Kind/*`/`Priority/*`/`Status/*` label taxonomy. Do not use for applying
|
||||
already-resolved label IDs or milestone IDs to a specific issue or PR — that write goes through
|
||||
`issue_write`/`pull_request_write` in `gitea-issues`/`gitea-prs`, not here.
|
||||
Use when reading or writing Gitea labels or milestones — "create a label", "what labels does
|
||||
this repo have", "close the milestone" — or to resolve label names to IDs, or infer a
|
||||
Kind/Priority/Status label from context, even when the user does not say "Gitea".
|
||||
Not applying them to an issue -> `gitea-issues`. Not to a PR -> `gitea-prs`.
|
||||
|
||||
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token scopes.
|
||||
|
||||
@@ -21,23 +16,18 @@ metadata:
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
- context7-gitea-tea-cli
|
||||
version: "0.1.1"
|
||||
version: "0.1.4"
|
||||
|
||||
allowed-tools: Bash mcp__gitea__label_read mcp__gitea__label_write mcp__gitea__milestone_read mcp__gitea__milestone_write
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Label writes take IDs, reads return names.** `label_read` is the only tool that returns full label objects (`id`, `name`, `color`, `description`). Issue/PR responses slim labels down to name strings. Before any label is applied to an issue or PR (in `gitea-issues`/`gitea-prs`), resolve names → IDs here via `label_read method: "list_repo_labels"` — never pass a name string where an ID is expected.
|
||||
- **Milestones are referenced by ID everywhere, never by title.** `milestone_write` update/delete take `id`. The one place titles show up as the sole handle is the `pull_request_read` response (see next gotcha).
|
||||
- **Milestone representation differs between issues and PRs.** `issue_read` returns `milestone: {id, title}` — an object. `pull_request_read` returns `milestone: "title string"` — title only, no ID. You cannot recover a milestone ID from a PR response directly; call `milestone_read method: "list"` and match by title instead.
|
||||
- **Repo labels and org labels are separate pools, never mixed in one call.** `label_read`/`label_write` take either `owner`+`repo` (repo-scoped methods) or `org` (org-scoped methods) — passing both or neither for a given method is a caller error, not something the schema enforces for you. Repo and org labels can both apply to the same issue, but you list/create/edit them through different method values.
|
||||
- **`milestone_write` accepts `"update"` and `"edit"` as the same operation.** Both method values map to the identical update call. Prefer `"update"` for consistency with `issue_write`/`pull_request_write`.
|
||||
- **`exclusive` is documented as an org-labels-only flag — it isn't what enforces exclusivity here.** Gitea's docs scope the settable/server-enforced `exclusive` flag to org labels only, and the live `label_write` schema for `create_repo_label`/`edit_repo_label` doesn't document accepting it at all. This repo's `Kind/*`, `Priority/*`, `Status/*` groups still behave as one-label-per-scope, but that's a manually-enforced convention this skill implements client-side, not a guaranteed server behavior for repo labels: applying a new label within a scope (e.g. `Priority/High`) must replace any existing label in that same scope, not add alongside it, and nothing on the server enforces that for you. Label inference (see `references/label-inference.md`) must respect this — replace, don't stack.
|
||||
- **Pagination is manual on every list call.** `label_read` and `milestone_read` both default to `per_page: 30`. Iterate `page: 1, 2, ...` until the result count is less than `per_page` — there is no cursor or auto-pagination.
|
||||
- **Schema requiredness differs between the two tool families.** `milestone_read`/`milestone_write` have `owner` and `repo` as hard-required parameters (the call fails validation without them). `label_read`/`label_write` only hard-require `method` — `owner`/`repo`/`org` are functionally required per method but not schema-enforced, so passing none produces a runtime error from Gitea, not a client-side validation error.
|
||||
- **Applying a label takes a numeric ID, but issue/PR responses slim labels down to name strings.** An issue's existing labels yield no IDs — resolve name → ID with `label_read`.
|
||||
- **`pull_request_read` returns `milestone` as a bare title string** where `issue_read` returns `{id, title}` — recover the milestone's ID by listing milestones and matching the title.
|
||||
- **Never assume a `Kind/*`/`Priority/*`/`Status/*` scope is exclusive — read each label's own `exclusive` field.** `list_repo_labels` returns it on every repo label, so it is always *readable* per label; `label_write` documents it as "(org only)" because it is only *settable* through the org create methods. Where it is `true` Gitea enforces one-per-scope itself, and replacing rather than stacking on a label whose `exclusive` is `false` destroys a valid label.
|
||||
|
||||
## Step 1 — Resolve owner and repo
|
||||
## Step 1 — Resolve owner, repo and org
|
||||
|
||||
Before any tool call, extract `owner` and `repo` from the git remote (skip this if an orchestrating caller already passed them in):
|
||||
|
||||
@@ -47,10 +37,15 @@ git remote get-url origin
|
||||
|
||||
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
|
||||
|
||||
The `*_org_label*` methods take `org`, not `owner`/`repo`. Pass that same `owner` as `org` — it is the org name whenever the owner is an organisation, and the remote URL does not say whether it is one.
|
||||
|
||||
Read the failure text before interpreting it. `list_org_labels` needs the `read:organization` token scope, which this skill's declared scopes (`write:issue`, `write:repository`) do not carry, so it fails with `token does not have at least one of required scope(s), required=[read:organization]` *before* it ever determines org-vs-user. Report that: the org pool went unchecked, not empty. Only a not-found response is evidence the owner is a user account with no org pool.
|
||||
|
||||
## Step 2 — Dispatch
|
||||
|
||||
| Task | Tool | method |
|
||||
|---|---|---|
|
||||
| Resolve a label name to its ID | `label_read` | `"list_repo_labels"`, then `"list_org_labels"` |
|
||||
| List repo labels | `label_read` | `"list_repo_labels"` |
|
||||
| Get one repo label by ID | `label_read` | `"get_repo_label"` |
|
||||
| List org labels | `label_read` | `"list_org_labels"` |
|
||||
@@ -63,6 +58,6 @@ If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea r
|
||||
| Update / close a milestone | `milestone_write` | `"update"` |
|
||||
| Delete a milestone | `milestone_write` | `"delete"` |
|
||||
|
||||
For full parameter detail and step-by-step call sequences, read `references/labels.md` (label operations) or `references/milestones.md` (milestone operations). For mapping conversation context to a label to apply, read `references/label-inference.md`.
|
||||
Every list method paginates manually — `per_page` defaults to 30, so iterate `page: 1, 2, ...` until a page returns fewer results than `per_page`. A truncated list silently breaks name → ID resolution.
|
||||
|
||||
Applying resolved label IDs or a milestone ID to a specific issue or PR is out of scope here — that's `issue_write`/`pull_request_write` in the composing skill (`gitea-issues`/`gitea-prs`).
|
||||
If the task is a label operation, read `references/labels.md`; if a milestone operation, read `references/milestones.md`. If the label to apply has to be derived from conversation context rather than named, read `references/label-inference.md`.
|
||||
|
||||
@@ -12,19 +12,20 @@ description) to this repo's `Kind/*` / `Priority/*` / `Status/*` label taxonomy.
|
||||
`gitea-issues` and `gitea-prs` before creating or updating an issue/PR, and directly when the user
|
||||
asks to label something without naming exact labels.
|
||||
|
||||
## Scoped labels are mutually exclusive — replace, don't stack
|
||||
## Branching on exclusivity
|
||||
|
||||
Each of `Kind/*`, `Priority/*`, `Status/*` is treated as a scoped-label group by convention (the `/`
|
||||
delimiter naming pattern). Gitea's `exclusive` flag — the mechanism that would let the server itself
|
||||
enforce one-label-per-scope — is documented as an org-labels-only setting, and the repo-level
|
||||
`label_write` methods used here don't accept it at all. So exclusivity within these scopes is a
|
||||
convention this skill enforces client-side, not something the server guarantees: applying a new
|
||||
label within a scope is expected to replace any existing label in that same scope on the target
|
||||
issue/PR, not add alongside it. When inference
|
||||
selects a `Priority/High` label and the issue already carries `Priority/Medium`, the write should
|
||||
result in only `Priority/High` remaining — use `replace_labels` scoped to that group's labels, or at
|
||||
minimum remove the superseded label before adding the new one. Never leave two labels from the same
|
||||
scope applied at once.
|
||||
`references/labels.md` owns the exclusivity rule and the read-versus-write asymmetry behind it. Read
|
||||
it there rather than assuming a scope's behaviour from its name. Inference needs only the branch:
|
||||
carry each candidate label's own `exclusive` value forward from the resolution call and act on it.
|
||||
|
||||
- **`exclusive: true`** — the server drops the sibling on write. Add the label and let it; do not
|
||||
pre-remove the label already there, and do not compute a replacement set client-side. Inferring
|
||||
`Priority/High` onto an issue carrying `Priority/Medium` needs no special handling.
|
||||
- **`exclusive: false`** — **add alongside, never replace.** Stripping a co-existing label in the
|
||||
same scope destroys a valid one: an issue can legitimately carry `Kind/Bug` and `Kind/Security`
|
||||
at once.
|
||||
|
||||
This skill enforces no client-side exclusivity convention of its own.
|
||||
|
||||
## Signal → label mapping
|
||||
|
||||
@@ -37,6 +38,7 @@ scope applied at once.
|
||||
| Improvement to existing behavior, "make X better", refactor with behavior change | `Kind/Enhancement` |
|
||||
| Docs-only change, README/comment/guide updates | `Kind/Documentation` |
|
||||
| Vulnerability, credential exposure, injection risk, auth bypass | `Kind/Security` |
|
||||
| Test coverage, "add tests for X", a missing or flaky test, a test-only change | `Kind/Testing` |
|
||||
|
||||
**`Priority/*`** (urgency):
|
||||
|
||||
@@ -44,6 +46,7 @@ scope applied at once.
|
||||
|---|---|
|
||||
| "blocking", "critical", "urgent", production-down | `Priority/Critical` |
|
||||
| "soon", "high priority", "should do this sprint" | `Priority/High` |
|
||||
| "low priority", "nice to have", "whenever", explicitly deferred | `Priority/Low` |
|
||||
| No urgency signal present | `Priority/Medium` (default) |
|
||||
|
||||
**`Status/*`** (workflow state):
|
||||
@@ -52,17 +55,27 @@ scope applied at once.
|
||||
|---|---|
|
||||
| Explicit statement that the work is blocked on something else | `Status/Blocked` |
|
||||
|
||||
The label names in all three tables are the taxonomy this guide was written against; none of them is
|
||||
guaranteed to exist on the target repo. Step 2 below resolves every inferred name against the live
|
||||
label set, and a name that does not resolve is reported rather than substituted.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. Read the conversation context (issue/PR title, body, or the triggering discussion) for the
|
||||
signals above.
|
||||
2. Call `label_read method: "list_repo_labels"` (see `references/labels.md`) to get the current
|
||||
label set with IDs — inference must never guess an ID, only a name, then resolve it.
|
||||
3. Match inferred label names against the resolved list (case-insensitive). If a scope group
|
||||
already has a different label applied on the target and a new one is inferred for that same
|
||||
scope, plan to replace rather than add (see above).
|
||||
label set with IDs and each label's `exclusive` value — inference must never guess an ID, only a
|
||||
name, then resolve it. Both pools can apply to one issue, so for any inferred name absent from
|
||||
the repo pool, also call `label_read method: "list_org_labels"` with `org` set to the repo's
|
||||
`owner` before treating it as unresolved. Read that call's failure text: a
|
||||
`required=[read:organization]` scope error means the org pool was never queried — report the
|
||||
missing token scope rather than reporting the label unresolved. Only a not-found response means
|
||||
there is no org pool and the name is genuinely absent.
|
||||
3. Match inferred label names against the resolved list (case-insensitive) and carry each match's
|
||||
`exclusive` value forward: `true` means the server replaces the sibling on write, `false` means
|
||||
the label is added alongside whatever is already applied (see above).
|
||||
4. **Low-confidence inference omits the label.** If no signal confidently maps to a `Kind/*` value,
|
||||
do not guess — omit `Kind/*` entirely rather than default to one. `Priority/Medium` is the one
|
||||
exception: it's the explicit default when no urgency signal is present, not a guess.
|
||||
5. Hand the resolved IDs (plus which scopes to replace) to the caller's `issue_write`/
|
||||
5. Hand the resolved IDs, each with its `exclusive` value, to the caller's `issue_write`/
|
||||
`pull_request_write` call — this skill does not apply labels to an issue or PR itself.
|
||||
|
||||
@@ -3,6 +3,7 @@ topic: labels
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
---
|
||||
|
||||
# Label operations
|
||||
@@ -37,7 +38,7 @@ or **org-scoped** labels — never both in one call. Pick the method family (`*_
|
||||
| `name` | string | required for create |
|
||||
| `color` | string | hex `#RRGGBB`, required for create |
|
||||
| `description` | string | optional |
|
||||
| `exclusive` | boolean | org labels only |
|
||||
| `exclusive` | boolean | accepted as a *write* param on org creates only — repo labels still carry and enforce `exclusive`, set outside this tool surface |
|
||||
| `is_archived` | boolean | repo labels only |
|
||||
|
||||
Note: unlike `milestone_read`/`milestone_write`, `owner`/`repo`/`org` are **not** schema-required on
|
||||
@@ -46,29 +47,50 @@ runtime error from Gitea rather than a client-side validation error.
|
||||
|
||||
## List repo labels
|
||||
|
||||
```
|
||||
```text
|
||||
label_read method: "list_repo_labels" owner: <owner> repo: <repo> per_page: 50
|
||||
```
|
||||
|
||||
Paginate (`page: 1, 2, ...`) until the returned count is less than `per_page`. This is the only way
|
||||
to build a complete name → ID map — there is no lookup-by-name endpoint.
|
||||
|
||||
Every returned repo label carries its own `exclusive` boolean, so exclusivity is always *readable*
|
||||
per repo label. That does not contradict `label_write`'s schema, which annotates `exclusive` as
|
||||
"(org only)": reading and setting are different questions, and only the setting half is org-scoped
|
||||
(see "Create a label" below). Where the field is `true` Gitea enforces one-label-per-scope
|
||||
server-side; where it is `false` labels in that scope stack legitimately. Read the field — never
|
||||
infer exclusivity from the `/` in a name, and never carry another repo's map over.
|
||||
|
||||
On the instance this skill was authored against (`Defame1297/holocron`) the split ran: every
|
||||
`Priority/*`, `Reviewed/*` and `Status/*` label `exclusive: true`, every `Kind/*` label and
|
||||
`Compat/Breaking` `exclusive: false`. That is one repo's configuration at one point in time, recorded
|
||||
as a worked example of what the field looks like in practice — it is not a property of the taxonomy
|
||||
and says nothing about the repo you are called against.
|
||||
|
||||
## Get one label
|
||||
|
||||
```
|
||||
```text
|
||||
label_read method: "get_repo_label" owner: <owner> repo: <repo> id: <id>
|
||||
```
|
||||
|
||||
## Resolve a name to an ID
|
||||
|
||||
There is no direct name lookup. List all repo labels (paginating if needed), scan for a
|
||||
case-insensitive name match, and extract `id`. This is the required first step before any label
|
||||
application on an issue or PR — the actual `add_labels`/`replace_labels`/`remove_label` call lives
|
||||
in `gitea-issues`/`gitea-prs` via `issue_write`/`pull_request_write`, which take numeric IDs only.
|
||||
The tool surface carries no lookup-by-name method. List all repo labels (paginating if needed),
|
||||
scan for a case-insensitive name match, and extract `id`. Both pools can apply to one issue: if the
|
||||
name is not in `list_repo_labels`, also check `list_org_labels` before reporting it unresolved. That method
|
||||
takes `org`, not `owner`/`repo` — pass the repo's `owner` as `org`, which is what it means when the
|
||||
owner is an organisation. Its failure modes are not interchangeable. `token does not have at least
|
||||
one of required scope(s), required=[read:organization]` means the org pool was never queried — report
|
||||
that missing scope rather than reporting the label unresolved. Only a not-found response means the
|
||||
owner is a user account with no org pool, making the miss a real miss.
|
||||
|
||||
Resolution is the required first step before any label application on an issue or PR — the actual
|
||||
`add_labels`/`replace_labels`/`remove_label` call lives in `gitea-issues`/`gitea-prs` via
|
||||
`issue_write`/`pull_request_write`, which take numeric IDs only.
|
||||
|
||||
## Create a label
|
||||
|
||||
```
|
||||
```text
|
||||
label_write method: "create_repo_label"
|
||||
owner: <owner> repo: <repo>
|
||||
name: "Kind/Bug"
|
||||
@@ -77,11 +99,14 @@ label_write method: "create_repo_label"
|
||||
```
|
||||
|
||||
For an org label, use `method: "create_org_label"` with `org:` instead of `owner`/`repo`, and
|
||||
`exclusive: true` if the label belongs to a mutually-exclusive scope group.
|
||||
`exclusive: true` if the label belongs to a mutually-exclusive scope group. `label_write` accepts
|
||||
`exclusive` on org methods only, so a repo label's exclusivity cannot be set or cleared through this
|
||||
tool — it is set in the Gitea UI or against the REST API directly, and read back via
|
||||
`list_repo_labels`.
|
||||
|
||||
## Edit a label
|
||||
|
||||
```
|
||||
```text
|
||||
label_write method: "edit_repo_label" owner: <owner> repo: <repo> id: <id> color: "#ff0000"
|
||||
```
|
||||
|
||||
@@ -89,7 +114,7 @@ Only pass the fields being changed — `id` plus any of `name`/`color`/`descript
|
||||
|
||||
## Delete a label
|
||||
|
||||
```
|
||||
```text
|
||||
label_write method: "delete_repo_label" owner: <owner> repo: <repo> id: <id>
|
||||
```
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ schema level — there's no scope variant to omit them for.
|
||||
|
||||
## List milestones
|
||||
|
||||
```
|
||||
```text
|
||||
milestone_read method: "list" owner: <owner> repo: <repo> state: "open"
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Report each as: id, title, state, due date, open/closed issue counts.
|
||||
|
||||
## Get one milestone
|
||||
|
||||
```
|
||||
```text
|
||||
milestone_read method: "get" owner: <owner> repo: <repo> id: <id>
|
||||
```
|
||||
|
||||
@@ -60,7 +60,7 @@ milestone_read method: "get" owner: <owner> repo: <repo> id: <id>
|
||||
Needed whenever the only handle available is a title — e.g. a `pull_request_read` response, which
|
||||
returns `milestone` as a bare title string rather than `{id, title}`. Call:
|
||||
|
||||
```
|
||||
```text
|
||||
milestone_read method: "list" owner: <owner> repo: <repo> name: <title>
|
||||
```
|
||||
|
||||
@@ -69,7 +69,7 @@ title typo or case mismatch), fall back to listing without the filter and matchi
|
||||
|
||||
## Create a milestone
|
||||
|
||||
```
|
||||
```text
|
||||
milestone_write method: "create"
|
||||
owner: <owner> repo: <repo>
|
||||
title: "v1.0"
|
||||
@@ -82,7 +82,7 @@ this milestone via `issue_write`/`pull_request_write`.
|
||||
|
||||
## Update or close a milestone
|
||||
|
||||
```
|
||||
```text
|
||||
milestone_write method: "update" owner: <owner> repo: <repo> id: <id> state: "closed"
|
||||
```
|
||||
|
||||
@@ -90,7 +90,7 @@ Only pass the fields being changed — `id` plus any of `title`/`description`/`d
|
||||
|
||||
## Delete a milestone
|
||||
|
||||
```
|
||||
```text
|
||||
milestone_write method: "delete" owner: <owner> repo: <repo> id: <id>
|
||||
```
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
- **URL:** context7:/websites/gitea
|
||||
- **Description:** Official Gitea docs mirror on Context7 (docs.gitea.com content) — scoped/exclusive label conventions and milestone/label state-transition semantics
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md, references/label-inference.md
|
||||
- **Contributing files:** SKILL.md, references/labels.md, references/label-inference.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
@@ -4,22 +4,28 @@ List, read, create, update, merge, and review Gitea pull requests.
|
||||
|
||||
## What it does
|
||||
|
||||
This skill handles the pull request lifecycle within the Gitea integration suite — listing and reading PRs (details, diff, changed files, CI status, reviews), creating them (title, body, labels), updating them (title, body, assignees, labels, milestone), managing reviewers, closing/reopening, merging with a chosen strategy and post-merge branch cleanup, and the full code-review flow (create a review with inline comments, submit it, dismiss or delete it). It composes `gitea-labels-milestones` for label/milestone ID resolution rather than duplicating that logic, and defers to `gitea-issues` for anything that turns out to be an issue rather than a PR (they share one number space) and to `gitea-branches`/`gitea-files` for the underlying branch/file operations behind a PR.
|
||||
This skill handles the pull request lifecycle within the Gitea integration suite — listing and reading PRs (details, diff, changed files, CI status, reviews), creating them (title, body, labels), updating them (title, body, assignees, labels, milestone), adding and removing reviewers, closing/reopening, merging with a chosen strategy and post-merge branch cleanup, and the full code-review flow (create a review with inline comments, submit it, dismiss or delete it, reply to a review comment, and resolve or unresolve a comment thread). It composes `gitea-labels-milestones` for label/milestone ID resolution rather than duplicating that logic — `milestone` applies on an update only, never on create — and defers to `gitea-issues` for anything that turns out to be an issue rather than a PR (they share one number space) and to `gitea-branches`/`gitea-files` for the underlying branch/file operations behind a PR.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
```text
|
||||
/gitea-prs
|
||||
```
|
||||
|
||||
Describe the PR or review task: list PRs, get a PR's status/diff/reviews, create or update a PR, merge one, or create/submit/dismiss a code review. The skill will determine owner/repo from context and resolve any label or milestone names via `gitea-labels-milestones` before writing them.
|
||||
Describe the PR or review task: list PRs, get a PR's status/diff/reviews, create or update a PR, merge one, or create/submit/dismiss a code review. The skill resolves `owner`/`repo` from the `origin` git remote (or takes them from an orchestrating caller) and resolves any label or milestone names via `gitea-labels-milestones` before writing them.
|
||||
|
||||
## Before you start
|
||||
|
||||
Requires a Gitea MCP server configured with a token holding `write:issue` + `write:repository`.
|
||||
Requires a git remote named `origin` pointing at the Gitea instance, unless an orchestrating caller
|
||||
(e.g. `gitea-workflow`) already resolved `owner`/`repo` for you.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents — Gotchas, composition with `gitea-labels-milestones`, and the dispatch table |
|
||||
| `SKILL.md` | Skill instructions for agents — Gotchas, the dispatch table, and label/milestone ID resolution via `gitea-labels-milestones` |
|
||||
| `references/pull-requests.md` | Execution detail for `list_pull_requests`, `pull_request_read` (get/get_diff/get_files/get_status), and `pull_request_write` (create/update/close/reopen/update_branch/add_reviewers/remove_reviewers) |
|
||||
| `references/reviews.md` | Execution detail for `pull_request_review_write` (create/submit/delete/dismiss) and the review-related `pull_request_read` methods |
|
||||
| `references/reviews.md` | Execution detail for `pull_request_review_write` (create/submit/delete/dismiss, plus the comment-thread methods reply_comment/resolve_thread/unresolve_thread) and the review-related `pull_request_read` methods |
|
||||
| `references/merging.md` | The merge workflow — CI vs. review/branch-protection gates, merge styles, branch cleanup, and the post-merge issue-close check |
|
||||
| `references/sources.md` | Research sources backing the PR/review guidance |
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
name: gitea-prs
|
||||
|
||||
description: >
|
||||
Use when listing, reading, creating, updating, merging, or reviewing Gitea pull requests —
|
||||
getting PR status/diff/changed files/CI status, opening a PR, updating title/body/reviewers,
|
||||
closing/reopening, merging with a chosen strategy, or submitting/dismissing a code review with
|
||||
inline comments. Composes `gitea-labels-milestones` to resolve label names or milestone titles
|
||||
to the numeric IDs `pull_request_write` requires, rather than duplicating that resolution logic.
|
||||
Do not use for issues (`gitea-issues`) or branch/commit operations (`gitea-branches`) — a number
|
||||
the user mentions may refer to either an issue or a PR since they share one number space, so
|
||||
confirm which domain applies before dispatching.
|
||||
Use when listing, reading, creating, updating, merging, or reviewing Gitea pull requests — even
|
||||
when the user does not say "Gitea". A number the user names may be an issue or a PR — they share
|
||||
one number space — so confirm which domain applies before dispatching. Not issues ->
|
||||
`gitea-issues`. Not branch or commit operations -> `gitea-branches`.
|
||||
|
||||
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token scopes.
|
||||
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token
|
||||
scopes. Requires git remote "origin" pointing to the Gitea instance for owner/repo resolution when
|
||||
invoked directly by a human; an orchestrating caller (e.g. gitea-workflow) may pass owner/repo
|
||||
already resolved.
|
||||
|
||||
metadata:
|
||||
category: integration
|
||||
@@ -20,49 +19,42 @@ metadata:
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
- context7-gitea-tea-cli
|
||||
version: "0.1.1"
|
||||
version: "0.1.2"
|
||||
|
||||
allowed-tools: mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp__gitea__pull_request_write mcp__gitea__pull_request_review_write
|
||||
allowed-tools: Bash mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp__gitea__pull_request_write mcp__gitea__pull_request_review_write
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Issues and PRs share one number space.** A number the user mentions (`#42`) might be an issue, not a PR — there is only one counter per repo. If you're not certain, call `pull_request_read method: "get"` and treat a 404 as "this number is an issue, not a PR" (or check `is_pull` on an `issue_read` response first if you already have one).
|
||||
- **`pull_request_read method: "get"` returns `review_scomments`, not `review_comments`.** Source-level typo in gitea-mcp v1.3.0. Never reference `review_comments` — it will always be undefined.
|
||||
- **`draft: true` on create prepends `"WIP:"` to the title.** Gitea has no first-class draft field — it implements draft PRs via title prefix. To un-draft, call `update` and pass the title without the `WIP:` prefix.
|
||||
- **Cross-repo fork PRs require `head` as `"fork-owner:branch-name"`.** A bare branch name causes Gitea to search the base repo for it and return 422. Same-repo PRs use a bare branch name.
|
||||
- **PR `milestone` is a bare title string, not `{id, title}`.** Unlike issues, you cannot recover a milestone's ID from a PR response. If you need the ID (e.g. to filter or to pass to another write), call into `gitea-labels-milestones` and match by title via `milestone_read method: "list"`.
|
||||
- **CI status and review/approval state are independent merge gates.** `get_status` only reports CI. Branch-protection rules (required approvals, requested-reviewer coverage, stale-approval handling) are enforced server-side by the merge call itself and will error if unmet — passing CI does not mean the merge will succeed.
|
||||
- **Reviews move through a state machine, not a single write.** `create` opens a review in `PENDING` state with inline comments attached; `submit` finalizes it with a terminal `state` (`APPROVED`/`REQUEST_CHANGES`/`COMMENT`). A submitted review can be `dismiss`ed afterward, but never deleted — `delete` only removes a review that was never submitted.
|
||||
- **Merging a PR does not auto-close linked issues.** Unlike GitHub, Gitea has no merge-triggers-close event. It does parse closing keywords (`Fixes #N`, `Closes #N`) in commit messages landing on the default branch, so a non-squash merge that preserves those commit messages may auto-close the issue — but a squash merge rewrites history into one commit, so survival of the keyword depends on the squash commit's message. Always call `issue_read method: "get"` on any referenced issue after merging to check whether it already closed before deciding to close it explicitly.
|
||||
- **Issues and PRs share one number space.** `#42` may be an issue rather than a PR. When unsure, call `pull_request_read method: "get"` and read a 404 as "that number is an issue" — hand it to `gitea-issues`.
|
||||
- **`pull_request_write method: "create"` discards most optional parameters in silence.** `milestone`, `assignee`, `assignees`, `reviewers` and `team_reviewers` are accepted, dropped, and left out of the response, so a drop is indistinguishable from never passing them. `labels` *does* apply on `"create"`, so labels landing is no evidence the milestone did.
|
||||
|
||||
## Composing `gitea-labels-milestones`
|
||||
## Step 1 — Resolve owner and repo
|
||||
|
||||
Before any `pull_request_write` call that includes a `labels` or `milestone` parameter, resolve names/titles to numeric IDs via `gitea-labels-milestones` — `label_read method: "list_repo_labels"` for label name → ID, `milestone_read method: "list"` for milestone title → ID. Never pass a label name string or milestone title string directly to `pull_request_write`; both parameters take numeric IDs only. This skill does not duplicate that lookup logic — it composes the shared skill.
|
||||
Extract them from the git remote before any tool call, skipping this when an orchestrating caller already passed them in:
|
||||
|
||||
## Dispatch
|
||||
```bash
|
||||
git remote get-url origin
|
||||
```
|
||||
|
||||
| Task | Tool | method |
|
||||
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
|
||||
|
||||
## Step 2 — Dispatch
|
||||
|
||||
Confirm the number names a PR, not an issue, before writing to it.
|
||||
|
||||
| Task | Tool | Reference |
|
||||
|---|---|---|
|
||||
| List PRs | `list_pull_requests` | — |
|
||||
| Get PR details | `pull_request_read` | `"get"` |
|
||||
| Get PR diff | `pull_request_read` | `"get_diff"` |
|
||||
| Get PR changed files | `pull_request_read` | `"get_files"` |
|
||||
| Get PR CI status | `pull_request_read` | `"get_status"` |
|
||||
| Get PR reviews | `pull_request_read` | `"get_reviews"` |
|
||||
| Get one review | `pull_request_read` | `"get_review"` |
|
||||
| Get review inline comments | `pull_request_read` | `"get_review_comments"` |
|
||||
| Create a PR | `pull_request_write` | `"create"` |
|
||||
| Update a PR | `pull_request_write` | `"update"` |
|
||||
| Close a PR | `pull_request_write` | `"close"` |
|
||||
| Reopen a PR | `pull_request_write` | `"reopen"` |
|
||||
| Merge a PR | `pull_request_write` | `"merge"` |
|
||||
| Update branch from base | `pull_request_write` | `"update_branch"` |
|
||||
| Add reviewers | `pull_request_write` | `"add_reviewers"` |
|
||||
| Remove reviewers | `pull_request_write` | `"remove_reviewers"` |
|
||||
| Create a review | `pull_request_review_write` | `"create"` |
|
||||
| Submit a review | `pull_request_review_write` | `"submit"` |
|
||||
| Delete a review | `pull_request_review_write` | `"delete"` |
|
||||
| Dismiss a review | `pull_request_review_write` | `"dismiss"` |
|
||||
| List PRs; read a PR's details, diff, changed files or CI status | `list_pull_requests`, `pull_request_read` | `references/pull-requests.md` |
|
||||
| Create a PR — subject to the silent-drop Gotcha above | `pull_request_write` | `references/pull-requests.md` |
|
||||
| Update, close, reopen or retarget a PR, sync it with its base, or add/remove reviewers | `pull_request_write` | `references/pull-requests.md` |
|
||||
| Merge a PR, or judge whether it can merge | `pull_request_write method: "merge"` | `references/merging.md` |
|
||||
| Read, create, submit, dismiss or delete a code review, or reply to and resolve a review comment thread | `pull_request_read`, `pull_request_review_write` | `references/reviews.md` |
|
||||
|
||||
For full parameter detail on listing/reading/creating/updating/closing PRs, read `references/pull-requests.md`. For review-specific detail (create/submit/delete/dismiss, inline comment shape), read `references/reviews.md`. For the merge workflow specifically (CI gate, merge styles, branch cleanup, post-merge issue check), read `references/merging.md`.
|
||||
Read the reference for the row you land on before making the call. Each carries the parameter signatures, the per-method behaviour and the response-shape quirks the row cannot, and every write method has at least one parameter that behaves differently from its issue-side counterpart.
|
||||
|
||||
## Step 3 — Resolving labels and milestones
|
||||
|
||||
`labels` and `milestone` take numeric IDs, never name or title strings. Before a `pull_request_write` call carrying either, resolve them through `gitea-labels-milestones`: `label_read method: "list_repo_labels"` for a label name, `milestone_read method: "list"` for a milestone title.
|
||||
|
||||
Resolve a milestone only when the call is an `"update"` — on `"create"` the lookup is wasted, per the Gotcha above. Recovering an existing PR's milestone ID needs the same lookup, because `pull_request_read` returns `milestone` as a bare title string and never an ID.
|
||||
|
||||
@@ -7,7 +7,7 @@ source_keys:
|
||||
|
||||
# Pull request read/write execution detail
|
||||
|
||||
Parameter signatures below are cross-checked live against the deployed gitea-mcp server tool schemas at authoring time — not copied verbatim from the plugin's research doc for this domain, which has a known history of drifting from the deployed server (e.g. a prior `type` parameter that no longer exists on `list_issues`, and the `review_scomments` typo covered in `references/reviews.md`). Re-verify via `ToolSearch` before trusting this file if the gitea-mcp version changes.
|
||||
Parameter signatures below are cross-checked live against the deployed gitea-mcp server tool schemas at authoring time — not copied verbatim from the plugin's research doc for this domain, which has a known history of drifting from the deployed server. These files were last verified against gitea-mcp **v1.7.0**, as reported by `get_gitea_mcp_server_version`. Re-verify via `ToolSearch` before trusting this file if the deployed version differs — drift has bitten this skill in both directions, adding methods it does not list and fixing quirks it still warns about.
|
||||
|
||||
## `list_pull_requests`
|
||||
|
||||
@@ -29,14 +29,14 @@ List responses trim PRs down to summary fields — `head`/`base` are bare ref st
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `pull_number` (number, required)
|
||||
- `review_id` (number, optional) — required for `"get_review"` and `"get_review_comments"`; see `references/reviews.md`
|
||||
- `review_id` (number, optional) — required for `"get_review"`, which errors with `review_id is required` without it. **Optional** for `"get_review_comments"`: omit it to list every inline comment on the PR in one call. See `references/reviews.md`
|
||||
- `binary` (boolean, optional) — include binary diff content for `"get_diff"`
|
||||
- `page` (number, optional, default 1)
|
||||
- `per_page` (number, optional, default 30)
|
||||
|
||||
`"get"`, `"get_diff"`, `"get_files"`, and `"get_status"` are covered here. `"get_reviews"`, `"get_review"`, and `"get_review_comments"` are covered in `references/reviews.md`.
|
||||
|
||||
- `"get"` returns the full PR object: state, draft, merged, mergeable flags; `head`/`base` as full objects (`{ref, sha, repo?}`); `milestone` as a bare title string (not `{id, title}`); `review_scomments` (typo, see `references/reviews.md`).
|
||||
- `"get"` returns the full PR object: state, draft, merged, mergeable flags; `head`/`base` as full objects (`{ref, sha, repo?}`); `milestone` as a bare title string (not `{id, title}`); and `review_comments`, *when present*, as an integer count rather than comment objects — it was absent from a live `"get"` on a PR with no inline comments, so verify the key before reading it (see `references/reviews.md`).
|
||||
- `"get_diff"` returns raw diff text.
|
||||
- `"get_files"` returns the list of changed file objects.
|
||||
- `"get_status"` returns the combined commit status for the PR's head commit — CI result only, not review/approval state (see `references/merging.md`).
|
||||
@@ -68,7 +68,7 @@ List responses trim PRs down to summary fields — `head`/`base` are bare ref st
|
||||
|
||||
Merge-specific parameters (`merge_style`, `delete_branch`, `force_merge`, `merge_when_checks_succeed`, `head_commit_id`, `message` as merge commit message) are covered in `references/merging.md`.
|
||||
|
||||
**`"create"` silently drops most optional parameters.** `"create"` reads only `owner`, `repo`, `title`, `body`, `head`, `base`, `draft`, `labels`, and `deadline`. Every other optional parameter — including `assignee`, `assignees`, `milestone`, `reviewers`, `team_reviewers` and `remove_deadline` — is accepted without error and discarded. There is no error, no warning, and nothing in the response distinguishing a dropped parameter from one that was never passed: the response simply omits the key. Setting any of them requires a second call after the PR exists — `"update"` for `assignee`/`assignees`/`milestone`, `"add_reviewers"` for `reviewers`/`team_reviewers`.
|
||||
**`"create"` silently drops most optional parameters.** `"create"` reads only `owner`, `repo`, `title`, `body`, `head`, `base`, `draft`, `labels`, and `deadline`. Every other optional parameter — including `assignee`, `assignees`, `milestone`, `reviewers`, `team_reviewers` and `remove_deadline` — is accepted without error and discarded. Nothing marks the drop: no error, no warning, and nothing in the response distinguishing a dropped parameter from one that was never passed — the response simply omits the key. Setting any of them requires a second call after the PR exists — `"update"` for `assignee`/`assignees`/`milestone`, `"add_reviewers"` for `reviewers`/`team_reviewers`.
|
||||
|
||||
Two things make this easy to miss:
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ source_keys:
|
||||
|
||||
# PR review execution detail
|
||||
|
||||
Parameter signatures below are cross-checked live against the deployed gitea-mcp server tool schema, not copied from the plugin's research doc verbatim — same sourcing discipline as `references/pull-requests.md`.
|
||||
Parameter signatures below are cross-checked live against the deployed gitea-mcp server tool schema, not copied from the plugin's research doc verbatim — same sourcing discipline as `references/pull-requests.md`. Last verified against **v1.7.0**, as reported by `get_gitea_mcp_server_version`.
|
||||
|
||||
## Review state machine
|
||||
|
||||
@@ -21,23 +21,34 @@ A review is not a single write. It moves through states:
|
||||
## `pull_request_review_write`
|
||||
|
||||
**Parameters:**
|
||||
- `method` (string, required) — `"create"` | `"submit"` | `"delete"` | `"dismiss"`
|
||||
- `method` (string, required) — `"create"` | `"submit"` | `"delete"` | `"dismiss"` | `"reply_comment"` | `"resolve_thread"` | `"unresolve_thread"`
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `pull_number` (number, required)
|
||||
- `review_id` (number, required for every method except `"create"`, which returns the ID to use for the follow-up `submit`/`delete`/`dismiss` call)
|
||||
- `pull_number` (number, required for every method except `"resolve_thread"` and `"unresolve_thread"`, which locate the thread from `comment_id` alone) — the schema's own `required` list is only `method`/`owner`/`repo`, so a missing `pull_number` surfaces as a runtime error, not client-side validation
|
||||
- `review_id` (number) — required for `"submit"`, `"delete"` and `"dismiss"`; `"create"` returns the ID to use for that follow-up call. Not used by `"reply_comment"`, `"resolve_thread"` or `"unresolve_thread"`, which key off `comment_id` instead
|
||||
- `comment_id` (number, required for `"reply_comment"`, `"resolve_thread"` and `"unresolve_thread"`) — an individual review comment's ID, obtained from `pull_request_read method: "get_review_comments"`. For the two thread methods this must be the thread's **first** comment, not an arbitrary one in it
|
||||
- `state` (string, optional) — `"APPROVED"` | `"REQUEST_CHANGES"` | `"COMMENT"` | `"PENDING"` — set on `"create"` (typically `"PENDING"`, or a terminal state to create-and-submit in one call if the server supports it) or `"submit"` (terminal state)
|
||||
- `body` (string, optional) — overall review comment text
|
||||
- `body` (string, optional) — the overall review comment text on `"create"`/`"submit"`; on `"reply_comment"` it is the reply text and is the payload of the call
|
||||
- `commit_id` (string, optional, for `"create"`) — anchors inline comments to a specific commit SHA (typically the PR's current head SHA from `pull_request_read method: "get"`)
|
||||
- `message` (string, optional, for `"dismiss"`) — dismissal reason
|
||||
- `comments` (array of objects, optional, for `"create"`) — inline comments, each: `{path, body, old_line_num, new_line_num}` — `path` is the file path, `body` is the comment text, `new_line_num` anchors to a line in the new (added) side of the diff, `old_line_num` anchors to a line in the old (removed) side; use whichever side the comment applies to, not both
|
||||
|
||||
## Comment threads
|
||||
|
||||
Three further methods act on an individual review comment rather than on a review as a whole. They sit outside the state machine above — a thread can be replied to or resolved whatever state its parent review is in — and none of them takes a `review_id`.
|
||||
|
||||
- **`reply_comment`** — posts `body` as a reply to the comment named by `comment_id`, threading under it rather than starting a new top-level comment. Takes `pull_number`.
|
||||
- **`resolve_thread`** — marks the thread containing `comment_id` resolved. Pass the thread's **first** comment ID; another ID in the same thread is not equivalent. Does not take `pull_number`.
|
||||
- **`unresolve_thread`** — reopens a resolved thread, under the same first-comment rule.
|
||||
|
||||
Get the `comment_id` from `pull_request_read method: "get_review_comments"`. Call it with no `review_id` to list every inline comment on the PR, then pick the thread to act on; scoping it to one `review_id` only finds threads opened by that review.
|
||||
|
||||
## Reading reviews (`pull_request_read`)
|
||||
|
||||
- `method: "get_reviews"` — array of review summaries: `id`, `state`, `body`, `user` (login), `comments_count`, `submitted_at`, `html_url`, `stale` (bool — the PR was pushed to after this review was submitted, meaning it may be outdated), `official` (bool), `dismissed` (bool).
|
||||
- `method: "get_review"` (requires `review_id`) — single review detail.
|
||||
- `method: "get_review_comments"` (requires `review_id`) — array of inline comments: `id`, `body`, `path`, `position`, `old_position`, `diff_hunk`, `user`, `html_url`, `created_at`, `updated_at`.
|
||||
- `method: "get_review"` (requires `review_id` — omitting it fails with `review_id is required`) — single review detail.
|
||||
- `method: "get_review_comments"` (`review_id` **optional** — omit it to list every inline comment on the PR in one call, rather than one review's) — array of inline comments: `id`, `body`, `path`, `position`, `old_position`, `diff_hunk`, `user`, `html_url`, `created_at`, `updated_at`.
|
||||
|
||||
**`review_scomments` typo:** the full PR object returned by `pull_request_read method: "get"` includes a field named `review_scomments` (a count), not `review_comments` — a source-level misspelling in gitea-mcp v1.3.0's `slim.go`. Do not write code or instructions that reference `review_comments` on that response; it will always be `undefined`. This is distinct from the `get_review_comments` method above, which is spelled correctly and returns the actual comment objects.
|
||||
**`review_comments` on the `"get"` response is a count, not the comments — and it may be absent.** Where the full PR object returned by `pull_request_read method: "get"` carries `review_comments`, it is an integer: the number of inline review comments. Presence is not guaranteed. A live `"get"` against a PR with zero inline comments carried no such key at all — only `comments`, which counts issue-style comments, not review ones. Treat it as present only when non-zero, and check for the key before reading it rather than assuming the response shape. Either way it is distinct from the `get_review_comments` method above, which returns the actual comment objects; reading the count is no substitute for that call. Older gitea-mcp releases misspelled this key as `review_scomments`; the misspelling was corrected upstream and the deployed v1.7.0 response carries no such key, so treat any instruction that reaches for `review_scomments` as stale.
|
||||
|
||||
**Inline-comment field names differ between write and read.** The `comments` array on `pull_request_review_write method: "create"` uses `old_line_num`/`new_line_num`. The `get_review_comments` read response uses different field names for the same concept — `position` (new-side line) and `old_position` (old-side line). Do not assume the same key names apply on both sides of the round trip.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## gitea-mcp-repo
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp
|
||||
- **Description:** Official gitea-mcp repository (v1.3.0) — `operation/*.go` source files documenting all 55 MCP tools, their parameters, and CLI flags. Live tool schemas (`list_pull_requests`, `pull_request_read`, `pull_request_write`, `pull_request_review_write`) were verified directly against the deployed MCP server via `ToolSearch` at authoring time, per this repo's process for resolving schema-vs-docs drift, rather than copied from the derived research doc.
|
||||
- **Description:** Official gitea-mcp repository — `operation/*.go` source files documenting the MCP tools, their parameters, and CLI flags. Live tool schemas (`list_pull_requests`, `pull_request_read`, `pull_request_write`, `pull_request_review_write`) are verified directly against the deployed MCP server via `ToolSearch`, per this repo's process for resolving schema-vs-docs drift, rather than copied from the derived research doc. **Last verified against v1.7.0**, as reported by `get_gitea_mcp_server_version`; the files were originally authored against v1.3.0 and the read/review side had drifted by three defects before that re-verification.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md, references/pull-requests.md, references/reviews.md, references/merging.md
|
||||
- **Status:** `extracted`
|
||||
@@ -11,7 +11,7 @@
|
||||
## gitea-mcp-slim-go
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/pull/slim.go
|
||||
- **Description:** Slim response shape structs from gitea-mcp source — defines exactly which fields the MCP server returns for PRs and reviews, including the `review_scomments` typo and the PR-response milestone-as-title-string quirk.
|
||||
- **Description:** Slim response shape structs from gitea-mcp source — defines exactly which fields the MCP server returns for PRs and reviews, including the PR-response milestone-as-title-string quirk. The `review_scomments` misspelling this file documented at v1.3.0 was corrected upstream; v1.7.0 returns `review_comments`.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md, references/pull-requests.md, references/reviews.md
|
||||
- **Status:** `extracted`
|
||||
@@ -21,7 +21,7 @@
|
||||
- **URL:** context7:/websites/gitea
|
||||
- **Description:** Official Gitea docs mirror on Context7 — branch protection rules, PR review/merge gating behavior, and automatic issue/PR cross-reference linking. Backfills the external/best-practice gap left by the original docs.gitea.com fetch timeout.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md, references/merging.md
|
||||
- **Contributing files:** references/merging.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
@@ -6,9 +6,15 @@ Manage Gitea releases and tags — list, create, and delete releases (with draft
|
||||
|
||||
This skill handles release and tag operations for a Gitea repository. It creates releases from a tag/target commitish with title, notes, and draft/prerelease flags; lists and paginates releases and tags; retrieves the latest release; and deletes releases and tags as separate, independent destructive operations. It resolves the numeric release id required for deletion instead of assuming a tag name will work.
|
||||
|
||||
## Before you start
|
||||
|
||||
Requires a Gitea MCP server configured with a token holding `write:repository`. Requires a git remote
|
||||
named `origin` pointing at the Gitea instance, unless an orchestrating caller already resolved
|
||||
`owner`/`repo` for you.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
```text
|
||||
/gitea-releases
|
||||
```
|
||||
|
||||
@@ -19,6 +25,6 @@ Describe your release/tag task: list releases, get the latest release, create a
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `references/call-signatures.md` | Tool parameters and response shapes derived from gitea-mcp source (see `references/sources.md`); input params for 3 of the 9 tools additionally live-cross-checked |
|
||||
| `references/call-signatures.md` | Tool parameters and response shapes derived from gitea-mcp source (see `references/sources.md`); input params for all nine tools additionally cross-checked live against gitea-mcp v1.7.0 |
|
||||
| `references/conventions.md` | Semver/draft/prerelease practitioner conventions and pagination behavior |
|
||||
| `references/sources.md` | Research sources backing the call signatures and conventions |
|
||||
|
||||
@@ -2,31 +2,46 @@
|
||||
name: gitea-releases
|
||||
|
||||
description: >
|
||||
Use when managing Gitea releases and tags for a repository: listing, creating, or deleting
|
||||
releases (with draft/prerelease flags and release notes), and listing, creating, or deleting the
|
||||
underlying git tags. Use even if the user doesn't say "release" explicitly — "cut a v1.2.0",
|
||||
"publish a prerelease", "tag this commit", or "what's the latest release" all apply. Do not use
|
||||
for git branch or commit history operations (use gitea-branches) or for issue/PR management (use
|
||||
gitea-issues / gitea-prs).
|
||||
Use when managing Gitea releases or the git tags underneath them — list, get, create, or
|
||||
delete either — even when the user does not say "release" or "Gitea": "cut a v1.2.0",
|
||||
"publish a prerelease", "tag this commit", "what's the latest release". Not branches or
|
||||
commit history -> `gitea-branches`. Not issues -> `gitea-issues`. Not pull requests ->
|
||||
`gitea-prs`.
|
||||
|
||||
compatibility: Requires Gitea MCP server configured with a token with write:repository scope, which
|
||||
gates every release and tag tool here. Requires git remote "origin" pointing to the Gitea instance
|
||||
for owner/repo resolution, unless an orchestrating caller passes them already resolved.
|
||||
|
||||
metadata:
|
||||
category: gitea
|
||||
category: integration
|
||||
version: "0.1.0"
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
- context7-gitea-tea-cli
|
||||
|
||||
allowed-tools: Bash mcp__gitea__list_releases mcp__gitea__get_release mcp__gitea__get_latest_release mcp__gitea__create_release mcp__gitea__delete_release mcp__gitea__list_tags mcp__gitea__get_tag mcp__gitea__create_tag mcp__gitea__delete_tag
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`delete_release` takes a numeric `id`, never a tag name.** `delete_tag` is the mirror opposite — it takes the `tag_name` string, never a numeric id. These two tools are asymmetric on purpose; passing a tag name to `delete_release` or a numeric id to `delete_tag` fails. Always resolve the numeric release id via `list_releases` or `get_release` first if you only have a tag name in hand.
|
||||
- **Deleting a release does not delete its tag.** They are separate destructive operations against separate resources — a release is a wrapper (title, notes, draft/prerelease flags, assets) around a tag, not the tag itself. If the intent is to remove both, call `delete_release` and `delete_tag` separately.
|
||||
- **`list_releases`/`list_tags` default to `per_page: 20`**, unlike most other gitea-mcp tools which default to 30. The MCP layer does no auto-pagination — to get a complete result set, loop `page` upward until a page returns fewer than `per_page` results.
|
||||
- **`is_draft`/`is_pre_release` are explicit booleans the caller sets on `create_release` — never inferred from `tag_name`.** Note the input param is `is_draft`, which maps to the `draft` field on the *response* object (see Dispatch table below and `references/call-signatures.md`) — `draft` is never a valid input key. Practitioner convention (per the `tea` CLI) uses `-beta`/`-rc` suffixes for prereleases (e.g. `v2.0.0-beta.1`), but Gitea does not enforce or infer this from the tag string. If the user names a tag that looks like a prerelease, set `is_pre_release: true` explicitly rather than assuming the flag is redundant with the name.
|
||||
- **Tag names are conventionally semver, `v`-prefixed** (`v1.2.0`, `v2.0.0-beta.1`), but this is a practitioner convention, not a Gitea constraint — don't reject or rewrite a caller-supplied tag name that doesn't follow it.
|
||||
- **`delete_release` takes the numeric `id`, never a `tag_name`; `delete_tag` takes the tag name, never an id.** Holding only a tag name, resolve the release id through `list_releases` or `get_release` first — a tag name passed to `delete_release` fails, and that failure is not evidence the release is already gone.
|
||||
- **Deleting a release never deletes its tag**, and the reverse direction is *unconfirmed* — verify with `list_releases`/`get_release` after `delete_tag`. Removing both takes two independent destructive calls.
|
||||
- **Set `is_draft`/`is_pre_release` explicitly on every `create_release`** — Gitea infers neither from a `-beta`/`-rc` tag name, so `v2.0.0-beta.1` publishes as a full release and becomes the repo's latest. `draft`/`prerelease` are output field names only; passing `draft` as an input key is silently ignored.
|
||||
- **`list_releases`/`list_tags` default `per_page` to 20**, where most other gitea-mcp list tools default to 30 — a caller assuming 30 under-counts pages.
|
||||
|
||||
## Dispatch table
|
||||
## Step 1 — Resolve owner and repo
|
||||
|
||||
`owner` and `repo` are required on every tool below. Extract them from the git remote, unless an orchestrating caller passed them in already:
|
||||
|
||||
```bash
|
||||
git remote get-url origin
|
||||
```
|
||||
|
||||
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
|
||||
|
||||
## Step 2 — Dispatch
|
||||
|
||||
| Action | Tool | Required params | Optional params |
|
||||
|---|---|---|---|
|
||||
@@ -40,13 +55,19 @@ metadata:
|
||||
| Create tag | `create_tag` | `owner`, `repo`, `tag_name` | `target`, `message` |
|
||||
| Delete tag | `delete_tag` | `owner`, `repo`, `tag_name` | — |
|
||||
|
||||
`target` (on `create_release`/`create_tag`) is a commitish — a branch name, existing tag, or commit SHA — the point the new tag is cut from. See `references/call-signatures.md` for response shapes.
|
||||
`target` (on `create_release`/`create_tag`) is a commitish — a branch name, existing tag, or commit SHA — the point the new tag is cut from.
|
||||
|
||||
## Workflow
|
||||
Pass a caller-supplied `tag_name` through verbatim — the API accepts any string; semver is convention, not constraint.
|
||||
|
||||
- [ ] **Creating a release:** Call `create_release` directly with `tag_name` + `target` + `title` — Gitea is assumed to create the underlying tag automatically if `tag_name` doesn't already exist (this is plausible behavior inferred from the API shape, not directly confirmed in the research docs), so a separate `create_tag` call is only needed when you want to tag a commit without wrapping it in a release yet. Verify the tag exists afterward if this matters to the caller. Set `is_pre_release`/`is_draft` explicitly per the Gotchas above; don't leave them to default inference.
|
||||
- [ ] **Deleting a release safely:** Resolve the numeric id first — call `list_releases` (paginate if needed, see Gotchas) or `get_release` if the id is already known, find the entry matching the target `tag_name`, then call `delete_release` with that `id`. Never pass `tag_name` to `delete_release`.
|
||||
- [ ] **Deleting a tag along with its release:** Delete the release first (frees the id lookup), then call `delete_tag` with the `tag_name` separately — confirm both are intended before proceeding, since each is an independent irreversible operation.
|
||||
- [ ] **Listing every page:** If the caller needs all releases or tags (not just the first page), loop `page: 1, 2, 3...` until a response has fewer than `per_page` entries.
|
||||
## Step 3 — Procedure for the scenario in hand
|
||||
|
||||
If exact response field shapes or additional conventions are needed, read `references/call-signatures.md` and `references/conventions.md`.
|
||||
These four are mutually exclusive — pick the one row the request lands on.
|
||||
|
||||
| Scenario | Procedure |
|
||||
|---|---|
|
||||
| Create a release | Call `create_release` with `tag_name`, `target`, `title`, and `is_draft`/`is_pre_release` set explicitly — never left to default. This surface carries no update or edit tool, so a wrong flag is repairable only by delete-and-recreate (`references/conventions.md`). A separate `create_tag` is only needed to tag a commit without wrapping it in a release — whether `create_release` creates a missing tag is unconfirmed, so verify with `get_tag`. |
|
||||
| Delete a release | Resolve the numeric `id` per the first Gotcha, confirm intent, then call `delete_release`. The tag survives. |
|
||||
| Delete a tag along with its release | Delete the release first, then call `delete_tag` — confirm both are intended before proceeding, since each is irreversible on its own. |
|
||||
| List every page | Loop `page: 1, 2, 3...` until a response returns fewer than `per_page` entries. Nothing here auto-paginates. |
|
||||
|
||||
If exact input params or response field shapes are needed, read `references/call-signatures.md`. If the caller raises semver tag naming, draft/prerelease semantics, release-notes sourcing, or how a release relates to its tag, read `references/conventions.md`.
|
||||
|
||||
@@ -11,11 +11,12 @@ Signatures and response shapes are derived from gitea-mcp source (`operation/*.g
|
||||
see `references/sources.md`) rather than copied from upstream API docs, which can drift from the
|
||||
deployed gitea-mcp version — but this is a source-code extraction, not a live MCP tool call.
|
||||
|
||||
Input parameter schemas for 3 of the 9 tools here — `create_release`, `delete_tag`, and
|
||||
`get_latest_release` — were additionally cross-checked live via `ToolSearch` against the deployed
|
||||
`mcp__gitea__*` tools in session 2026-07-05, and confirmed to match exactly (required/optional
|
||||
params and names). That check covered only input params for those 3 tools, not response shapes,
|
||||
and not the other 6 tools — treat the rest of this document as source-derived, not live-verified.
|
||||
Input parameter schemas for all 9 tools here were additionally cross-checked live via `ToolSearch`
|
||||
against the deployed `mcp__gitea__*` tools and confirmed to match exactly — required and optional
|
||||
params, names, and defaults. Last verified against gitea-mcp **v1.7.0**, as reported by
|
||||
`get_gitea_mcp_server_version`. That check covers input params only: the response shapes below
|
||||
remain source-derived, not live-verified, so re-verify them if a response reads differently than
|
||||
documented here.
|
||||
|
||||
`owner` and `repo` are required strings on every tool below and are omitted from the per-tool lists
|
||||
for brevity.
|
||||
@@ -32,7 +33,7 @@ for brevity.
|
||||
|
||||
**`get_latest_release`**
|
||||
- No parameters beyond `owner`/`repo`.
|
||||
- Returns a single release object for the most recently published release. It is assumed (by analogy with typical "latest release" semantics) that this excludes drafts and prereleases, but that exclusion is not directly confirmed by any of the research docs — verify with `list_releases` if the caller depends on this.
|
||||
- Returns a single release object for the most recently published release. The deployed tool's own description reads "the most recent published (non-draft) release" — it names drafts as excluded and is silent on prereleases, so whether prereleases are also excluded is *unconfirmed*; verify with `list_releases` if the caller depends on it. Either way a release created without `is_pre_release: true` is eligible, which is why that flag has to be set in the create call (see `conventions.md`).
|
||||
|
||||
**`create_release`**
|
||||
- Required: `tag_name` (string), `target` (string — branch, tag, or commit SHA to cut the tag from), `title` (string)
|
||||
@@ -44,7 +45,7 @@ for brevity.
|
||||
- Does not delete the underlying tag.
|
||||
|
||||
**Release object shape** (returned by list/get/create/latest):
|
||||
```
|
||||
```text
|
||||
id, tag_name, target, title, body, draft, prerelease, html_url, author, created_at, published_at
|
||||
```
|
||||
`author` is the creator's login. `body` holds the release notes.
|
||||
|
||||
@@ -12,11 +12,20 @@ additional tool schemas.
|
||||
|
||||
## Release wraps a tag, not the reverse
|
||||
|
||||
A release is a title, body (notes), and draft/prerelease flags layered on top of an existing or
|
||||
newly-created tag. The tag is the git-level object (a name pointing at a commit); the release is a
|
||||
Gitea-level metadata wrapper around it. This is why `delete_release` and `delete_tag` are separate
|
||||
calls with separate identifiers (numeric id vs. tag name) — removing the wrapper never implies
|
||||
removing the underlying pointer, and vice versa.
|
||||
A release is a title, body (notes), and draft/prerelease flags layered on top of a tag. The tag is
|
||||
the git-level object (a name pointing at a commit); the release is a Gitea-level metadata wrapper
|
||||
around it. This is why `delete_release` and `delete_tag` are separate calls with separate
|
||||
identifiers (numeric id vs. tag name) — removing the wrapper never implies removing the underlying
|
||||
pointer, and vice versa.
|
||||
|
||||
### Whether `create_release` creates a missing tag is unconfirmed
|
||||
|
||||
`create_release` takes both `tag_name` and `target` (a commitish), and that shape *suggests* Gitea
|
||||
creates the tag at `target` when `tag_name` does not already exist. That is inferred from the API
|
||||
shape, not confirmed by any source this skill was built from (`references/sources.md`) — so treat it
|
||||
as an assumption, not behaviour. When the caller depends on the tag existing, verify it afterward
|
||||
with `get_tag` (or `list_tags`) rather than reporting it as created. `create_tag` is the only call
|
||||
confirmed to create one.
|
||||
|
||||
## Semver tag naming
|
||||
|
||||
@@ -33,6 +42,13 @@ commonly used to signal a prerelease to humans. When a user asks to "cut a beta"
|
||||
release candidate," set `is_pre_release: true` explicitly in the same call rather than relying on
|
||||
the tag string to carry that meaning.
|
||||
|
||||
Getting this wrong is not cheaply repairable. The gitea-mcp release surface is `create_release`,
|
||||
`get_release`, `get_latest_release`, `list_releases` and `delete_release` — there is **no update or
|
||||
edit tool**, so a published release's flags cannot be corrected in place. The only remedy is
|
||||
`delete_release` plus a fresh `create_release`, a destructive round trip; meanwhile a beta published
|
||||
without `is_pre_release` is the release `get_latest_release` returns. Set the flags in the create
|
||||
call.
|
||||
|
||||
Note the input/output naming mismatch: the input param is `is_draft`, but the release object
|
||||
returned by the API uses `draft` (and `prerelease`) as the field names. `draft` is never a valid
|
||||
input key — passing `draft: true` to `create_release` is silently ignored rather than erroring.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## gitea-mcp-repo
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp
|
||||
- **Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags.
|
||||
- **Description:** Official gitea-mcp repository; operation/*.go source files documenting the MCP tools, their parameters, and CLI flags. Originally extracted at v1.3.0; the input parameter schemas in `references/call-signatures.md` were re-verified live via `ToolSearch` against the deployed server, **last verified at v1.7.0** as reported by `get_gitea_mcp_server_version`.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/api-reference.md (Releases and Tags section); plugins/gitea/docs/research/docs/gitea/troubleshooting.md (`delete_release` numeric-id gotcha, `per_page` defaults)
|
||||
|
||||
**Contributing files:**
|
||||
@@ -42,7 +42,6 @@
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/workflow-conventions.md (Release and tag conventions section)
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Gotchas — semver tag naming)
|
||||
- references/conventions.md
|
||||
- references/conventions.md (semver tag naming, release-notes sourcing)
|
||||
|
||||
**Status:** `extracted`
|
||||
|
||||
@@ -4,11 +4,11 @@ Human-facing entry point and router for the Gitea integration.
|
||||
|
||||
## What it does
|
||||
|
||||
This skill is the conversational front door to the Gitea suite — it replaces the old flat `/gitea` skill. On its own it never calls a Gitea MCP tool; it composes the six domain skills (`gitea-issues`, `gitea-labels-milestones`, `gitea-prs`, `gitea-branches`, `gitea-files`, `gitea-releases`). It handles the no-args status check-in (open issues + open PRs), resolves ambiguous issue-or-PR numbers before dispatching (issues and PRs share one number space), and points a user or agent to the right domain skill when it's unclear which one applies.
|
||||
This skill is the conversational front door to the Gitea suite — it replaces the old flat `/gitea` skill. On its own it never calls a Gitea MCP tool; it composes the six domain skills (`gitea-issues`, `gitea-labels-milestones`, `gitea-prs`, `gitea-branches`, `gitea-files`, `gitea-releases`). It handles the no-args status check-in (open issues + open PRs), which preserves the original flat `/gitea` skill's default behavior; resolves ambiguous issue-or-PR numbers before dispatching (issues and PRs share one number space); and points a user or agent to the right domain skill when it's unclear which one applies.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
```text
|
||||
/gitea-workflow
|
||||
```
|
||||
|
||||
@@ -18,5 +18,8 @@ Invoke with no arguments for a status check-in, with a bare number to resolve an
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents — Gotchas, status view, ambiguous-number resolution, and the domain-skill index |
|
||||
| `SKILL.md` | Skill instructions for agents — Gotchas, the dispatch table keyed on invocation shape, and the common report gate every branch ends in — each branch's own format lives with its reference file |
|
||||
| `references/status-checkin.md` | Loaded when the skill is invoked with no specific request — the two parallel open-issue/open-PR reads and the two-section report |
|
||||
| `references/number-resolution.md` | Loaded when the request carries a bare number that says neither "issue" nor "PR" — the `is_pull` resolution call and the hidden-permission-error 404 |
|
||||
| `references/skill-index.md` | Loaded when the request names a capability but not which skill owns it — the six-skill routing index |
|
||||
| `references/sources.md` | Research sources backing the routing/status guidance |
|
||||
|
||||
@@ -2,25 +2,18 @@
|
||||
name: gitea-workflow
|
||||
|
||||
description: >
|
||||
Use when a human wants a general or ambiguous Gitea status check or isn't sure which Gitea
|
||||
domain skill applies — a no-args check-in ("what's going on in the repo", "any updates?"),
|
||||
a bare-numbered reference that could be an issue or a PR ("what's the status of #42", "what's
|
||||
happening with #17"), or a request to discover which Gitea capability handles a task. This is
|
||||
the human-facing entry point and router for the Gitea integration — it replaces the old flat
|
||||
`/gitea` invocation (now `/gitea-workflow`) and composes the six domain skills
|
||||
(`gitea-issues`, `gitea-labels-milestones`, `gitea-prs`, `gitea-branches`, `gitea-files`,
|
||||
`gitea-releases`) rather than calling any Gitea MCP tool directly. Do not use this skill when
|
||||
the domain is already known and unambiguous — invoke the matching domain skill directly instead
|
||||
(e.g. "create an issue" → `gitea-issues`, "merge PR #10" → `gitea-prs`, "cut a release" →
|
||||
`gitea-releases`). Do not use for local git operations with no Gitea component (use
|
||||
`git-workflow`).
|
||||
Use when a human wants an ambiguous Gitea status check — a no-args check-in, a bare number
|
||||
asked *about* without saying issue or PR ("status of #42"), or a capability whose owning skill
|
||||
is unclear. Not an agent caller -> `gitea-orchestrate`. Not a stated action on a number
|
||||
("close #42") -> `gitea-issues`. Not an unambiguous PR request -> `gitea-prs`. Not local-only
|
||||
git -> `git-workflow`.
|
||||
|
||||
compatibility: Requires Gitea MCP server configured with a token; delegates all calls to the six
|
||||
domain skills, which in turn require write:issue and write:repository scopes at minimum.
|
||||
|
||||
metadata:
|
||||
category: integration
|
||||
version: "0.1.0"
|
||||
version: "0.1.3"
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
@@ -29,46 +22,22 @@ metadata:
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **This skill never calls a Gitea MCP tool itself.** Every read or write goes through one of the six domain skills. If a request needs a raw `mcp__gitea__*` call that no domain skill exposes, that's a gap in a domain skill, not something to patch here.
|
||||
- **Issues and PRs share one number space** — a bare number like `#42` could be either. Never guess from context clues alone; resolve it with a real call (see Step 2) before dispatching.
|
||||
- **A 404 on the resolution call doesn't necessarily mean the number doesn't exist.** Gitea hides permission errors as not-found (documented in `gitea-issues`' Gotchas). If resolution 404s unexpectedly, say so and suggest checking token scope rather than reporting "no such issue or PR."
|
||||
- **This skill never calls a Gitea MCP tool itself.** Every read and write goes through a domain skill. A needed `mcp__gitea__*` call that no domain skill exposes is a gap in that skill, not something to patch here.
|
||||
|
||||
## Step 1 — Default status view (no args)
|
||||
## Dispatch
|
||||
|
||||
When invoked with no specific request, give a status check-in:
|
||||
The invocation's shape selects exactly one branch.
|
||||
|
||||
1. Invoke `gitea-issues` to list open issues (`state: "open"`).
|
||||
2. Invoke `gitea-prs` to list open PRs (`state: "open"`).
|
||||
3. Run both in parallel — they're independent reads.
|
||||
4. Report as two sections, "Open Issues" and "Open Pull Requests", each as a compact list (number, title). This preserves the original flat `/gitea` skill's default behavior.
|
||||
| Invocation shape | Flow | Reference |
|
||||
|---|---|---|
|
||||
| No arguments, no specific request | Repo status check-in | `references/status-checkin.md` |
|
||||
| A bare number the user asks *about*, with neither "issue" nor "PR" said and no action stated | Resolve which domain the number belongs to | `references/number-resolution.md` |
|
||||
| A named capability whose owning skill is unclear | Route to the domain skill that owns it | `references/skill-index.md` |
|
||||
|
||||
## Step 2 — Resolve an ambiguous number
|
||||
A bare number carrying a stated action ("close #42", "merge #42", "label #42") is not row 2: that is a write, and row 2 only presents detail. Resolve the domain per `references/number-resolution.md`, then hand the action to `gitea-issues` or `gitea-prs` to perform.
|
||||
|
||||
When the user references a bare number without saying "issue" or "PR" (e.g. "what's going on with #42"):
|
||||
Read only the reference file matching the selected branch — each is self-contained for its concern.
|
||||
|
||||
1. Invoke `gitea-issues` to run `issue_read method: "get"` on that number.
|
||||
2. Check the response's `is_pull` field:
|
||||
- `true` → it's a PR. Invoke `gitea-prs` for full PR detail (status, diff, reviews as appropriate to the request) and present that instead.
|
||||
- `false` or absent → it's an issue. Present the issue detail already retrieved.
|
||||
3. If the resolution call 404s, don't conclude the number doesn't exist — report the 404 and suggest verifying token scope (`write:issue`) per `gitea-issues`' Gotchas, since permission errors are hidden as not-found in Gitea.
|
||||
## Report
|
||||
|
||||
Never dispatch to `gitea-issues` or `gitea-prs` based on guessing from phrasing alone ("that sounds like a bug" is not evidence) — always resolve first.
|
||||
|
||||
## Step 3 — Route explicit but domain-unclear requests
|
||||
|
||||
For requests that name a capability but not obviously which skill owns it, use this index:
|
||||
|
||||
| Skill | Covers |
|
||||
|---|---|
|
||||
| `gitea-issues` | List/read/create/update issues, comments, search across issues and PRs. Composes `gitea-labels-milestones` for label/milestone resolution. |
|
||||
| `gitea-labels-milestones` | Label and milestone CRUD, label inference from conversation context, resolving names/titles to the numeric IDs writes require. Cross-cutting — used by both `gitea-issues` and `gitea-prs`. |
|
||||
| `gitea-prs` | List/read/create/update/merge PRs, code reviews. Composes `gitea-labels-milestones` the same way `gitea-issues` does. |
|
||||
| `gitea-branches` | Branch list/create/delete, plus commit history (list commits, get a single commit by SHA). |
|
||||
| `gitea-files` | Read/write/delete individual files, list a directory, walk the full repo tree. |
|
||||
| `gitea-releases` | Release and tag CRUD — draft/prerelease flags, release notes, semver tags. |
|
||||
|
||||
If a request clearly names one of these (e.g. "create a milestone" → `gitea-labels-milestones`, "read this file from the repo" → `gitea-files`), invoke that skill directly rather than routing through here. Use this table only when the user or an upstream agent is unsure which skill applies.
|
||||
|
||||
## Step 4 — Report
|
||||
|
||||
Present results in plain language. For the status view, two labeled sections. For a resolved ambiguous number, say which domain it turned out to be before showing detail ("That's a pull request:" / "That's an issue:"). For routing, name the skill and hand off — don't duplicate its output format, let it report.
|
||||
Every branch ends here. Present results in plain language; the branch's reference file carries its format.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
topic: number-resolution
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
---
|
||||
|
||||
# Resolving a bare issue-or-PR number
|
||||
|
||||
The user has referenced a bare number without saying "issue" or "PR" (e.g. "what's going on with #42"). Resolve it with a real call — never dispatch to `gitea-issues` or `gitea-prs` by guessing from phrasing alone, because "that sounds like a bug" is not evidence and the two domains share one number space.
|
||||
|
||||
1. Invoke `gitea-issues` to run `issue_read method: "get"` on that number.
|
||||
2. Check the response's `is_pull` field:
|
||||
- `true` → it's a PR. Invoke `gitea-prs` for full PR detail (status, diff, reviews as appropriate to the request) and present that instead.
|
||||
- `false` or absent → it's an issue. Present the issue detail already retrieved.
|
||||
3. If the resolution call 404s, don't conclude the number doesn't exist. Gitea hides permission errors as not-found (documented in `gitea-issues`' Gotchas), so report the 404 and suggest verifying the token carries `write:issue` rather than reporting "no such issue or PR."
|
||||
|
||||
If the user stated an action on the number rather than asking about it, resolution is only step one: hand the action, with the resolved domain, to `gitea-issues` or `gitea-prs` to carry out. Presenting detail is not a substitute for performing the write.
|
||||
|
||||
Then report per `SKILL.md`'s Report section, saying which domain the number turned out to be before showing detail ("That's a pull request:" / "That's an issue:") — otherwise the user cannot tell the resolution happened.
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
topic: skill-index
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
---
|
||||
|
||||
# Domain-skill index
|
||||
|
||||
The request names a capability but not obviously which skill owns it. Find the owner here, then invoke it:
|
||||
|
||||
| Skill | Covers |
|
||||
|---|---|
|
||||
| `gitea-issues` | List/read/create/update issues, comments, search across issues and PRs. Composes `gitea-labels-milestones` for label/milestone resolution. |
|
||||
| `gitea-labels-milestones` | Label and milestone CRUD, label inference from conversation context, resolving names/titles to the numeric IDs writes require. Cross-cutting — used by both `gitea-issues` and `gitea-prs`. |
|
||||
| `gitea-prs` | List/read/create/update/merge PRs, code reviews. Composes `gitea-labels-milestones` the same way `gitea-issues` does. |
|
||||
| `gitea-branches` | Branch list/create/rename/delete, plus commit history (list commits, get a single commit by SHA). |
|
||||
| `gitea-files` | Read/write/delete individual files, list a directory, walk the full repo tree. |
|
||||
| `gitea-releases` | Release and tag CRUD — draft/prerelease flags, release notes, semver tags. |
|
||||
|
||||
A request that already names its own owner (e.g. "create a milestone" → `gitea-labels-milestones`, "read this file from the repo" → `gitea-files`) never needed this index — invoke that skill directly rather than routing through here.
|
||||
|
||||
Then report per `SKILL.md`'s Report section: name the skill and hand off — don't duplicate its output format, let it report.
|
||||
@@ -3,9 +3,9 @@
|
||||
## gitea-mcp-repo
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp
|
||||
- **Description:** Official gitea-mcp repository (v1.3.0) — `operation/*.go` source files documenting all 55 MCP tools. This skill's status view relies on `list_issues`/`list_pull_requests` semantics (via `gitea-issues`/`gitea-prs`), and its ambiguous-number resolution relies on `issue_read`'s `is_pull` field, both verified against this source at authoring time.
|
||||
- **Description:** Official gitea-mcp repository (v1.3.0) — `operation/*.go` source files documenting all 55 MCP tools. This skill's status view relies on `list_issues`/`list_pull_requests` semantics (via `gitea-issues`/`gitea-prs`), its ambiguous-number resolution relies on `issue_read`'s `is_pull` field, and its domain-skill index groups that tool surface by owning skill — all verified against this source at authoring time.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Contributing files:** references/status-checkin.md, references/number-resolution.md, references/skill-index.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## gitea-mcp-slim-go
|
||||
@@ -13,7 +13,7 @@
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/issue/slim.go
|
||||
- **Description:** Slim response shape structs from gitea-mcp source — confirms `is_pull` is present on a single-item `issue_read` response, the field this skill's resolution step depends on to distinguish an issue from a PR sharing the same number.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Contributing files:** references/number-resolution.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-websites-gitea
|
||||
@@ -21,7 +21,7 @@
|
||||
- **URL:** context7:/websites/gitea
|
||||
- **Description:** Official Gitea docs mirror on Context7 — confirms issues and pull requests share a single per-repository number sequence, and that Gitea returns 404 for permission failures rather than a distinct 403, both facts this skill's resolution and error-handling steps depend on.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Contributing files:** references/number-resolution.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
topic: status-checkin
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
---
|
||||
|
||||
# Status check-in (invoked with no request)
|
||||
|
||||
Give a repo status check-in:
|
||||
|
||||
1. Invoke `gitea-issues` to list open issues (`state: "open"`).
|
||||
2. Invoke `gitea-prs` to list open PRs (`state: "open"`). Run this alongside step 1 — the two are independent reads, so serialising them only adds latency.
|
||||
3. Report as two sections, "Open Issues" and "Open Pull Requests", each a compact list of number and title.
|
||||
|
||||
Then report per `SKILL.md`'s Report section.
|
||||
Reference in New Issue
Block a user